Skip to content

Commit 962d470

Browse files
authored
Merge branch 'main' into codex/issue-1708-404-session-reset
2 parents 454ac33 + 1eb80c4 commit 962d470

File tree

15 files changed

+220
-34
lines changed

15 files changed

+220
-34
lines changed

docs/migration-SKILL.md

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ Notes:
8686
| `JSONRPCError` | `JSONRPCErrorResponse` |
8787
| `JSONRPCErrorSchema` | `JSONRPCErrorResponseSchema` |
8888
| `isJSONRPCError` | `isJSONRPCErrorResponse` |
89-
| `isJSONRPCResponse` | `isJSONRPCResultResponse` |
89+
| `isJSONRPCResponse` (deprecated in v1) | `isJSONRPCResultResponse` (**not** v2's new `isJSONRPCResponse`, which correctly matches both result and error) |
9090
| `ResourceReference` | `ResourceTemplateReference` |
9191
| `ResourceReferenceSchema` | `ResourceTemplateReferenceSchema` |
9292
| `IsomorphicHeaders` | REMOVED (use Web Standard `Headers`) |
@@ -98,7 +98,7 @@ Notes:
9898
| `StreamableHTTPError` | REMOVED (use `SdkError` with `SdkErrorCode.ClientHttp*`) |
9999
| `WebSocketClientTransport` | REMOVED (use `StreamableHTTPClientTransport` or `StdioClientTransport`) |
100100

101-
All other symbols from `@modelcontextprotocol/sdk/types.js` retain their original names (e.g., `CallToolResultSchema`, `ListToolsResultSchema`, etc.).
101+
All other **type** symbols from `@modelcontextprotocol/sdk/types.js` retain their original names. **Zod schemas** (e.g., `CallToolResultSchema`, `ListToolsResultSchema`) are no longer part of the public API — they are internal to the SDK. For runtime validation, use type guard functions like `isCallToolResult` instead of `CallToolResultSchema.safeParse()`.
102102

103103
### Error class changes
104104

@@ -435,6 +435,13 @@ const tool = await client.callTool({ name: 'my-tool', arguments: {} });
435435

436436
Remove unused schema imports: `CallToolResultSchema`, `CompatibilityCallToolResultSchema`, `ElicitResultSchema`, `CreateMessageResultSchema`, etc., when they were only used in `request()`/`send()`/`callTool()` calls.
437437

438+
If `CallToolResultSchema` was used for **runtime validation** (not just as a `request()` argument), replace with the `isCallToolResult` type guard:
439+
440+
| v1 pattern | v2 replacement |
441+
| --------------------------------------------------- | -------------------------- |
442+
| `CallToolResultSchema.safeParse(value).success` | `isCallToolResult(value)` |
443+
| `CallToolResultSchema.parse(value)` | Use `isCallToolResult(value)` then cast, or use `CallToolResult` type |
444+
438445
## 12. Experimental: `TaskCreationParams.ttl` no longer accepts `null`
439446

440447
`TaskCreationParams.ttl` changed from `z.union([z.number(), z.null()]).optional()` to `z.number().optional()`. Per the MCP spec, `null` TTL (unlimited lifetime) is only valid in server responses (`Task.ttl`), not in client requests. Omit `ttl` to let the server decide.
@@ -480,7 +487,10 @@ new McpServer(
480487
new McpServer({ name: 'server', version: '1.0.0' }, {});
481488
```
482489

483-
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';`
484494

485495
## 15. Migration Steps (apply in this order)
486496

docs/migration.md

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,18 @@ const result = await client.callTool({ name: 'my-tool', arguments: {} });
442442

443443
The return type is now inferred from the method name via `ResultTypeMap`. For example, `client.request({ method: 'tools/call', ... })` returns `Promise<CallToolResult | CreateTaskResult>`.
444444

445+
If you were using `CallToolResultSchema` for **runtime validation** (not just in `request()`/`callTool()` calls), use the new `isCallToolResult` type guard instead:
446+
447+
```typescript
448+
// v1: runtime validation with Zod schema
449+
import { CallToolResultSchema } from '@modelcontextprotocol/sdk/types.js';
450+
if (CallToolResultSchema.safeParse(value).success) { /* ... */ }
451+
452+
// v2: use the type guard
453+
import { isCallToolResult } from '@modelcontextprotocol/client';
454+
if (isCallToolResult(value)) { /* ... */ }
455+
```
456+
445457
### Client list methods return empty results for missing capabilities
446458

447459
`Client.listPrompts()`, `listResources()`, `listResourceTemplates()`, and `listTools()` now return empty results when the server didn't advertise the corresponding capability, instead of sending the request. This respects the MCP spec's capability negotiation.
@@ -482,14 +494,16 @@ The following deprecated type aliases have been removed from `@modelcontextproto
482494
| `JSONRPCError` | `JSONRPCErrorResponse` |
483495
| `JSONRPCErrorSchema` | `JSONRPCErrorResponseSchema` |
484496
| `isJSONRPCError` | `isJSONRPCErrorResponse` |
485-
| `isJSONRPCResponse` | `isJSONRPCResultResponse` |
497+
| `isJSONRPCResponse` | `isJSONRPCResultResponse` (see note below) |
486498
| `ResourceReferenceSchema` | `ResourceTemplateReferenceSchema` |
487499
| `ResourceReference` | `ResourceTemplateReference` |
488500
| `IsomorphicHeaders` | Use Web Standard `Headers` |
489501
| `AuthInfo` (from `server/auth/types.js`) | `AuthInfo` (now re-exported by `@modelcontextprotocol/client` and `@modelcontextprotocol/server`) |
490502

491503
All other types and schemas exported from `@modelcontextprotocol/sdk/types.js` retain their original names — import them from `@modelcontextprotocol/client` or `@modelcontextprotocol/server`.
492504

505+
> **Note on `isJSONRPCResponse`:** v1's `isJSONRPCResponse` was a deprecated alias that only checked for *result* responses (it was equivalent to `isJSONRPCResultResponse`). v2 removes the deprecated alias and introduces a **new** `isJSONRPCResponse` with corrected semantics — it checks for *any* response (either result or error). If you are migrating v1 code that used `isJSONRPCResponse`, rename it to `isJSONRPCResultResponse` to preserve the original behavior. Use the new `isJSONRPCResponse` only when you want to match both result and error responses.
506+
493507
**Before (v1):**
494508

495509
```typescript
@@ -835,7 +849,8 @@ This means Cloudflare Workers users no longer need to explicitly pass the valida
835849
**Before (v1) - Cloudflare Workers required explicit configuration:**
836850

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

840855
const server = new McpServer(
841856
{ name: 'my-server', version: '1.0.0' },
@@ -858,12 +873,15 @@ const server = new McpServer(
858873
);
859874
```
860875

861-
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:
862877

863878
```typescript
879+
// Runtime-aware default (auto-selects AjvJsonSchemaValidator or CfWorkerJsonSchemaValidator)
864880
import { DefaultJsonSchemaValidator } from '@modelcontextprotocol/server/_shims';
865-
// or
866-
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';
867885
```
868886

869887
## Unchanged APIs

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:^",

packages/client/src/client/client.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ export type ClientOptions = ProtocolOptions & {
162162
* The validator is used to validate structured content returned by tools
163163
* against their declared output schemas.
164164
*
165-
* @default {@linkcode DefaultJsonSchemaValidator} ({@linkcode index.AjvJsonSchemaValidator | AjvJsonSchemaValidator} on Node.js, {@linkcode index.CfWorkerJsonSchemaValidator | CfWorkerJsonSchemaValidator} on Cloudflare Workers)
165+
* @default {@linkcode DefaultJsonSchemaValidator} ({@linkcode index.AjvJsonSchemaValidator | AjvJsonSchemaValidator} on Node.js, `CfWorkerJsonSchemaValidator` on Cloudflare Workers)
166166
*/
167167
jsonSchemaValidator?: jsonSchemaValidator;
168168

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/**
2+
* Cloudflare Workers JSON Schema validator, available as a sub-path export.
3+
*
4+
* @example
5+
* ```ts
6+
* import { CfWorkerJsonSchemaValidator } from '@modelcontextprotocol/client/validators/cf-worker';
7+
* ```
8+
*/
9+
export { CfWorkerJsonSchemaValidator } from '@modelcontextprotocol/core';
10+
export type { CfWorkerSchemaDraft } from '@modelcontextprotocol/core';

packages/client/tsdown.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ export default defineConfig({
44
failOnWarn: 'ci-only',
55
// 1. Entry Points
66
// Directly matches package.json include/exclude globs
7-
entry: ['src/index.ts', 'src/shimsNode.ts', 'src/shimsWorkerd.ts', 'src/shimsBrowser.ts'],
7+
entry: ['src/index.ts', 'src/shimsNode.ts', 'src/shimsWorkerd.ts', 'src/shimsBrowser.ts', 'src/validators/cfWorker.ts'],
88

99
// 2. Output Configuration
1010
format: ['esm'],

packages/core/src/exports/public/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,11 +104,13 @@ export { ProtocolError, UrlElicitationRequiredError } from '../../types/errors.j
104104
export {
105105
assertCompleteRequestPrompt,
106106
assertCompleteRequestResourceTemplate,
107+
isCallToolResult,
107108
isInitializedNotification,
108109
isInitializeRequest,
109110
isJSONRPCErrorResponse,
110111
isJSONRPCNotification,
111112
isJSONRPCRequest,
113+
isJSONRPCResponse,
112114
isJSONRPCResultResponse,
113115
isTaskAugmentedRequestParams,
114116
parseJSONRPCMessage
@@ -137,7 +139,6 @@ export { InMemoryTaskMessageQueue, InMemoryTaskStore } from '../../experimental/
137139
export type { StandardSchemaWithJSON } from '../../util/standardSchema.js';
138140
export { AjvJsonSchemaValidator } from '../../validators/ajvProvider.js';
139141
export type { CfWorkerSchemaDraft } from '../../validators/cfWorkerProvider.js';
140-
export { CfWorkerJsonSchemaValidator } from '../../validators/cfWorkerProvider.js';
141142
// fromJsonSchema is intentionally NOT exported here — the server and client packages
142143
// provide runtime-aware wrappers that default to the appropriate validator via _shims.
143144
export type { JsonSchemaType, JsonSchemaValidator, jsonSchemaValidator, JsonSchemaValidatorResult } from '../../validators/types.js';

packages/core/src/index.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,11 @@ export * from './validators/fromJsonSchema.js';
2828
* Choose a validator based on your runtime environment:
2929
*
3030
* - {@linkcode AjvJsonSchemaValidator}: Best for Node.js (default, fastest)
31-
* Import from: @modelcontextprotocol/sdk/validators/ajv
32-
* Requires peer dependencies: ajv, ajv-formats
31+
* Bundled — no additional dependencies required.
3332
*
3433
* - {@linkcode CfWorkerJsonSchemaValidator}: Best for edge runtimes
35-
* Import from: @modelcontextprotocol/sdk/validators/cfworker
36-
* Requires peer dependency: @cfworker/json-schema
34+
* Import from: `@modelcontextprotocol/server/validators/cf-worker` or `@modelcontextprotocol/client/validators/cf-worker`
35+
* Bundled — no additional dependencies required.
3736
*
3837
* @example For Node.js with AJV
3938
* ```ts source="./index.examples.ts#validation_ajv"
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import { describe, expect, it } from 'vitest';
2+
3+
import { JSONRPC_VERSION } from './constants.js';
4+
import { isCallToolResult, isJSONRPCErrorResponse, isJSONRPCResponse, isJSONRPCResultResponse } from './guards.js';
5+
6+
describe('isJSONRPCResponse', () => {
7+
it('returns true for a valid result response', () => {
8+
expect(
9+
isJSONRPCResponse({
10+
jsonrpc: JSONRPC_VERSION,
11+
id: 1,
12+
result: {}
13+
})
14+
).toBe(true);
15+
});
16+
17+
it('returns true for a valid error response', () => {
18+
expect(
19+
isJSONRPCResponse({
20+
jsonrpc: JSONRPC_VERSION,
21+
id: 1,
22+
error: { code: -32_600, message: 'Invalid Request' }
23+
})
24+
).toBe(true);
25+
});
26+
27+
it('returns false for a request', () => {
28+
expect(
29+
isJSONRPCResponse({
30+
jsonrpc: JSONRPC_VERSION,
31+
id: 1,
32+
method: 'test'
33+
})
34+
).toBe(false);
35+
});
36+
37+
it('returns false for a notification', () => {
38+
expect(
39+
isJSONRPCResponse({
40+
jsonrpc: JSONRPC_VERSION,
41+
method: 'test'
42+
})
43+
).toBe(false);
44+
});
45+
46+
it('returns false for arbitrary objects', () => {
47+
expect(isJSONRPCResponse({ foo: 'bar' })).toBe(false);
48+
});
49+
50+
it('narrows the type correctly', () => {
51+
const value: unknown = {
52+
jsonrpc: JSONRPC_VERSION,
53+
id: 1,
54+
result: { content: [] }
55+
};
56+
if (isJSONRPCResponse(value)) {
57+
// Type should be narrowed to JSONRPCResponse
58+
expect(value.jsonrpc).toBe(JSONRPC_VERSION);
59+
expect(value.id).toBe(1);
60+
}
61+
});
62+
63+
it('agrees with isJSONRPCResultResponse || isJSONRPCErrorResponse', () => {
64+
const values = [
65+
{ jsonrpc: JSONRPC_VERSION, id: 1, result: {} },
66+
{ jsonrpc: JSONRPC_VERSION, id: 2, error: { code: -1, message: 'err' } },
67+
{ jsonrpc: JSONRPC_VERSION, id: 3, method: 'test' },
68+
{ jsonrpc: JSONRPC_VERSION, method: 'notify' },
69+
{ foo: 'bar' },
70+
null,
71+
42
72+
];
73+
for (const v of values) {
74+
expect(isJSONRPCResponse(v)).toBe(isJSONRPCResultResponse(v) || isJSONRPCErrorResponse(v));
75+
}
76+
});
77+
});
78+
79+
describe('isCallToolResult', () => {
80+
it('returns false for an empty object (content is required)', () => {
81+
expect(isCallToolResult({})).toBe(false);
82+
});
83+
84+
it('returns true for a result with content', () => {
85+
expect(
86+
isCallToolResult({
87+
content: [{ type: 'text', text: 'hello' }]
88+
})
89+
).toBe(true);
90+
});
91+
92+
it('returns true for a result with isError', () => {
93+
expect(
94+
isCallToolResult({
95+
content: [{ type: 'text', text: 'fail' }],
96+
isError: true
97+
})
98+
).toBe(true);
99+
});
100+
101+
it('returns true for a result with structuredContent', () => {
102+
expect(
103+
isCallToolResult({
104+
content: [],
105+
structuredContent: { key: 'value' }
106+
})
107+
).toBe(true);
108+
});
109+
110+
it('returns false for non-objects', () => {
111+
expect(isCallToolResult(null)).toBe(false);
112+
expect(isCallToolResult(42)).toBe(false);
113+
expect(isCallToolResult('string')).toBe(false);
114+
});
115+
116+
it('returns false for invalid content items', () => {
117+
expect(
118+
isCallToolResult({
119+
content: [{ type: 'invalid' }]
120+
})
121+
).toBe(false);
122+
});
123+
});

packages/core/src/types/guards.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
import {
2+
CallToolResultSchema,
23
InitializedNotificationSchema,
34
InitializeRequestSchema,
45
JSONRPCErrorResponseSchema,
56
JSONRPCMessageSchema,
67
JSONRPCNotificationSchema,
78
JSONRPCRequestSchema,
9+
JSONRPCResponseSchema,
810
JSONRPCResultResponseSchema,
911
TaskAugmentedRequestParamsSchema
1012
} from './schemas.js';
1113
import type {
14+
CallToolResult,
1215
CompleteRequest,
1316
CompleteRequestPrompt,
1417
CompleteRequestResourceTemplate,
@@ -18,6 +21,7 @@ import type {
1821
JSONRPCMessage,
1922
JSONRPCNotification,
2023
JSONRPCRequest,
24+
JSONRPCResponse,
2125
JSONRPCResultResponse,
2226
TaskAugmentedRequestParams
2327
} from './types.js';
@@ -58,6 +62,25 @@ export const isJSONRPCResultResponse = (value: unknown): value is JSONRPCResultR
5862
export const isJSONRPCErrorResponse = (value: unknown): value is JSONRPCErrorResponse =>
5963
JSONRPCErrorResponseSchema.safeParse(value).success;
6064

65+
/**
66+
* Checks if a value is a valid {@linkcode JSONRPCResponse} (either a result or error response).
67+
* @param value - The value to check.
68+
*
69+
* @returns True if the value is a valid {@linkcode JSONRPCResponse}, false otherwise.
70+
*/
71+
export const isJSONRPCResponse = (value: unknown): value is JSONRPCResponse => JSONRPCResponseSchema.safeParse(value).success;
72+
73+
/**
74+
* Checks if a value is a valid {@linkcode CallToolResult}.
75+
* @param value - The value to check.
76+
*
77+
* @returns True if the value is a valid {@linkcode CallToolResult}, false otherwise.
78+
*/
79+
export const isCallToolResult = (value: unknown): value is CallToolResult => {
80+
if (typeof value !== 'object' || value === null || !('content' in value)) return false;
81+
return CallToolResultSchema.safeParse(value).success;
82+
};
83+
6184
/**
6285
* Checks if a value is a valid {@linkcode TaskAugmentedRequestParams}.
6386
* @param value - The value to check.

0 commit comments

Comments
 (0)