Skip to content

Commit 8d55531

Browse files
feat(core): per-revision spec reference types and the 2026-07-28 wire contract surface (#2252)
1 parent ab552c3 commit 8d55531

20 files changed

Lines changed: 4146 additions & 1005 deletions
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@modelcontextprotocol/core': patch
3+
'@modelcontextprotocol/codemod': patch
4+
---
5+
6+
Add per-revision spec reference types (2025-11-25 and 2026-07-28) with split comparison tests, and the 2026-07-28 wire contract surface: request-meta key constants, `RequestMetaEnvelopeSchema`, `server/discover` shapes, the typed `-32004` error, the `-32003` code constant, and a `resultType` passthrough on the base result. Types and constants only — no behavior changes.

.github/workflows/update-spec-types.yml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,16 @@ jobs:
3434
run: pnpm install
3535

3636
- name: Fetch latest spec types
37-
run: pnpm run fetch:spec-types
37+
run: pnpm run fetch:spec-types 2026-07-28
3838

3939
- name: Check for changes
4040
id: check_changes
4141
run: |
42-
if git diff --quiet packages/core/src/types/spec.types.ts; then
42+
if git diff --quiet packages/core/src/types/spec.types.2026-07-28.ts; then
4343
echo "has_changes=false" >> $GITHUB_OUTPUT
4444
else
4545
echo "has_changes=true" >> $GITHUB_OUTPUT
46-
LATEST_SHA=$(grep "Last updated from commit:" packages/core/src/types/spec.types.ts | cut -d: -f2 | tr -d ' ')
46+
LATEST_SHA=$(grep "Last updated from commit:" packages/core/src/types/spec.types.2026-07-28.ts | cut -d: -f2 | tr -d ' ')
4747
echo "sha=$LATEST_SHA" >> $GITHUB_OUTPUT
4848
fi
4949
@@ -59,12 +59,12 @@ jobs:
5959
git config user.email "github-actions[bot]@users.noreply.github.com"
6060
6161
git checkout -B update-spec-types
62-
git add packages/core/src/types/spec.types.ts
63-
git commit -m "chore: update spec.types.ts from upstream"
62+
git add packages/core/src/types/spec.types.2026-07-28.ts
63+
git commit -m "chore: update spec.types.2026-07-28.ts from upstream"
6464
git push -f --no-verify origin update-spec-types
6565
6666
# Create PR if it doesn't exist, or update if it does
67-
PR_BODY="This PR updates \`packages/core/src/types/spec.types.ts\` from the Model Context Protocol specification.
67+
PR_BODY="This PR updates \`packages/core/src/types/spec.types.2026-07-28.ts\` from the Model Context Protocol specification.
6868
6969
Source file: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/${{ steps.check_changes.outputs.sha }}/schema/draft/schema.ts
7070
@@ -77,7 +77,7 @@ jobs:
7777
gh pr edit "$EXISTING_PR" --body "$PR_BODY"
7878
else
7979
gh pr create \
80-
--title "chore: update spec.types.ts from upstream" \
80+
--title "chore: update spec.types.2026-07-28.ts from upstream" \
8181
--body "$PR_BODY" \
8282
--base main \
8383
--head update-spec-types

.prettierignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ node_modules
1010
pnpm-lock.yaml
1111

1212
# Ignore generated files
13-
src/spec.types.ts
13+
**/src/types/spec.types.2025-11-25.ts
14+
**/src/types/spec.types.2026-07-28.ts
1415

1516
# Batch test cloned repos and results
1617
packages/codemod/batch-test/repos

REVIEW.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ When verifying spec compliance, consult the spec directly rather than relying on
7575

7676
### Schema Compliance
7777

78-
- When editing Zod protocol schemas in `schemas.ts`, verify unknown-key handling matches the spec `schema.ts`: if the spec type has no `additionalProperties: false`, the SDK schema must use `z.looseObject()` / `.catchall(z.unknown())` rather than implicit strict — over-strict Zod (incl. `z.literal('object')` on `type`) rejects spec-valid payloads from other SDKs. Also confirm `spec.types.test.ts` still passes bidirectionally. (#1768, #1849, #1169)
78+
- When editing Zod protocol schemas in `schemas.ts`, verify unknown-key handling matches the spec `schema.ts`: if the spec type has no `additionalProperties: false`, the SDK schema must use `z.looseObject()` / `.catchall(z.unknown())` rather than implicit strict — over-strict Zod (incl. `z.literal('object')` on `type`) rejects spec-valid payloads from other SDKs. Also confirm the `spec.types.*.test.ts` comparisons still pass bidirectionally. (#1768, #1849, #1169)
7979

8080
### Async / Lifecycle
8181

common/eslint-config/eslint.config.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ export default defineConfig(
9696
},
9797
{
9898
// Ignore generated protocol types everywhere
99-
ignores: ['**/spec.types.ts']
99+
ignores: ['**/spec.types.2025-11-25.ts', '**/spec.types.2026-07-28.ts']
100100
},
101101
{
102102
files: ['packages/client/**/*.ts', 'packages/server/**/*.ts'],

packages/codemod/src/generated/specSchemaMap.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ export const SPEC_SCHEMA_NAMES: ReadonlySet<string> = new Set([
2727
'CreateMessageResultWithToolsSchema',
2828
'CreateTaskResultSchema',
2929
'CursorSchema',
30+
'DiscoverRequestSchema',
31+
'DiscoverResultSchema',
3032
'ElicitRequestFormParamsSchema',
3133
'ElicitRequestParamsSchema',
3234
'ElicitRequestSchema',
@@ -112,6 +114,7 @@ export const SPEC_SCHEMA_NAMES: ReadonlySet<string> = new Set([
112114
'ReadResourceResultSchema',
113115
'RelatedTaskMetadataSchema',
114116
'RequestIdSchema',
117+
'RequestMetaEnvelopeSchema',
115118
'RequestMetaSchema',
116119
'RequestSchema',
117120
'ResourceContentsSchema',

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,14 +71,18 @@ export * from '../../types/types.js';
7171

7272
// Constants
7373
export {
74+
CLIENT_CAPABILITIES_META_KEY,
75+
CLIENT_INFO_META_KEY,
7476
DEFAULT_NEGOTIATED_PROTOCOL_VERSION,
7577
INTERNAL_ERROR,
7678
INVALID_PARAMS,
7779
INVALID_REQUEST,
7880
JSONRPC_VERSION,
7981
LATEST_PROTOCOL_VERSION,
82+
LOG_LEVEL_META_KEY,
8083
METHOD_NOT_FOUND,
8184
PARSE_ERROR,
85+
PROTOCOL_VERSION_META_KEY,
8286
RELATED_TASK_META_KEY,
8387
SUPPORTED_PROTOCOL_VERSIONS
8488
} from '../../types/constants.js';
@@ -87,7 +91,7 @@ export {
8791
export { ProtocolErrorCode } from '../../types/enums.js';
8892

8993
// Error classes
90-
export { ProtocolError, UrlElicitationRequiredError } from '../../types/errors.js';
94+
export { ProtocolError, UnsupportedProtocolVersionError, UrlElicitationRequiredError } from '../../types/errors.js';
9195

9296
// Type guards and message parsing
9397
export {

packages/core/src/types/constants.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,39 @@ export const SUPPORTED_PROTOCOL_VERSIONS = [LATEST_PROTOCOL_VERSION, '2025-06-18
44

55
export const RELATED_TASK_META_KEY = 'io.modelcontextprotocol/related-task';
66

7+
/* Reserved `_meta` keys for the per-request envelope (protocol revision 2026-07-28) */
8+
9+
/**
10+
* `_meta` key carrying the MCP protocol version governing a request.
11+
*
12+
* For the HTTP transport, the value must match the `MCP-Protocol-Version` header.
13+
*/
14+
export const PROTOCOL_VERSION_META_KEY = 'io.modelcontextprotocol/protocolVersion';
15+
16+
/**
17+
* `_meta` key identifying the client software making a request.
18+
*/
19+
export const CLIENT_INFO_META_KEY = 'io.modelcontextprotocol/clientInfo';
20+
21+
/**
22+
* `_meta` key carrying the client's capabilities for a request.
23+
*
24+
* Capabilities are declared per request rather than once at initialization;
25+
* servers must not infer capabilities from prior requests.
26+
*/
27+
export const CLIENT_CAPABILITIES_META_KEY = 'io.modelcontextprotocol/clientCapabilities';
28+
29+
/**
30+
* `_meta` key carrying the desired log level for a request.
31+
*
32+
* When absent, the server must not send `notifications/message` notifications
33+
* for the request.
34+
*
35+
* @deprecated Deprecated as of protocol version 2026-07-28 (SEP-2577); remains
36+
* in the specification for at least twelve months.
37+
*/
38+
export const LOG_LEVEL_META_KEY = 'io.modelcontextprotocol/logLevel';
39+
740
/* JSON-RPC types */
841
export const JSONRPC_VERSION = '2.0';
942

packages/core/src/types/enums.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,15 @@ export enum ProtocolErrorCode {
1212

1313
// MCP-specific error codes
1414
ResourceNotFound = -32_002,
15+
/**
16+
* Processing the request requires a capability the client did not declare
17+
* in the request's `clientCapabilities` (protocol revision 2026-07-28).
18+
*/
19+
MissingRequiredClientCapability = -32_003,
20+
/**
21+
* The request's protocol version is unknown to the server or unsupported
22+
* by it (protocol revision 2026-07-28).
23+
*/
24+
UnsupportedProtocolVersion = -32_004,
1525
UrlElicitationRequired = -32_042
1626
}

packages/core/src/types/errors.ts

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ProtocolErrorCode } from './enums.js';
2-
import type { ElicitRequestURLParams } from './types.js';
2+
import type { ElicitRequestURLParams, UnsupportedProtocolVersionErrorData } from './types.js';
33

44
/**
55
* Protocol errors are JSON-RPC errors that cross the wire as error responses.
@@ -27,6 +27,13 @@ export class ProtocolError extends Error {
2727
}
2828
}
2929

30+
if (code === ProtocolErrorCode.UnsupportedProtocolVersion && data) {
31+
const errorData = data as Partial<UnsupportedProtocolVersionErrorData>;
32+
if (Array.isArray(errorData.supported) && typeof errorData.requested === 'string') {
33+
return new UnsupportedProtocolVersionError({ supported: errorData.supported, requested: errorData.requested }, message);
34+
}
35+
}
36+
3037
// Default to generic ProtocolError
3138
return new ProtocolError(code, message, data);
3239
}
@@ -47,3 +54,32 @@ export class UrlElicitationRequiredError extends ProtocolError {
4754
return (this.data as { elicitations: ElicitRequestURLParams[] })?.elicitations ?? [];
4855
}
4956
}
57+
58+
/**
59+
* Error type for the `-32004` UnsupportedProtocolVersion protocol error (protocol
60+
* revision 2026-07-28): the request's protocol version is unknown to the server or
61+
* unsupported by it.
62+
*
63+
* The error data lists the protocol versions the receiver supports (`supported`),
64+
* so the sender can choose a mutually supported version and retry, and echoes the
65+
* version that was requested (`requested`).
66+
*/
67+
export class UnsupportedProtocolVersionError extends ProtocolError {
68+
constructor(data: UnsupportedProtocolVersionErrorData, message: string = `Unsupported protocol version: ${data.requested}`) {
69+
super(ProtocolErrorCode.UnsupportedProtocolVersion, message, data);
70+
}
71+
72+
/**
73+
* Protocol versions the receiver supports.
74+
*/
75+
get supported(): string[] {
76+
return (this.data as UnsupportedProtocolVersionErrorData).supported;
77+
}
78+
79+
/**
80+
* The protocol version that was requested.
81+
*/
82+
get requested(): string {
83+
return (this.data as UnsupportedProtocolVersionErrorData).requested;
84+
}
85+
}

0 commit comments

Comments
 (0)