Skip to content

Commit b0255e5

Browse files
authored
Merge pull request #410 from cliffhall/update-protocol-version
Update protocol version
2 parents 7e18c70 + d11673f commit b0255e5

File tree

4 files changed

+220
-14
lines changed

4 files changed

+220
-14
lines changed

src/client/auth.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ describe("OAuth Authorization", () => {
3939
const [url, options] = calls[0];
4040
expect(url.toString()).toBe("https://auth.example.com/.well-known/oauth-authorization-server");
4141
expect(options.headers).toEqual({
42-
"MCP-Protocol-Version": "2024-11-05"
42+
"MCP-Protocol-Version": "2025-03-26"
4343
});
4444
});
4545

@@ -478,4 +478,4 @@ describe("OAuth Authorization", () => {
478478
).rejects.toThrow("Dynamic client registration failed");
479479
});
480480
});
481-
});
481+
});

src/client/index.test.ts

+188
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,194 @@ test("should reject unsupported protocol version", async () => {
165165
expect(clientTransport.close).toHaveBeenCalled();
166166
});
167167

168+
test("should connect new client to old, supported server version", async () => {
169+
const OLD_VERSION = SUPPORTED_PROTOCOL_VERSIONS[1];
170+
const server = new Server(
171+
{
172+
name: "test server",
173+
version: "1.0",
174+
},
175+
{
176+
capabilities: {
177+
resources: {},
178+
tools: {},
179+
},
180+
},
181+
);
182+
183+
server.setRequestHandler(InitializeRequestSchema, (_request) => ({
184+
protocolVersion: OLD_VERSION,
185+
capabilities: {
186+
resources: {},
187+
tools: {},
188+
},
189+
serverInfo: {
190+
name: "old server",
191+
version: "1.0",
192+
},
193+
}));
194+
195+
server.setRequestHandler(ListResourcesRequestSchema, () => ({
196+
resources: [],
197+
}));
198+
199+
server.setRequestHandler(ListToolsRequestSchema, () => ({
200+
tools: [],
201+
}));
202+
203+
const [clientTransport, serverTransport] =
204+
InMemoryTransport.createLinkedPair();
205+
206+
const client = new Client(
207+
{
208+
name: "new client",
209+
version: "1.0",
210+
protocolVersion: LATEST_PROTOCOL_VERSION,
211+
},
212+
{
213+
capabilities: {
214+
sampling: {},
215+
},
216+
enforceStrictCapabilities: true,
217+
},
218+
);
219+
220+
await Promise.all([
221+
client.connect(clientTransport),
222+
server.connect(serverTransport),
223+
]);
224+
225+
expect(client.getServerVersion()).toEqual({
226+
name: "old server",
227+
version: "1.0",
228+
});
229+
});
230+
231+
test("should negotiate version when client is old, and newer server supports its version", async () => {
232+
const OLD_VERSION = SUPPORTED_PROTOCOL_VERSIONS[1];
233+
const server = new Server(
234+
{
235+
name: "new server",
236+
version: "1.0",
237+
},
238+
{
239+
capabilities: {
240+
resources: {},
241+
tools: {},
242+
},
243+
},
244+
);
245+
246+
server.setRequestHandler(InitializeRequestSchema, (_request) => ({
247+
protocolVersion: LATEST_PROTOCOL_VERSION,
248+
capabilities: {
249+
resources: {},
250+
tools: {},
251+
},
252+
serverInfo: {
253+
name: "new server",
254+
version: "1.0",
255+
},
256+
}));
257+
258+
server.setRequestHandler(ListResourcesRequestSchema, () => ({
259+
resources: [],
260+
}));
261+
262+
server.setRequestHandler(ListToolsRequestSchema, () => ({
263+
tools: [],
264+
}));
265+
266+
const [clientTransport, serverTransport] =
267+
InMemoryTransport.createLinkedPair();
268+
269+
const client = new Client(
270+
{
271+
name: "old client",
272+
version: "1.0",
273+
protocolVersion: OLD_VERSION,
274+
},
275+
{
276+
capabilities: {
277+
sampling: {},
278+
},
279+
enforceStrictCapabilities: true,
280+
},
281+
);
282+
283+
await Promise.all([
284+
client.connect(clientTransport),
285+
server.connect(serverTransport),
286+
]);
287+
288+
expect(client.getServerVersion()).toEqual({
289+
name: "new server",
290+
version: "1.0",
291+
});
292+
});
293+
294+
test("should throw when client is old, and server doesn't support its version", async () => {
295+
const OLD_VERSION = SUPPORTED_PROTOCOL_VERSIONS[1];
296+
const FUTURE_VERSION = "FUTURE_VERSION";
297+
const server = new Server(
298+
{
299+
name: "new server",
300+
version: "1.0",
301+
},
302+
{
303+
capabilities: {
304+
resources: {},
305+
tools: {},
306+
},
307+
},
308+
);
309+
310+
server.setRequestHandler(InitializeRequestSchema, (_request) => ({
311+
protocolVersion: FUTURE_VERSION,
312+
capabilities: {
313+
resources: {},
314+
tools: {},
315+
},
316+
serverInfo: {
317+
name: "new server",
318+
version: "1.0",
319+
},
320+
}));
321+
322+
server.setRequestHandler(ListResourcesRequestSchema, () => ({
323+
resources: [],
324+
}));
325+
326+
server.setRequestHandler(ListToolsRequestSchema, () => ({
327+
tools: [],
328+
}));
329+
330+
const [clientTransport, serverTransport] =
331+
InMemoryTransport.createLinkedPair();
332+
333+
const client = new Client(
334+
{
335+
name: "old client",
336+
version: "1.0",
337+
protocolVersion: OLD_VERSION,
338+
},
339+
{
340+
capabilities: {
341+
sampling: {},
342+
},
343+
enforceStrictCapabilities: true,
344+
},
345+
);
346+
347+
await Promise.all([
348+
expect(client.connect(clientTransport)).rejects.toThrow(
349+
"Server's protocol version is not supported: FUTURE_VERSION"
350+
),
351+
server.connect(serverTransport),
352+
]);
353+
354+
});
355+
168356
test("should respect server capabilities", async () => {
169357
const server = new Server(
170358
{

src/types.test.ts

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { LATEST_PROTOCOL_VERSION, SUPPORTED_PROTOCOL_VERSIONS } from "./types.js";
2+
3+
describe("Types", () => {
4+
5+
test("should have correct latest protocol version", () => {
6+
expect(LATEST_PROTOCOL_VERSION).toBeDefined();
7+
expect(LATEST_PROTOCOL_VERSION).toBe("2025-03-26");
8+
});
9+
test("should have correct supported protocol versions", () => {
10+
expect(SUPPORTED_PROTOCOL_VERSIONS).toBeDefined();
11+
expect(SUPPORTED_PROTOCOL_VERSIONS).toBeInstanceOf(Array);
12+
expect(SUPPORTED_PROTOCOL_VERSIONS).toContain(LATEST_PROTOCOL_VERSION);
13+
expect(SUPPORTED_PROTOCOL_VERSIONS).toContain("2024-11-05");
14+
expect(SUPPORTED_PROTOCOL_VERSIONS).toContain("2024-10-07");
15+
});
16+
17+
});

src/types.ts

+13-12
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { z, ZodTypeAny } from "zod";
22

3-
export const LATEST_PROTOCOL_VERSION = "2024-11-05";
3+
export const LATEST_PROTOCOL_VERSION = "2025-03-26";
44
export const SUPPORTED_PROTOCOL_VERSIONS = [
55
LATEST_PROTOCOL_VERSION,
6+
"2024-11-05",
67
"2024-10-07",
78
];
89

@@ -754,11 +755,11 @@ export const PromptListChangedNotificationSchema = NotificationSchema.extend({
754755
/* Tools */
755756
/**
756757
* Additional properties describing a Tool to clients.
757-
*
758-
* NOTE: all properties in ToolAnnotations are **hints**.
759-
* They are not guaranteed to provide a faithful description of
758+
*
759+
* NOTE: all properties in ToolAnnotations are **hints**.
760+
* They are not guaranteed to provide a faithful description of
760761
* tool behavior (including descriptive properties like `title`).
761-
*
762+
*
762763
* Clients should never make tool use decisions based on ToolAnnotations
763764
* received from untrusted servers.
764765
*/
@@ -771,27 +772,27 @@ export const ToolAnnotationsSchema = z
771772

772773
/**
773774
* If true, the tool does not modify its environment.
774-
*
775+
*
775776
* Default: false
776777
*/
777778
readOnlyHint: z.optional(z.boolean()),
778779

779780
/**
780781
* If true, the tool may perform destructive updates to its environment.
781782
* If false, the tool performs only additive updates.
782-
*
783+
*
783784
* (This property is meaningful only when `readOnlyHint == false`)
784-
*
785+
*
785786
* Default: true
786787
*/
787788
destructiveHint: z.optional(z.boolean()),
788789

789790
/**
790-
* If true, calling the tool repeatedly with the same arguments
791+
* If true, calling the tool repeatedly with the same arguments
791792
* will have no additional effect on the its environment.
792-
*
793+
*
793794
* (This property is meaningful only when `readOnlyHint == false`)
794-
*
795+
*
795796
* Default: false
796797
*/
797798
idempotentHint: z.optional(z.boolean()),
@@ -801,7 +802,7 @@ export const ToolAnnotationsSchema = z
801802
* entities. If false, the tool's domain of interaction is closed.
802803
* For example, the world of a web search tool is open, whereas that
803804
* of a memory tool is not.
804-
*
805+
*
805806
* Default: true
806807
*/
807808
openWorldHint: z.optional(z.boolean()),

0 commit comments

Comments
 (0)