Merge pull request #342 from jonathanhefner/type-checked-quickstart
Rewrite Quickstart guide with type-checked code examples
This commit is contained in:
commit
1b537192ca
48 changed files with 1076 additions and 552 deletions
|
|
@ -2,5 +2,6 @@ examples/basic-host/**/*.ts
|
|||
examples/basic-host/**/*.tsx
|
||||
examples/basic-server-*/**/*.ts
|
||||
examples/basic-server-*/**/*.tsx
|
||||
examples/quickstart/**/*.ts
|
||||
**/vendor/**
|
||||
SKILL.md
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ View (App) <--PostMessageTransport--> Host (AppBridge) <--MCP Client--> MCP Serv
|
|||
|
||||
## Documentation
|
||||
|
||||
JSDoc `@example` tags should pull type-checked code from companion `.examples.ts` files (e.g., `app.ts` → `app.examples.ts`). Use ` ```ts source="./file.examples.ts#regionName" ` fences referencing `//#region regionName` blocks, then run `npm run sync:snippets`. Region names follow `exportedName_variant` or `ClassName_methodName_variant` pattern (e.g., `useApp_basicUsage`, `App_hostCapabilities_checkAfterConnection`).
|
||||
JSDoc `@example` tags should pull type-checked code from companion `.examples.ts` files (e.g., `app.ts` → `app.examples.ts`). Use ` ```ts source="./file.examples.ts#regionName" ` fences referencing `//#region regionName` blocks; region names follow `exportedName_variant` or `ClassName_methodName_variant` pattern (e.g., `useApp_basicUsage`, `App_hostCapabilities_checkAfterConnection`). For whole-file inclusion (any file type), omit the `#regionName`. Run `npm run sync:snippets` to sync.
|
||||
|
||||
Standalone docs in `docs/` (listed in `typedoc.config.mjs` `projectDocuments`) can also have type-checked companion `.ts`/`.tsx` files using the same pattern.
|
||||
|
||||
|
|
|
|||
BIN
docs/quickstart-success.png
Normal file
BIN
docs/quickstart-success.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 58 KiB |
|
|
@ -4,200 +4,351 @@ title: Quickstart
|
|||
|
||||
# Build Your First MCP App
|
||||
|
||||
This tutorial walks you through building an MCP App—a tool with an interactive UI that renders inside MCP hosts like Claude Desktop.
|
||||
This tutorial walks you through building an MCP App—a tool with an interactive **View** (a UI that renders inside an iframe) that displays in MCP hosts like Claude Desktop.
|
||||
|
||||
## What You'll Build
|
||||
|
||||
A simple app that fetches the current server time and displays it in a clickable UI. You'll learn the core pattern: **MCP Apps = Tool + UI Resource**.
|
||||
A simple app that fetches the current server time and displays it in an interactive View. You'll learn the core pattern: **MCP Apps = Tool + UI Resource**.
|
||||
|
||||
> [!NOTE]
|
||||
> The complete example is available at [`examples/basic-server-vanillajs`](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-vanillajs).
|
||||
> The complete example is available at [`examples/quickstart`](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/quickstart).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Familiarity with MCP concepts, especially [Tools](https://modelcontextprotocol.io/docs/learn/server-concepts#tools) and [Resources](https://modelcontextprotocol.io/docs/learn/server-concepts#resources)
|
||||
- Familiarity with the [MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk)
|
||||
- Node.js 18+
|
||||
This tutorial assumes you've built an MCP server before and are comfortable with [Tools](https://modelcontextprotocol.io/docs/learn/server-concepts#tools) and [Resources](https://modelcontextprotocol.io/docs/learn/server-concepts#resources). If not, the [official MCP quickstart](https://modelcontextprotocol.io/docs/develop/build-server) is a good place to start.
|
||||
|
||||
> [!TIP]
|
||||
> New to building MCP servers? Start with the [official MCP quickstart guide](https://modelcontextprotocol.io/docs/develop/build-server) to learn the core concepts first.
|
||||
We'll use the [MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk) to build the server.
|
||||
|
||||
## 1. Project Setup
|
||||
You'll also need Node.js 18+.
|
||||
|
||||
Create a new directory and initialize:
|
||||
## 1. Set up the project
|
||||
|
||||
We'll set up a minimal TypeScript project with Vite for bundling.
|
||||
|
||||
Start by creating a project directory:
|
||||
|
||||
```bash
|
||||
mkdir my-mcp-app && cd my-mcp-app
|
||||
npm init -y
|
||||
```
|
||||
|
||||
Install dependencies:
|
||||
Install the dependencies you'll need:
|
||||
|
||||
```bash
|
||||
npm install @modelcontextprotocol/ext-apps @modelcontextprotocol/sdk
|
||||
npm install -D typescript vite vite-plugin-singlefile express cors @types/express @types/cors tsx
|
||||
npm init -y
|
||||
npm install @modelcontextprotocol/ext-apps @modelcontextprotocol/sdk express cors
|
||||
npm install -D typescript vite vite-plugin-singlefile @types/express @types/cors @types/node tsx concurrently cross-env
|
||||
```
|
||||
|
||||
Create `tsconfig.json`:
|
||||
Configure your [`package.json`](https://github.com/modelcontextprotocol/ext-apps/blob/main/examples/quickstart/package.json):
|
||||
|
||||
```json
|
||||
```bash
|
||||
npm pkg set type=module
|
||||
npm pkg set scripts.build="tsc --noEmit && tsc -p tsconfig.server.json && cross-env INPUT=mcp-app.html vite build"
|
||||
npm pkg set scripts.start="concurrently 'cross-env NODE_ENV=development INPUT=mcp-app.html vite build --watch' 'tsx watch main.ts'"
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Create <a href="https://github.com/modelcontextprotocol/ext-apps/blob/main/examples/quickstart/tsconfig.json"><code>tsconfig.json</code></a>:</summary>
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
```json source="../examples/quickstart/tsconfig.json"
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"target": "ESNext",
|
||||
"lib": ["ESNext", "DOM", "DOM.Iterable"],
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"noEmit": true,
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"outDir": "dist"
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": ["*.ts", "src/**/*.ts"]
|
||||
"include": ["src", "server.ts", "main.ts"]
|
||||
}
|
||||
```
|
||||
|
||||
Create `vite.config.ts` — this bundles your UI into a single HTML file:
|
||||
</details>
|
||||
|
||||
```typescript
|
||||
<details>
|
||||
<summary>Create <a href="https://github.com/modelcontextprotocol/ext-apps/blob/main/examples/quickstart/tsconfig.server.json"><code>tsconfig.server.json</code></a> — for compiling server-side code:</summary>
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
```json source="../examples/quickstart/tsconfig.server.json"
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"lib": ["ES2022"],
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"declaration": true,
|
||||
"emitDeclarationOnly": true,
|
||||
"outDir": "./dist",
|
||||
"rootDir": ".",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": ["server.ts", "main.ts"]
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Create <a href="https://github.com/modelcontextprotocol/ext-apps/blob/main/examples/quickstart/vite.config.ts"><code>vite.config.ts</code></a> — bundles UI into a single HTML file:</summary>
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
```ts source="../examples/quickstart/vite.config.ts"
|
||||
import { defineConfig } from "vite";
|
||||
import { viteSingleFile } from "vite-plugin-singlefile";
|
||||
|
||||
const INPUT = process.env.INPUT;
|
||||
if (!INPUT) {
|
||||
throw new Error("INPUT environment variable is not set");
|
||||
}
|
||||
|
||||
const isDevelopment = process.env.NODE_ENV === "development";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [viteSingleFile()],
|
||||
build: {
|
||||
outDir: "dist",
|
||||
sourcemap: isDevelopment ? "inline" : undefined,
|
||||
cssMinify: !isDevelopment,
|
||||
minify: !isDevelopment,
|
||||
|
||||
rollupOptions: {
|
||||
input: process.env.INPUT,
|
||||
input: INPUT,
|
||||
},
|
||||
outDir: "dist",
|
||||
emptyOutDir: false,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
Add to your `package.json`:
|
||||
</details>
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "INPUT=mcp-app.html vite build",
|
||||
"serve": "npx tsx server.ts"
|
||||
}
|
||||
}
|
||||
Your `my-mcp-app` directory should now contain:
|
||||
|
||||
```
|
||||
my-mcp-app/
|
||||
├── package.json
|
||||
├── tsconfig.json
|
||||
├── tsconfig.server.json
|
||||
└── vite.config.ts
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> **Full files:** [`package.json`](https://github.com/modelcontextprotocol/ext-apps/blob/main/examples/basic-server-vanillajs/package.json), [`tsconfig.json`](https://github.com/modelcontextprotocol/ext-apps/blob/main/examples/basic-server-vanillajs/tsconfig.json), [`vite.config.ts`](https://github.com/modelcontextprotocol/ext-apps/blob/main/examples/basic-server-vanillajs/vite.config.ts)
|
||||
With the project scaffolded, let's write the server code.
|
||||
|
||||
## 2. Create the Server
|
||||
## 2. Register the tool and UI resource
|
||||
|
||||
MCP Apps use a **two-part registration**:
|
||||
|
||||
1. A **tool** that the LLM/host calls
|
||||
2. A **resource** that serves the UI HTML
|
||||
2. A **resource** that contains the View HTML
|
||||
|
||||
The tool's `_meta` field links them together.
|
||||
The tool's `_meta` field links them together via the resource's URI. When an MCP Apps-capable host calls the tool, it will also read the resource and render the View.
|
||||
|
||||
Create `server.ts`:
|
||||
Create [`server.ts`](https://github.com/modelcontextprotocol/ext-apps/blob/main/examples/quickstart/server.ts), which registers the tool and its UI resource:
|
||||
|
||||
```typescript
|
||||
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
||||
<!-- prettier-ignore -->
|
||||
```ts source="../examples/quickstart/server.ts"
|
||||
import {
|
||||
registerAppTool,
|
||||
registerAppResource,
|
||||
registerAppTool,
|
||||
RESOURCE_MIME_TYPE,
|
||||
} from "@modelcontextprotocol/ext-apps/server";
|
||||
import cors from "cors";
|
||||
import express from "express";
|
||||
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
|
||||
const server = new McpServer({
|
||||
name: "My MCP App Server",
|
||||
version: "1.0.0",
|
||||
});
|
||||
const DIST_DIR = path.join(import.meta.dirname, "dist");
|
||||
|
||||
// Two-part registration: tool + resource, tied together by the resource URI.
|
||||
const resourceUri = "ui://get-time/mcp-app.html";
|
||||
|
||||
// Register a tool with UI metadata. When the host calls this tool, it reads
|
||||
// `_meta.ui.resourceUri` to know which resource to fetch and render as an
|
||||
// interactive UI.
|
||||
registerAppTool(
|
||||
server,
|
||||
"get-time",
|
||||
{
|
||||
title: "Get Time",
|
||||
description: "Returns the current server time.",
|
||||
inputSchema: {},
|
||||
_meta: { ui: { resourceUri } },
|
||||
},
|
||||
async () => {
|
||||
const time = new Date().toISOString();
|
||||
return {
|
||||
content: [{ type: "text", text: time }],
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
// Register the resource, which returns the bundled HTML/JavaScript for the UI.
|
||||
registerAppResource(
|
||||
server,
|
||||
resourceUri,
|
||||
resourceUri,
|
||||
{ mimeType: RESOURCE_MIME_TYPE },
|
||||
async () => {
|
||||
const html = await fs.readFile(
|
||||
path.join(import.meta.dirname, "dist", "mcp-app.html"),
|
||||
"utf-8",
|
||||
);
|
||||
return {
|
||||
contents: [
|
||||
{ uri: resourceUri, mimeType: RESOURCE_MIME_TYPE, text: html },
|
||||
],
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
// Start an Express server that exposes the MCP endpoint.
|
||||
const expressApp = express();
|
||||
expressApp.use(cors());
|
||||
expressApp.use(express.json());
|
||||
|
||||
expressApp.post("/mcp", async (req, res) => {
|
||||
const transport = new StreamableHTTPServerTransport({
|
||||
sessionIdGenerator: undefined,
|
||||
enableJsonResponse: true,
|
||||
/**
|
||||
* Creates a new MCP server instance with tools and resources registered.
|
||||
*/
|
||||
export function createServer(): McpServer {
|
||||
const server = new McpServer({
|
||||
name: "Quickstart MCP App Server",
|
||||
version: "1.0.0",
|
||||
});
|
||||
res.on("close", () => transport.close());
|
||||
await server.connect(transport);
|
||||
await transport.handleRequest(req, res, req.body);
|
||||
});
|
||||
|
||||
expressApp.listen(3001, (err) => {
|
||||
if (err) {
|
||||
console.error("Error starting server:", err);
|
||||
process.exit(1);
|
||||
// Two-part registration: tool + resource, tied together by the resource URI.
|
||||
const resourceUri = "ui://get-time/mcp-app.html";
|
||||
|
||||
// Register a tool with UI metadata. When the host calls this tool, it reads
|
||||
// `_meta.ui.resourceUri` to know which resource to fetch and render as an
|
||||
// interactive UI.
|
||||
registerAppTool(
|
||||
server,
|
||||
"get-time",
|
||||
{
|
||||
title: "Get Time",
|
||||
description: "Returns the current server time.",
|
||||
inputSchema: {},
|
||||
_meta: { ui: { resourceUri } }, // Links this tool to its UI resource
|
||||
},
|
||||
async () => {
|
||||
const time = new Date().toISOString();
|
||||
return { content: [{ type: "text", text: time }] };
|
||||
},
|
||||
);
|
||||
|
||||
// Register the resource, which returns the bundled HTML/JavaScript for the UI.
|
||||
registerAppResource(
|
||||
server,
|
||||
resourceUri,
|
||||
resourceUri,
|
||||
{ mimeType: RESOURCE_MIME_TYPE },
|
||||
async () => {
|
||||
const html = await fs.readFile(path.join(DIST_DIR, "mcp-app.html"), "utf-8");
|
||||
|
||||
return {
|
||||
contents: [
|
||||
{ uri: resourceUri, mimeType: RESOURCE_MIME_TYPE, text: html },
|
||||
],
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
return server;
|
||||
}
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Create <a href="https://github.com/modelcontextprotocol/ext-apps/blob/main/examples/quickstart/main.ts"><code>main.ts</code></a> — the entry point that starts the server:</summary>
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
```ts source="../examples/quickstart/main.ts"
|
||||
import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
|
||||
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
||||
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
||||
import cors from "cors";
|
||||
import type { Request, Response } from "express";
|
||||
import { createServer } from "./server.js";
|
||||
|
||||
/**
|
||||
* Starts an MCP server with Streamable HTTP transport in stateless mode.
|
||||
*
|
||||
* @param createServer - Factory function that creates a new McpServer instance per request.
|
||||
*/
|
||||
export async function startStreamableHTTPServer(
|
||||
createServer: () => McpServer,
|
||||
): Promise<void> {
|
||||
const port = parseInt(process.env.PORT ?? "3001", 10);
|
||||
|
||||
const app = createMcpExpressApp({ host: "0.0.0.0" });
|
||||
app.use(cors());
|
||||
|
||||
app.all("/mcp", async (req: Request, res: Response) => {
|
||||
const server = createServer();
|
||||
const transport = new StreamableHTTPServerTransport({
|
||||
sessionIdGenerator: undefined,
|
||||
});
|
||||
|
||||
res.on("close", () => {
|
||||
transport.close().catch(() => {});
|
||||
server.close().catch(() => {});
|
||||
});
|
||||
|
||||
try {
|
||||
await server.connect(transport);
|
||||
await transport.handleRequest(req, res, req.body);
|
||||
} catch (error) {
|
||||
console.error("MCP error:", error);
|
||||
if (!res.headersSent) {
|
||||
res.status(500).json({
|
||||
jsonrpc: "2.0",
|
||||
error: { code: -32603, message: "Internal server error" },
|
||||
id: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const httpServer = app.listen(port, (err) => {
|
||||
if (err) {
|
||||
console.error("Failed to start server:", err);
|
||||
process.exit(1);
|
||||
}
|
||||
console.log(`MCP server listening on http://localhost:${port}/mcp`);
|
||||
});
|
||||
|
||||
const shutdown = () => {
|
||||
console.log("\nShutting down...");
|
||||
httpServer.close(() => process.exit(0));
|
||||
};
|
||||
|
||||
process.on("SIGINT", shutdown);
|
||||
process.on("SIGTERM", shutdown);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an MCP server with stdio transport.
|
||||
*
|
||||
* @param createServer - Factory function that creates a new McpServer instance.
|
||||
*/
|
||||
export async function startStdioServer(
|
||||
createServer: () => McpServer,
|
||||
): Promise<void> {
|
||||
await createServer().connect(new StdioServerTransport());
|
||||
}
|
||||
|
||||
async function main() {
|
||||
if (process.argv.includes("--stdio")) {
|
||||
await startStdioServer(createServer);
|
||||
} else {
|
||||
await startStreamableHTTPServer(createServer);
|
||||
}
|
||||
console.log("Server listening on http://localhost:3001/mcp");
|
||||
}
|
||||
|
||||
main().catch((e) => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
});
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> **Full file:** [`server.ts`](https://github.com/modelcontextprotocol/ext-apps/blob/main/examples/basic-server-vanillajs/server.ts)
|
||||
</details>
|
||||
|
||||
Then, verify your server compiles:
|
||||
Your `my-mcp-app` directory should now contain:
|
||||
|
||||
```
|
||||
my-mcp-app/
|
||||
├── main.ts
|
||||
├── package.json
|
||||
├── server.ts
|
||||
├── tsconfig.json
|
||||
├── tsconfig.server.json
|
||||
└── vite.config.ts
|
||||
```
|
||||
|
||||
Let's verify everything compiles:
|
||||
|
||||
```bash
|
||||
npx tsc --noEmit
|
||||
```
|
||||
|
||||
No output means success. If you see errors, check for typos in `server.ts`.
|
||||
No output means success! If you see errors, check for typos in `server.ts` or `main.ts`.
|
||||
|
||||
## 3. Build the UI
|
||||
The server can return the current time when the tool is called. Now let's build the UI to display it.
|
||||
|
||||
Create `mcp-app.html`:
|
||||
## 3. Build the View
|
||||
|
||||
```html
|
||||
The View consists of an HTML page and a script that connects to the host.
|
||||
|
||||
Create [`mcp-app.html`](https://github.com/modelcontextprotocol/ext-apps/blob/main/examples/quickstart/mcp-app.html), the HTML for your View:
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
```html source="../examples/quickstart/mcp-app.html"
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
|
@ -214,9 +365,10 @@ Create `mcp-app.html`:
|
|||
</html>
|
||||
```
|
||||
|
||||
Create `src/mcp-app.ts`:
|
||||
Create [`src/mcp-app.ts`](https://github.com/modelcontextprotocol/ext-apps/blob/main/examples/quickstart/src/mcp-app.ts), which connects to the host and handles user interactions:
|
||||
|
||||
```typescript
|
||||
<!-- prettier-ignore -->
|
||||
```ts source="../examples/quickstart/src/mcp-app.ts"
|
||||
import { App } from "@modelcontextprotocol/ext-apps";
|
||||
|
||||
// Get element references
|
||||
|
|
@ -226,7 +378,8 @@ const getTimeBtn = document.getElementById("get-time-btn")!;
|
|||
// Create app instance
|
||||
const app = new App({ name: "Get Time App", version: "1.0.0" });
|
||||
|
||||
// Register handlers BEFORE connecting
|
||||
// Handle tool results from the server. Set before `app.connect()` to avoid
|
||||
// missing the initial tool result.
|
||||
app.ontoolresult = (result) => {
|
||||
const time = result.content?.find((c) => c.type === "text")?.text;
|
||||
serverTimeEl.textContent = time ?? "[ERROR]";
|
||||
|
|
@ -234,6 +387,7 @@ app.ontoolresult = (result) => {
|
|||
|
||||
// Wire up button click
|
||||
getTimeBtn.addEventListener("click", async () => {
|
||||
// `app.callServerTool()` lets the UI request fresh data from the server
|
||||
const result = await app.callServerTool({ name: "get-time", arguments: {} });
|
||||
const time = result.content?.find((c) => c.type === "text")?.text;
|
||||
serverTimeEl.textContent = time ?? "[ERROR]";
|
||||
|
|
@ -243,30 +397,50 @@ getTimeBtn.addEventListener("click", async () => {
|
|||
app.connect();
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> **Full files:** [`mcp-app.html`](https://github.com/modelcontextprotocol/ext-apps/blob/main/examples/basic-server-vanillajs/mcp-app.html), [`src/mcp-app.ts`](https://github.com/modelcontextprotocol/ext-apps/blob/main/examples/basic-server-vanillajs/src/mcp-app.ts)
|
||||
Your `my-mcp-app` directory should now contain:
|
||||
|
||||
Build the UI:
|
||||
```
|
||||
my-mcp-app/
|
||||
├── main.ts
|
||||
├── mcp-app.html
|
||||
├── package.json
|
||||
├── server.ts
|
||||
├── src/
|
||||
│ └── mcp-app.ts
|
||||
├── tsconfig.json
|
||||
├── tsconfig.server.json
|
||||
└── vite.config.ts
|
||||
```
|
||||
|
||||
Now let's build the bundled View:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
This produces `dist/mcp-app.html` which contains your bundled app:
|
||||
This produces `dist/mcp-app.html`:
|
||||
|
||||
```console
|
||||
$ ls dist/mcp-app.html
|
||||
dist/mcp-app.html
|
||||
$ ls dist/
|
||||
mcp-app.html
|
||||
```
|
||||
|
||||
## 4. Test It
|
||||
The View will connect to the host, receive the tool result, and display it. Let's see it in action!
|
||||
|
||||
You'll need two terminals.
|
||||
## 4. See it in action
|
||||
|
||||
**Terminal 1** — Build and start your server:
|
||||
You'll need two terminal windows.
|
||||
|
||||
**Terminal 1** — Start your server (with watch mode):
|
||||
|
||||
```bash
|
||||
npm run build && npm run serve
|
||||
npm start
|
||||
```
|
||||
|
||||
You should see:
|
||||
|
||||
```console
|
||||
MCP server listening on http://localhost:3001/mcp
|
||||
```
|
||||
|
||||
**Terminal 2** — Run the test host (from the [ext-apps repo](https://github.com/modelcontextprotocol/ext-apps)):
|
||||
|
|
@ -275,18 +449,23 @@ npm run build && npm run serve
|
|||
git clone https://github.com/modelcontextprotocol/ext-apps.git
|
||||
cd ext-apps/examples/basic-host
|
||||
npm install
|
||||
npm run start
|
||||
npm start
|
||||
```
|
||||
|
||||
Open http://localhost:8080 in your browser:
|
||||
|
||||
1. Select **get-time** from the "Tool Name" dropdown
|
||||
2. Click **Call Tool**
|
||||
3. Your UI renders in the sandbox below
|
||||
3. Your View renders in the sandbox below
|
||||
4. Click **Get Server Time** — the current time appears!
|
||||
|
||||

|
||||
|
||||
You've built your first MCP App!
|
||||
|
||||
## Next Steps
|
||||
|
||||
- **Host communication**: Add [`sendMessage()`](https://modelcontextprotocol.github.io/ext-apps/api/classes/app.App.html#sendmessage), [`sendLog()`](https://modelcontextprotocol.github.io/ext-apps/api/classes/app.App.html#sendlog), and [`sendOpenLink()`](https://modelcontextprotocol.github.io/ext-apps/api/classes/app.App.html#sendopenlink) to interact with the host — see [`src/mcp-app.ts`](https://github.com/modelcontextprotocol/ext-apps/blob/main/examples/basic-server-vanillajs/src/mcp-app.ts)
|
||||
- **Continue learning**: The [`basic-server-vanillajs`](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-vanillajs) example builds on this quickstart with host communication, theming, and lifecycle handlers
|
||||
- **React version**: Compare with [`basic-server-react`](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-react) for a React-based UI
|
||||
- **Other frameworks**: See also [Vue](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-vue), [Svelte](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-svelte), [Preact](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-preact), and [Solid](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-server-solid) examples
|
||||
- **API reference**: See the full [API documentation](https://modelcontextprotocol.github.io/ext-apps/api/)
|
||||
|
|
|
|||
|
|
@ -145,11 +145,11 @@
|
|||
}
|
||||
|
||||
.appIframePanel {
|
||||
min-height: 200px;
|
||||
min-height: 100px;
|
||||
|
||||
iframe {
|
||||
width: 100%;
|
||||
height: 600px;
|
||||
height: 400px;
|
||||
box-sizing: border-box;
|
||||
border: 3px dashed var(--color-border);
|
||||
border-radius: 4px;
|
||||
|
|
|
|||
|
|
@ -4,10 +4,6 @@
|
|||
* Or: node dist/index.js [--stdio]
|
||||
*/
|
||||
|
||||
/**
|
||||
* Shared utilities for running MCP servers with Streamable HTTP transport.
|
||||
*/
|
||||
|
||||
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
||||
import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
|
||||
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||
|
|
@ -16,22 +12,15 @@ import cors from "cors";
|
|||
import type { Request, Response } from "express";
|
||||
import { createServer } from "./server.js";
|
||||
|
||||
export interface ServerOptions {
|
||||
port: number;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an MCP server with Streamable HTTP transport in stateless mode.
|
||||
*
|
||||
* @param createServer - Factory function that creates a new McpServer instance per request.
|
||||
* @param options - Server configuration options.
|
||||
*/
|
||||
export async function startServer(
|
||||
export async function startStreamableHTTPServer(
|
||||
createServer: () => McpServer,
|
||||
options: ServerOptions,
|
||||
): Promise<void> {
|
||||
const { port, name = "MCP Server" } = options;
|
||||
const port = parseInt(process.env.PORT ?? "3001", 10);
|
||||
|
||||
const app = createMcpExpressApp({ host: "0.0.0.0" });
|
||||
app.use(cors());
|
||||
|
|
@ -67,7 +56,7 @@ export async function startServer(
|
|||
console.error("Failed to start server:", err);
|
||||
process.exit(1);
|
||||
}
|
||||
console.log(`${name} listening on http://localhost:${port}/mcp`);
|
||||
console.log(`MCP server listening on http://localhost:${port}/mcp`);
|
||||
});
|
||||
|
||||
const shutdown = () => {
|
||||
|
|
@ -79,12 +68,22 @@ export async function startServer(
|
|||
process.on("SIGTERM", shutdown);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an MCP server with stdio transport.
|
||||
*
|
||||
* @param createServer - Factory function that creates a new McpServer instance.
|
||||
*/
|
||||
export async function startStdioServer(
|
||||
createServer: () => McpServer,
|
||||
): Promise<void> {
|
||||
await createServer().connect(new StdioServerTransport());
|
||||
}
|
||||
|
||||
async function main() {
|
||||
if (process.argv.includes("--stdio")) {
|
||||
await createServer().connect(new StdioServerTransport());
|
||||
await startStdioServer(createServer);
|
||||
} else {
|
||||
const port = parseInt(process.env.PORT ?? "3001", 10);
|
||||
await startServer(createServer, { port, name: "Basic MCP App Server (Preact)" });
|
||||
await startStreamableHTTPServer(createServer);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ export function createServer(): McpServer {
|
|||
title: "Get Time",
|
||||
description: "Returns the current server time as an ISO 8601 string.",
|
||||
inputSchema: {},
|
||||
_meta: { ui: { resourceUri } },
|
||||
_meta: { ui: { resourceUri } }, // Links this tool to its UI resource
|
||||
},
|
||||
async (): Promise<CallToolResult> => {
|
||||
const time = new Date().toISOString();
|
||||
|
|
|
|||
|
|
@ -12,22 +12,15 @@ import cors from "cors";
|
|||
import type { Request, Response } from "express";
|
||||
import { createServer } from "./server.js";
|
||||
|
||||
export interface ServerOptions {
|
||||
port: number;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an MCP server with Streamable HTTP transport in stateless mode.
|
||||
*
|
||||
* @param createServer - Factory function that creates a new McpServer instance per request.
|
||||
* @param options - Server configuration options.
|
||||
*/
|
||||
export async function startServer(
|
||||
export async function startStreamableHTTPServer(
|
||||
createServer: () => McpServer,
|
||||
options: ServerOptions,
|
||||
): Promise<void> {
|
||||
const { port, name = "MCP Server" } = options;
|
||||
const port = parseInt(process.env.PORT ?? "3001", 10);
|
||||
|
||||
const app = createMcpExpressApp({ host: "0.0.0.0" });
|
||||
app.use(cors());
|
||||
|
|
@ -63,7 +56,7 @@ export async function startServer(
|
|||
console.error("Failed to start server:", err);
|
||||
process.exit(1);
|
||||
}
|
||||
console.log(`${name} listening on http://localhost:${port}/mcp`);
|
||||
console.log(`MCP server listening on http://localhost:${port}/mcp`);
|
||||
});
|
||||
|
||||
const shutdown = () => {
|
||||
|
|
@ -75,12 +68,22 @@ export async function startServer(
|
|||
process.on("SIGTERM", shutdown);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an MCP server with stdio transport.
|
||||
*
|
||||
* @param createServer - Factory function that creates a new McpServer instance.
|
||||
*/
|
||||
export async function startStdioServer(
|
||||
createServer: () => McpServer,
|
||||
): Promise<void> {
|
||||
await createServer().connect(new StdioServerTransport());
|
||||
}
|
||||
|
||||
async function main() {
|
||||
if (process.argv.includes("--stdio")) {
|
||||
await createServer().connect(new StdioServerTransport());
|
||||
await startStdioServer(createServer);
|
||||
} else {
|
||||
const port = parseInt(process.env.PORT ?? "3001", 10);
|
||||
await startServer(createServer, { port, name: "Basic MCP App Server (React)" });
|
||||
await startStreamableHTTPServer(createServer);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ export function createServer(): McpServer {
|
|||
title: "Get Time",
|
||||
description: "Returns the current server time as an ISO 8601 string.",
|
||||
inputSchema: {},
|
||||
_meta: { ui: { resourceUri } },
|
||||
_meta: { ui: { resourceUri } }, // Links this tool to its UI resource
|
||||
},
|
||||
async (): Promise<CallToolResult> => {
|
||||
const time = new Date().toISOString();
|
||||
|
|
|
|||
|
|
@ -4,10 +4,6 @@
|
|||
* Or: node dist/index.js [--stdio]
|
||||
*/
|
||||
|
||||
/**
|
||||
* Shared utilities for running MCP servers with Streamable HTTP transport.
|
||||
*/
|
||||
|
||||
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
||||
import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
|
||||
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||
|
|
@ -16,22 +12,15 @@ import cors from "cors";
|
|||
import type { Request, Response } from "express";
|
||||
import { createServer } from "./server.js";
|
||||
|
||||
export interface ServerOptions {
|
||||
port: number;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an MCP server with Streamable HTTP transport in stateless mode.
|
||||
*
|
||||
* @param createServer - Factory function that creates a new McpServer instance per request.
|
||||
* @param options - Server configuration options.
|
||||
*/
|
||||
export async function startServer(
|
||||
export async function startStreamableHTTPServer(
|
||||
createServer: () => McpServer,
|
||||
options: ServerOptions,
|
||||
): Promise<void> {
|
||||
const { port, name = "MCP Server" } = options;
|
||||
const port = parseInt(process.env.PORT ?? "3001", 10);
|
||||
|
||||
const app = createMcpExpressApp({ host: "0.0.0.0" });
|
||||
app.use(cors());
|
||||
|
|
@ -67,7 +56,7 @@ export async function startServer(
|
|||
console.error("Failed to start server:", err);
|
||||
process.exit(1);
|
||||
}
|
||||
console.log(`${name} listening on http://localhost:${port}/mcp`);
|
||||
console.log(`MCP server listening on http://localhost:${port}/mcp`);
|
||||
});
|
||||
|
||||
const shutdown = () => {
|
||||
|
|
@ -79,12 +68,22 @@ export async function startServer(
|
|||
process.on("SIGTERM", shutdown);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an MCP server with stdio transport.
|
||||
*
|
||||
* @param createServer - Factory function that creates a new McpServer instance.
|
||||
*/
|
||||
export async function startStdioServer(
|
||||
createServer: () => McpServer,
|
||||
): Promise<void> {
|
||||
await createServer().connect(new StdioServerTransport());
|
||||
}
|
||||
|
||||
async function main() {
|
||||
if (process.argv.includes("--stdio")) {
|
||||
await createServer().connect(new StdioServerTransport());
|
||||
await startStdioServer(createServer);
|
||||
} else {
|
||||
const port = parseInt(process.env.PORT ?? "3001", 10);
|
||||
await startServer(createServer, { port, name: "Basic MCP App Server (Solid)" });
|
||||
await startStreamableHTTPServer(createServer);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ export function createServer(): McpServer {
|
|||
title: "Get Time",
|
||||
description: "Returns the current server time as an ISO 8601 string.",
|
||||
inputSchema: {},
|
||||
_meta: { ui: { resourceUri } },
|
||||
_meta: { ui: { resourceUri } }, // Links this tool to its UI resource
|
||||
},
|
||||
async (): Promise<CallToolResult> => {
|
||||
const time = new Date().toISOString();
|
||||
|
|
|
|||
|
|
@ -4,10 +4,6 @@
|
|||
* Or: node dist/index.js [--stdio]
|
||||
*/
|
||||
|
||||
/**
|
||||
* Shared utilities for running MCP servers with Streamable HTTP transport.
|
||||
*/
|
||||
|
||||
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
||||
import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
|
||||
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||
|
|
@ -16,22 +12,15 @@ import cors from "cors";
|
|||
import type { Request, Response } from "express";
|
||||
import { createServer } from "./server.js";
|
||||
|
||||
export interface ServerOptions {
|
||||
port: number;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an MCP server with Streamable HTTP transport in stateless mode.
|
||||
*
|
||||
* @param createServer - Factory function that creates a new McpServer instance per request.
|
||||
* @param options - Server configuration options.
|
||||
*/
|
||||
export async function startServer(
|
||||
export async function startStreamableHTTPServer(
|
||||
createServer: () => McpServer,
|
||||
options: ServerOptions,
|
||||
): Promise<void> {
|
||||
const { port, name = "MCP Server" } = options;
|
||||
const port = parseInt(process.env.PORT ?? "3001", 10);
|
||||
|
||||
const app = createMcpExpressApp({ host: "0.0.0.0" });
|
||||
app.use(cors());
|
||||
|
|
@ -67,7 +56,7 @@ export async function startServer(
|
|||
console.error("Failed to start server:", err);
|
||||
process.exit(1);
|
||||
}
|
||||
console.log(`${name} listening on http://localhost:${port}/mcp`);
|
||||
console.log(`MCP server listening on http://localhost:${port}/mcp`);
|
||||
});
|
||||
|
||||
const shutdown = () => {
|
||||
|
|
@ -79,12 +68,22 @@ export async function startServer(
|
|||
process.on("SIGTERM", shutdown);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an MCP server with stdio transport.
|
||||
*
|
||||
* @param createServer - Factory function that creates a new McpServer instance.
|
||||
*/
|
||||
export async function startStdioServer(
|
||||
createServer: () => McpServer,
|
||||
): Promise<void> {
|
||||
await createServer().connect(new StdioServerTransport());
|
||||
}
|
||||
|
||||
async function main() {
|
||||
if (process.argv.includes("--stdio")) {
|
||||
await createServer().connect(new StdioServerTransport());
|
||||
await startStdioServer(createServer);
|
||||
} else {
|
||||
const port = parseInt(process.env.PORT ?? "3001", 10);
|
||||
await startServer(createServer, { port, name: "Basic MCP App Server (Svelte)" });
|
||||
await startStreamableHTTPServer(createServer);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ export function createServer(): McpServer {
|
|||
title: "Get Time",
|
||||
description: "Returns the current server time as an ISO 8601 string.",
|
||||
inputSchema: {},
|
||||
_meta: { ui: { resourceUri } },
|
||||
_meta: { ui: { resourceUri } }, // Links this tool to its UI resource
|
||||
},
|
||||
async (): Promise<CallToolResult> => {
|
||||
const time = new Date().toISOString();
|
||||
|
|
|
|||
|
|
@ -4,10 +4,6 @@
|
|||
* Or: node dist/index.js [--stdio]
|
||||
*/
|
||||
|
||||
/**
|
||||
* Shared utilities for running MCP servers with Streamable HTTP transport.
|
||||
*/
|
||||
|
||||
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
||||
import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
|
||||
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||
|
|
@ -16,22 +12,15 @@ import cors from "cors";
|
|||
import type { Request, Response } from "express";
|
||||
import { createServer } from "./server.js";
|
||||
|
||||
export interface ServerOptions {
|
||||
port: number;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an MCP server with Streamable HTTP transport in stateless mode.
|
||||
*
|
||||
* @param createServer - Factory function that creates a new McpServer instance per request.
|
||||
* @param options - Server configuration options.
|
||||
*/
|
||||
export async function startServer(
|
||||
export async function startStreamableHTTPServer(
|
||||
createServer: () => McpServer,
|
||||
options: ServerOptions,
|
||||
): Promise<void> {
|
||||
const { port, name = "MCP Server" } = options;
|
||||
const port = parseInt(process.env.PORT ?? "3001", 10);
|
||||
|
||||
const app = createMcpExpressApp({ host: "0.0.0.0" });
|
||||
app.use(cors());
|
||||
|
|
@ -67,7 +56,7 @@ export async function startServer(
|
|||
console.error("Failed to start server:", err);
|
||||
process.exit(1);
|
||||
}
|
||||
console.log(`${name} listening on http://localhost:${port}/mcp`);
|
||||
console.log(`MCP server listening on http://localhost:${port}/mcp`);
|
||||
});
|
||||
|
||||
const shutdown = () => {
|
||||
|
|
@ -79,12 +68,22 @@ export async function startServer(
|
|||
process.on("SIGTERM", shutdown);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an MCP server with stdio transport.
|
||||
*
|
||||
* @param createServer - Factory function that creates a new McpServer instance.
|
||||
*/
|
||||
export async function startStdioServer(
|
||||
createServer: () => McpServer,
|
||||
): Promise<void> {
|
||||
await createServer().connect(new StdioServerTransport());
|
||||
}
|
||||
|
||||
async function main() {
|
||||
if (process.argv.includes("--stdio")) {
|
||||
await createServer().connect(new StdioServerTransport());
|
||||
await startStdioServer(createServer);
|
||||
} else {
|
||||
const port = parseInt(process.env.PORT ?? "3102", 10);
|
||||
await startServer(createServer, { port, name: "Basic MCP App Server (Vanilla JS)" });
|
||||
await startStreamableHTTPServer(createServer);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ export function createServer(): McpServer {
|
|||
outputSchema: z.object({
|
||||
time: z.string(),
|
||||
}),
|
||||
_meta: { ui: { resourceUri } },
|
||||
_meta: { ui: { resourceUri } }, // Links this tool to its UI resource
|
||||
},
|
||||
async (): Promise<CallToolResult> => {
|
||||
const time = new Date().toISOString();
|
||||
|
|
|
|||
|
|
@ -4,10 +4,6 @@
|
|||
* Or: node dist/index.js [--stdio]
|
||||
*/
|
||||
|
||||
/**
|
||||
* Shared utilities for running MCP servers with Streamable HTTP transport.
|
||||
*/
|
||||
|
||||
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
||||
import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
|
||||
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||
|
|
@ -16,22 +12,15 @@ import cors from "cors";
|
|||
import type { Request, Response } from "express";
|
||||
import { createServer } from "./server.js";
|
||||
|
||||
export interface ServerOptions {
|
||||
port: number;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an MCP server with Streamable HTTP transport in stateless mode.
|
||||
*
|
||||
* @param createServer - Factory function that creates a new McpServer instance per request.
|
||||
* @param options - Server configuration options.
|
||||
*/
|
||||
export async function startServer(
|
||||
export async function startStreamableHTTPServer(
|
||||
createServer: () => McpServer,
|
||||
options: ServerOptions,
|
||||
): Promise<void> {
|
||||
const { port, name = "MCP Server" } = options;
|
||||
const port = parseInt(process.env.PORT ?? "3001", 10);
|
||||
|
||||
const app = createMcpExpressApp({ host: "0.0.0.0" });
|
||||
app.use(cors());
|
||||
|
|
@ -67,7 +56,7 @@ export async function startServer(
|
|||
console.error("Failed to start server:", err);
|
||||
process.exit(1);
|
||||
}
|
||||
console.log(`${name} listening on http://localhost:${port}/mcp`);
|
||||
console.log(`MCP server listening on http://localhost:${port}/mcp`);
|
||||
});
|
||||
|
||||
const shutdown = () => {
|
||||
|
|
@ -79,12 +68,22 @@ export async function startServer(
|
|||
process.on("SIGTERM", shutdown);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an MCP server with stdio transport.
|
||||
*
|
||||
* @param createServer - Factory function that creates a new McpServer instance.
|
||||
*/
|
||||
export async function startStdioServer(
|
||||
createServer: () => McpServer,
|
||||
): Promise<void> {
|
||||
await createServer().connect(new StdioServerTransport());
|
||||
}
|
||||
|
||||
async function main() {
|
||||
if (process.argv.includes("--stdio")) {
|
||||
await createServer().connect(new StdioServerTransport());
|
||||
await startStdioServer(createServer);
|
||||
} else {
|
||||
const port = parseInt(process.env.PORT ?? "3001", 10);
|
||||
await startServer(createServer, { port, name: "Basic MCP App Server (Vue)" });
|
||||
await startStreamableHTTPServer(createServer);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ export function createServer(): McpServer {
|
|||
title: "Get Time",
|
||||
description: "Returns the current server time as an ISO 8601 string.",
|
||||
inputSchema: {},
|
||||
_meta: { ui: { resourceUri } },
|
||||
_meta: { ui: { resourceUri } }, // Links this tool to its UI resource
|
||||
},
|
||||
async (): Promise<CallToolResult> => {
|
||||
const time = new Date().toISOString();
|
||||
|
|
|
|||
|
|
@ -4,10 +4,6 @@
|
|||
* Or: node dist/index.js [--stdio]
|
||||
*/
|
||||
|
||||
/**
|
||||
* Shared utilities for running MCP servers with Streamable HTTP transport.
|
||||
*/
|
||||
|
||||
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
||||
import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
|
||||
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||
|
|
@ -16,22 +12,15 @@ import cors from "cors";
|
|||
import type { Request, Response } from "express";
|
||||
import { createServer } from "./server.js";
|
||||
|
||||
export interface ServerOptions {
|
||||
port: number;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an MCP server with Streamable HTTP transport in stateless mode.
|
||||
*
|
||||
* @param createServer - Factory function that creates a new McpServer instance per request.
|
||||
* @param options - Server configuration options.
|
||||
*/
|
||||
export async function startServer(
|
||||
export async function startStreamableHTTPServer(
|
||||
createServer: () => McpServer,
|
||||
options: ServerOptions,
|
||||
): Promise<void> {
|
||||
const { port, name = "MCP Server" } = options;
|
||||
const port = parseInt(process.env.PORT ?? "3001", 10);
|
||||
|
||||
const app = createMcpExpressApp({ host: "0.0.0.0" });
|
||||
app.use(cors());
|
||||
|
|
@ -67,7 +56,7 @@ export async function startServer(
|
|||
console.error("Failed to start server:", err);
|
||||
process.exit(1);
|
||||
}
|
||||
console.log(`${name} listening on http://localhost:${port}/mcp`);
|
||||
console.log(`MCP server listening on http://localhost:${port}/mcp`);
|
||||
});
|
||||
|
||||
const shutdown = () => {
|
||||
|
|
@ -79,12 +68,22 @@ export async function startServer(
|
|||
process.on("SIGTERM", shutdown);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an MCP server with stdio transport.
|
||||
*
|
||||
* @param createServer - Factory function that creates a new McpServer instance.
|
||||
*/
|
||||
export async function startStdioServer(
|
||||
createServer: () => McpServer,
|
||||
): Promise<void> {
|
||||
await createServer().connect(new StdioServerTransport());
|
||||
}
|
||||
|
||||
async function main() {
|
||||
if (process.argv.includes("--stdio")) {
|
||||
await createServer().connect(new StdioServerTransport());
|
||||
await startStdioServer(createServer);
|
||||
} else {
|
||||
const port = parseInt(process.env.PORT ?? "3103", 10);
|
||||
await startServer(createServer, { port, name: "Marketing" });
|
||||
await startStreamableHTTPServer(createServer);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,10 +4,6 @@
|
|||
* Or: node dist/index.js [--stdio]
|
||||
*/
|
||||
|
||||
/**
|
||||
* Shared utilities for running MCP servers with Streamable HTTP transport.
|
||||
*/
|
||||
|
||||
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
||||
import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
|
||||
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||
|
|
@ -16,22 +12,15 @@ import cors from "cors";
|
|||
import type { Request, Response } from "express";
|
||||
import { createServer } from "./server.js";
|
||||
|
||||
export interface ServerOptions {
|
||||
port: number;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an MCP server with Streamable HTTP transport in stateless mode.
|
||||
*
|
||||
* @param createServer - Factory function that creates a new McpServer instance per request.
|
||||
* @param options - Server configuration options.
|
||||
*/
|
||||
export async function startServer(
|
||||
export async function startStreamableHTTPServer(
|
||||
createServer: () => McpServer,
|
||||
options: ServerOptions,
|
||||
): Promise<void> {
|
||||
const { port, name = "MCP Server" } = options;
|
||||
const port = parseInt(process.env.PORT ?? "3001", 10);
|
||||
|
||||
const app = createMcpExpressApp({ host: "0.0.0.0" });
|
||||
app.use(cors());
|
||||
|
|
@ -67,7 +56,7 @@ export async function startServer(
|
|||
console.error("Failed to start server:", err);
|
||||
process.exit(1);
|
||||
}
|
||||
console.log(`${name} listening on http://localhost:${port}/mcp`);
|
||||
console.log(`MCP server listening on http://localhost:${port}/mcp`);
|
||||
});
|
||||
|
||||
const shutdown = () => {
|
||||
|
|
@ -79,12 +68,22 @@ export async function startServer(
|
|||
process.on("SIGTERM", shutdown);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an MCP server with stdio transport.
|
||||
*
|
||||
* @param createServer - Factory function that creates a new McpServer instance.
|
||||
*/
|
||||
export async function startStdioServer(
|
||||
createServer: () => McpServer,
|
||||
): Promise<void> {
|
||||
await createServer().connect(new StdioServerTransport());
|
||||
}
|
||||
|
||||
async function main() {
|
||||
if (process.argv.includes("--stdio")) {
|
||||
await createServer().connect(new StdioServerTransport());
|
||||
await startStdioServer(createServer);
|
||||
} else {
|
||||
const port = parseInt(process.env.PORT ?? "3104", 10);
|
||||
await startServer(createServer, { port, name: "Cohort Heatmap Server" });
|
||||
await startStreamableHTTPServer(createServer);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,10 +4,6 @@
|
|||
* Or: node dist/index.js [--stdio]
|
||||
*/
|
||||
|
||||
/**
|
||||
* Shared utilities for running MCP servers with Streamable HTTP transport.
|
||||
*/
|
||||
|
||||
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
||||
import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
|
||||
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||
|
|
@ -16,22 +12,15 @@ import cors from "cors";
|
|||
import type { Request, Response } from "express";
|
||||
import { createServer } from "./server.js";
|
||||
|
||||
export interface ServerOptions {
|
||||
port: number;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an MCP server with Streamable HTTP transport in stateless mode.
|
||||
*
|
||||
* @param createServer - Factory function that creates a new McpServer instance per request.
|
||||
* @param options - Server configuration options.
|
||||
*/
|
||||
export async function startServer(
|
||||
export async function startStreamableHTTPServer(
|
||||
createServer: () => McpServer,
|
||||
options: ServerOptions,
|
||||
): Promise<void> {
|
||||
const { port, name = "MCP Server" } = options;
|
||||
const port = parseInt(process.env.PORT ?? "3001", 10);
|
||||
|
||||
const app = createMcpExpressApp({ host: "0.0.0.0" });
|
||||
app.use(cors());
|
||||
|
|
@ -67,7 +56,7 @@ export async function startServer(
|
|||
console.error("Failed to start server:", err);
|
||||
process.exit(1);
|
||||
}
|
||||
console.log(`${name} listening on http://localhost:${port}/mcp`);
|
||||
console.log(`MCP server listening on http://localhost:${port}/mcp`);
|
||||
});
|
||||
|
||||
const shutdown = () => {
|
||||
|
|
@ -79,15 +68,22 @@ export async function startServer(
|
|||
process.on("SIGTERM", shutdown);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an MCP server with stdio transport.
|
||||
*
|
||||
* @param createServer - Factory function that creates a new McpServer instance.
|
||||
*/
|
||||
export async function startStdioServer(
|
||||
createServer: () => McpServer,
|
||||
): Promise<void> {
|
||||
await createServer().connect(new StdioServerTransport());
|
||||
}
|
||||
|
||||
async function main() {
|
||||
if (process.argv.includes("--stdio")) {
|
||||
await createServer().connect(new StdioServerTransport());
|
||||
await startStdioServer(createServer);
|
||||
} else {
|
||||
const port = parseInt(process.env.PORT ?? "3105", 10);
|
||||
await startServer(createServer, {
|
||||
port,
|
||||
name: "Customer Segmentation Server",
|
||||
});
|
||||
await startStreamableHTTPServer(createServer);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue