Rewrite docs/server.md as a code-heavy, prose-light how-to guide (#1552)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
1eb31236e7
commit
653c5d000c
1 changed files with 375 additions and 156 deletions
|
|
@ -7,6 +7,7 @@
|
|||
* @module
|
||||
*/
|
||||
|
||||
//#region imports
|
||||
import { randomUUID } from 'node:crypto';
|
||||
|
||||
import { createMcpExpressApp } from '@modelcontextprotocol/express';
|
||||
|
|
@ -14,22 +15,20 @@ 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';
|
||||
//#endregion imports
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Instructions
|
||||
// Server instructions
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/** Example: Providing server instructions to guide LLM usage. */
|
||||
/** Example: McpServer with instructions for LLM guidance. */
|
||||
function instructions_basic() {
|
||||
//#region instructions_basic
|
||||
const server = new McpServer(
|
||||
{ name: 'db-server', version: '1.0.0' },
|
||||
{
|
||||
name: 'multi-tool-server',
|
||||
version: '1.0.0'
|
||||
},
|
||||
{
|
||||
instructions: `This server provides data-pipeline tools. Always call "validate-schema"
|
||||
before calling "transform-data" to avoid runtime errors.`
|
||||
instructions:
|
||||
'Always call list_tables before running queries. Use validate_schema before migrate_schema for safe migrations. Results are limited to 1000 rows.'
|
||||
}
|
||||
);
|
||||
//#endregion instructions_basic
|
||||
|
|
@ -95,6 +94,59 @@ function registerTool_resourceLink(server: McpServer) {
|
|||
//#endregion registerTool_resourceLink
|
||||
}
|
||||
|
||||
/** Example: Tool with explicit error handling using isError. */
|
||||
function registerTool_errorHandling(server: McpServer) {
|
||||
//#region registerTool_errorHandling
|
||||
server.registerTool(
|
||||
'fetch-data',
|
||||
{
|
||||
description: 'Fetch data from a URL',
|
||||
inputSchema: z.object({ url: z.string() })
|
||||
},
|
||||
async ({ url }): Promise<CallToolResult> => {
|
||||
try {
|
||||
const res = await fetch(url);
|
||||
if (!res.ok) {
|
||||
return {
|
||||
content: [{ type: 'text', text: `HTTP ${res.status}: ${res.statusText}` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
const text = await res.text();
|
||||
return { content: [{ type: 'text', text }] };
|
||||
} catch (error) {
|
||||
return {
|
||||
content: [{ type: 'text', text: `Failed: ${error instanceof Error ? error.message : String(error)}` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
||||
//#endregion registerTool_errorHandling
|
||||
}
|
||||
|
||||
/** Example: Tool with annotations hinting at behavior. */
|
||||
function registerTool_annotations(server: McpServer) {
|
||||
//#region registerTool_annotations
|
||||
server.registerTool(
|
||||
'delete-file',
|
||||
{
|
||||
description: 'Delete a file from the project',
|
||||
inputSchema: z.object({ path: z.string() }),
|
||||
annotations: {
|
||||
title: 'Delete File',
|
||||
destructiveHint: true,
|
||||
idempotentHint: true
|
||||
}
|
||||
},
|
||||
async ({ path }): Promise<CallToolResult> => {
|
||||
// ... perform deletion ...
|
||||
return { content: [{ type: 'text', text: `Deleted ${path}` }] };
|
||||
}
|
||||
);
|
||||
//#endregion registerTool_annotations
|
||||
}
|
||||
|
||||
/** Example: Registering a static resource at a fixed URI. */
|
||||
function registerResource_static(server: McpServer) {
|
||||
//#region registerResource_static
|
||||
|
|
@ -228,6 +280,44 @@ function registerTool_logging() {
|
|||
return server;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Progress
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/** Example: Tool that sends progress notifications during a long-running operation. */
|
||||
function registerTool_progress(server: McpServer) {
|
||||
//#region registerTool_progress
|
||||
server.registerTool(
|
||||
'process-files',
|
||||
{
|
||||
description: 'Process files with progress updates',
|
||||
inputSchema: z.object({ files: z.array(z.string()) })
|
||||
},
|
||||
async ({ files }, ctx): Promise<CallToolResult> => {
|
||||
const progressToken = ctx.mcpReq._meta?.progressToken;
|
||||
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
// ... process files[i] ...
|
||||
|
||||
if (progressToken !== undefined) {
|
||||
await ctx.mcpReq.notify({
|
||||
method: 'notifications/progress',
|
||||
params: {
|
||||
progressToken,
|
||||
progress: i + 1,
|
||||
total: files.length,
|
||||
message: `Processed ${files[i]}`
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return { content: [{ type: 'text', text: `Processed ${files.length} files` }] };
|
||||
}
|
||||
);
|
||||
//#endregion registerTool_progress
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Server-initiated requests
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
@ -310,6 +400,24 @@ function registerTool_elicitation(server: McpServer) {
|
|||
//#endregion registerTool_elicitation
|
||||
}
|
||||
|
||||
/** Example: Tool that requests the client's filesystem roots. */
|
||||
function registerTool_roots(server: McpServer) {
|
||||
//#region registerTool_roots
|
||||
server.registerTool(
|
||||
'list-workspace-files',
|
||||
{
|
||||
description: 'List files across all workspace roots',
|
||||
inputSchema: z.object({})
|
||||
},
|
||||
async (_args, _ctx): Promise<CallToolResult> => {
|
||||
const { roots } = await server.server.listRoots();
|
||||
const summary = roots.map(r => `${r.name ?? r.uri}: ${r.uri}`).join('\n');
|
||||
return { content: [{ type: 'text', text: summary }] };
|
||||
}
|
||||
);
|
||||
//#endregion registerTool_roots
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Transports
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
@ -363,6 +471,39 @@ async function stdio_basic() {
|
|||
//#endregion stdio_basic
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Shutdown
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/** Example: Graceful shutdown for a stateful multi-session HTTP server. */
|
||||
function shutdown_statefulHttp(app: ReturnType<typeof createMcpExpressApp>, transports: Map<string, NodeStreamableHTTPServerTransport>) {
|
||||
//#region shutdown_statefulHttp
|
||||
// Capture the http.Server so it can be closed on shutdown
|
||||
const httpServer = app.listen(3000);
|
||||
|
||||
process.on('SIGINT', async () => {
|
||||
httpServer.close();
|
||||
|
||||
for (const [sessionId, transport] of transports) {
|
||||
await transport.close();
|
||||
transports.delete(sessionId);
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
});
|
||||
//#endregion shutdown_statefulHttp
|
||||
}
|
||||
|
||||
/** Example: Graceful shutdown for a stdio server. */
|
||||
function shutdown_stdio(server: McpServer) {
|
||||
//#region shutdown_stdio
|
||||
process.on('SIGINT', async () => {
|
||||
await server.close();
|
||||
process.exit(0);
|
||||
});
|
||||
//#endregion shutdown_stdio
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// DNS rebinding protection
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
@ -397,9 +538,13 @@ function dnsRebinding_allowedHosts() {
|
|||
void instructions_basic;
|
||||
void registerTool_basic;
|
||||
void registerTool_resourceLink;
|
||||
void registerTool_errorHandling;
|
||||
void registerTool_annotations;
|
||||
void registerTool_logging;
|
||||
void registerTool_progress;
|
||||
void registerTool_sampling;
|
||||
void registerTool_elicitation;
|
||||
void registerTool_roots;
|
||||
void registerResource_static;
|
||||
void registerResource_template;
|
||||
void registerPrompt_basic;
|
||||
|
|
@ -408,5 +553,7 @@ void streamableHttp_stateful;
|
|||
void streamableHttp_stateless;
|
||||
void streamableHttp_jsonResponse;
|
||||
void stdio_basic;
|
||||
void shutdown_statefulHttp;
|
||||
void shutdown_stdio;
|
||||
void dnsRebinding_basic;
|
||||
void dnsRebinding_allowedHosts;
|
||||
|
|
|
|||
Loading…
Reference in a new issue