diff --git a/.prettierignore b/.prettierignore
index dd3c59ba..f5edcf9d 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -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
diff --git a/AGENTS.md b/AGENTS.md
index ae8903a4..9bbd2f05 100644
--- a/AGENTS.md
+++ b/AGENTS.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.
diff --git a/docs/quickstart-success.png b/docs/quickstart-success.png
new file mode 100644
index 00000000..058fb3f2
Binary files /dev/null and b/docs/quickstart-success.png differ
diff --git a/docs/quickstart.md b/docs/quickstart.md
index 9481df31..63e93713 100644
--- a/docs/quickstart.md
+++ b/docs/quickstart.md
@@ -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
+```
+
+Install the dependencies you'll need:
+
+```bash
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
```
-Install dependencies:
+Configure your [`package.json`](https://github.com/modelcontextprotocol/ext-apps/blob/main/examples/quickstart/package.json):
```bash
-npm install @modelcontextprotocol/ext-apps @modelcontextprotocol/sdk
-npm install -D typescript vite vite-plugin-singlefile express cors @types/express @types/cors tsx
+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'"
```
-Create `tsconfig.json`:
+
+Create tsconfig.json:
-```json
+
+```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,
+ "skipLibCheck": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noFallthroughCasesInSwitch": true
+ },
+ "include": ["src", "server.ts", "main.ts"]
+}
+```
+
+
+
+
+Create tsconfig.server.json — for compiling server-side code:
+
+
+```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,
- "outDir": "dist"
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noFallthroughCasesInSwitch": true
},
- "include": ["*.ts", "src/**/*.ts"]
+ "include": ["server.ts", "main.ts"]
}
```
-Create `vite.config.ts` — this bundles your UI into a single HTML file:
+
-```typescript
+
+Create vite.config.ts — bundles UI into a single HTML file:
+
+
+```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`:
+
-```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";
+
+```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 },
- ],
- };
- },
-);
+/**
+ * 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",
+ });
+
+ // 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;
+}
+```
+
+
+Create main.ts — the entry point that starts the server:
-// Start an Express server that exposes the MCP endpoint.
-const expressApp = express();
-expressApp.use(cors());
-expressApp.use(express.json());
+
+```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 {
+ 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,
+ });
+ }
+ }
+ });
-expressApp.post("/mcp", async (req, res) => {
- const transport = new StreamableHTTPServerTransport({
- sessionIdGenerator: undefined,
- enableJsonResponse: true,
+ 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`);
});
- 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);
+ 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 {
+ 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)
+
+
+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
+```
-Then, verify your server compiles:
+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`.
+
+The server can return the current time when the tool is called. Now let's build the UI to display it.
+
+## 3. Build the View
-## 3. Build the UI
+The View consists of an HTML page and a script that connects to the host.
-Create `mcp-app.html`:
+Create [`mcp-app.html`](https://github.com/modelcontextprotocol/ext-apps/blob/main/examples/quickstart/mcp-app.html), the HTML for your View:
-```html
+
+```html source="../examples/quickstart/mcp-app.html"
@@ -214,9 +365,10 @@ Create `mcp-app.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
+
+```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!
+
+## 4. See it in action
-You'll need two terminals.
+You'll need two terminal windows.
-**Terminal 1** — Build and start your server:
+**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/)
diff --git a/examples/basic-host/src/index.module.css b/examples/basic-host/src/index.module.css
index eb4d6795..ec18e2bf 100644
--- a/examples/basic-host/src/index.module.css
+++ b/examples/basic-host/src/index.module.css
@@ -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;
diff --git a/examples/basic-server-preact/main.ts b/examples/basic-server-preact/main.ts
index d9a51a2e..76426326 100644
--- a/examples/basic-server-preact/main.ts
+++ b/examples/basic-server-preact/main.ts
@@ -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 {
- 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 {
+ 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);
}
}
diff --git a/examples/basic-server-preact/server.ts b/examples/basic-server-preact/server.ts
index 2d999645..bf14ea59 100644
--- a/examples/basic-server-preact/server.ts
+++ b/examples/basic-server-preact/server.ts
@@ -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 => {
const time = new Date().toISOString();
diff --git a/examples/basic-server-react/main.ts b/examples/basic-server-react/main.ts
index 39840a13..ec187b68 100644
--- a/examples/basic-server-react/main.ts
+++ b/examples/basic-server-react/main.ts
@@ -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 {
- 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 {
+ 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);
}
}
diff --git a/examples/basic-server-react/server.ts b/examples/basic-server-react/server.ts
index 416b217f..23f6dda5 100644
--- a/examples/basic-server-react/server.ts
+++ b/examples/basic-server-react/server.ts
@@ -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 => {
const time = new Date().toISOString();
diff --git a/examples/basic-server-solid/main.ts b/examples/basic-server-solid/main.ts
index 23353486..c8d9de22 100644
--- a/examples/basic-server-solid/main.ts
+++ b/examples/basic-server-solid/main.ts
@@ -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 {
- 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 {
+ 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);
}
}
diff --git a/examples/basic-server-solid/server.ts b/examples/basic-server-solid/server.ts
index 33f76a13..2ed2d935 100644
--- a/examples/basic-server-solid/server.ts
+++ b/examples/basic-server-solid/server.ts
@@ -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 => {
const time = new Date().toISOString();
diff --git a/examples/basic-server-svelte/main.ts b/examples/basic-server-svelte/main.ts
index 762a40eb..6c50a254 100644
--- a/examples/basic-server-svelte/main.ts
+++ b/examples/basic-server-svelte/main.ts
@@ -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 {
- 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 {
+ 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);
}
}
diff --git a/examples/basic-server-svelte/server.ts b/examples/basic-server-svelte/server.ts
index dd2dba66..da603ca8 100644
--- a/examples/basic-server-svelte/server.ts
+++ b/examples/basic-server-svelte/server.ts
@@ -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 => {
const time = new Date().toISOString();
diff --git a/examples/basic-server-vanillajs/main.ts b/examples/basic-server-vanillajs/main.ts
index d53d53b5..286fa34f 100644
--- a/examples/basic-server-vanillajs/main.ts
+++ b/examples/basic-server-vanillajs/main.ts
@@ -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 {
- 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 {
+ 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);
}
}
diff --git a/examples/basic-server-vanillajs/server.ts b/examples/basic-server-vanillajs/server.ts
index 07e82c20..5b2daf70 100644
--- a/examples/basic-server-vanillajs/server.ts
+++ b/examples/basic-server-vanillajs/server.ts
@@ -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 => {
const time = new Date().toISOString();
diff --git a/examples/basic-server-vue/main.ts b/examples/basic-server-vue/main.ts
index 0304600e..669a0718 100644
--- a/examples/basic-server-vue/main.ts
+++ b/examples/basic-server-vue/main.ts
@@ -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 {
- 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 {
+ 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);
}
}
diff --git a/examples/basic-server-vue/server.ts b/examples/basic-server-vue/server.ts
index 73cb54d4..5aa72de0 100644
--- a/examples/basic-server-vue/server.ts
+++ b/examples/basic-server-vue/server.ts
@@ -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 => {
const time = new Date().toISOString();
diff --git a/examples/budget-allocator-server/main.ts b/examples/budget-allocator-server/main.ts
index 534e876d..cc488f0b 100644
--- a/examples/budget-allocator-server/main.ts
+++ b/examples/budget-allocator-server/main.ts
@@ -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 {
- 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 {
+ 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);
}
}
diff --git a/examples/cohort-heatmap-server/main.ts b/examples/cohort-heatmap-server/main.ts
index 0b716d1d..d59903b3 100644
--- a/examples/cohort-heatmap-server/main.ts
+++ b/examples/cohort-heatmap-server/main.ts
@@ -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 {
- 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 {
+ 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);
}
}
diff --git a/examples/customer-segmentation-server/main.ts b/examples/customer-segmentation-server/main.ts
index 7be715a4..130076c5 100644
--- a/examples/customer-segmentation-server/main.ts
+++ b/examples/customer-segmentation-server/main.ts
@@ -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 {
- 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 {
+ 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);
}
}
diff --git a/examples/debug-server/main.ts b/examples/debug-server/main.ts
index 5aa8ddbc..3c89a975 100644
--- a/examples/debug-server/main.ts
+++ b/examples/debug-server/main.ts
@@ -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 {
- 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 {
+ 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 ?? "3110", 10);
- await startServer(createServer, { port, name: "Debug Server" });
+ await startStreamableHTTPServer(createServer);
}
}
diff --git a/examples/integration-server/main.ts b/examples/integration-server/main.ts
index 5340e8a8..c45787e2 100644
--- a/examples/integration-server/main.ts
+++ b/examples/integration-server/main.ts
@@ -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 {
- 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 {
+ 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: "Integration Test Server" });
+ await startStreamableHTTPServer(createServer);
}
}
diff --git a/examples/map-server/main.ts b/examples/map-server/main.ts
index 44956598..f91f57b6 100644
--- a/examples/map-server/main.ts
+++ b/examples/map-server/main.ts
@@ -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 {
- 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 +52,7 @@ export async function startServer(
});
const httpServer = app.listen(port, () => {
- console.log(`${name} listening on http://localhost:${port}/mcp`);
+ console.log(`MCP server listening on http://localhost:${port}/mcp`);
});
const shutdown = () => {
@@ -75,12 +64,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 {
+ 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: "CesiumJS Map Server" });
+ await startStreamableHTTPServer(createServer);
}
}
diff --git a/examples/pdf-server/main.ts b/examples/pdf-server/main.ts
index 2b0e3ff9..310a3127 100644
--- a/examples/pdf-server/main.ts
+++ b/examples/pdf-server/main.ts
@@ -23,19 +23,13 @@ import {
DEFAULT_PDF,
} from "./server.js";
-export interface ServerOptions {
- port: number;
- name?: string;
-}
-
/**
* Starts an MCP server with Streamable HTTP transport in stateless mode.
*/
-export async function startServer(
+export async function startStreamableHTTPServer(
createServer: () => McpServer,
- options: ServerOptions,
): Promise {
- 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());
@@ -71,7 +65,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 = () => {
@@ -83,6 +77,17 @@ 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 {
+ await createServer().connect(new StdioServerTransport());
+}
+
function parseArgs(): { urls: string[]; stdio: boolean } {
const args = process.argv.slice(2);
const urls: string[] = [];
@@ -132,10 +137,9 @@ async function main() {
);
if (stdio) {
- await createServer().connect(new StdioServerTransport());
+ await startStdioServer(createServer);
} else {
- const port = parseInt(process.env.PORT ?? "3120", 10);
- await startServer(createServer, { port, name: "PDF Server" });
+ await startStreamableHTTPServer(createServer);
}
}
diff --git a/examples/qr-server/server.py b/examples/qr-server/server.py
index 5595ef3d..02384c7b 100755
--- a/examples/qr-server/server.py
+++ b/examples/qr-server/server.py
@@ -25,7 +25,7 @@
VIEW_URI = "ui://qr-server/view.html"
HOST = os.environ.get("HOST", "0.0.0.0") # 0.0.0.0 for Docker compatibility
-PORT = int(os.environ.get("PORT", "3108"))
+PORT = int(os.environ.get("PORT", "3001"))
mcp = FastMCP("QR Code Server")
diff --git a/examples/quickstart/.gitignore b/examples/quickstart/.gitignore
new file mode 100644
index 00000000..b9470778
--- /dev/null
+++ b/examples/quickstart/.gitignore
@@ -0,0 +1,2 @@
+node_modules/
+dist/
diff --git a/examples/quickstart/README.md b/examples/quickstart/README.md
new file mode 100644
index 00000000..74e070db
--- /dev/null
+++ b/examples/quickstart/README.md
@@ -0,0 +1,3 @@
+# Quickstart Server
+
+This is the example code for the [Quickstart guide](../../docs/quickstart.md).
diff --git a/examples/quickstart/main.ts b/examples/quickstart/main.ts
new file mode 100644
index 00000000..06895a49
--- /dev/null
+++ b/examples/quickstart/main.ts
@@ -0,0 +1,87 @@
+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 {
+ 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 {
+ await createServer().connect(new StdioServerTransport());
+}
+
+async function main() {
+ if (process.argv.includes("--stdio")) {
+ await startStdioServer(createServer);
+ } else {
+ await startStreamableHTTPServer(createServer);
+ }
+}
+
+main().catch((e) => {
+ console.error(e);
+ process.exit(1);
+});
diff --git a/examples/quickstart/mcp-app.html b/examples/quickstart/mcp-app.html
new file mode 100644
index 00000000..bf276aa5
--- /dev/null
+++ b/examples/quickstart/mcp-app.html
@@ -0,0 +1,14 @@
+
+
+
+
+ Get Time App
+
+
+
+ Server Time: Loading...
+
+
+
+
+
diff --git a/examples/quickstart/package.json b/examples/quickstart/package.json
new file mode 100644
index 00000000..86933d40
--- /dev/null
+++ b/examples/quickstart/package.json
@@ -0,0 +1,34 @@
+{
+ "name": "@modelcontextprotocol/quickstart",
+ "version": "0.4.1",
+ "type": "module",
+ "private": true,
+ "description": "Quickstart MCP App Server example",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/modelcontextprotocol/ext-apps",
+ "directory": "examples/quickstart"
+ },
+ "license": "MIT",
+ "scripts": {
+ "build": "tsc --noEmit && tsc -p tsconfig.server.json && cross-env INPUT=mcp-app.html vite build",
+ "start": "concurrently 'cross-env NODE_ENV=development INPUT=mcp-app.html vite build --watch' 'tsx watch main.ts'"
+ },
+ "dependencies": {
+ "@modelcontextprotocol/ext-apps": "^0.4.1",
+ "@modelcontextprotocol/sdk": "^1.24.0",
+ "cors": "^2.8.5",
+ "express": "^5.1.0"
+ },
+ "devDependencies": {
+ "@types/cors": "^2.8.19",
+ "concurrently": "^9.2.1",
+ "@types/express": "^5.0.0",
+ "@types/node": "^22.0.0",
+ "cross-env": "^10.1.0",
+ "tsx": "^4.21.0",
+ "typescript": "^5.9.3",
+ "vite": "^6.0.0",
+ "vite-plugin-singlefile": "^2.3.0"
+ }
+}
diff --git a/examples/quickstart/server.ts b/examples/quickstart/server.ts
new file mode 100644
index 00000000..eb3c3b1a
--- /dev/null
+++ b/examples/quickstart/server.ts
@@ -0,0 +1,60 @@
+import {
+ registerAppResource,
+ registerAppTool,
+ RESOURCE_MIME_TYPE,
+} from "@modelcontextprotocol/ext-apps/server";
+import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
+import fs from "node:fs/promises";
+import path from "node:path";
+
+const DIST_DIR = path.join(import.meta.dirname, "dist");
+
+/**
+ * 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",
+ });
+
+ // 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;
+}
diff --git a/examples/quickstart/src/mcp-app.ts b/examples/quickstart/src/mcp-app.ts
new file mode 100644
index 00000000..5780bb70
--- /dev/null
+++ b/examples/quickstart/src/mcp-app.ts
@@ -0,0 +1,26 @@
+import { App } from "@modelcontextprotocol/ext-apps";
+
+// Get element references
+const serverTimeEl = document.getElementById("server-time")!;
+const getTimeBtn = document.getElementById("get-time-btn")!;
+
+// Create app instance
+const app = new App({ name: "Get Time App", version: "1.0.0" });
+
+// 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]";
+};
+
+// 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]";
+});
+
+// Connect to host
+app.connect();
diff --git a/examples/quickstart/tsconfig.json b/examples/quickstart/tsconfig.json
new file mode 100644
index 00000000..6c553b5d
--- /dev/null
+++ b/examples/quickstart/tsconfig.json
@@ -0,0 +1,19 @@
+{
+ "compilerOptions": {
+ "target": "ESNext",
+ "lib": ["ESNext", "DOM", "DOM.Iterable"],
+ "module": "ESNext",
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "verbatimModuleSyntax": true,
+ "noEmit": true,
+ "strict": true,
+ "skipLibCheck": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noFallthroughCasesInSwitch": true
+ },
+ "include": ["src", "server.ts", "main.ts"]
+}
diff --git a/examples/quickstart/tsconfig.server.json b/examples/quickstart/tsconfig.server.json
new file mode 100644
index 00000000..7e65f5f7
--- /dev/null
+++ b/examples/quickstart/tsconfig.server.json
@@ -0,0 +1,19 @@
+{
+ "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"]
+}
diff --git a/examples/quickstart/vite.config.ts b/examples/quickstart/vite.config.ts
new file mode 100644
index 00000000..6ff6d997
--- /dev/null
+++ b/examples/quickstart/vite.config.ts
@@ -0,0 +1,24 @@
+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: {
+ sourcemap: isDevelopment ? "inline" : undefined,
+ cssMinify: !isDevelopment,
+ minify: !isDevelopment,
+
+ rollupOptions: {
+ input: INPUT,
+ },
+ outDir: "dist",
+ emptyOutDir: false,
+ },
+});
diff --git a/examples/say-server/server.py b/examples/say-server/server.py
index bc1313e0..64b7417a 100755
--- a/examples/say-server/server.py
+++ b/examples/say-server/server.py
@@ -58,7 +58,7 @@
VIEW_URI = "ui://say-demo/view.html"
HOST = os.environ.get("HOST", "0.0.0.0")
-PORT = int(os.environ.get("PORT", "3109"))
+PORT = int(os.environ.get("PORT", "3001"))
# Speaker icon as SVG data URI
SPEAKER_ICON = Icon(
diff --git a/examples/scenario-modeler-server/main.ts b/examples/scenario-modeler-server/main.ts
index b7cc7d00..dbd3ab6a 100644
--- a/examples/scenario-modeler-server/main.ts
+++ b/examples/scenario-modeler-server/main.ts
@@ -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 {
- 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 {
+ 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 ?? "3106", 10);
- await startServer(createServer, { port, name: "SaaS Scenario Modeler" });
+ await startStreamableHTTPServer(createServer);
}
}
diff --git a/examples/shadertoy-server/main.ts b/examples/shadertoy-server/main.ts
index 717f5e64..ac8f6ad8 100644
--- a/examples/shadertoy-server/main.ts
+++ b/examples/shadertoy-server/main.ts
@@ -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 {
- 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 {
+ 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: "ShaderToy Server" });
+ await startStreamableHTTPServer(createServer);
}
}
diff --git a/examples/sheet-music-server/main.ts b/examples/sheet-music-server/main.ts
index 4b206e72..8b143a7a 100644
--- a/examples/sheet-music-server/main.ts
+++ b/examples/sheet-music-server/main.ts
@@ -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 {
- 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 {
+ 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: "Sheet Music Server" });
+ await startStreamableHTTPServer(createServer);
}
}
diff --git a/examples/system-monitor-server/main.ts b/examples/system-monitor-server/main.ts
index 19624809..365a5604 100644
--- a/examples/system-monitor-server/main.ts
+++ b/examples/system-monitor-server/main.ts
@@ -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 {
- 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 {
+ 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 ?? "3107", 10);
- await startServer(createServer, { port, name: "System Monitor Server" });
+ await startStreamableHTTPServer(createServer);
}
}
diff --git a/examples/threejs-server/main.ts b/examples/threejs-server/main.ts
index 1f779a97..13a2084a 100644
--- a/examples/threejs-server/main.ts
+++ b/examples/threejs-server/main.ts
@@ -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 {
- 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 {
+ 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 ?? "3108", 10);
- await startServer(createServer, { port, name: "Three.js Server" });
+ await startStreamableHTTPServer(createServer);
}
}
diff --git a/examples/transcript-server/main.ts b/examples/transcript-server/main.ts
index 2a927041..68db8dc7 100644
--- a/examples/transcript-server/main.ts
+++ b/examples/transcript-server/main.ts
@@ -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,19 +12,13 @@ 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.
*/
-export async function startServer(
+export async function startStreamableHTTPServer(
createServer: () => McpServer,
- options: ServerOptions,
): Promise {
- 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());
@@ -64,7 +54,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 = () => {
@@ -76,12 +66,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 {
+ 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 ?? "3109", 10);
- await startServer(createServer, { port, name: "Transcript Server" });
+ await startStreamableHTTPServer(createServer);
}
}
diff --git a/examples/video-resource-server/main.ts b/examples/video-resource-server/main.ts
index ff304639..082fa5ab 100644
--- a/examples/video-resource-server/main.ts
+++ b/examples/video-resource-server/main.ts
@@ -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 {
- 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 {
+ 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: "Video Resource Server" });
+ await startStreamableHTTPServer(createServer);
}
}
diff --git a/examples/wiki-explorer-server/main.ts b/examples/wiki-explorer-server/main.ts
index d83ac427..cc7b3a22 100644
--- a/examples/wiki-explorer-server/main.ts
+++ b/examples/wiki-explorer-server/main.ts
@@ -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 {
- 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 {
+ 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 ?? "3109", 10);
- await startServer(createServer, { port, name: "Wiki Explorer" });
+ await startStreamableHTTPServer(createServer);
}
}
diff --git a/package-lock.json b/package-lock.json
index b3d94905..9907c20f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -568,6 +568,45 @@
"@modelcontextprotocol/ext-apps": "^0.4.1"
}
},
+ "examples/quickstart": {
+ "name": "@modelcontextprotocol/quickstart",
+ "version": "0.4.1",
+ "license": "MIT",
+ "dependencies": {
+ "@modelcontextprotocol/ext-apps": "^0.4.1",
+ "@modelcontextprotocol/sdk": "^1.24.0",
+ "cors": "^2.8.5",
+ "express": "^5.1.0"
+ },
+ "devDependencies": {
+ "@types/cors": "^2.8.19",
+ "@types/express": "^5.0.0",
+ "@types/node": "^22.0.0",
+ "concurrently": "^9.2.1",
+ "cross-env": "^10.1.0",
+ "tsx": "^4.21.0",
+ "typescript": "^5.9.3",
+ "vite": "^6.0.0",
+ "vite-plugin-singlefile": "^2.3.0"
+ }
+ },
+ "examples/quickstart/node_modules/@types/node": {
+ "version": "22.19.7",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.7.tgz",
+ "integrity": "sha512-MciR4AKGHWl7xwxkBa6xUGxQJ4VBOmPTF7sL+iGzuahOFaO0jHCsuEfS80pan1ef4gWId1oWOweIhrDEYLuaOw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~6.21.0"
+ }
+ },
+ "examples/quickstart/node_modules/undici-types": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
+ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"examples/say-server": {
"name": "@modelcontextprotocol/server-say",
"version": "0.4.1",
@@ -897,6 +936,7 @@
"integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@babel/code-frame": "^7.28.6",
"@babel/generator": "^7.28.6",
@@ -2333,11 +2373,16 @@
"resolved": "examples/basic-host",
"link": true
},
+ "node_modules/@modelcontextprotocol/quickstart": {
+ "resolved": "examples/quickstart",
+ "link": true
+ },
"node_modules/@modelcontextprotocol/sdk": {
"version": "1.25.3",
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.25.3.tgz",
"integrity": "sha512-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@hono/node-server": "^1.19.9",
"ajv": "^8.17.1",
@@ -3417,6 +3462,7 @@
"integrity": "sha512-Y1Cs7hhTc+a5E9Va/xwKlAJoariQyHY+5zBgCZg4PFWNYQ1nMN9sjK1zhw1gK69DuqVP++sht/1GZg1aRwmAXQ==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@sveltejs/vite-plugin-svelte-inspector": "^4.0.1",
"debug": "^4.4.1",
@@ -3623,6 +3669,7 @@
"integrity": "sha512-N2clP5pJhB2YnZJ3PIHFk5RkygRX5WO/5f0WC08tp0wd+sv0rsJk3MqWn3CbNmT2J505a5336jaQj4ph1AdMug==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"undici-types": "~6.21.0"
}
@@ -3647,6 +3694,7 @@
"integrity": "sha512-Lpo8kgb/igvMIPeNV2rsYKTgaORYdO1XGVZ4Qz3akwOj0ySGYMPlQWa8BaLn0G63D1aSaAQ5ldR06wCpChQCjA==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"csstype": "^3.2.2"
}
@@ -4049,6 +4097,7 @@
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"license": "MIT",
+ "peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -4390,6 +4439,7 @@
}
],
"license": "MIT",
+ "peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
@@ -5105,6 +5155,7 @@
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
"dev": true,
"license": "ISC",
+ "peer": true,
"engines": {
"node": ">=12"
}
@@ -5577,6 +5628,7 @@
"resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
"integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"accepts": "^2.0.0",
"body-parser": "^2.2.1",
@@ -7126,6 +7178,7 @@
"resolved": "https://registry.npmjs.org/preact/-/preact-10.28.2.tgz",
"integrity": "sha512-lbteaWGzGHdlIuiJ0l2Jq454m6kcpI1zNje6d8MlGAFlYvP2GO4ibnat7P74Esfz4sPTdM6UxtTwh/d3pwM9JA==",
"license": "MIT",
+ "peer": true,
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/preact"
@@ -7335,6 +7388,7 @@
"integrity": "sha512-9FwVqlgUHzbXtDg9RCMgodF3Ua4Na6Gau+Sdt9vyCN4RhHfVKX2DCHy3BjMLTDd47ITDhYAnTwGulWTblJSDLg==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@types/estree": "1.0.8"
},
@@ -7456,6 +7510,7 @@
"resolved": "https://registry.npmjs.org/seroval/-/seroval-1.5.0.tgz",
"integrity": "sha512-OE4cvmJ1uSPrKorFIH9/w/Qwuvi/IMcGbv5RKgcJ/zjA/IohDLU6SVaxFN9FwajbP7nsX0dQqMDes1whk3y+yw==",
"license": "MIT",
+ "peer": true,
"engines": {
"node": ">=10"
}
@@ -7746,6 +7801,7 @@
"resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.9.11.tgz",
"integrity": "sha512-WEJtcc5mkh/BnHA6Yrg4whlF8g6QwpmXXRg4P2ztPmcKeHHlH4+djYecBLhSpecZY2RRECXYUwIc/C2r3yzQ4Q==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"csstype": "^3.1.0",
"seroval": "~1.5.0",
@@ -7924,6 +7980,7 @@
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.48.0.tgz",
"integrity": "sha512-+NUe82VoFP1RQViZI/esojx70eazGF4u0O/9ucqZ4rPcOZD+n5EVp17uYsqwdzjUjZyTpGKunHbDziW6AIAVkQ==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@jridgewell/remapping": "^2.3.4",
"@jridgewell/sourcemap-codec": "^1.5.0",
@@ -9013,6 +9070,7 @@
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"devOptional": true,
"license": "Apache-2.0",
+ "peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -9106,6 +9164,7 @@
"integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"esbuild": "^0.25.0",
"fdir": "^6.4.4",
@@ -9400,6 +9459,7 @@
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.27.tgz",
"integrity": "sha512-aJ/UtoEyFySPBGarREmN4z6qNKpbEguYHMmXSiOGk69czc+zhs0NF6tEFrY8TZKAl8N/LYAkd4JHVd5E/AsSmw==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@vue/compiler-dom": "3.5.27",
"@vue/compiler-sfc": "3.5.27",
@@ -9560,6 +9620,7 @@
"integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==",
"dev": true,
"license": "ISC",
+ "peer": true,
"bin": {
"yaml": "bin.mjs"
},
@@ -9610,6 +9671,7 @@
"resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz",
"integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
"license": "MIT",
+ "peer": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
diff --git a/scripts/sync-snippets.ts b/scripts/sync-snippets.ts
index 3933727a..4e9cfa8d 100644
--- a/scripts/sync-snippets.ts
+++ b/scripts/sync-snippets.ts
@@ -1,17 +1,29 @@
/**
* Code Snippet Sync Script
*
- * This script syncs code snippets from `.examples.ts/.examples.tsx` files
- * into JSDoc comments containing labeled code fences.
+ * This script syncs code snippets into JSDoc comments and markdown files
+ * containing labeled code fences.
*
- * The script replaces the content inside code fences that have a path#region
- * reference in their info string.
+ * ## Supported Source Files
+ *
+ * - **Full-file inclusion**: Any file type (e.g., `.json`, `.yaml`, `.sh`, `.ts`)
+ * - **Region extraction**: Only `.ts` and `.tsx` files (using `//#region` markers)
*
* ## Code Fence Format
*
+ * Full-file inclusion (any file type):
+ *
+ * ``````typescript
+ * ```json source="./config.json"
+ * // entire file content is synced here
+ * ```
+ * ``````
+ *
+ * Region extraction (.ts/.tsx only):
+ *
* ``````typescript
* ```ts source="./path.examples.ts#regionName"
- * // code is synced here
+ * // region content is synced here
* ```
* ``````
*
@@ -55,10 +67,10 @@ interface LabeledCodeFence {
displayName?: string;
/** Relative path to the example file (e.g., "./app.examples.ts") */
examplePath: string;
- /** Region name (e.g., "App_basicUsage") */
- regionName: string;
- /** Language from the code fence (ts or tsx) */
- language: "ts" | "tsx";
+ /** Region name (e.g., "App_basicUsage"), or undefined for whole file */
+ regionName?: string;
+ /** Language from the code fence (e.g., "ts", "json", "yaml") */
+ language: string;
/** Character index of the opening fence line start */
openingFenceStart: number;
/** Character index after the opening fence line (after newline) */
@@ -69,22 +81,12 @@ interface LabeledCodeFence {
linePrefix: string;
}
-/**
- * Represents extracted region content from an example file.
- */
-interface RegionContent {
- /** The dedented code content */
- code: string;
- /** Language for code fence (ts or tsx) */
- language: "ts" | "tsx";
-}
-
/**
* Cache for example file regions to avoid re-reading files.
- * Key: absolute example file path
- * Value: Map
+ * Key: `${absoluteExamplePath}#${regionName}` (empty regionName for whole file)
+ * Value: extracted code string
*/
-type RegionCache = Map>;
+type RegionCache = Map;
/**
* Processing result for a source file.
@@ -97,18 +99,20 @@ interface FileProcessingResult {
}
// JSDoc patterns - for code fences inside JSDoc comments with " * " prefix
-// Matches: ``` [displayName] source="#"
+// Matches: ``` [displayName] source="" or source="#"
// Example: " * ```ts my-app.ts source="./app.examples.ts#App_basicUsage""
// Example: " * ```ts source="./app.examples.ts#App_basicUsage""
+// Example: " * ```ts source="./complete-example.ts"" (whole file)
const JSDOC_LABELED_FENCE_PATTERN =
- /^(\s*\*\s*)```(ts|tsx)(?:\s+(\S+))?\s+source="([^"#]+)#([^"]+)"/;
+ /^(\s*\*\s*)```(\w+)(?:\s+(\S+))?\s+source="([^"#]+)(?:#([^"]+))?"/;
const JSDOC_CLOSING_FENCE_PATTERN = /^(\s*\*\s*)```\s*$/;
// Markdown patterns - for plain code fences in markdown files (no prefix)
-// Matches: ``` [displayName] source="#"
+// Matches: ``` [displayName] source="" or source="#"
// Example: ```tsx source="./patterns.tsx#chunkedDataServer"
+// Example: ```tsx source="./complete-example.tsx" (whole file)
const MARKDOWN_LABELED_FENCE_PATTERN =
- /^```(ts|tsx)(?:\s+(\S+))?\s+source="([^"#]+)#([^"]+)"/;
+ /^```(\w+)(?:\s+(\S+))?\s+source="([^"#]+)(?:#([^"]+))?"/;
const MARKDOWN_CLOSING_FENCE_PATTERN = /^```\s*$/;
/**
@@ -184,7 +188,7 @@ function findLabeledCodeFences(
displayName,
examplePath,
regionName,
- language: language as "ts" | "tsx",
+ language,
openingFenceStart,
openingFenceEnd,
closingFenceStart,
@@ -242,6 +246,14 @@ function extractRegion(
regionName: string,
examplePath: string,
): string {
+ // Region extraction only supported for .ts/.tsx files (uses //#region syntax)
+ if (!examplePath.endsWith(".ts") && !examplePath.endsWith(".tsx")) {
+ throw new Error(
+ `Region extraction (#${regionName}) is only supported for .ts/.tsx files. ` +
+ `Use full-file inclusion (without #regionName) for: ${examplePath}`,
+ );
+ }
+
const regionStart = `//#region ${regionName}`;
const regionEnd = `//#endregion ${regionName}`;
@@ -281,53 +293,46 @@ function extractRegion(
* Get or load a region from the cache.
* @param sourceFilePath The source file requesting the region
* @param examplePath The relative path to the example file
- * @param regionName The region name to extract
+ * @param regionName The region name to extract, or undefined for whole file
* @param cache The region cache
- * @returns The region content
+ * @returns The extracted code string
*/
function getOrLoadRegion(
sourceFilePath: string,
examplePath: string,
- regionName: string,
+ regionName: string | undefined,
cache: RegionCache,
-): RegionContent {
+): string {
// Resolve the example path relative to the source file
const sourceDir = dirname(sourceFilePath);
const absoluteExamplePath = resolve(sourceDir, examplePath);
- // Check cache first
- let fileCache = cache.get(absoluteExamplePath);
- if (fileCache) {
- const cached = fileCache.get(regionName);
- if (cached) {
- return cached;
- }
- }
+ // File content is always cached with key ending in "#" (empty region)
+ const fileKey = `${absoluteExamplePath}#`;
+ let fileContent = cache.get(fileKey);
- // Load the example file
- let exampleContent: string;
- try {
- exampleContent = readFileSync(absoluteExamplePath, "utf-8");
- } catch {
- throw new Error(`Example file not found: ${absoluteExamplePath}`);
+ if (fileContent === undefined) {
+ try {
+ fileContent = readFileSync(absoluteExamplePath, "utf-8").trim();
+ } catch {
+ throw new Error(`Example file not found: ${absoluteExamplePath}`);
+ }
+ cache.set(fileKey, fileContent);
}
- // Initialize file cache if needed
- if (!fileCache) {
- fileCache = new Map();
- cache.set(absoluteExamplePath, fileCache);
+ // If no region name, return whole file
+ if (!regionName) {
+ return fileContent;
}
- // Determine language from file extension
- const language: "ts" | "tsx" = absoluteExamplePath.endsWith(".tsx")
- ? "tsx"
- : "ts";
+ // Extract region from cached file content, cache the result
+ const regionKey = `${absoluteExamplePath}#${regionName}`;
+ let regionContent = cache.get(regionKey);
- // Extract the region
- const code = extractRegion(exampleContent, regionName, examplePath);
-
- const regionContent: RegionContent = { code, language };
- fileCache.set(regionName, regionContent);
+ if (regionContent === undefined) {
+ regionContent = extractRegion(fileContent, regionName, examplePath);
+ cache.set(regionKey, regionContent);
+ }
return regionContent;
}
@@ -393,17 +398,14 @@ function processFile(
const fence = fences[i];
try {
- const regionContent = getOrLoadRegion(
+ const code = getOrLoadRegion(
filePath,
fence.examplePath,
fence.regionName,
cache,
);
- const formattedCode = formatCodeLines(
- regionContent.code,
- fence.linePrefix,
- );
+ const formattedCode = formatCodeLines(code, fence.linePrefix);
// Replace content between opening fence end and closing fence start
content =
diff --git a/tests/e2e/servers.spec.ts b/tests/e2e/servers.spec.ts
index 0f2d8ec1..420da936 100644
--- a/tests/e2e/servers.spec.ts
+++ b/tests/e2e/servers.spec.ts
@@ -17,6 +17,7 @@ const DYNAMIC_MASKS: Record = {
"cohort-heatmap": ['[class*="heatmapWrapper"]'], // Heatmap grid (random data)
"customer-segmentation": [".chart-container"], // Scatter plot (random data)
"debug-server": ["#event-log", "#callback-table-body"], // Event log and callback counts (dynamic)
+ quickstart: ["#server-time"], // Server time display
"say-server": [".playBtn", ".playOverlayBtn"], // Play buttons may have different states
shadertoy: ["#canvas"], // WebGL shader canvas (animated)
"system-monitor": [
@@ -101,6 +102,11 @@ const ALL_SERVERS = [
{ key: "map-server", name: "CesiumJS Map Server", dir: "map-server" },
{ key: "pdf-server", name: "PDF Server", dir: "pdf-server" },
{ key: "qr-server", name: "QR Code Server", dir: "qr-server" },
+ {
+ key: "quickstart",
+ name: "Quickstart MCP App Server",
+ dir: "quickstart",
+ },
{ key: "say-server", name: "Say Demo", dir: "say-server" },
{
key: "scenario-modeler",
diff --git a/tests/e2e/servers.spec.ts-snapshots/quickstart.png b/tests/e2e/servers.spec.ts-snapshots/quickstart.png
new file mode 100644
index 00000000..18ad40e8
Binary files /dev/null and b/tests/e2e/servers.spec.ts-snapshots/quickstart.png differ