Skip to content

Commit 8aa2eea

Browse files
committed
merge commit
2 parents 5a90b2d + 160902e commit 8aa2eea

File tree

7 files changed

+120
-9
lines changed

7 files changed

+120
-9
lines changed

.npmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
registry = "https://registry.npmjs.org/"
2+
manage-package-manager-versions=false
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/**
2+
* Example: Custom Protocol Version Support
3+
*
4+
* This demonstrates how to support protocol versions not yet in the SDK.
5+
* First version in the list is used as fallback when client requests
6+
* an unsupported version.
7+
*
8+
* Run with: pnpm tsx src/customProtocolVersion.ts
9+
*/
10+
11+
import { randomUUID } from 'node:crypto';
12+
import { createServer } from 'node:http';
13+
14+
import { NodeStreamableHTTPServerTransport } from '@modelcontextprotocol/node';
15+
import type { CallToolResult } from '@modelcontextprotocol/server';
16+
import { McpServer, SUPPORTED_PROTOCOL_VERSIONS } from '@modelcontextprotocol/server';
17+
18+
// Add support for a newer protocol version (first in list is fallback)
19+
const CUSTOM_VERSIONS = ['2026-01-01', ...SUPPORTED_PROTOCOL_VERSIONS];
20+
21+
const server = new McpServer(
22+
{ name: 'custom-protocol-server', version: '1.0.0' },
23+
{
24+
supportedProtocolVersions: CUSTOM_VERSIONS,
25+
capabilities: { tools: {} }
26+
}
27+
);
28+
29+
// Register a tool that shows the protocol configuration
30+
server.registerTool(
31+
'get-protocol-info',
32+
{
33+
title: 'Protocol Info',
34+
description: 'Returns protocol version configuration',
35+
inputSchema: {}
36+
},
37+
async (): Promise<CallToolResult> => ({
38+
content: [
39+
{
40+
type: 'text',
41+
text: JSON.stringify({ supportedVersions: CUSTOM_VERSIONS }, null, 2)
42+
}
43+
]
44+
})
45+
);
46+
47+
// Create transport - server passes versions automatically during connect()
48+
const transport = new NodeStreamableHTTPServerTransport({
49+
sessionIdGenerator: () => randomUUID()
50+
});
51+
52+
await server.connect(transport);
53+
54+
// Simple HTTP server
55+
const PORT = process.env.MCP_PORT ? Number.parseInt(process.env.MCP_PORT, 10) : 3000;
56+
57+
createServer(async (req, res) => {
58+
if (req.url === '/mcp') {
59+
await transport.handleRequest(req, res);
60+
} else {
61+
res.writeHead(404).end('Not Found');
62+
}
63+
}).listen(PORT, () => {
64+
console.log(`MCP server with custom protocol versions on port ${PORT}`);
65+
console.log(`Supported versions: ${CUSTOM_VERSIONS.join(', ')}`);
66+
});

packages/client/src/client/client.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,7 @@ import {
6262
ReadResourceResultSchema,
6363
safeParse,
6464
SdkError,
65-
SdkErrorCode,
66-
SUPPORTED_PROTOCOL_VERSIONS
65+
SdkErrorCode
6766
} from '@modelcontextprotocol/core';
6867

6968
import { ExperimentalClientTasks } from '../experimental/tasks/client.js';
@@ -478,7 +477,7 @@ export class Client<
478477
{
479478
method: 'initialize',
480479
params: {
481-
protocolVersion: LATEST_PROTOCOL_VERSION,
480+
protocolVersion: this._supportedProtocolVersions[0] ?? LATEST_PROTOCOL_VERSION,
482481
capabilities: this._capabilities,
483482
clientInfo: this._clientInfo
484483
}
@@ -491,7 +490,7 @@ export class Client<
491490
throw new Error(`Server sent invalid initialize result: ${result}`);
492491
}
493492

494-
if (!SUPPORTED_PROTOCOL_VERSIONS.includes(result.protocolVersion)) {
493+
if (!this._supportedProtocolVersions.includes(result.protocolVersion)) {
495494
throw new Error(`Server's protocol version is not supported: ${result.protocolVersion}`);
496495
}
497496

packages/core/src/shared/protocol.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import {
4747
ProtocolError,
4848
ProtocolErrorCode,
4949
RELATED_TASK_META_KEY,
50+
SUPPORTED_PROTOCOL_VERSIONS,
5051
TaskStatusNotificationSchema
5152
} from '../types/types.js';
5253
import type { AnySchema, SchemaOutput } from '../util/zodCompat.js';
@@ -64,6 +65,14 @@ export type ProgressCallback = (progress: Progress) => void;
6465
* Additional initialization options.
6566
*/
6667
export type ProtocolOptions = {
68+
/**
69+
* Protocol versions supported. First version is preferred (sent by client,
70+
* used as fallback by server). Passed to transport during connect().
71+
*
72+
* @default SUPPORTED_PROTOCOL_VERSIONS
73+
*/
74+
supportedProtocolVersions?: string[];
75+
6776
/**
6877
* Whether to restrict emitted requests to only those that the remote side has indicated that they can handle, through their advertised capabilities.
6978
*
@@ -343,6 +352,8 @@ export abstract class Protocol<SendRequestT extends Request, SendNotificationT e
343352

344353
private _requestResolvers: Map<RequestId, (response: JSONRPCResultResponse | Error) => void> = new Map();
345354

355+
protected _supportedProtocolVersions: string[];
356+
346357
/**
347358
* Callback for when the connection is closed for any reason.
348359
*
@@ -368,6 +379,8 @@ export abstract class Protocol<SendRequestT extends Request, SendNotificationT e
368379
fallbackNotificationHandler?: (notification: Notification) => Promise<void>;
369380

370381
constructor(private _options?: ProtocolOptions) {
382+
this._supportedProtocolVersions = _options?.supportedProtocolVersions ?? SUPPORTED_PROTOCOL_VERSIONS;
383+
371384
this.setNotificationHandler('notifications/cancelled', notification => {
372385
this._oncancel(notification);
373386
});
@@ -637,6 +650,9 @@ export abstract class Protocol<SendRequestT extends Request, SendNotificationT e
637650
}
638651
};
639652

653+
// Pass supported protocol versions to transport for header validation
654+
transport.setSupportedProtocolVersions?.(this._supportedProtocolVersions);
655+
640656
await this._transport.start();
641657
}
642658

packages/core/src/shared/transport.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,4 +125,10 @@ export interface Transport {
125125
* Sets the protocol version used for the connection (called when the initialize response is received).
126126
*/
127127
setProtocolVersion?: (version: string) => void;
128+
129+
/**
130+
* Sets the supported protocol versions for header validation (called during connect).
131+
* This allows the server to pass its supported versions to the transport.
132+
*/
133+
setSupportedProtocolVersions?: (versions: string[]) => void;
128134
}

packages/server/src/server/server.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,7 @@ import {
5353
ProtocolErrorCode,
5454
safeParse,
5555
SdkError,
56-
SdkErrorCode,
57-
SUPPORTED_PROTOCOL_VERSIONS
56+
SdkErrorCode
5857
} from '@modelcontextprotocol/core';
5958

6059
import { ExperimentalServerTasks } from '../experimental/tasks/server.js';
@@ -455,7 +454,9 @@ export class Server<
455454
this._clientCapabilities = request.params.capabilities;
456455
this._clientVersion = request.params.clientInfo;
457456

458-
const protocolVersion = SUPPORTED_PROTOCOL_VERSIONS.includes(requestedVersion) ? requestedVersion : LATEST_PROTOCOL_VERSION;
457+
const protocolVersion = this._supportedProtocolVersions.includes(requestedVersion)
458+
? requestedVersion
459+
: (this._supportedProtocolVersions[0] ?? LATEST_PROTOCOL_VERSION);
459460

460461
return {
461462
protocolVersion,

packages/server/src/server/streamableHttp.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,18 @@ export interface WebStandardStreamableHTTPServerTransportOptions {
142142
* client reconnection timing for polling behavior.
143143
*/
144144
retryInterval?: number;
145+
146+
/**
147+
* List of protocol versions that this transport will accept.
148+
* Used to validate the mcp-protocol-version header in incoming requests.
149+
*
150+
* Note: When using Server.connect(), the server automatically passes its
151+
* supportedProtocolVersions to the transport, so you typically don't need
152+
* to set this option directly.
153+
*
154+
* @default SUPPORTED_PROTOCOL_VERSIONS
155+
*/
156+
supportedProtocolVersions?: string[];
145157
}
146158

147159
/**
@@ -220,6 +232,7 @@ export class WebStandardStreamableHTTPServerTransport implements Transport {
220232
private _allowedOrigins?: string[];
221233
private _enableDnsRebindingProtection: boolean;
222234
private _retryInterval?: number;
235+
private _supportedProtocolVersions: string[];
223236

224237
sessionId?: string;
225238
onclose?: () => void;
@@ -236,6 +249,7 @@ export class WebStandardStreamableHTTPServerTransport implements Transport {
236249
this._allowedOrigins = options.allowedOrigins;
237250
this._enableDnsRebindingProtection = options.enableDnsRebindingProtection ?? false;
238251
this._retryInterval = options.retryInterval;
252+
this._supportedProtocolVersions = options.supportedProtocolVersions ?? SUPPORTED_PROTOCOL_VERSIONS;
239253
}
240254

241255
/**
@@ -249,6 +263,14 @@ export class WebStandardStreamableHTTPServerTransport implements Transport {
249263
this._started = true;
250264
}
251265

266+
/**
267+
* Sets the supported protocol versions for header validation.
268+
* Called by the server during connect() to pass its supported versions.
269+
*/
270+
setSupportedProtocolVersions(versions: string[]): void {
271+
this._supportedProtocolVersions = versions;
272+
}
273+
252274
/**
253275
* Helper to create a JSON error response
254276
*/
@@ -848,11 +870,11 @@ export class WebStandardStreamableHTTPServerTransport implements Transport {
848870
private validateProtocolVersion(req: Request): Response | undefined {
849871
const protocolVersion = req.headers.get('mcp-protocol-version');
850872

851-
if (protocolVersion !== null && !SUPPORTED_PROTOCOL_VERSIONS.includes(protocolVersion)) {
873+
if (protocolVersion !== null && !this._supportedProtocolVersions.includes(protocolVersion)) {
852874
return this.createJsonErrorResponse(
853875
400,
854876
-32_000,
855-
`Bad Request: Unsupported protocol version: ${protocolVersion} (supported versions: ${SUPPORTED_PROTOCOL_VERSIONS.join(', ')})`
877+
`Bad Request: Unsupported protocol version: ${protocolVersion} (supported versions: ${this._supportedProtocolVersions.join(', ')})`
856878
);
857879
}
858880
return undefined;

0 commit comments

Comments
 (0)