diff --git a/docs/server.md b/docs/server.md index 105871bc1..941ca8740 100644 --- a/docs/server.md +++ b/docs/server.md @@ -4,93 +4,112 @@ title: Server ## Server overview -This SDK lets you build MCP servers in TypeScript and connect them to different transports. For most use cases you will use `McpServer` from `@modelcontextprotocol/server` and choose one of: +This guide covers SDK usage for building MCP servers in TypeScript. For protocol-level details and message formats, see the [MCP specification](https://modelcontextprotocol.io/specification/latest/). -- **Streamable HTTP** (recommended for remote servers) -- **HTTP + SSE** (deprecated, for backwards compatibility only) -- **stdio** (for local, process‑spawned integrations) +Building a server takes three steps: -For a complete, runnable example server, see: +1. Create an {@linkcode @modelcontextprotocol/server!server/mcp.McpServer | McpServer} and register your [tools, resources, and prompts](#tools-resources-and-prompts). +2. Create a transport — [Streamable HTTP](#streamable-http) for remote servers or [stdio](#stdio) for local, process‑spawned integrations. +3. Wire the transport into your HTTP framework (or use stdio directly) and call `server.connect(transport)`. -- [`simpleStreamableHttp.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/simpleStreamableHttp.ts) – feature‑rich Streamable HTTP server -- [`jsonResponseStreamableHttp.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/jsonResponseStreamableHttp.ts) – Streamable HTTP with JSON response mode -- [`simpleStatelessStreamableHttp.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/simpleStatelessStreamableHttp.ts) – stateless Streamable HTTP server -- [`simpleSseServer.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/simpleSseServer.ts) – deprecated HTTP+SSE transport -- [`sseAndStreamableHttpCompatibleServer.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/sseAndStreamableHttpCompatibleServer.ts) – backwards‑compatible server for old and new clients +The sections below cover each of these. For a feature‑rich starting point, see [`simpleStreamableHttp.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/simpleStreamableHttp.ts) — remove what you don't need and register your own tools, resources, and prompts. For stateless or JSON‑response‑mode alternatives, see the examples linked in [Transports](#transports) below. ## Transports ### Streamable HTTP -Streamable HTTP is the modern, fully featured transport. It supports: +Streamable HTTP is the HTTP‑based transport. It supports: - Request/response over HTTP POST - Server‑to‑client notifications over SSE (when enabled) - Optional JSON‑only response mode with no SSE - Session management and resumability -Key examples: +A minimal stateful setup: -- [`simpleStreamableHttp.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/simpleStreamableHttp.ts) – sessions, logging, tasks, elicitation, auth hooks -- [`jsonResponseStreamableHttp.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/jsonResponseStreamableHttp.ts) – `enableJsonResponse: true`, no SSE -- [`standaloneSseWithGetStreamableHttp.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/standaloneSseWithGetStreamableHttp.ts) – notifications with Streamable HTTP GET + SSE +```ts source="../examples/server/src/serverGuide.examples.ts#streamableHttp_stateful" +const server = new McpServer({ name: 'my-server', version: '1.0.0' }); -See the MCP spec for full transport details: `https://modelcontextprotocol.io/specification/2025-11-25/basic/transports` +const transport = new NodeStreamableHTTPServerTransport({ + sessionIdGenerator: () => randomUUID() +}); + +await server.connect(transport); +``` -### Stateless vs stateful sessions +> [!NOTE] +> For full runnable examples, see [`simpleStreamableHttp.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/simpleStreamableHttp.ts) (sessions, logging, tasks, elicitation, auth hooks), [`jsonResponseStreamableHttp.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/jsonResponseStreamableHttp.ts) (`enableJsonResponse: true`, no SSE), and [`standaloneSseWithGetStreamableHttp.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/standaloneSseWithGetStreamableHttp.ts) (notifications with Streamable HTTP GET + SSE). +> +> For protocol details, see [Transports](https://modelcontextprotocol.io/specification/latest/basic/transports) in the MCP specification. + +#### Stateless vs stateful sessions Streamable HTTP can run: - **Stateless** – no session tracking, ideal for simple API‑style servers. - **Stateful** – sessions have IDs, and you can enable resumability and advanced features. -Examples: +The key difference is the `sessionIdGenerator` option. Pass `undefined` for stateless mode: -- Stateless Streamable HTTP: [`simpleStatelessStreamableHttp.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/simpleStatelessStreamableHttp.ts) -- Stateful with resumability: [`simpleStreamableHttp.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/simpleStreamableHttp.ts) +```ts source="../examples/server/src/serverGuide.examples.ts#streamableHttp_stateless" +const server = new McpServer({ name: 'my-server', version: '1.0.0' }); -### Deprecated HTTP + SSE +const transport = new NodeStreamableHTTPServerTransport({ + sessionIdGenerator: undefined +}); -The older HTTP+SSE transport (protocol version 2024‑11‑05) is supported only for backwards compatibility. New implementations should prefer Streamable HTTP. +await server.connect(transport); +``` -Examples: +> [!NOTE] +> For full runnable examples, see [`simpleStatelessStreamableHttp.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/simpleStatelessStreamableHttp.ts) (stateless) and [`simpleStreamableHttp.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/simpleStreamableHttp.ts) (stateful with resumability). -- Legacy SSE server: [`simpleSseServer.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/simpleSseServer.ts) -- Backwards‑compatible server (Streamable HTTP + SSE): - [`sseAndStreamableHttpCompatibleServer.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/sseAndStreamableHttpCompatibleServer.ts) +#### JSON response mode -## Running your server +If you do not need SSE streaming, set `enableJsonResponse: true`. The server will return plain JSON responses to every POST and reject GET requests with `405`: -For a minimal “getting started” experience: +```ts source="../examples/server/src/serverGuide.examples.ts#streamableHttp_jsonResponse" +const server = new McpServer({ name: 'my-server', version: '1.0.0' }); -1. Start from [`simpleStreamableHttp.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/simpleStreamableHttp.ts). -2. Remove features you do not need (tasks, advanced logging, OAuth, etc.). -3. Register your own tools, resources and prompts. +const transport = new NodeStreamableHTTPServerTransport({ + sessionIdGenerator: () => randomUUID(), + enableJsonResponse: true +}); -For more detailed patterns (stateless vs stateful, JSON response mode, CORS, DNS rebind protection), see the examples above and the MCP spec sections on transports. +await server.connect(transport); +``` -## DNS rebinding protection +> [!NOTE] +> For a full runnable example, see [`jsonResponseStreamableHttp.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/jsonResponseStreamableHttp.ts). -MCP servers running on localhost are vulnerable to DNS rebinding attacks. Use `createMcpExpressApp()` to create an Express app with DNS rebinding protection enabled by default: +### stdio -```typescript -import { createMcpExpressApp } from '@modelcontextprotocol/express'; +For local, process‑spawned integrations (Claude Desktop, CLI tools), use {@linkcode @modelcontextprotocol/server!server/stdio.StdioServerTransport | StdioServerTransport}: -// Protection auto-enabled (default host is 127.0.0.1) +```ts source="../examples/server/src/serverGuide.examples.ts#stdio_basic" +const server = new McpServer({ name: 'my-server', version: '1.0.0' }); +const transport = new StdioServerTransport(); +await server.connect(transport); +``` + +## DNS rebinding protection + +MCP servers running on localhost are vulnerable to DNS rebinding attacks. Use `createMcpExpressApp()` from `@modelcontextprotocol/express` to create an Express app with DNS rebinding protection enabled by default: + +```ts source="../examples/server/src/serverGuide.examples.ts#dnsRebinding_basic" +// Default: DNS rebinding protection auto-enabled (host is 127.0.0.1) const app = createMcpExpressApp(); -// Protection auto-enabled for localhost -const app = createMcpExpressApp({ host: 'localhost' }); +// DNS rebinding protection also auto-enabled for localhost +const appLocal = createMcpExpressApp({ host: 'localhost' }); -// No auto protection when binding to all interfaces, unless you provide allowedHosts -const app = createMcpExpressApp({ host: '0.0.0.0' }); +// No automatic protection when binding to all interfaces +const appOpen = createMcpExpressApp({ host: '0.0.0.0' }); ``` When binding to `0.0.0.0` / `::`, provide an allow-list of hosts: -```typescript -import { createMcpExpressApp } from '@modelcontextprotocol/express'; - +```ts source="../examples/server/src/serverGuide.examples.ts#dnsRebinding_allowedHosts" const app = createMcpExpressApp({ host: '0.0.0.0', allowedHosts: ['localhost', '127.0.0.1', 'myhost.local'] @@ -103,19 +122,19 @@ const app = createMcpExpressApp({ Tools let MCP clients ask your server to take actions. They are usually the main way that LLMs call into your application. -A typical registration with `registerTool` looks like this: +A typical registration with {@linkcode @modelcontextprotocol/server!server/mcp.McpServer#registerTool | registerTool}: -```typescript +```ts source="../examples/server/src/serverGuide.examples.ts#registerTool_basic" server.registerTool( 'calculate-bmi', { title: 'BMI Calculator', description: 'Calculate Body Mass Index', - inputSchema: { + inputSchema: z.object({ weightKg: z.number(), heightM: z.number() - }, - outputSchema: { bmi: z.number() } + }), + outputSchema: z.object({ bmi: z.number() }) }, async ({ weightKg, heightM }) => { const output = { bmi: weightKg / (heightM * heightM) }; @@ -127,24 +146,84 @@ server.registerTool( ); ``` -This snippet is illustrative only; for runnable servers that expose tools, see: +> [!NOTE] +> For full runnable examples, see [`simpleStreamableHttp.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/simpleStreamableHttp.ts) and [`toolWithSampleServer.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/toolWithSampleServer.ts). +> +> For protocol details, see [Tools](https://modelcontextprotocol.io/specification/latest/server/tools) in the MCP specification. + +#### `ResourceLink` outputs + +Tools can return `resource_link` content items to reference large resources without embedding them directly, allowing clients to fetch only what they need: + +```ts source="../examples/server/src/serverGuide.examples.ts#registerTool_resourceLink" +server.registerTool( + 'list-files', + { + title: 'List Files', + description: 'Returns files as resource links without embedding content' + }, + async (): Promise => { + const links: ResourceLink[] = [ + { + type: 'resource_link', + uri: 'file:///projects/readme.md', + name: 'README', + mimeType: 'text/markdown' + }, + { + type: 'resource_link', + uri: 'file:///projects/config.json', + name: 'Config', + mimeType: 'application/json' + } + ]; + return { content: links }; + } +); +``` + +> [!NOTE] +> For a full runnable example with `ResourceLink` outputs, see [`simpleStreamableHttp.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/simpleStreamableHttp.ts). + +#### Logging + +Use `ctx.mcpReq.log(level, data)` (from {@linkcode @modelcontextprotocol/server!index.ServerContext | ServerContext}) inside a tool handler to send structured log messages to the client. The server must declare the `logging` capability: -- [`simpleStreamableHttp.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/simpleStreamableHttp.ts) -- [`toolWithSampleServer.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/toolWithSampleServer.ts) +```ts source="../examples/server/src/serverGuide.examples.ts#logging_capability" +const server = new McpServer({ name: 'my-server', version: '1.0.0' }, { capabilities: { logging: {} } }); +``` -#### ResourceLink outputs +Then log from any tool callback: -Tools can return `resource_link` content items to reference large resources without embedding them directly, allowing clients to fetch only what they need. +```ts source="../examples/server/src/serverGuide.examples.ts#registerTool_logging" +server.registerTool( + 'fetch-data', + { + description: 'Fetch data from an API', + inputSchema: z.object({ url: z.string() }) + }, + async ({ url }, ctx): Promise => { + await ctx.mcpReq.log('info', `Fetching ${url}`); + const res = await fetch(url); + await ctx.mcpReq.log('debug', `Response status: ${res.status}`); + const text = await res.text(); + return { content: [{ type: 'text', text }] }; + } +); +``` -The README’s `list-files` example shows the pattern conceptually; for concrete usage, see the Streamable HTTP examples in `examples/server/src`. +> [!NOTE] +> For logging in a full server, see [`simpleStreamableHttp.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/simpleStreamableHttp.ts) and [`jsonResponseStreamableHttp.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/jsonResponseStreamableHttp.ts). +> +> For protocol details, see [Logging](https://modelcontextprotocol.io/specification/latest/server/utilities/logging) in the MCP specification. ### Resources Resources expose data to clients, but should not perform heavy computation or side‑effects. They are ideal for configuration, documents, or other reference data. -Conceptually, you might register resources like: +A static resource at a fixed URI: -```typescript +```ts source="../examples/server/src/serverGuide.examples.ts#registerResource_static" server.registerResource( 'config', 'config://app', @@ -159,9 +238,39 @@ server.registerResource( ); ``` -Dynamic resources use `ResourceTemplate` and can support completions on path parameters. For full runnable examples of resources: +Dynamic resources use {@linkcode @modelcontextprotocol/server!server/mcp.ResourceTemplate | ResourceTemplate} and can support completions on path parameters: -- [`simpleStreamableHttp.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/simpleStreamableHttp.ts) +```ts source="../examples/server/src/serverGuide.examples.ts#registerResource_template" +server.registerResource( + 'user-profile', + new ResourceTemplate('user://{userId}/profile', { + list: async () => ({ + resources: [ + { uri: 'user://123/profile', name: 'Alice' }, + { uri: 'user://456/profile', name: 'Bob' } + ] + }) + }), + { + title: 'User Profile', + description: 'User profile data', + mimeType: 'application/json' + }, + async (uri, { userId }) => ({ + contents: [ + { + uri: uri.href, + text: JSON.stringify({ userId, name: 'Example User' }) + } + ] + }) +); +``` + +> [!NOTE] +> For full runnable examples of resources, see [`simpleStreamableHttp.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/simpleStreamableHttp.ts). +> +> For protocol details, see [Resources](https://modelcontextprotocol.io/specification/latest/server/resources) in the MCP specification. ### Prompts @@ -169,20 +278,22 @@ Prompts are reusable templates that help humans (or client UIs) talk to models i A minimal prompt: -```typescript +```ts source="../examples/server/src/serverGuide.examples.ts#registerPrompt_basic" server.registerPrompt( 'review-code', { title: 'Code Review', description: 'Review code for best practices and potential issues', - argsSchema: { code: z.string() } + argsSchema: z.object({ + code: z.string() + }) }, ({ code }) => ({ messages: [ { - role: 'user', + role: 'user' as const, content: { - type: 'text', + type: 'text' as const, text: `Please review this code:\n\n${code}` } } @@ -191,35 +302,55 @@ server.registerPrompt( ); ``` -For prompts integrated into a full server, see: - -- [`simpleStreamableHttp.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/simpleStreamableHttp.ts) +> [!NOTE] +> For prompts integrated into a full server, see [`simpleStreamableHttp.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/simpleStreamableHttp.ts). +> +> For protocol details, see [Prompts](https://modelcontextprotocol.io/specification/latest/server/prompts) in the MCP specification. ### Completions -Both prompts and resources can support argument completions. On the client side, you use `client.complete()` with a reference to the prompt or resource and the partially‑typed argument. - -See the MCP spec sections on prompts and resources for complete details, and [`simpleStreamableHttp.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/client/src/simpleStreamableHttp.ts) for client‑side usage patterns. - -### Display names and metadata - -Tools, resources and prompts support a `title` field for human‑readable names. Older APIs can also attach `annotations.title`. To compute the correct display name on the client, use: +Both prompts and resources can support argument completions. Wrap a field in the `argsSchema` with {@linkcode @modelcontextprotocol/server!server/completable.completable | completable()} to provide autocompletion suggestions: -- `getDisplayName` from `@modelcontextprotocol/client` - -## Multi‑node deployment patterns - -The SDK supports multi‑node deployments using Streamable HTTP. The high‑level patterns and diagrams live with the runnable server examples: - -- [`examples/server/README.md`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/README.md#multi-node-deployment-patterns) +```ts source="../examples/server/src/serverGuide.examples.ts#registerPrompt_completion" +server.registerPrompt( + 'review-code', + { + title: 'Code Review', + description: 'Review code for best practices', + argsSchema: z.object({ + language: completable(z.string().describe('Programming language'), value => + ['typescript', 'javascript', 'python', 'rust', 'go'].filter(lang => lang.startsWith(value)) + ) + }) + }, + ({ language }) => ({ + messages: [ + { + role: 'user' as const, + content: { + type: 'text' as const, + text: `Review this ${language} code for best practices.` + } + } + ] + }) +); +``` -## Backwards compatibility +For client-side completion usage, see the [Client guide](client.md). -To handle both modern and legacy clients: +## More server features -- Run a backwards‑compatible server: - - [`sseAndStreamableHttpCompatibleServer.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/sseAndStreamableHttpCompatibleServer.ts) -- Use a client that falls back from Streamable HTTP to SSE: - - [`streamableHttpWithSseFallbackClient.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/client/src/streamableHttpWithSseFallbackClient.ts) +The sections above cover the essentials. The SDK supports several additional capabilities — each is demonstrated in the runnable examples and covered in more detail in the linked references. -For the detailed protocol rules, see the “Backwards compatibility” section of the MCP spec. +| Feature | Description | Reference | +|---------|-------------|-----------| +| Web Standard transport | Deploy on Cloudflare Workers, Deno, or Bun | [`honoWebStandardStreamableHttp.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/honoWebStandardStreamableHttp.ts) | +| Session management | Per-session transport routing, initialization, and cleanup | [`simpleStreamableHttp.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/simpleStreamableHttp.ts) | +| Resumability | Replay missed SSE events via an event store | [`inMemoryEventStore.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/inMemoryEventStore.ts) | +| CORS | Expose MCP headers (`mcp-session-id`, etc.) for browser clients | [`simpleStreamableHttp.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/simpleStreamableHttp.ts) | +| Tool annotations | Hint whether tools are read-only, destructive, etc. | [`simpleStreamableHttp.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/simpleStreamableHttp.ts) | +| Elicitation | Request user input (forms or URLs) during tool execution | [Capabilities guide](capabilities.md#elicitation), [`elicitationFormExample.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/elicitationFormExample.ts) | +| Sampling | Request LLM completions from the connected client | [Capabilities guide](capabilities.md#sampling), [`toolWithSampleServer.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/toolWithSampleServer.ts) | +| Tasks (experimental) | Long-running operations with polling and resumption | [Capabilities guide](capabilities.md#task-based-execution-experimental), [`simpleStreamableHttp.ts`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/src/simpleStreamableHttp.ts) | +| Multi‑node deployment | Stateless, persistent‑storage, and distributed routing patterns | [`examples/server/README.md`](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/examples/server/README.md#multi-node-deployment-patterns) | diff --git a/examples/server/src/serverGuide.examples.ts b/examples/server/src/serverGuide.examples.ts new file mode 100644 index 000000000..271d0d39b --- /dev/null +++ b/examples/server/src/serverGuide.examples.ts @@ -0,0 +1,306 @@ +/** + * Type-checked examples for docs/server.md. + * + * Regions are synced into markdown code fences via `pnpm sync:snippets`. + * Each function wraps a single region. The function name matches the region name. + * + * @module + */ + +import { randomUUID } from 'node:crypto'; + +import { createMcpExpressApp } from '@modelcontextprotocol/express'; +import { NodeStreamableHTTPServerTransport } from '@modelcontextprotocol/node'; +import type { CallToolResult, ResourceLink } from '@modelcontextprotocol/server'; +import { completable, McpServer, ResourceTemplate, StdioServerTransport } from '@modelcontextprotocol/server'; +import * as z from 'zod/v4'; + +// --------------------------------------------------------------------------- +// Tools, resources, and prompts +// --------------------------------------------------------------------------- + +/** Example: Registering a tool with inputSchema, outputSchema, and structuredContent. */ +function registerTool_basic(server: McpServer) { + //#region registerTool_basic + server.registerTool( + 'calculate-bmi', + { + title: 'BMI Calculator', + description: 'Calculate Body Mass Index', + inputSchema: z.object({ + weightKg: z.number(), + heightM: z.number() + }), + outputSchema: z.object({ bmi: z.number() }) + }, + async ({ weightKg, heightM }) => { + const output = { bmi: weightKg / (heightM * heightM) }; + return { + content: [{ type: 'text', text: JSON.stringify(output) }], + structuredContent: output + }; + } + ); + //#endregion registerTool_basic +} + +/** Example: Tool returning resource_link content items. */ +function registerTool_resourceLink(server: McpServer) { + //#region registerTool_resourceLink + server.registerTool( + 'list-files', + { + title: 'List Files', + description: 'Returns files as resource links without embedding content' + }, + async (): Promise => { + const links: ResourceLink[] = [ + { + type: 'resource_link', + uri: 'file:///projects/readme.md', + name: 'README', + mimeType: 'text/markdown' + }, + { + type: 'resource_link', + uri: 'file:///projects/config.json', + name: 'Config', + mimeType: 'application/json' + } + ]; + return { content: links }; + } + ); + //#endregion registerTool_resourceLink +} + +/** Example: Registering a static resource at a fixed URI. */ +function registerResource_static(server: McpServer) { + //#region registerResource_static + server.registerResource( + 'config', + 'config://app', + { + title: 'Application Config', + description: 'Application configuration data', + mimeType: 'text/plain' + }, + async uri => ({ + contents: [{ uri: uri.href, text: 'App configuration here' }] + }) + ); + //#endregion registerResource_static +} + +/** Example: Dynamic resource with ResourceTemplate and listing. */ +function registerResource_template(server: McpServer) { + //#region registerResource_template + server.registerResource( + 'user-profile', + new ResourceTemplate('user://{userId}/profile', { + list: async () => ({ + resources: [ + { uri: 'user://123/profile', name: 'Alice' }, + { uri: 'user://456/profile', name: 'Bob' } + ] + }) + }), + { + title: 'User Profile', + description: 'User profile data', + mimeType: 'application/json' + }, + async (uri, { userId }) => ({ + contents: [ + { + uri: uri.href, + text: JSON.stringify({ userId, name: 'Example User' }) + } + ] + }) + ); + //#endregion registerResource_template +} + +/** Example: Registering a prompt with argsSchema. */ +function registerPrompt_basic(server: McpServer) { + //#region registerPrompt_basic + server.registerPrompt( + 'review-code', + { + title: 'Code Review', + description: 'Review code for best practices and potential issues', + argsSchema: z.object({ + code: z.string() + }) + }, + ({ code }) => ({ + messages: [ + { + role: 'user' as const, + content: { + type: 'text' as const, + text: `Please review this code:\n\n${code}` + } + } + ] + }) + ); + //#endregion registerPrompt_basic +} + +/** Example: Prompt with completable argsSchema for autocompletion. */ +function registerPrompt_completion(server: McpServer) { + //#region registerPrompt_completion + server.registerPrompt( + 'review-code', + { + title: 'Code Review', + description: 'Review code for best practices', + argsSchema: z.object({ + language: completable(z.string().describe('Programming language'), value => + ['typescript', 'javascript', 'python', 'rust', 'go'].filter(lang => lang.startsWith(value)) + ) + }) + }, + ({ language }) => ({ + messages: [ + { + role: 'user' as const, + content: { + type: 'text' as const, + text: `Review this ${language} code for best practices.` + } + } + ] + }) + ); + //#endregion registerPrompt_completion +} + +// --------------------------------------------------------------------------- +// Logging +// --------------------------------------------------------------------------- + +/** Example: Server with logging capability + tool that logs progress messages. */ +function registerTool_logging() { + //#region logging_capability + const server = new McpServer({ name: 'my-server', version: '1.0.0' }, { capabilities: { logging: {} } }); + //#endregion logging_capability + + //#region registerTool_logging + server.registerTool( + 'fetch-data', + { + description: 'Fetch data from an API', + inputSchema: z.object({ url: z.string() }) + }, + async ({ url }, ctx): Promise => { + await ctx.mcpReq.log('info', `Fetching ${url}`); + const res = await fetch(url); + await ctx.mcpReq.log('debug', `Response status: ${res.status}`); + const text = await res.text(); + return { content: [{ type: 'text', text }] }; + } + ); + //#endregion registerTool_logging + return server; +} + +// --------------------------------------------------------------------------- +// Transports +// --------------------------------------------------------------------------- + +/** Example: Stateful Streamable HTTP transport with session management. */ +async function streamableHttp_stateful() { + //#region streamableHttp_stateful + const server = new McpServer({ name: 'my-server', version: '1.0.0' }); + + const transport = new NodeStreamableHTTPServerTransport({ + sessionIdGenerator: () => randomUUID() + }); + + await server.connect(transport); + //#endregion streamableHttp_stateful +} + +/** Example: Stateless Streamable HTTP transport (no session persistence). */ +async function streamableHttp_stateless() { + //#region streamableHttp_stateless + const server = new McpServer({ name: 'my-server', version: '1.0.0' }); + + const transport = new NodeStreamableHTTPServerTransport({ + sessionIdGenerator: undefined + }); + + await server.connect(transport); + //#endregion streamableHttp_stateless +} + +/** Example: Streamable HTTP with JSON response mode (no SSE). */ +async function streamableHttp_jsonResponse() { + //#region streamableHttp_jsonResponse + const server = new McpServer({ name: 'my-server', version: '1.0.0' }); + + const transport = new NodeStreamableHTTPServerTransport({ + sessionIdGenerator: () => randomUUID(), + enableJsonResponse: true + }); + + await server.connect(transport); + //#endregion streamableHttp_jsonResponse +} + +/** Example: stdio transport for local process-spawned integrations. */ +async function stdio_basic() { + //#region stdio_basic + const server = new McpServer({ name: 'my-server', version: '1.0.0' }); + const transport = new StdioServerTransport(); + await server.connect(transport); + //#endregion stdio_basic +} + +// --------------------------------------------------------------------------- +// DNS rebinding protection +// --------------------------------------------------------------------------- + +/** Example: createMcpExpressApp with different host bindings. */ +function dnsRebinding_basic() { + //#region dnsRebinding_basic + // Default: DNS rebinding protection auto-enabled (host is 127.0.0.1) + const app = createMcpExpressApp(); + + // DNS rebinding protection also auto-enabled for localhost + const appLocal = createMcpExpressApp({ host: 'localhost' }); + + // No automatic protection when binding to all interfaces + const appOpen = createMcpExpressApp({ host: '0.0.0.0' }); + //#endregion dnsRebinding_basic + return { app, appLocal, appOpen }; +} + +/** Example: createMcpExpressApp with allowedHosts for non-localhost binding. */ +function dnsRebinding_allowedHosts() { + //#region dnsRebinding_allowedHosts + const app = createMcpExpressApp({ + host: '0.0.0.0', + allowedHosts: ['localhost', '127.0.0.1', 'myhost.local'] + }); + //#endregion dnsRebinding_allowedHosts + return app; +} + +// Suppress unused-function warnings (functions exist solely for type-checking) +void registerTool_basic; +void registerTool_resourceLink; +void registerTool_logging; +void registerResource_static; +void registerResource_template; +void registerPrompt_basic; +void registerPrompt_completion; +void streamableHttp_stateful; +void streamableHttp_stateless; +void streamableHttp_jsonResponse; +void stdio_basic; +void dnsRebinding_basic; +void dnsRebinding_allowedHosts;