Skip to content

Commit 186e4e5

Browse files
Merge branch 'main' into fix/type-guard-methods
2 parents 418d4a1 + 7ba58da commit 186e4e5

File tree

26 files changed

+617
-194
lines changed

26 files changed

+617
-194
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@modelcontextprotocol/server': patch
3+
---
4+
5+
Prevent stack overflow in StreamableHTTPServerTransport.close() with re-entrant guard

.changeset/odd-forks-enjoy.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@modelcontextprotocol/client": patch
3+
---
4+
5+
fix(client): append custom Accept headers to spec-required defaults in StreamableHTTPClientTransport
6+
7+
Custom Accept headers provided via `requestInit.headers` are now appended to the spec-mandated Accept types instead of being overwritten. This ensures the required media types (`application/json, text/event-stream` for POST; `text/event-stream` for GET SSE) are always present while allowing users to include additional types for proxy/gateway routing.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@modelcontextprotocol/core': patch
3+
---
4+
5+
Allow additional JSON Schema properties in elicitInput's requestedSchema type by adding .catchall(z.unknown()), matching the pattern used by inputSchema. This fixes type incompatibility when using Zod v4's .toJSONSchema() output which includes extra properties like $schema and additionalProperties.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ Next steps:
135135
## Documentation
136136

137137
- Local SDK docs:
138-
- [docs/server.md](docs/server.md) – building MCP servers, transports, tools/resources/prompts, sampling, elicitation, tasks, and deployment patterns.
138+
- [docs/server.md](docs/server.md) – building MCP servers: transports, tools, resources, prompts, server-initiated requests, and deployment
139139
- [docs/client.md](docs/client.md) – building MCP clients: connecting, tools, resources, prompts, server-initiated requests, and error handling
140140
- [docs/faq.md](docs/faq.md) – frequently asked questions and troubleshooting
141141
- External references:

docs/documents.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ children:
1111
# Documents
1212

1313
- [Server Quickstart](./server-quickstart.md) – build a weather server from scratch and connect it to VS Code
14-
- [Server](./server.md) – building MCP servers, transports, tools/resources/prompts, sampling, elicitation, tasks, and deployment patterns
14+
- [Server](./server.md) – building MCP servers: transports, tools, resources, prompts, server-initiated requests, and deployment
1515
- [Client Quickstart](./client-quickstart.md) – build an LLM-powered chatbot that connects to an MCP server and calls its tools
1616
- [Client](./client.md) – building MCP clients: connecting, tools, resources, prompts, server-initiated requests, and error handling
1717
- [FAQ](./faq.md) – frequently asked questions and troubleshooting

docs/migration-SKILL.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -487,7 +487,10 @@ new McpServer(
487487
new McpServer({ name: 'server', version: '1.0.0' }, {});
488488
```
489489

490-
Access validators via `_shims` export: `import { DefaultJsonSchemaValidator } from '@modelcontextprotocol/server/_shims';`
490+
Access validators explicitly:
491+
- Runtime-aware default: `import { DefaultJsonSchemaValidator } from '@modelcontextprotocol/server/_shims';`
492+
- AJV (Node.js): `import { AjvJsonSchemaValidator } from '@modelcontextprotocol/server';`
493+
- CF Worker: `import { CfWorkerJsonSchemaValidator } from '@modelcontextprotocol/server/validators/cf-worker';`
491494

492495
## 15. Migration Steps (apply in this order)
493496

docs/migration.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -849,7 +849,8 @@ This means Cloudflare Workers users no longer need to explicitly pass the valida
849849
**Before (v1) - Cloudflare Workers required explicit configuration:**
850850

851851
```typescript
852-
import { McpServer, CfWorkerJsonSchemaValidator } from '@modelcontextprotocol/server';
852+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
853+
import { CfWorkerJsonSchemaValidator } from '@modelcontextprotocol/sdk/validation/cfworker';
853854

854855
const server = new McpServer(
855856
{ name: 'my-server', version: '1.0.0' },
@@ -872,12 +873,15 @@ const server = new McpServer(
872873
);
873874
```
874875

875-
You can still explicitly override the validator if needed. The validators are available via the `_shims` export:
876+
You can still explicitly override the validator if needed:
876877

877878
```typescript
879+
// Runtime-aware default (auto-selects AjvJsonSchemaValidator or CfWorkerJsonSchemaValidator)
878880
import { DefaultJsonSchemaValidator } from '@modelcontextprotocol/server/_shims';
879-
// or
880-
import { AjvJsonSchemaValidator, CfWorkerJsonSchemaValidator } from '@modelcontextprotocol/server';
881+
882+
// Specific validators
883+
import { AjvJsonSchemaValidator } from '@modelcontextprotocol/server';
884+
import { CfWorkerJsonSchemaValidator } from '@modelcontextprotocol/server/validators/cf-worker';
881885
```
882886

883887
## Unchanged APIs

docs/server.md

Lines changed: 218 additions & 146 deletions
Large diffs are not rendered by default.

examples/server/src/serverGuide.examples.ts

Lines changed: 155 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,29 +7,28 @@
77
* @module
88
*/
99

10+
//#region imports
1011
import { randomUUID } from 'node:crypto';
1112

1213
import { createMcpExpressApp } from '@modelcontextprotocol/express';
1314
import { NodeStreamableHTTPServerTransport } from '@modelcontextprotocol/node';
1415
import type { CallToolResult, ResourceLink } from '@modelcontextprotocol/server';
1516
import { completable, McpServer, ResourceTemplate, StdioServerTransport } from '@modelcontextprotocol/server';
1617
import * as z from 'zod/v4';
18+
//#endregion imports
1719

1820
// ---------------------------------------------------------------------------
19-
// Instructions
21+
// Server instructions
2022
// ---------------------------------------------------------------------------
2123

22-
/** Example: Providing server instructions to guide LLM usage. */
24+
/** Example: McpServer with instructions for LLM guidance. */
2325
function instructions_basic() {
2426
//#region instructions_basic
2527
const server = new McpServer(
28+
{ name: 'db-server', version: '1.0.0' },
2629
{
27-
name: 'multi-tool-server',
28-
version: '1.0.0'
29-
},
30-
{
31-
instructions: `This server provides data-pipeline tools. Always call "validate-schema"
32-
before calling "transform-data" to avoid runtime errors.`
30+
instructions:
31+
'Always call list_tables before running queries. Use validate_schema before migrate_schema for safe migrations. Results are limited to 1000 rows.'
3332
}
3433
);
3534
//#endregion instructions_basic
@@ -95,6 +94,59 @@ function registerTool_resourceLink(server: McpServer) {
9594
//#endregion registerTool_resourceLink
9695
}
9796

97+
/** Example: Tool with explicit error handling using isError. */
98+
function registerTool_errorHandling(server: McpServer) {
99+
//#region registerTool_errorHandling
100+
server.registerTool(
101+
'fetch-data',
102+
{
103+
description: 'Fetch data from a URL',
104+
inputSchema: z.object({ url: z.string() })
105+
},
106+
async ({ url }): Promise<CallToolResult> => {
107+
try {
108+
const res = await fetch(url);
109+
if (!res.ok) {
110+
return {
111+
content: [{ type: 'text', text: `HTTP ${res.status}: ${res.statusText}` }],
112+
isError: true
113+
};
114+
}
115+
const text = await res.text();
116+
return { content: [{ type: 'text', text }] };
117+
} catch (error) {
118+
return {
119+
content: [{ type: 'text', text: `Failed: ${error instanceof Error ? error.message : String(error)}` }],
120+
isError: true
121+
};
122+
}
123+
}
124+
);
125+
//#endregion registerTool_errorHandling
126+
}
127+
128+
/** Example: Tool with annotations hinting at behavior. */
129+
function registerTool_annotations(server: McpServer) {
130+
//#region registerTool_annotations
131+
server.registerTool(
132+
'delete-file',
133+
{
134+
description: 'Delete a file from the project',
135+
inputSchema: z.object({ path: z.string() }),
136+
annotations: {
137+
title: 'Delete File',
138+
destructiveHint: true,
139+
idempotentHint: true
140+
}
141+
},
142+
async ({ path }): Promise<CallToolResult> => {
143+
// ... perform deletion ...
144+
return { content: [{ type: 'text', text: `Deleted ${path}` }] };
145+
}
146+
);
147+
//#endregion registerTool_annotations
148+
}
149+
98150
/** Example: Registering a static resource at a fixed URI. */
99151
function registerResource_static(server: McpServer) {
100152
//#region registerResource_static
@@ -228,6 +280,44 @@ function registerTool_logging() {
228280
return server;
229281
}
230282

283+
// ---------------------------------------------------------------------------
284+
// Progress
285+
// ---------------------------------------------------------------------------
286+
287+
/** Example: Tool that sends progress notifications during a long-running operation. */
288+
function registerTool_progress(server: McpServer) {
289+
//#region registerTool_progress
290+
server.registerTool(
291+
'process-files',
292+
{
293+
description: 'Process files with progress updates',
294+
inputSchema: z.object({ files: z.array(z.string()) })
295+
},
296+
async ({ files }, ctx): Promise<CallToolResult> => {
297+
const progressToken = ctx.mcpReq._meta?.progressToken;
298+
299+
for (let i = 0; i < files.length; i++) {
300+
// ... process files[i] ...
301+
302+
if (progressToken !== undefined) {
303+
await ctx.mcpReq.notify({
304+
method: 'notifications/progress',
305+
params: {
306+
progressToken,
307+
progress: i + 1,
308+
total: files.length,
309+
message: `Processed ${files[i]}`
310+
}
311+
});
312+
}
313+
}
314+
315+
return { content: [{ type: 'text', text: `Processed ${files.length} files` }] };
316+
}
317+
);
318+
//#endregion registerTool_progress
319+
}
320+
231321
// ---------------------------------------------------------------------------
232322
// Server-initiated requests
233323
// ---------------------------------------------------------------------------
@@ -310,6 +400,24 @@ function registerTool_elicitation(server: McpServer) {
310400
//#endregion registerTool_elicitation
311401
}
312402

403+
/** Example: Tool that requests the client's filesystem roots. */
404+
function registerTool_roots(server: McpServer) {
405+
//#region registerTool_roots
406+
server.registerTool(
407+
'list-workspace-files',
408+
{
409+
description: 'List files across all workspace roots',
410+
inputSchema: z.object({})
411+
},
412+
async (_args, _ctx): Promise<CallToolResult> => {
413+
const { roots } = await server.server.listRoots();
414+
const summary = roots.map(r => `${r.name ?? r.uri}: ${r.uri}`).join('\n');
415+
return { content: [{ type: 'text', text: summary }] };
416+
}
417+
);
418+
//#endregion registerTool_roots
419+
}
420+
313421
// ---------------------------------------------------------------------------
314422
// Transports
315423
// ---------------------------------------------------------------------------
@@ -363,6 +471,39 @@ async function stdio_basic() {
363471
//#endregion stdio_basic
364472
}
365473

474+
// ---------------------------------------------------------------------------
475+
// Shutdown
476+
// ---------------------------------------------------------------------------
477+
478+
/** Example: Graceful shutdown for a stateful multi-session HTTP server. */
479+
function shutdown_statefulHttp(app: ReturnType<typeof createMcpExpressApp>, transports: Map<string, NodeStreamableHTTPServerTransport>) {
480+
//#region shutdown_statefulHttp
481+
// Capture the http.Server so it can be closed on shutdown
482+
const httpServer = app.listen(3000);
483+
484+
process.on('SIGINT', async () => {
485+
httpServer.close();
486+
487+
for (const [sessionId, transport] of transports) {
488+
await transport.close();
489+
transports.delete(sessionId);
490+
}
491+
492+
process.exit(0);
493+
});
494+
//#endregion shutdown_statefulHttp
495+
}
496+
497+
/** Example: Graceful shutdown for a stdio server. */
498+
function shutdown_stdio(server: McpServer) {
499+
//#region shutdown_stdio
500+
process.on('SIGINT', async () => {
501+
await server.close();
502+
process.exit(0);
503+
});
504+
//#endregion shutdown_stdio
505+
}
506+
366507
// ---------------------------------------------------------------------------
367508
// DNS rebinding protection
368509
// ---------------------------------------------------------------------------
@@ -397,9 +538,13 @@ function dnsRebinding_allowedHosts() {
397538
void instructions_basic;
398539
void registerTool_basic;
399540
void registerTool_resourceLink;
541+
void registerTool_errorHandling;
542+
void registerTool_annotations;
400543
void registerTool_logging;
544+
void registerTool_progress;
401545
void registerTool_sampling;
402546
void registerTool_elicitation;
547+
void registerTool_roots;
403548
void registerResource_static;
404549
void registerResource_template;
405550
void registerPrompt_basic;
@@ -408,5 +553,7 @@ void streamableHttp_stateful;
408553
void streamableHttp_stateless;
409554
void streamableHttp_jsonResponse;
410555
void stdio_basic;
556+
void shutdown_statefulHttp;
557+
void shutdown_stdio;
411558
void dnsRebinding_basic;
412559
void dnsRebinding_allowedHosts;

packages/client/package.json

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@
2424
"types": "./dist/index.d.mts",
2525
"import": "./dist/index.mjs"
2626
},
27+
"./validators/cf-worker": {
28+
"types": "./dist/validators/cfWorker.d.mts",
29+
"import": "./dist/validators/cfWorker.mjs"
30+
},
2731
"./_shims": {
2832
"workerd": {
2933
"types": "./dist/shimsWorkerd.d.mts",
@@ -67,14 +71,6 @@
6771
"pkce-challenge": "catalog:runtimeShared",
6872
"zod": "catalog:runtimeShared"
6973
},
70-
"peerDependencies": {
71-
"@cfworker/json-schema": "catalog:runtimeShared"
72-
},
73-
"peerDependenciesMeta": {
74-
"@cfworker/json-schema": {
75-
"optional": true
76-
}
77-
},
7874
"devDependencies": {
7975
"@modelcontextprotocol/core": "workspace:^",
8076
"@modelcontextprotocol/tsconfig": "workspace:^",

0 commit comments

Comments
 (0)