diff --git a/packages/middleware/node/src/streamableHttp.examples.ts b/packages/middleware/node/src/streamableHttp.examples.ts new file mode 100644 index 000000000..fb4bef841 --- /dev/null +++ b/packages/middleware/node/src/streamableHttp.examples.ts @@ -0,0 +1,56 @@ +/** + * Type-checked examples for `streamableHttp.ts`. + * + * These examples are synced into JSDoc comments via the sync-snippets script. + * Each function's region markers define the code snippet that appears in the docs. + * + * @module + */ + +import { randomUUID } from 'node:crypto'; +import type { IncomingMessage, ServerResponse } from 'node:http'; + +import { McpServer } from '@modelcontextprotocol/server'; + +import { NodeStreamableHTTPServerTransport } from './streamableHttp.js'; + +/** + * Example: Stateful Streamable HTTP transport (Node.js). + */ +async function NodeStreamableHTTPServerTransport_stateful() { + //#region NodeStreamableHTTPServerTransport_stateful + const server = new McpServer({ name: 'my-server', version: '1.0.0' }); + + const transport = new NodeStreamableHTTPServerTransport({ + sessionIdGenerator: () => randomUUID() + }); + + await server.connect(transport); + //#endregion NodeStreamableHTTPServerTransport_stateful +} + +/** + * Example: Stateless Streamable HTTP transport (Node.js). + */ +async function NodeStreamableHTTPServerTransport_stateless() { + //#region NodeStreamableHTTPServerTransport_stateless + const transport = new NodeStreamableHTTPServerTransport({ + sessionIdGenerator: undefined + }); + //#endregion NodeStreamableHTTPServerTransport_stateless + return transport; +} + +// Stubs for Express-style app +declare const app: { post(path: string, handler: (req: IncomingMessage & { body?: unknown }, res: ServerResponse) => void): void }; + +/** + * Example: Using with a pre-parsed request body (e.g. Express). + */ +function NodeStreamableHTTPServerTransport_express(transport: NodeStreamableHTTPServerTransport) { + //#region NodeStreamableHTTPServerTransport_express + app.post('/mcp', (req, res) => { + transport.handleRequest(req, res, req.body); + }); + //#endregion NodeStreamableHTTPServerTransport_express +} diff --git a/packages/middleware/node/src/streamableHttp.ts b/packages/middleware/node/src/streamableHttp.ts index 59f8d2b82..68a0c224f 100644 --- a/packages/middleware/node/src/streamableHttp.ts +++ b/packages/middleware/node/src/streamableHttp.ts @@ -28,25 +28,6 @@ export type StreamableHTTPServerTransportOptions = WebStandardStreamableHTTPServ * This is a wrapper around {@linkcode WebStandardStreamableHTTPServerTransport} that provides Node.js HTTP compatibility. * It uses the `@hono/node-server` library to convert between Node.js HTTP and Web Standard APIs. * - * Usage example: - * - * ```typescript - * // Stateful mode - server sets the session ID - * const statefulTransport = new StreamableHTTPServerTransport({ - * sessionIdGenerator: () => randomUUID(), - * }); - * - * // Stateless mode - explicitly set session ID to undefined - * const statelessTransport = new StreamableHTTPServerTransport({ - * sessionIdGenerator: undefined, - * }); - * - * // Using with pre-parsed request body - * app.post('/mcp', (req, res) => { - * transport.handleRequest(req, res, req.body); - * }); - * ``` - * * In stateful mode: * - Session ID is generated and included in response headers * - Session ID is always included in initialization responses @@ -57,6 +38,31 @@ export type StreamableHTTPServerTransportOptions = WebStandardStreamableHTTPServ * In stateless mode: * - No Session ID is included in any responses * - No session validation is performed + * + * @example Stateful setup + * ```ts source="./streamableHttp.examples.ts#NodeStreamableHTTPServerTransport_stateful" + * const server = new McpServer({ name: 'my-server', version: '1.0.0' }); + * + * const transport = new NodeStreamableHTTPServerTransport({ + * sessionIdGenerator: () => randomUUID() + * }); + * + * await server.connect(transport); + * ``` + * + * @example Stateless setup + * ```ts source="./streamableHttp.examples.ts#NodeStreamableHTTPServerTransport_stateless" + * const transport = new NodeStreamableHTTPServerTransport({ + * sessionIdGenerator: undefined + * }); + * ``` + * + * @example Using with a pre-parsed request body (e.g. Express) + * ```ts source="./streamableHttp.examples.ts#NodeStreamableHTTPServerTransport_express" + * app.post('/mcp', (req, res) => { + * transport.handleRequest(req, res, req.body); + * }); + * ``` */ export class NodeStreamableHTTPServerTransport implements Transport { private _webStandardTransport: WebStandardStreamableHTTPServerTransport; diff --git a/packages/server/src/server/completable.examples.ts b/packages/server/src/server/completable.examples.ts new file mode 100644 index 000000000..b0655d243 --- /dev/null +++ b/packages/server/src/server/completable.examples.ts @@ -0,0 +1,46 @@ +/** + * Type-checked examples for `completable.ts`. + * + * These examples are synced into JSDoc comments via the sync-snippets script. + * Each function's region markers define the code snippet that appears in the docs. + * + * @module + */ + +import * as z from 'zod/v4'; + +import { completable } from './completable.js'; +import { McpServer } from './mcp.js'; + +/** + * Example: Using completable() in a prompt registration. + */ +function completable_basicUsage() { + const server = new McpServer({ name: 'my-server', version: '1.0.0' }); + + //#region completable_basicUsage + server.registerPrompt( + 'review-code', + { + title: 'Code Review', + 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.` + } + } + ] + }) + ); + //#endregion completable_basicUsage + return server; +} diff --git a/packages/server/src/server/completable.ts b/packages/server/src/server/completable.ts index 126f88f55..f80cdad95 100644 --- a/packages/server/src/server/completable.ts +++ b/packages/server/src/server/completable.ts @@ -21,6 +21,32 @@ export type CompletableSchema = T & { /** * Wraps a Zod type to provide autocompletion capabilities. Useful for, e.g., prompt arguments in MCP. * + * @example + * ```ts source="./completable.examples.ts#completable_basicUsage" + * server.registerPrompt( + * 'review-code', + * { + * title: 'Code Review', + * 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.` + * } + * } + * ] + * }) + * ); + * ``` + * * @see {@linkcode server/mcp.McpServer.registerPrompt | McpServer.registerPrompt} for using completable schemas in prompt argument definitions */ export function completable(schema: T, complete: CompleteCallback): CompletableSchema { diff --git a/packages/server/src/server/mcp.examples.ts b/packages/server/src/server/mcp.examples.ts new file mode 100644 index 000000000..740c1bf18 --- /dev/null +++ b/packages/server/src/server/mcp.examples.ts @@ -0,0 +1,145 @@ +/** + * Type-checked examples for `mcp.ts`. + * + * These examples are synced into JSDoc comments via the sync-snippets script. + * Each function's region markers define the code snippet that appears in the docs. + * + * @module + */ + +import type { CallToolResult } from '@modelcontextprotocol/core'; +import * as z from 'zod/v4'; + +import { McpServer } from './mcp.js'; +import { StdioServerTransport } from './stdio.js'; + +/** + * Example: Creating a new McpServer. + */ +function McpServer_basicUsage() { + //#region McpServer_basicUsage + const server = new McpServer({ + name: 'my-server', + version: '1.0.0' + }); + //#endregion McpServer_basicUsage + return server; +} + +/** + * Example: Registering a tool with inputSchema and outputSchema. + */ +function McpServer_registerTool_basic(server: McpServer) { + //#region McpServer_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 McpServer_registerTool_basic +} + +/** + * Example: Registering a static resource at a fixed URI. + */ +function McpServer_registerResource_static(server: McpServer) { + //#region McpServer_registerResource_static + server.registerResource( + 'config', + 'config://app', + { + title: 'Application Config', + mimeType: 'text/plain' + }, + async uri => ({ + contents: [{ uri: uri.href, text: 'App configuration here' }] + }) + ); + //#endregion McpServer_registerResource_static +} + +/** + * Example: Registering a prompt with an argument schema. + */ +function McpServer_registerPrompt_basic(server: McpServer) { + //#region McpServer_registerPrompt_basic + server.registerPrompt( + 'review-code', + { + title: 'Code Review', + description: 'Review code for best practices', + 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 McpServer_registerPrompt_basic +} + +/** + * Example: Connecting an McpServer to a stdio transport. + */ +async function McpServer_connect_stdio() { + //#region McpServer_connect_stdio + const server = new McpServer({ name: 'my-server', version: '1.0.0' }); + const transport = new StdioServerTransport(); + await server.connect(transport); + //#endregion McpServer_connect_stdio +} + +/** + * Example: Sending a log message to the client. + */ +async function McpServer_sendLoggingMessage_basic(server: McpServer) { + //#region McpServer_sendLoggingMessage_basic + await server.sendLoggingMessage({ + level: 'info', + data: 'Processing complete' + }); + //#endregion McpServer_sendLoggingMessage_basic +} + +/** + * Example: Logging from inside a tool handler via ctx.mcpReq.log(). + */ +function McpServer_registerTool_logging(server: McpServer) { + //#region McpServer_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 McpServer_registerTool_logging +} diff --git a/packages/server/src/server/mcp.ts b/packages/server/src/server/mcp.ts index 244586e56..316074e2d 100644 --- a/packages/server/src/server/mcp.ts +++ b/packages/server/src/server/mcp.ts @@ -54,6 +54,14 @@ import { Server } from './server.js'; * High-level MCP server that provides a simpler API for working with resources, tools, and prompts. * For advanced usage (like sending notifications or setting custom request handlers), use the underlying * {@linkcode Server} instance available via the {@linkcode McpServer.server | server} property. + * + * @example + * ```ts source="./mcp.examples.ts#McpServer_basicUsage" + * const server = new McpServer({ + * name: 'my-server', + * version: '1.0.0' + * }); + * ``` */ export class McpServer { /** @@ -93,6 +101,13 @@ export class McpServer { * Attaches to the given transport, starts it, and starts listening for messages. * * The `server` object assumes ownership of the {@linkcode Transport}, replacing any callbacks that have already been set, and expects that it is the only user of the {@linkcode Transport} instance going forward. + * + * @example + * ```ts source="./mcp.examples.ts#McpServer_connect_stdio" + * const server = new McpServer({ name: 'my-server', version: '1.0.0' }); + * const transport = new StdioServerTransport(); + * await server.connect(transport); + * ``` */ async connect(transport: Transport): Promise { return await this.server.connect(transport); @@ -549,6 +564,21 @@ export class McpServer { /** * Registers a resource with a config object and callback. * For static resources, use a URI string. For dynamic resources, use a {@linkcode ResourceTemplate}. + * + * @example + * ```ts source="./mcp.examples.ts#McpServer_registerResource_static" + * server.registerResource( + * 'config', + * 'config://app', + * { + * title: 'Application Config', + * mimeType: 'text/plain' + * }, + * async uri => ({ + * contents: [{ uri: uri.href, text: 'App configuration here' }] + * }) + * ); + * ``` */ registerResource(name: string, uriOrTemplate: string, config: ResourceMetadata, readCallback: ReadResourceCallback): RegisteredResource; registerResource( @@ -812,6 +842,29 @@ export class McpServer { /** * Registers a tool with a config object and callback. + * + * @example + * ```ts source="./mcp.examples.ts#McpServer_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 + * }; + * } + * ); + * ``` */ registerTool( name: string, @@ -846,6 +899,29 @@ export class McpServer { /** * Registers a prompt with a config object and callback. + * + * @example + * ```ts source="./mcp.examples.ts#McpServer_registerPrompt_basic" + * server.registerPrompt( + * 'review-code', + * { + * title: 'Code Review', + * description: 'Review code for best practices', + * 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}` + * } + * } + * ] + * }) + * ); + * ``` */ registerPrompt( name: string, @@ -890,6 +966,14 @@ export class McpServer { * @see {@linkcode LoggingMessageNotification} * @param params * @param sessionId Optional for stateless transports and backward compatibility. + * + * @example + * ```ts source="./mcp.examples.ts#McpServer_sendLoggingMessage_basic" + * await server.sendLoggingMessage({ + * level: 'info', + * data: 'Processing complete' + * }); + * ``` */ async sendLoggingMessage(params: LoggingMessageNotification['params'], sessionId?: string) { return this.server.sendLoggingMessage(params, sessionId); diff --git a/packages/server/src/server/stdio.examples.ts b/packages/server/src/server/stdio.examples.ts new file mode 100644 index 000000000..de4603eaa --- /dev/null +++ b/packages/server/src/server/stdio.examples.ts @@ -0,0 +1,22 @@ +/** + * Type-checked examples for `stdio.ts`. + * + * These examples are synced into JSDoc comments via the sync-snippets script. + * Each function's region markers define the code snippet that appears in the docs. + * + * @module + */ + +import { McpServer } from './mcp.js'; +import { StdioServerTransport } from './stdio.js'; + +/** + * Example: Basic stdio transport usage. + */ +async function StdioServerTransport_basicUsage() { + //#region StdioServerTransport_basicUsage + const server = new McpServer({ name: 'my-server', version: '1.0.0' }); + const transport = new StdioServerTransport(); + await server.connect(transport); + //#endregion StdioServerTransport_basicUsage +} diff --git a/packages/server/src/server/stdio.ts b/packages/server/src/server/stdio.ts index e022f69ab..562c6861c 100644 --- a/packages/server/src/server/stdio.ts +++ b/packages/server/src/server/stdio.ts @@ -8,6 +8,13 @@ import { process } from '@modelcontextprotocol/server/_shims'; * Server transport for stdio: this communicates with an MCP client by reading from the current process' `stdin` and writing to `stdout`. * * This transport is only available in Node.js environments. + * + * @example + * ```ts source="./stdio.examples.ts#StdioServerTransport_basicUsage" + * const server = new McpServer({ name: 'my-server', version: '1.0.0' }); + * const transport = new StdioServerTransport(); + * await server.connect(transport); + * ``` */ export class StdioServerTransport implements Transport { private _readBuffer: ReadBuffer = new ReadBuffer(); diff --git a/packages/server/src/server/streamableHttp.examples.ts b/packages/server/src/server/streamableHttp.examples.ts new file mode 100644 index 000000000..a805c1dce --- /dev/null +++ b/packages/server/src/server/streamableHttp.examples.ts @@ -0,0 +1,66 @@ +/** + * Type-checked examples for `streamableHttp.ts`. + * + * These examples are synced into JSDoc comments via the sync-snippets script. + * Each function's region markers define the code snippet that appears in the docs. + * + * @module + */ + +import { McpServer } from './mcp.js'; +import { WebStandardStreamableHTTPServerTransport } from './streamableHttp.js'; + +/** + * Example: Stateful Streamable HTTP transport (Web Standard). + */ +async function WebStandardStreamableHTTPServerTransport_stateful() { + //#region WebStandardStreamableHTTPServerTransport_stateful + const server = new McpServer({ name: 'my-server', version: '1.0.0' }); + + const transport = new WebStandardStreamableHTTPServerTransport({ + sessionIdGenerator: () => crypto.randomUUID() + }); + + await server.connect(transport); + //#endregion WebStandardStreamableHTTPServerTransport_stateful +} + +/** + * Example: Stateless Streamable HTTP transport (Web Standard). + */ +async function WebStandardStreamableHTTPServerTransport_stateless() { + //#region WebStandardStreamableHTTPServerTransport_stateless + const transport = new WebStandardStreamableHTTPServerTransport({ + sessionIdGenerator: undefined + }); + //#endregion WebStandardStreamableHTTPServerTransport_stateless + return transport; +} + +// Stubs for framework-specific examples +declare const app: { all(path: string, handler: (c: { req: { raw: Request } }) => Promise): void }; + +/** + * Example: Using with Hono.js. + */ +function WebStandardStreamableHTTPServerTransport_hono(transport: WebStandardStreamableHTTPServerTransport) { + //#region WebStandardStreamableHTTPServerTransport_hono + app.all('/mcp', async c => { + return transport.handleRequest(c.req.raw); + }); + //#endregion WebStandardStreamableHTTPServerTransport_hono +} + +/** + * Example: Using with Cloudflare Workers. + */ +function WebStandardStreamableHTTPServerTransport_workers(transport: WebStandardStreamableHTTPServerTransport) { + //#region WebStandardStreamableHTTPServerTransport_workers + const worker = { + async fetch(request: Request): Promise { + return transport.handleRequest(request); + } + }; + //#endregion WebStandardStreamableHTTPServerTransport_workers + return worker; +} diff --git a/packages/server/src/server/streamableHttp.ts b/packages/server/src/server/streamableHttp.ts index b5038d018..74e689892 100644 --- a/packages/server/src/server/streamableHttp.ts +++ b/packages/server/src/server/streamableHttp.ts @@ -176,32 +176,6 @@ export interface HandleRequestOptions { * * This transport works on any runtime that supports Web Standards: Node.js 18+, Cloudflare Workers, Deno, Bun, etc. * - * Usage example: - * - * ```typescript - * // Stateful mode - server sets the session ID - * const statefulTransport = new WebStandardStreamableHTTPServerTransport({ - * sessionIdGenerator: () => crypto.randomUUID(), - * }); - * - * // Stateless mode - explicitly set session ID to undefined - * const statelessTransport = new WebStandardStreamableHTTPServerTransport({ - * sessionIdGenerator: undefined, - * }); - * - * // Hono.js usage - * app.all('/mcp', async (c) => { - * return transport.handleRequest(c.req.raw); - * }); - * - * // Cloudflare Workers usage - * export default { - * async fetch(request: Request): Promise { - * return transport.handleRequest(request); - * } - * }; - * ``` - * * In stateful mode: * - Session ID is generated and included in response headers * - Session ID is always included in initialization responses @@ -212,6 +186,40 @@ export interface HandleRequestOptions { * In stateless mode: * - No Session ID is included in any responses * - No session validation is performed + * + * @example Stateful setup + * ```ts source="./streamableHttp.examples.ts#WebStandardStreamableHTTPServerTransport_stateful" + * const server = new McpServer({ name: 'my-server', version: '1.0.0' }); + * + * const transport = new WebStandardStreamableHTTPServerTransport({ + * sessionIdGenerator: () => crypto.randomUUID() + * }); + * + * await server.connect(transport); + * ``` + * + * @example Stateless setup + * ```ts source="./streamableHttp.examples.ts#WebStandardStreamableHTTPServerTransport_stateless" + * const transport = new WebStandardStreamableHTTPServerTransport({ + * sessionIdGenerator: undefined + * }); + * ``` + * + * @example Hono.js + * ```ts source="./streamableHttp.examples.ts#WebStandardStreamableHTTPServerTransport_hono" + * app.all('/mcp', async c => { + * return transport.handleRequest(c.req.raw); + * }); + * ``` + * + * @example Cloudflare Workers + * ```ts source="./streamableHttp.examples.ts#WebStandardStreamableHTTPServerTransport_workers" + * const worker = { + * async fetch(request: Request): Promise { + * return transport.handleRequest(request); + * } + * }; + * ``` */ export class WebStandardStreamableHTTPServerTransport implements Transport { // when sessionId is not set (undefined), it means the transport is in stateless mode