feat(core): per-revision spec reference types and the 2026-07-28 wire contract surface (#2252)

This commit is contained in:
Felix Weinberger 2026-06-08 18:31:57 +01:00 • committed by GitHub
commit 8d55531dab
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 4146 additions and 1005 deletions

View file

@ -0,0 +1,6 @@
---
'@modelcontextprotocol/core': patch
'@modelcontextprotocol/codemod': patch
---
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.

View file

@ -34,16 +34,16 @@ jobs:
run: pnpm install
- name: Fetch latest spec types
run: pnpm run fetch:spec-types
run: pnpm run fetch:spec-types 2026-07-28
- name: Check for changes
id: check_changes
run: |
if git diff --quiet packages/core/src/types/spec.types.ts; then
if git diff --quiet packages/core/src/types/spec.types.2026-07-28.ts; then
echo "has_changes=false" >> $GITHUB_OUTPUT
else
echo "has_changes=true" >> $GITHUB_OUTPUT
LATEST_SHA=$(grep "Last updated from commit:" packages/core/src/types/spec.types.ts | cut -d: -f2 | tr -d ' ')
LATEST_SHA=$(grep "Last updated from commit:" packages/core/src/types/spec.types.2026-07-28.ts | cut -d: -f2 | tr -d ' ')
echo "sha=$LATEST_SHA" >> $GITHUB_OUTPUT
fi
@ -59,12 +59,12 @@ jobs:
git config user.email "github-actions[bot]@users.noreply.github.com"
git checkout -B update-spec-types
git add packages/core/src/types/spec.types.ts
git commit -m "chore: update spec.types.ts from upstream"
git add packages/core/src/types/spec.types.2026-07-28.ts
git commit -m "chore: update spec.types.2026-07-28.ts from upstream"
git push -f --no-verify origin update-spec-types
# Create PR if it doesn't exist, or update if it does
PR_BODY="This PR updates \`packages/core/src/types/spec.types.ts\` from the Model Context Protocol specification.
PR_BODY="This PR updates \`packages/core/src/types/spec.types.2026-07-28.ts\` from the Model Context Protocol specification.
Source file: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/${{ steps.check_changes.outputs.sha }}/schema/draft/schema.ts
@ -77,7 +77,7 @@ jobs:
gh pr edit "$EXISTING_PR" --body "$PR_BODY"
else
gh pr create \
--title "chore: update spec.types.ts from upstream" \
--title "chore: update spec.types.2026-07-28.ts from upstream" \
--body "$PR_BODY" \
--base main \
--head update-spec-types

View file

@ -10,7 +10,8 @@ node_modules
pnpm-lock.yaml
# Ignore generated files
src/spec.types.ts
**/src/types/spec.types.2025-11-25.ts
**/src/types/spec.types.2026-07-28.ts
# Batch test cloned repos and results
packages/codemod/batch-test/repos

View file

@ -75,7 +75,7 @@ When verifying spec compliance, consult the spec directly rather than relying on
### Schema Compliance
- 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)
- 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)
### Async / Lifecycle

View file

@ -96,7 +96,7 @@ export default defineConfig(
},
{
// Ignore generated protocol types everywhere
ignores: ['**/spec.types.ts']
ignores: ['**/spec.types.2025-11-25.ts', '**/spec.types.2026-07-28.ts']
},
{
files: ['packages/client/**/*.ts', 'packages/server/**/*.ts'],

View file

@ -27,6 +27,8 @@ export const SPEC_SCHEMA_NAMES: ReadonlySet<string> = new Set([
'CreateMessageResultWithToolsSchema',
'CreateTaskResultSchema',
'CursorSchema',
'DiscoverRequestSchema',
'DiscoverResultSchema',
'ElicitRequestFormParamsSchema',
'ElicitRequestParamsSchema',
'ElicitRequestSchema',
@ -112,6 +114,7 @@ export const SPEC_SCHEMA_NAMES: ReadonlySet<string> = new Set([
'ReadResourceResultSchema',
'RelatedTaskMetadataSchema',
'RequestIdSchema',
'RequestMetaEnvelopeSchema',
'RequestMetaSchema',
'RequestSchema',
'ResourceContentsSchema',

View file

@ -71,14 +71,18 @@ export * from '../../types/types.js';
// Constants
export {
CLIENT_CAPABILITIES_META_KEY,
CLIENT_INFO_META_KEY,
DEFAULT_NEGOTIATED_PROTOCOL_VERSION,
INTERNAL_ERROR,
INVALID_PARAMS,
INVALID_REQUEST,
JSONRPC_VERSION,
LATEST_PROTOCOL_VERSION,
LOG_LEVEL_META_KEY,
METHOD_NOT_FOUND,
PARSE_ERROR,
PROTOCOL_VERSION_META_KEY,
RELATED_TASK_META_KEY,
SUPPORTED_PROTOCOL_VERSIONS
} from '../../types/constants.js';
@ -87,7 +91,7 @@ export {
export { ProtocolErrorCode } from '../../types/enums.js';
// Error classes
export { ProtocolError, UrlElicitationRequiredError } from '../../types/errors.js';
export { ProtocolError, UnsupportedProtocolVersionError, UrlElicitationRequiredError } from '../../types/errors.js';
// Type guards and message parsing
export {

View file

@ -4,6 +4,39 @@ export const SUPPORTED_PROTOCOL_VERSIONS = [LATEST_PROTOCOL_VERSION, '2025-06-18
export const RELATED_TASK_META_KEY = 'io.modelcontextprotocol/related-task';
/* Reserved `_meta` keys for the per-request envelope (protocol revision 2026-07-28) */
/**
* `_meta` key carrying the MCP protocol version governing a request.
*
* For the HTTP transport, the value must match the `MCP-Protocol-Version` header.
*/
export const PROTOCOL_VERSION_META_KEY = 'io.modelcontextprotocol/protocolVersion';
/**
* `_meta` key identifying the client software making a request.
*/
export const CLIENT_INFO_META_KEY = 'io.modelcontextprotocol/clientInfo';
/**
* `_meta` key carrying the client's capabilities for a request.
*
* Capabilities are declared per request rather than once at initialization;
* servers must not infer capabilities from prior requests.
*/
export const CLIENT_CAPABILITIES_META_KEY = 'io.modelcontextprotocol/clientCapabilities';
/**
* `_meta` key carrying the desired log level for a request.
*
* When absent, the server must not send `notifications/message` notifications
* for the request.
*
* @deprecated Deprecated as of protocol version 2026-07-28 (SEP-2577); remains
* in the specification for at least twelve months.
*/
export const LOG_LEVEL_META_KEY = 'io.modelcontextprotocol/logLevel';
/* JSON-RPC types */
export const JSONRPC_VERSION = '2.0';

View file

@ -12,5 +12,15 @@ export enum ProtocolErrorCode {
// MCP-specific error codes
ResourceNotFound = -32_002,
/**
* Processing the request requires a capability the client did not declare
* in the request's `clientCapabilities` (protocol revision 2026-07-28).
*/
MissingRequiredClientCapability = -32_003,
/**
* The request's protocol version is unknown to the server or unsupported
* by it (protocol revision 2026-07-28).
*/
UnsupportedProtocolVersion = -32_004,
UrlElicitationRequired = -32_042
}

View file

@ -1,5 +1,5 @@
import { ProtocolErrorCode } from './enums.js';
import type { ElicitRequestURLParams } from './types.js';
import type { ElicitRequestURLParams, UnsupportedProtocolVersionErrorData } from './types.js';
/**
* Protocol errors are JSON-RPC errors that cross the wire as error responses.
@ -27,6 +27,13 @@ export class ProtocolError extends Error {
}
}
if (code === ProtocolErrorCode.UnsupportedProtocolVersion && data) {
const errorData = data as Partial<UnsupportedProtocolVersionErrorData>;
if (Array.isArray(errorData.supported) && typeof errorData.requested === 'string') {
return new UnsupportedProtocolVersionError({ supported: errorData.supported, requested: errorData.requested }, message);
}
}
// Default to generic ProtocolError
return new ProtocolError(code, message, data);
}
@ -47,3 +54,32 @@ export class UrlElicitationRequiredError extends ProtocolError {
return (this.data as { elicitations: ElicitRequestURLParams[] })?.elicitations ?? [];
}
}
/**
* Error type for the `-32004` UnsupportedProtocolVersion protocol error (protocol
* revision 2026-07-28): the request's protocol version is unknown to the server or
* unsupported by it.
*
* The error data lists the protocol versions the receiver supports (`supported`),
* so the sender can choose a mutually supported version and retry, and echoes the
* version that was requested (`requested`).
*/
export class UnsupportedProtocolVersionError extends ProtocolError {
constructor(data: UnsupportedProtocolVersionErrorData, message: string = `Unsupported protocol version: ${data.requested}`) {
super(ProtocolErrorCode.UnsupportedProtocolVersion, message, data);
}
/**
* Protocol versions the receiver supports.
*/
get supported(): string[] {
return (this.data as UnsupportedProtocolVersionErrorData).supported;
}
/**
* The protocol version that was requested.
*/
get requested(): string {
return (this.data as UnsupportedProtocolVersionErrorData).requested;
}
}

View file

@ -1,6 +1,13 @@
import * as z from 'zod/v4';
import { JSONRPC_VERSION, RELATED_TASK_META_KEY } from './constants.js';
import {
CLIENT_CAPABILITIES_META_KEY,
CLIENT_INFO_META_KEY,
JSONRPC_VERSION,
LOG_LEVEL_META_KEY,
PROTOCOL_VERSION_META_KEY,
RELATED_TASK_META_KEY
} from './constants.js';
import type {
JSONArray,
JSONObject,
@ -113,7 +120,14 @@ export const ResultSchema = z.looseObject({
* See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields)
* for notes on `_meta` usage.
*/
_meta: RequestMetaSchema.optional()
_meta: RequestMetaSchema.optional(),
/**
* Indicates the type of the result, allowing the receiver to determine how to
* parse the result object. Servers implementing protocol revision 2026-07-28 or
* later always include this field; results from earlier revisions omit it, and
* an absent value must be treated as `"complete"`.
*/
resultType: z.string().optional()
});
/**
@ -552,6 +566,43 @@ export const InitializedNotificationSchema = NotificationSchema.extend({
params: NotificationsParamsSchema.optional()
});
/* Discovery */
/**
* A request from the client asking the server to advertise its supported protocol
* versions, capabilities, and other metadata (protocol revision 2026-07-28). Servers
* MUST implement `server/discover`. Clients MAY call it but are not required to —
* version negotiation can also happen inline via the per-request `_meta` envelope.
*/
export const DiscoverRequestSchema = RequestSchema.extend({
method: z.literal('server/discover'),
params: BaseRequestParamsSchema.optional()
});
/**
* The result returned by the server for a `server/discover` request.
*/
export const DiscoverResultSchema = ResultSchema.extend({
/**
* MCP protocol versions this server supports. The client should choose a
* version from this list for use in subsequent requests.
*/
supportedVersions: z.array(z.string()),
/**
* The capabilities of the server.
*/
capabilities: ServerCapabilitiesSchema,
/**
* Information about the server software implementation.
*/
serverInfo: ImplementationSchema,
/**
* Instructions describing how to use the server and its features.
*
* This can be used by clients to improve the LLM's understanding of available tools, resources, etc. It can be thought of like a "hint" to the model. For example, this information MAY be added to the system prompt.
*/
instructions: z.string().optional()
});
/* Ping */
/**
* A ping, issued by either the server or the client, to check that the other party is still alive. The receiver must promptly respond, or else may be disconnected.
@ -1509,6 +1560,48 @@ export const LoggingMessageNotificationSchema = NotificationSchema.extend({
params: LoggingMessageNotificationParamsSchema
});
/* Per-request `_meta` envelope */
/**
* The per-request `_meta` envelope carried by every request under protocol revision
* 2026-07-28: the protocol version governing the request, the client implementation
* info, and the client's capabilities — declared per request rather than once at
* initialization — plus the optional log-level opt-in.
*
* This schema models the complete envelope on its own. The base request schemas
* ({@linkcode RequestMetaSchema}) deliberately stay lenient so the same wire schemas
* parse requests from earlier protocol revisions (no envelope) as well; envelope
* requiredness is enforced per request at dispatch time, not here.
*/
export const RequestMetaEnvelopeSchema = z.looseObject({
/**
* If specified, the caller is requesting out-of-band progress notifications for this request (as represented by notifications/progress). The value of this parameter is an opaque token that will be attached to any subsequent notifications. The receiver is not obligated to provide these notifications.
*/
progressToken: ProgressTokenSchema.optional(),
/**
* The MCP protocol version being used for this request. For the HTTP transport,
* the value must match the `MCP-Protocol-Version` header.
*/
[PROTOCOL_VERSION_META_KEY]: z.string(),
/**
* Identifies the client software making the request.
*/
[CLIENT_INFO_META_KEY]: ImplementationSchema,
/**
* The client's capabilities for this specific request. An empty object means the
* client supports no optional capabilities. Servers must not infer capabilities
* from prior requests.
*/
[CLIENT_CAPABILITIES_META_KEY]: ClientCapabilitiesSchema,
/**
* The desired log level for this request. When absent, the server must not send
* `notifications/message` notifications for the request.
*
* @deprecated Deprecated as of protocol version 2026-07-28 (SEP-2577); remains
* in the specification for at least twelve months.
*/
[LOG_LEVEL_META_KEY]: LoggingLevelSchema.optional()
});
/* Sampling */
/**
* Hints to use for model selection.

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -58,6 +58,8 @@ const SPEC_SCHEMA_KEYS = [
'CreateMessageResultWithToolsSchema',
'CreateTaskResultSchema',
'CursorSchema',
'DiscoverRequestSchema',
'DiscoverResultSchema',
'ElicitationCompleteNotificationSchema',
'ElicitationCompleteNotificationParamsSchema',
'ElicitRequestSchema',
@ -133,6 +135,7 @@ const SPEC_SCHEMA_KEYS = [
'RelatedTaskMetadataSchema',
'RequestSchema',
'RequestIdSchema',
'RequestMetaEnvelopeSchema',
'RequestMetaSchema',
'ResourceSchema',
'ResourceContentsSchema',

View file

@ -34,6 +34,8 @@ import type {
CreateMessageResultWithToolsSchema,
CreateTaskResultSchema,
CursorSchema,
DiscoverRequestSchema,
DiscoverResultSchema,
ElicitationCompleteNotificationParamsSchema,
ElicitationCompleteNotificationSchema,
ElicitRequestFormParamsSchema,
@ -106,6 +108,7 @@ import type {
ReadResourceResultSchema,
RelatedTaskMetadataSchema,
RequestIdSchema,
RequestMetaEnvelopeSchema,
RequestMetaSchema,
RequestSchema,
ResourceContentsSchema,
@ -200,6 +203,11 @@ export type JSONRPCResultResponse = Infer<typeof JSONRPCResultResponseSchema>;
export type JSONRPCMessage = Infer<typeof JSONRPCMessageSchema>;
export type RequestParams = Infer<typeof BaseRequestParamsSchema>;
export type NotificationParams = Infer<typeof NotificationsParamsSchema>;
/**
* The per-request `_meta` envelope carried by every request under protocol revision
* 2026-07-28 (protocol version, client info, client capabilities, optional log level).
*/
export type RequestMetaEnvelope = Infer<typeof RequestMetaEnvelopeSchema>;
/* Empty result */
export type EmptyResult = Infer<typeof EmptyResultSchema>;
@ -224,6 +232,10 @@ export type ServerCapabilities = Infer<typeof ServerCapabilitiesSchema>;
export type InitializeResult = Infer<typeof InitializeResultSchema>;
export type InitializedNotification = Infer<typeof InitializedNotificationSchema>;
/* Discovery */
export type DiscoverRequest = Infer<typeof DiscoverRequestSchema>;
export type DiscoverResult = Infer<typeof DiscoverResultSchema>;
/* Ping */
export type PingRequest = Infer<typeof PingRequestSchema>;
@ -458,6 +470,22 @@ export interface InternalError extends JSONRPCErrorObject {
code: typeof INTERNAL_ERROR;
}
/**
* Data carried by a `-32004` UnsupportedProtocolVersion protocol error
* (protocol revision 2026-07-28).
*/
export interface UnsupportedProtocolVersionErrorData {
/**
* Protocol versions the receiver supports. The sender should choose a
* mutually supported version from this list and retry.
*/
supported: string[];
/**
* The protocol version that was requested.
*/
requested: string;
}
/**
* Callback type for list changed notifications.
*/

View file

@ -0,0 +1,950 @@
/**
* Compares the SDK's types against the frozen 2025-11-25 release schema
* (spec.types.2025-11-25.ts). The 2026-07-28 comparison lives in
* spec.types.2026-07-28.test.ts.
*
* This contains:
* - Static type checks to verify the Spec's types are compatible with the SDK's types
* (mutually assignable — no type-level workarounds should be needed)
* - Runtime checks to verify each Spec type has a static check
* (note: a few don't have SDK types, see MISSING_SDK_TYPES below)
*/
import fs from 'node:fs';
import path from 'node:path';
import type * as SpecTypes from '../src/types/spec.types.2025-11-25.js';
import type * as SDKTypes from '../src/types/index.js';
/* eslint-disable @typescript-eslint/no-unused-vars */
// Adds the `jsonrpc` property to a type, to match the on-wire format of notifications.
type WithJSONRPC<T> = T & { jsonrpc: '2.0' };
// Adds the `jsonrpc` and `id` properties to a type, to match the on-wire format of requests.
type WithJSONRPCRequest<T> = T & { jsonrpc: '2.0'; id: SDKTypes.RequestId };
const sdkTypeChecks = {
RequestParams: (sdk: SDKTypes.RequestParams, spec: SpecTypes.RequestParams) => {
sdk = spec;
spec = sdk;
},
NotificationParams: (sdk: SDKTypes.NotificationParams, spec: SpecTypes.NotificationParams) => {
sdk = spec;
spec = sdk;
},
CancelledNotificationParams: (sdk: SDKTypes.CancelledNotificationParams, spec: SpecTypes.CancelledNotificationParams) => {
sdk = spec;
spec = sdk;
},
InitializeRequestParams: (sdk: SDKTypes.InitializeRequestParams, spec: SpecTypes.InitializeRequestParams) => {
// @ts-expect-error 2025-11-25 types capabilities.experimental values as `object`; the SDK follows the 2026-07-28 schema's JSONObject
sdk = spec;
spec = sdk;
},
ProgressNotificationParams: (sdk: SDKTypes.ProgressNotificationParams, spec: SpecTypes.ProgressNotificationParams) => {
sdk = spec;
spec = sdk;
},
ResourceRequestParams: (sdk: SDKTypes.ResourceRequestParams, spec: SpecTypes.ResourceRequestParams) => {
sdk = spec;
spec = sdk;
},
ReadResourceRequestParams: (sdk: SDKTypes.ReadResourceRequestParams, spec: SpecTypes.ReadResourceRequestParams) => {
sdk = spec;
spec = sdk;
},
SubscribeRequestParams: (sdk: SDKTypes.SubscribeRequestParams, spec: SpecTypes.SubscribeRequestParams) => {
sdk = spec;
spec = sdk;
},
UnsubscribeRequestParams: (sdk: SDKTypes.UnsubscribeRequestParams, spec: SpecTypes.UnsubscribeRequestParams) => {
sdk = spec;
spec = sdk;
},
ResourceUpdatedNotificationParams: (
sdk: SDKTypes.ResourceUpdatedNotificationParams,
spec: SpecTypes.ResourceUpdatedNotificationParams
) => {
sdk = spec;
spec = sdk;
},
GetPromptRequestParams: (sdk: SDKTypes.GetPromptRequestParams, spec: SpecTypes.GetPromptRequestParams) => {
sdk = spec;
spec = sdk;
},
CallToolRequestParams: (sdk: SDKTypes.CallToolRequestParams, spec: SpecTypes.CallToolRequestParams) => {
sdk = spec;
spec = sdk;
},
SetLevelRequestParams: (sdk: SDKTypes.SetLevelRequestParams, spec: SpecTypes.SetLevelRequestParams) => {
sdk = spec;
spec = sdk;
},
LoggingMessageNotificationParams: (
sdk: SDKTypes.LoggingMessageNotificationParams,
spec: SpecTypes.LoggingMessageNotificationParams
) => {
sdk = spec;
spec = sdk;
},
CreateMessageRequestParams: (sdk: SDKTypes.CreateMessageRequestParams, spec: SpecTypes.CreateMessageRequestParams) => {
// @ts-expect-error 2025-11-25 types `metadata` as `object`; the SDK follows the 2026-07-28 schema's JSONObject
sdk = spec;
// @ts-expect-error the SDK's JSONValue-typed tool inputSchema properties are not assignable to 2025-11-25's `object`
spec = sdk;
},
CompleteRequestParams: (sdk: SDKTypes.CompleteRequestParams, spec: SpecTypes.CompleteRequestParams) => {
sdk = spec;
spec = sdk;
},
ElicitRequestParams: (sdk: SDKTypes.ElicitRequestParams, spec: SpecTypes.ElicitRequestParams) => {
sdk = spec;
spec = sdk;
},
ElicitRequestFormParams: (sdk: SDKTypes.ElicitRequestFormParams, spec: SpecTypes.ElicitRequestFormParams) => {
sdk = spec;
spec = sdk;
},
ElicitRequestURLParams: (sdk: SDKTypes.ElicitRequestURLParams, spec: SpecTypes.ElicitRequestURLParams) => {
sdk = spec;
spec = sdk;
},
ElicitationCompleteNotification: (
sdk: WithJSONRPC<SDKTypes.ElicitationCompleteNotification>,
spec: SpecTypes.ElicitationCompleteNotification
) => {
sdk = spec;
spec = sdk;
},
PaginatedRequestParams: (sdk: SDKTypes.PaginatedRequestParams, spec: SpecTypes.PaginatedRequestParams) => {
sdk = spec;
spec = sdk;
},
CancelledNotification: (sdk: WithJSONRPC<SDKTypes.CancelledNotification>, spec: SpecTypes.CancelledNotification) => {
sdk = spec;
spec = sdk;
},
BaseMetadata: (sdk: SDKTypes.BaseMetadata, spec: SpecTypes.BaseMetadata) => {
sdk = spec;
spec = sdk;
},
Implementation: (sdk: SDKTypes.Implementation, spec: SpecTypes.Implementation) => {
sdk = spec;
spec = sdk;
},
ProgressNotification: (sdk: WithJSONRPC<SDKTypes.ProgressNotification>, spec: SpecTypes.ProgressNotification) => {
sdk = spec;
spec = sdk;
},
SubscribeRequest: (sdk: WithJSONRPCRequest<SDKTypes.SubscribeRequest>, spec: SpecTypes.SubscribeRequest) => {
sdk = spec;
spec = sdk;
},
UnsubscribeRequest: (sdk: WithJSONRPCRequest<SDKTypes.UnsubscribeRequest>, spec: SpecTypes.UnsubscribeRequest) => {
sdk = spec;
spec = sdk;
},
PaginatedRequest: (sdk: WithJSONRPCRequest<SDKTypes.PaginatedRequest>, spec: SpecTypes.PaginatedRequest) => {
sdk = spec;
spec = sdk;
},
PaginatedResult: (sdk: SDKTypes.PaginatedResult, spec: SpecTypes.PaginatedResult) => {
sdk = spec;
spec = sdk;
},
ListRootsRequest: (sdk: WithJSONRPCRequest<SDKTypes.ListRootsRequest>, spec: SpecTypes.ListRootsRequest) => {
sdk = spec;
spec = sdk;
},
ListRootsResult: (sdk: SDKTypes.ListRootsResult, spec: SpecTypes.ListRootsResult) => {
sdk = spec;
spec = sdk;
},
Root: (sdk: SDKTypes.Root, spec: SpecTypes.Root) => {
sdk = spec;
spec = sdk;
},
ElicitRequest: (sdk: WithJSONRPCRequest<SDKTypes.ElicitRequest>, spec: SpecTypes.ElicitRequest) => {
sdk = spec;
spec = sdk;
},
ElicitResult: (sdk: SDKTypes.ElicitResult, spec: SpecTypes.ElicitResult) => {
sdk = spec;
spec = sdk;
},
CompleteRequest: (sdk: WithJSONRPCRequest<SDKTypes.CompleteRequest>, spec: SpecTypes.CompleteRequest) => {
sdk = spec;
spec = sdk;
},
CompleteResult: (sdk: SDKTypes.CompleteResult, spec: SpecTypes.CompleteResult) => {
sdk = spec;
spec = sdk;
},
ProgressToken: (sdk: SDKTypes.ProgressToken, spec: SpecTypes.ProgressToken) => {
sdk = spec;
spec = sdk;
},
Cursor: (sdk: SDKTypes.Cursor, spec: SpecTypes.Cursor) => {
sdk = spec;
spec = sdk;
},
Request: (sdk: SDKTypes.Request, spec: SpecTypes.Request) => {
sdk = spec;
spec = sdk;
},
Result: (sdk: SDKTypes.Result, spec: SpecTypes.Result) => {
sdk = spec;
spec = sdk;
},
RequestId: (sdk: SDKTypes.RequestId, spec: SpecTypes.RequestId) => {
sdk = spec;
spec = sdk;
},
JSONRPCRequest: (sdk: SDKTypes.JSONRPCRequest, spec: SpecTypes.JSONRPCRequest) => {
sdk = spec;
spec = sdk;
},
JSONRPCNotification: (sdk: SDKTypes.JSONRPCNotification, spec: SpecTypes.JSONRPCNotification) => {
sdk = spec;
spec = sdk;
},
JSONRPCResponse: (sdk: SDKTypes.JSONRPCResponse, spec: SpecTypes.JSONRPCResponse) => {
sdk = spec;
spec = sdk;
},
EmptyResult: (sdk: SDKTypes.EmptyResult, spec: SpecTypes.EmptyResult) => {
sdk = spec;
spec = sdk;
},
Notification: (sdk: SDKTypes.Notification, spec: SpecTypes.Notification) => {
sdk = spec;
spec = sdk;
},
ClientResult: (sdk: SDKTypes.ClientResult, spec: SpecTypes.ClientResult) => {
sdk = spec;
spec = sdk;
},
ClientNotification: (sdk: WithJSONRPC<SDKTypes.ClientNotification>, spec: SpecTypes.ClientNotification) => {
sdk = spec;
spec = sdk;
},
ServerResult: (sdk: SDKTypes.ServerResult, spec: SpecTypes.ServerResult) => {
sdk = spec;
spec = sdk;
},
ResourceTemplateReference: (sdk: SDKTypes.ResourceTemplateReference, spec: SpecTypes.ResourceTemplateReference) => {
sdk = spec;
spec = sdk;
},
PromptReference: (sdk: SDKTypes.PromptReference, spec: SpecTypes.PromptReference) => {
sdk = spec;
spec = sdk;
},
ToolAnnotations: (sdk: SDKTypes.ToolAnnotations, spec: SpecTypes.ToolAnnotations) => {
sdk = spec;
spec = sdk;
},
Tool: (sdk: SDKTypes.Tool, spec: SpecTypes.Tool) => {
// @ts-expect-error 2025-11-25 types inputSchema/outputSchema properties as `object`; the SDK follows the 2026-07-28 schema's JSONValue
sdk = spec;
// @ts-expect-error the SDK's JSONValue-typed inputSchema/outputSchema properties are not assignable to 2025-11-25's `object`
spec = sdk;
},
ListToolsRequest: (sdk: WithJSONRPCRequest<SDKTypes.ListToolsRequest>, spec: SpecTypes.ListToolsRequest) => {
sdk = spec;
spec = sdk;
},
ListToolsResult: (sdk: SDKTypes.ListToolsResult, spec: SpecTypes.ListToolsResult) => {
// @ts-expect-error 2025-11-25 vs 2026-07-28 Tool typing; see the Tool check above
sdk = spec;
// @ts-expect-error 2025-11-25 vs 2026-07-28 Tool typing; see the Tool check above
spec = sdk;
},
CallToolResult: (sdk: SDKTypes.CallToolResult, spec: SpecTypes.CallToolResult) => {
sdk = spec;
spec = sdk;
},
CallToolRequest: (sdk: WithJSONRPCRequest<SDKTypes.CallToolRequest>, spec: SpecTypes.CallToolRequest) => {
sdk = spec;
spec = sdk;
},
ToolListChangedNotification: (sdk: WithJSONRPC<SDKTypes.ToolListChangedNotification>, spec: SpecTypes.ToolListChangedNotification) => {
sdk = spec;
spec = sdk;
},
ResourceListChangedNotification: (
sdk: WithJSONRPC<SDKTypes.ResourceListChangedNotification>,
spec: SpecTypes.ResourceListChangedNotification
) => {
sdk = spec;
spec = sdk;
},
PromptListChangedNotification: (
sdk: WithJSONRPC<SDKTypes.PromptListChangedNotification>,
spec: SpecTypes.PromptListChangedNotification
) => {
sdk = spec;
spec = sdk;
},
RootsListChangedNotification: (
sdk: WithJSONRPC<SDKTypes.RootsListChangedNotification>,
spec: SpecTypes.RootsListChangedNotification
) => {
sdk = spec;
spec = sdk;
},
ResourceUpdatedNotification: (sdk: WithJSONRPC<SDKTypes.ResourceUpdatedNotification>, spec: SpecTypes.ResourceUpdatedNotification) => {
sdk = spec;
spec = sdk;
},
SamplingMessage: (sdk: SDKTypes.SamplingMessage, spec: SpecTypes.SamplingMessage) => {
sdk = spec;
spec = sdk;
},
CreateMessageResult: (sdk: SDKTypes.CreateMessageResultWithTools, spec: SpecTypes.CreateMessageResult) => {
sdk = spec;
spec = sdk;
},
SetLevelRequest: (sdk: WithJSONRPCRequest<SDKTypes.SetLevelRequest>, spec: SpecTypes.SetLevelRequest) => {
sdk = spec;
spec = sdk;
},
PingRequest: (sdk: WithJSONRPCRequest<SDKTypes.PingRequest>, spec: SpecTypes.PingRequest) => {
sdk = spec;
spec = sdk;
},
InitializedNotification: (sdk: WithJSONRPC<SDKTypes.InitializedNotification>, spec: SpecTypes.InitializedNotification) => {
sdk = spec;
spec = sdk;
},
ListResourcesRequest: (sdk: WithJSONRPCRequest<SDKTypes.ListResourcesRequest>, spec: SpecTypes.ListResourcesRequest) => {
sdk = spec;
spec = sdk;
},
ListResourcesResult: (sdk: SDKTypes.ListResourcesResult, spec: SpecTypes.ListResourcesResult) => {
sdk = spec;
spec = sdk;
},
ListResourceTemplatesRequest: (
sdk: WithJSONRPCRequest<SDKTypes.ListResourceTemplatesRequest>,
spec: SpecTypes.ListResourceTemplatesRequest
) => {
sdk = spec;
spec = sdk;
},
ListResourceTemplatesResult: (sdk: SDKTypes.ListResourceTemplatesResult, spec: SpecTypes.ListResourceTemplatesResult) => {
sdk = spec;
spec = sdk;
},
ReadResourceRequest: (sdk: WithJSONRPCRequest<SDKTypes.ReadResourceRequest>, spec: SpecTypes.ReadResourceRequest) => {
sdk = spec;
spec = sdk;
},
ReadResourceResult: (sdk: SDKTypes.ReadResourceResult, spec: SpecTypes.ReadResourceResult) => {
sdk = spec;
spec = sdk;
},
ResourceContents: (sdk: SDKTypes.ResourceContents, spec: SpecTypes.ResourceContents) => {
sdk = spec;
spec = sdk;
},
TextResourceContents: (sdk: SDKTypes.TextResourceContents, spec: SpecTypes.TextResourceContents) => {
sdk = spec;
spec = sdk;
},
BlobResourceContents: (sdk: SDKTypes.BlobResourceContents, spec: SpecTypes.BlobResourceContents) => {
sdk = spec;
spec = sdk;
},
Resource: (sdk: SDKTypes.Resource, spec: SpecTypes.Resource) => {
sdk = spec;
spec = sdk;
},
ResourceTemplate: (sdk: SDKTypes.ResourceTemplateType, spec: SpecTypes.ResourceTemplate) => {
sdk = spec;
spec = sdk;
},
PromptArgument: (sdk: SDKTypes.PromptArgument, spec: SpecTypes.PromptArgument) => {
sdk = spec;
spec = sdk;
},
Prompt: (sdk: SDKTypes.Prompt, spec: SpecTypes.Prompt) => {
sdk = spec;
spec = sdk;
},
ListPromptsRequest: (sdk: WithJSONRPCRequest<SDKTypes.ListPromptsRequest>, spec: SpecTypes.ListPromptsRequest) => {
sdk = spec;
spec = sdk;
},
ListPromptsResult: (sdk: SDKTypes.ListPromptsResult, spec: SpecTypes.ListPromptsResult) => {
sdk = spec;
spec = sdk;
},
GetPromptRequest: (sdk: WithJSONRPCRequest<SDKTypes.GetPromptRequest>, spec: SpecTypes.GetPromptRequest) => {
sdk = spec;
spec = sdk;
},
TextContent: (sdk: SDKTypes.TextContent, spec: SpecTypes.TextContent) => {
sdk = spec;
spec = sdk;
},
ImageContent: (sdk: SDKTypes.ImageContent, spec: SpecTypes.ImageContent) => {
sdk = spec;
spec = sdk;
},
AudioContent: (sdk: SDKTypes.AudioContent, spec: SpecTypes.AudioContent) => {
sdk = spec;
spec = sdk;
},
EmbeddedResource: (sdk: SDKTypes.EmbeddedResource, spec: SpecTypes.EmbeddedResource) => {
sdk = spec;
spec = sdk;
},
ResourceLink: (sdk: SDKTypes.ResourceLink, spec: SpecTypes.ResourceLink) => {
sdk = spec;
spec = sdk;
},
ContentBlock: (sdk: SDKTypes.ContentBlock, spec: SpecTypes.ContentBlock) => {
sdk = spec;
spec = sdk;
},
PromptMessage: (sdk: SDKTypes.PromptMessage, spec: SpecTypes.PromptMessage) => {
sdk = spec;
spec = sdk;
},
GetPromptResult: (sdk: SDKTypes.GetPromptResult, spec: SpecTypes.GetPromptResult) => {
sdk = spec;
spec = sdk;
},
BooleanSchema: (sdk: SDKTypes.BooleanSchema, spec: SpecTypes.BooleanSchema) => {
sdk = spec;
spec = sdk;
},
StringSchema: (sdk: SDKTypes.StringSchema, spec: SpecTypes.StringSchema) => {
sdk = spec;
spec = sdk;
},
NumberSchema: (sdk: SDKTypes.NumberSchema, spec: SpecTypes.NumberSchema) => {
sdk = spec;
spec = sdk;
},
EnumSchema: (sdk: SDKTypes.EnumSchema, spec: SpecTypes.EnumSchema) => {
sdk = spec;
spec = sdk;
},
UntitledSingleSelectEnumSchema: (sdk: SDKTypes.UntitledSingleSelectEnumSchema, spec: SpecTypes.UntitledSingleSelectEnumSchema) => {
sdk = spec;
spec = sdk;
},
TitledSingleSelectEnumSchema: (sdk: SDKTypes.TitledSingleSelectEnumSchema, spec: SpecTypes.TitledSingleSelectEnumSchema) => {
sdk = spec;
spec = sdk;
},
SingleSelectEnumSchema: (sdk: SDKTypes.SingleSelectEnumSchema, spec: SpecTypes.SingleSelectEnumSchema) => {
sdk = spec;
spec = sdk;
},
UntitledMultiSelectEnumSchema: (sdk: SDKTypes.UntitledMultiSelectEnumSchema, spec: SpecTypes.UntitledMultiSelectEnumSchema) => {
sdk = spec;
spec = sdk;
},
TitledMultiSelectEnumSchema: (sdk: SDKTypes.TitledMultiSelectEnumSchema, spec: SpecTypes.TitledMultiSelectEnumSchema) => {
sdk = spec;
spec = sdk;
},
MultiSelectEnumSchema: (sdk: SDKTypes.MultiSelectEnumSchema, spec: SpecTypes.MultiSelectEnumSchema) => {
sdk = spec;
spec = sdk;
},
LegacyTitledEnumSchema: (sdk: SDKTypes.LegacyTitledEnumSchema, spec: SpecTypes.LegacyTitledEnumSchema) => {
sdk = spec;
spec = sdk;
},
PrimitiveSchemaDefinition: (sdk: SDKTypes.PrimitiveSchemaDefinition, spec: SpecTypes.PrimitiveSchemaDefinition) => {
sdk = spec;
spec = sdk;
},
JSONRPCErrorResponse: (sdk: SDKTypes.JSONRPCErrorResponse, spec: SpecTypes.JSONRPCErrorResponse) => {
sdk = spec;
spec = sdk;
},
JSONRPCResultResponse: (sdk: SDKTypes.JSONRPCResultResponse, spec: SpecTypes.JSONRPCResultResponse) => {
sdk = spec;
spec = sdk;
},
JSONRPCMessage: (sdk: SDKTypes.JSONRPCMessage, spec: SpecTypes.JSONRPCMessage) => {
sdk = spec;
spec = sdk;
},
CreateMessageRequest: (sdk: WithJSONRPCRequest<SDKTypes.CreateMessageRequest>, spec: SpecTypes.CreateMessageRequest) => {
// @ts-expect-error 2025-11-25 vs 2026-07-28 typing of params metadata/tools; see the CreateMessageRequestParams check above
sdk = spec;
// @ts-expect-error 2025-11-25 vs 2026-07-28 typing of params metadata/tools; see the CreateMessageRequestParams check above
spec = sdk;
},
InitializeRequest: (sdk: WithJSONRPCRequest<SDKTypes.InitializeRequest>, spec: SpecTypes.InitializeRequest) => {
// @ts-expect-error 2025-11-25 types capabilities.experimental values as `object`; the SDK follows the 2026-07-28 schema's JSONObject
sdk = spec;
spec = sdk;
},
InitializeResult: (sdk: SDKTypes.InitializeResult, spec: SpecTypes.InitializeResult) => {
// @ts-expect-error 2025-11-25 types capabilities.experimental values as `object`; the SDK follows the 2026-07-28 schema's JSONObject
sdk = spec;
spec = sdk;
},
ClientCapabilities: (sdk: SDKTypes.ClientCapabilities, spec: SpecTypes.ClientCapabilities) => {
// @ts-expect-error 2025-11-25 types experimental/sampling/elicitation/tasks blobs as `object`; the SDK follows the 2026-07-28 schema's JSONObject
sdk = spec;
spec = sdk;
},
ServerCapabilities: (sdk: SDKTypes.ServerCapabilities, spec: SpecTypes.ServerCapabilities) => {
// @ts-expect-error 2025-11-25 types experimental/logging/completions/tasks blobs as `object`; the SDK follows the 2026-07-28 schema's JSONObject
sdk = spec;
spec = sdk;
},
ClientRequest: (sdk: WithJSONRPCRequest<SDKTypes.ClientRequest>, spec: SpecTypes.ClientRequest) => {
// @ts-expect-error 2025-11-25 types capabilities.experimental values as `object` (via the InitializeRequest member); the SDK follows the 2026-07-28 schema's JSONObject
sdk = spec;
spec = sdk;
},
ServerRequest: (sdk: WithJSONRPCRequest<SDKTypes.ServerRequest>, spec: SpecTypes.ServerRequest) => {
// @ts-expect-error 2025-11-25 vs 2026-07-28 typing of CreateMessageRequest params; see the CreateMessageRequestParams check above
sdk = spec;
// @ts-expect-error 2025-11-25 vs 2026-07-28 typing of CreateMessageRequest params; see the CreateMessageRequestParams check above
spec = sdk;
},
LoggingMessageNotification: (sdk: WithJSONRPC<SDKTypes.LoggingMessageNotification>, spec: SpecTypes.LoggingMessageNotification) => {
sdk = spec;
spec = sdk;
},
ServerNotification: (sdk: WithJSONRPC<SDKTypes.ServerNotification>, spec: SpecTypes.ServerNotification) => {
sdk = spec;
spec = sdk;
},
LoggingLevel: (sdk: SDKTypes.LoggingLevel, spec: SpecTypes.LoggingLevel) => {
sdk = spec;
spec = sdk;
},
Icon: (sdk: SDKTypes.Icon, spec: SpecTypes.Icon) => {
sdk = spec;
spec = sdk;
},
Icons: (sdk: SDKTypes.Icons, spec: SpecTypes.Icons) => {
sdk = spec;
spec = sdk;
},
ModelHint: (sdk: SDKTypes.ModelHint, spec: SpecTypes.ModelHint) => {
sdk = spec;
spec = sdk;
},
ModelPreferences: (sdk: SDKTypes.ModelPreferences, spec: SpecTypes.ModelPreferences) => {
sdk = spec;
spec = sdk;
},
ToolChoice: (sdk: SDKTypes.ToolChoice, spec: SpecTypes.ToolChoice) => {
sdk = spec;
spec = sdk;
},
ToolUseContent: (sdk: SDKTypes.ToolUseContent, spec: SpecTypes.ToolUseContent) => {
sdk = spec;
spec = sdk;
},
ToolResultContent: (sdk: SDKTypes.ToolResultContent, spec: SpecTypes.ToolResultContent) => {
sdk = spec;
spec = sdk;
},
SamplingMessageContentBlock: (sdk: SDKTypes.SamplingMessageContentBlock, spec: SpecTypes.SamplingMessageContentBlock) => {
sdk = spec;
spec = sdk;
},
Annotations: (sdk: SDKTypes.Annotations, spec: SpecTypes.Annotations) => {
sdk = spec;
spec = sdk;
},
Role: (sdk: SDKTypes.Role, spec: SpecTypes.Role) => {
sdk = spec;
spec = sdk;
},
ToolExecution: (sdk: SDKTypes.ToolExecution, spec: SpecTypes.ToolExecution) => {
sdk = spec;
spec = sdk;
},
TaskStatus: (sdk: SDKTypes.TaskStatus, spec: SpecTypes.TaskStatus) => {
sdk = spec;
spec = sdk;
},
TaskMetadata: (sdk: SDKTypes.TaskMetadata, spec: SpecTypes.TaskMetadata) => {
sdk = spec;
spec = sdk;
},
RelatedTaskMetadata: (sdk: SDKTypes.RelatedTaskMetadata, spec: SpecTypes.RelatedTaskMetadata) => {
sdk = spec;
spec = sdk;
},
TaskAugmentedRequestParams: (sdk: SDKTypes.TaskAugmentedRequestParams, spec: SpecTypes.TaskAugmentedRequestParams) => {
sdk = spec;
spec = sdk;
},
Task: (sdk: SDKTypes.Task, spec: SpecTypes.Task) => {
sdk = spec;
spec = sdk;
},
CreateTaskResult: (sdk: SDKTypes.CreateTaskResult, spec: SpecTypes.CreateTaskResult) => {
sdk = spec;
spec = sdk;
},
GetTaskRequest: (sdk: WithJSONRPCRequest<SDKTypes.GetTaskRequest>, spec: SpecTypes.GetTaskRequest) => {
sdk = spec;
spec = sdk;
},
GetTaskResult: (sdk: SDKTypes.GetTaskResult, spec: SpecTypes.GetTaskResult) => {
sdk = spec;
spec = sdk;
},
GetTaskPayloadRequest: (sdk: WithJSONRPCRequest<SDKTypes.GetTaskPayloadRequest>, spec: SpecTypes.GetTaskPayloadRequest) => {
sdk = spec;
spec = sdk;
},
GetTaskPayloadResult: (sdk: SDKTypes.GetTaskPayloadResult, spec: SpecTypes.GetTaskPayloadResult) => {
sdk = spec;
spec = sdk;
},
ListTasksRequest: (sdk: WithJSONRPCRequest<SDKTypes.ListTasksRequest>, spec: SpecTypes.ListTasksRequest) => {
sdk = spec;
spec = sdk;
},
ListTasksResult: (sdk: SDKTypes.ListTasksResult, spec: SpecTypes.ListTasksResult) => {
sdk = spec;
spec = sdk;
},
CancelTaskRequest: (sdk: WithJSONRPCRequest<SDKTypes.CancelTaskRequest>, spec: SpecTypes.CancelTaskRequest) => {
sdk = spec;
spec = sdk;
},
CancelTaskResult: (sdk: SDKTypes.CancelTaskResult, spec: SpecTypes.CancelTaskResult) => {
sdk = spec;
spec = sdk;
},
TaskStatusNotificationParams: (sdk: SDKTypes.TaskStatusNotificationParams, spec: SpecTypes.TaskStatusNotificationParams) => {
sdk = spec;
spec = sdk;
},
TaskStatusNotification: (sdk: WithJSONRPC<SDKTypes.TaskStatusNotification>, spec: SpecTypes.TaskStatusNotification) => {
sdk = spec;
spec = sdk;
}
};
// ---------------------------------------------------------------------------
// Key-level assertions: verify that each SDK type and its corresponding spec
// type expose exactly the same set of named property keys. This catches cases
// where a Zod schema marks a field as `.optional()` but the spec does not (or
// vice-versa), which the mutual-assignability checks above cannot detect
// because optional fields satisfy structural subtyping in both directions.
// ---------------------------------------------------------------------------
/** Strip index signatures, keeping only explicitly-named keys. */
type KnownKeys<T> = keyof {
[K in keyof T as string extends K ? never : number extends K ? never : symbol extends K ? never : K]: T[K];
};
/**
* Assert that A and B have exactly the same set of known (named) keys.
* Resolves to `true` on match; a descriptive error type on mismatch.
*/
type AssertExactKeys<
A,
B,
Extra extends PropertyKey = Exclude<KnownKeys<A>, KnownKeys<B>>,
Missing extends PropertyKey = Exclude<KnownKeys<B>, KnownKeys<A>>
> = [Extra, Missing] extends [never, never] ? true : { _brand: 'KeyMismatch'; extra: Extra; missing: Missing };
/** Constraint: T must resolve to `true`. */
type Assert<T extends true> = T;
/**
* Same as {@link AssertExactKeys}, but tolerates the SDK's `resultType` key on
* result shapes: the SDK follows the 2026-07-28 schema's optional `resultType`
* passthrough (absent means "complete"), which is not in released 2025-11-25.
* Every other key still has to match exactly.
*/
type AssertExactKeysWithResultType<A, B> = AssertExactKeys<A, B & { resultType?: unknown }>;
/*
* Excluded from key-level assertions (21 entries):
*
* Union types — KnownKeys cannot meaningfully enumerate their members (15):
* ClientRequest, ServerRequest, ClientNotification, ServerNotification,
* ClientResult, ServerResult, JSONRPCMessage, JSONRPCResponse, ContentBlock,
* SamplingMessageContentBlock, ElicitRequestParams, PrimitiveSchemaDefinition,
* SingleSelectEnumSchema, MultiSelectEnumSchema, EnumSchema
*
* Primitive type aliases — no object keys to compare (6):
* Role, LoggingLevel, ProgressToken, RequestId, Cursor, TaskStatus
*/
// -- Simple types (88) --
type _K_RequestParams = Assert<AssertExactKeys<SDKTypes.RequestParams, SpecTypes.RequestParams>>;
type _K_NotificationParams = Assert<AssertExactKeys<SDKTypes.NotificationParams, SpecTypes.NotificationParams>>;
type _K_CancelledNotificationParams = Assert<AssertExactKeys<SDKTypes.CancelledNotificationParams, SpecTypes.CancelledNotificationParams>>;
type _K_InitializeRequestParams = Assert<AssertExactKeys<SDKTypes.InitializeRequestParams, SpecTypes.InitializeRequestParams>>;
type _K_ProgressNotificationParams = Assert<AssertExactKeys<SDKTypes.ProgressNotificationParams, SpecTypes.ProgressNotificationParams>>;
type _K_ResourceRequestParams = Assert<AssertExactKeys<SDKTypes.ResourceRequestParams, SpecTypes.ResourceRequestParams>>;
type _K_ReadResourceRequestParams = Assert<AssertExactKeys<SDKTypes.ReadResourceRequestParams, SpecTypes.ReadResourceRequestParams>>;
type _K_SubscribeRequestParams = Assert<AssertExactKeys<SDKTypes.SubscribeRequestParams, SpecTypes.SubscribeRequestParams>>;
type _K_UnsubscribeRequestParams = Assert<AssertExactKeys<SDKTypes.UnsubscribeRequestParams, SpecTypes.UnsubscribeRequestParams>>;
type _K_ResourceUpdatedNotificationParams = Assert<
AssertExactKeys<SDKTypes.ResourceUpdatedNotificationParams, SpecTypes.ResourceUpdatedNotificationParams>
>;
type _K_GetPromptRequestParams = Assert<AssertExactKeys<SDKTypes.GetPromptRequestParams, SpecTypes.GetPromptRequestParams>>;
type _K_CallToolRequestParams = Assert<AssertExactKeys<SDKTypes.CallToolRequestParams, SpecTypes.CallToolRequestParams>>;
type _K_SetLevelRequestParams = Assert<AssertExactKeys<SDKTypes.SetLevelRequestParams, SpecTypes.SetLevelRequestParams>>;
type _K_LoggingMessageNotificationParams = Assert<
AssertExactKeys<SDKTypes.LoggingMessageNotificationParams, SpecTypes.LoggingMessageNotificationParams>
>;
type _K_CreateMessageRequestParams = Assert<AssertExactKeys<SDKTypes.CreateMessageRequestParams, SpecTypes.CreateMessageRequestParams>>;
type _K_CompleteRequestParams = Assert<AssertExactKeys<SDKTypes.CompleteRequestParams, SpecTypes.CompleteRequestParams>>;
type _K_ElicitRequestFormParams = Assert<AssertExactKeys<SDKTypes.ElicitRequestFormParams, SpecTypes.ElicitRequestFormParams>>;
type _K_ElicitRequestURLParams = Assert<AssertExactKeys<SDKTypes.ElicitRequestURLParams, SpecTypes.ElicitRequestURLParams>>;
type _K_PaginatedRequestParams = Assert<AssertExactKeys<SDKTypes.PaginatedRequestParams, SpecTypes.PaginatedRequestParams>>;
type _K_BaseMetadata = Assert<AssertExactKeys<SDKTypes.BaseMetadata, SpecTypes.BaseMetadata>>;
type _K_Implementation = Assert<AssertExactKeys<SDKTypes.Implementation, SpecTypes.Implementation>>;
type _K_PaginatedResult = Assert<AssertExactKeysWithResultType<SDKTypes.PaginatedResult, SpecTypes.PaginatedResult>>;
type _K_ListRootsResult = Assert<AssertExactKeysWithResultType<SDKTypes.ListRootsResult, SpecTypes.ListRootsResult>>;
type _K_Root = Assert<AssertExactKeys<SDKTypes.Root, SpecTypes.Root>>;
type _K_ElicitResult = Assert<AssertExactKeysWithResultType<SDKTypes.ElicitResult, SpecTypes.ElicitResult>>;
type _K_CompleteResult = Assert<AssertExactKeysWithResultType<SDKTypes.CompleteResult, SpecTypes.CompleteResult>>;
type _K_Request = Assert<AssertExactKeys<SDKTypes.Request, SpecTypes.Request>>;
type _K_Result = Assert<AssertExactKeysWithResultType<SDKTypes.Result, SpecTypes.Result>>;
type _K_JSONRPCRequest = Assert<AssertExactKeys<SDKTypes.JSONRPCRequest, SpecTypes.JSONRPCRequest>>;
type _K_JSONRPCNotification = Assert<AssertExactKeys<SDKTypes.JSONRPCNotification, SpecTypes.JSONRPCNotification>>;
type _K_EmptyResult = Assert<AssertExactKeysWithResultType<SDKTypes.EmptyResult, SpecTypes.EmptyResult>>;
type _K_Notification = Assert<AssertExactKeys<SDKTypes.Notification, SpecTypes.Notification>>;
type _K_ResourceTemplateReference = Assert<AssertExactKeys<SDKTypes.ResourceTemplateReference, SpecTypes.ResourceTemplateReference>>;
// @ts-expect-error Genuine mismatch: SDK PromptReference is missing 'title' from spec
type _K_PromptReference = Assert<AssertExactKeys<SDKTypes.PromptReference, SpecTypes.PromptReference>>;
type _K_ToolAnnotations = Assert<AssertExactKeys<SDKTypes.ToolAnnotations, SpecTypes.ToolAnnotations>>;
type _K_Tool = Assert<AssertExactKeys<SDKTypes.Tool, SpecTypes.Tool>>;
type _K_ListToolsResult = Assert<AssertExactKeysWithResultType<SDKTypes.ListToolsResult, SpecTypes.ListToolsResult>>;
type _K_CallToolResult = Assert<AssertExactKeysWithResultType<SDKTypes.CallToolResult, SpecTypes.CallToolResult>>;
type _K_ListResourcesResult = Assert<AssertExactKeysWithResultType<SDKTypes.ListResourcesResult, SpecTypes.ListResourcesResult>>;
type _K_ListResourceTemplatesResult = Assert<
AssertExactKeysWithResultType<SDKTypes.ListResourceTemplatesResult, SpecTypes.ListResourceTemplatesResult>
>;
type _K_ReadResourceResult = Assert<AssertExactKeysWithResultType<SDKTypes.ReadResourceResult, SpecTypes.ReadResourceResult>>;
type _K_ResourceContents = Assert<AssertExactKeys<SDKTypes.ResourceContents, SpecTypes.ResourceContents>>;
type _K_TextResourceContents = Assert<AssertExactKeys<SDKTypes.TextResourceContents, SpecTypes.TextResourceContents>>;
type _K_BlobResourceContents = Assert<AssertExactKeys<SDKTypes.BlobResourceContents, SpecTypes.BlobResourceContents>>;
type _K_Resource = Assert<AssertExactKeys<SDKTypes.Resource, SpecTypes.Resource>>;
// @ts-expect-error Genuine mismatch: SDK PromptArgument is missing 'title' from spec
type _K_PromptArgument = Assert<AssertExactKeys<SDKTypes.PromptArgument, SpecTypes.PromptArgument>>;
type _K_Prompt = Assert<AssertExactKeys<SDKTypes.Prompt, SpecTypes.Prompt>>;
type _K_ListPromptsResult = Assert<AssertExactKeysWithResultType<SDKTypes.ListPromptsResult, SpecTypes.ListPromptsResult>>;
type _K_GetPromptResult = Assert<AssertExactKeysWithResultType<SDKTypes.GetPromptResult, SpecTypes.GetPromptResult>>;
type _K_TextContent = Assert<AssertExactKeys<SDKTypes.TextContent, SpecTypes.TextContent>>;
type _K_ImageContent = Assert<AssertExactKeys<SDKTypes.ImageContent, SpecTypes.ImageContent>>;
type _K_AudioContent = Assert<AssertExactKeys<SDKTypes.AudioContent, SpecTypes.AudioContent>>;
type _K_EmbeddedResource = Assert<AssertExactKeys<SDKTypes.EmbeddedResource, SpecTypes.EmbeddedResource>>;
type _K_ResourceLink = Assert<AssertExactKeys<SDKTypes.ResourceLink, SpecTypes.ResourceLink>>;
type _K_PromptMessage = Assert<AssertExactKeys<SDKTypes.PromptMessage, SpecTypes.PromptMessage>>;
type _K_BooleanSchema = Assert<AssertExactKeys<SDKTypes.BooleanSchema, SpecTypes.BooleanSchema>>;
type _K_StringSchema = Assert<AssertExactKeys<SDKTypes.StringSchema, SpecTypes.StringSchema>>;
type _K_NumberSchema = Assert<AssertExactKeys<SDKTypes.NumberSchema, SpecTypes.NumberSchema>>;
type _K_UntitledSingleSelectEnumSchema = Assert<
AssertExactKeys<SDKTypes.UntitledSingleSelectEnumSchema, SpecTypes.UntitledSingleSelectEnumSchema>
>;
type _K_TitledSingleSelectEnumSchema = Assert<
AssertExactKeys<SDKTypes.TitledSingleSelectEnumSchema, SpecTypes.TitledSingleSelectEnumSchema>
>;
type _K_UntitledMultiSelectEnumSchema = Assert<
AssertExactKeys<SDKTypes.UntitledMultiSelectEnumSchema, SpecTypes.UntitledMultiSelectEnumSchema>
>;
type _K_TitledMultiSelectEnumSchema = Assert<AssertExactKeys<SDKTypes.TitledMultiSelectEnumSchema, SpecTypes.TitledMultiSelectEnumSchema>>;
type _K_LegacyTitledEnumSchema = Assert<AssertExactKeys<SDKTypes.LegacyTitledEnumSchema, SpecTypes.LegacyTitledEnumSchema>>;
type _K_JSONRPCErrorResponse = Assert<AssertExactKeys<SDKTypes.JSONRPCErrorResponse, SpecTypes.JSONRPCErrorResponse>>;
type _K_JSONRPCResultResponse = Assert<AssertExactKeys<SDKTypes.JSONRPCResultResponse, SpecTypes.JSONRPCResultResponse>>;
type _K_InitializeResult = Assert<AssertExactKeysWithResultType<SDKTypes.InitializeResult, SpecTypes.InitializeResult>>;
// @ts-expect-error SDK follows the 2026-07-28 schema's `extensions` capability key; not in released 2025-11-25
type _K_ClientCapabilities = Assert<AssertExactKeys<SDKTypes.ClientCapabilities, SpecTypes.ClientCapabilities>>;
// @ts-expect-error SDK follows the 2026-07-28 schema's `extensions` capability key; not in released 2025-11-25
type _K_ServerCapabilities = Assert<AssertExactKeys<SDKTypes.ServerCapabilities, SpecTypes.ServerCapabilities>>;
type _K_SamplingMessage = Assert<AssertExactKeys<SDKTypes.SamplingMessage, SpecTypes.SamplingMessage>>;
type _K_Icon = Assert<AssertExactKeys<SDKTypes.Icon, SpecTypes.Icon>>;
type _K_Icons = Assert<AssertExactKeys<SDKTypes.Icons, SpecTypes.Icons>>;
type _K_ModelHint = Assert<AssertExactKeys<SDKTypes.ModelHint, SpecTypes.ModelHint>>;
type _K_ModelPreferences = Assert<AssertExactKeys<SDKTypes.ModelPreferences, SpecTypes.ModelPreferences>>;
type _K_ToolChoice = Assert<AssertExactKeys<SDKTypes.ToolChoice, SpecTypes.ToolChoice>>;
type _K_ToolUseContent = Assert<AssertExactKeys<SDKTypes.ToolUseContent, SpecTypes.ToolUseContent>>;
type _K_ToolResultContent = Assert<AssertExactKeys<SDKTypes.ToolResultContent, SpecTypes.ToolResultContent>>;
type _K_Annotations = Assert<AssertExactKeys<SDKTypes.Annotations, SpecTypes.Annotations>>;
type _K_ToolExecution = Assert<AssertExactKeys<SDKTypes.ToolExecution, SpecTypes.ToolExecution>>;
type _K_TaskMetadata = Assert<AssertExactKeys<SDKTypes.TaskMetadata, SpecTypes.TaskMetadata>>;
type _K_RelatedTaskMetadata = Assert<AssertExactKeys<SDKTypes.RelatedTaskMetadata, SpecTypes.RelatedTaskMetadata>>;
type _K_TaskAugmentedRequestParams = Assert<AssertExactKeys<SDKTypes.TaskAugmentedRequestParams, SpecTypes.TaskAugmentedRequestParams>>;
type _K_Task = Assert<AssertExactKeys<SDKTypes.Task, SpecTypes.Task>>;
type _K_CreateTaskResult = Assert<AssertExactKeysWithResultType<SDKTypes.CreateTaskResult, SpecTypes.CreateTaskResult>>;
type _K_GetTaskResult = Assert<AssertExactKeysWithResultType<SDKTypes.GetTaskResult, SpecTypes.GetTaskResult>>;
type _K_GetTaskPayloadResult = Assert<AssertExactKeysWithResultType<SDKTypes.GetTaskPayloadResult, SpecTypes.GetTaskPayloadResult>>;
type _K_ListTasksResult = Assert<AssertExactKeysWithResultType<SDKTypes.ListTasksResult, SpecTypes.ListTasksResult>>;
type _K_CancelTaskResult = Assert<AssertExactKeysWithResultType<SDKTypes.CancelTaskResult, SpecTypes.CancelTaskResult>>;
type _K_TaskStatusNotificationParams = Assert<
AssertExactKeys<SDKTypes.TaskStatusNotificationParams, SpecTypes.TaskStatusNotificationParams>
>;
// -- WithJSONRPC-wrapped notification types (11) --
// SDK notification types do not include `jsonrpc` — the spec types do. We wrap
// with WithJSONRPC<> to add the missing field before comparing keys.
type _K_ElicitationCompleteNotification = Assert<
AssertExactKeys<WithJSONRPC<SDKTypes.ElicitationCompleteNotification>, SpecTypes.ElicitationCompleteNotification>
>;
type _K_CancelledNotification = Assert<AssertExactKeys<WithJSONRPC<SDKTypes.CancelledNotification>, SpecTypes.CancelledNotification>>;
type _K_ProgressNotification = Assert<AssertExactKeys<WithJSONRPC<SDKTypes.ProgressNotification>, SpecTypes.ProgressNotification>>;
type _K_ToolListChangedNotification = Assert<
AssertExactKeys<WithJSONRPC<SDKTypes.ToolListChangedNotification>, SpecTypes.ToolListChangedNotification>
>;
type _K_ResourceListChangedNotification = Assert<
AssertExactKeys<WithJSONRPC<SDKTypes.ResourceListChangedNotification>, SpecTypes.ResourceListChangedNotification>
>;
type _K_PromptListChangedNotification = Assert<
AssertExactKeys<WithJSONRPC<SDKTypes.PromptListChangedNotification>, SpecTypes.PromptListChangedNotification>
>;
type _K_RootsListChangedNotification = Assert<
AssertExactKeys<WithJSONRPC<SDKTypes.RootsListChangedNotification>, SpecTypes.RootsListChangedNotification>
>;
type _K_ResourceUpdatedNotification = Assert<
AssertExactKeys<WithJSONRPC<SDKTypes.ResourceUpdatedNotification>, SpecTypes.ResourceUpdatedNotification>
>;
type _K_LoggingMessageNotification = Assert<
AssertExactKeys<WithJSONRPC<SDKTypes.LoggingMessageNotification>, SpecTypes.LoggingMessageNotification>
>;
type _K_InitializedNotification = Assert<AssertExactKeys<WithJSONRPC<SDKTypes.InitializedNotification>, SpecTypes.InitializedNotification>>;
type _K_TaskStatusNotification = Assert<AssertExactKeys<WithJSONRPC<SDKTypes.TaskStatusNotification>, SpecTypes.TaskStatusNotification>>;
// -- WithJSONRPCRequest-wrapped request types (21) --
// SDK request types do not include `jsonrpc` or `id` — the spec types do. We
// wrap with WithJSONRPCRequest<> to add the missing fields before comparing keys.
type _K_SubscribeRequest = Assert<AssertExactKeys<WithJSONRPCRequest<SDKTypes.SubscribeRequest>, SpecTypes.SubscribeRequest>>;
type _K_UnsubscribeRequest = Assert<AssertExactKeys<WithJSONRPCRequest<SDKTypes.UnsubscribeRequest>, SpecTypes.UnsubscribeRequest>>;
type _K_PaginatedRequest = Assert<AssertExactKeys<WithJSONRPCRequest<SDKTypes.PaginatedRequest>, SpecTypes.PaginatedRequest>>;
type _K_ListRootsRequest = Assert<AssertExactKeys<WithJSONRPCRequest<SDKTypes.ListRootsRequest>, SpecTypes.ListRootsRequest>>;
type _K_ElicitRequest = Assert<AssertExactKeys<WithJSONRPCRequest<SDKTypes.ElicitRequest>, SpecTypes.ElicitRequest>>;
type _K_CompleteRequest = Assert<AssertExactKeys<WithJSONRPCRequest<SDKTypes.CompleteRequest>, SpecTypes.CompleteRequest>>;
type _K_ListToolsRequest = Assert<AssertExactKeys<WithJSONRPCRequest<SDKTypes.ListToolsRequest>, SpecTypes.ListToolsRequest>>;
type _K_CallToolRequest = Assert<AssertExactKeys<WithJSONRPCRequest<SDKTypes.CallToolRequest>, SpecTypes.CallToolRequest>>;
type _K_SetLevelRequest = Assert<AssertExactKeys<WithJSONRPCRequest<SDKTypes.SetLevelRequest>, SpecTypes.SetLevelRequest>>;
type _K_PingRequest = Assert<AssertExactKeys<WithJSONRPCRequest<SDKTypes.PingRequest>, SpecTypes.PingRequest>>;
type _K_ListResourcesRequest = Assert<AssertExactKeys<WithJSONRPCRequest<SDKTypes.ListResourcesRequest>, SpecTypes.ListResourcesRequest>>;
type _K_ListResourceTemplatesRequest = Assert<
AssertExactKeys<WithJSONRPCRequest<SDKTypes.ListResourceTemplatesRequest>, SpecTypes.ListResourceTemplatesRequest>
>;
type _K_ReadResourceRequest = Assert<AssertExactKeys<WithJSONRPCRequest<SDKTypes.ReadResourceRequest>, SpecTypes.ReadResourceRequest>>;
type _K_ListPromptsRequest = Assert<AssertExactKeys<WithJSONRPCRequest<SDKTypes.ListPromptsRequest>, SpecTypes.ListPromptsRequest>>;
type _K_GetPromptRequest = Assert<AssertExactKeys<WithJSONRPCRequest<SDKTypes.GetPromptRequest>, SpecTypes.GetPromptRequest>>;
type _K_CreateMessageRequest = Assert<AssertExactKeys<WithJSONRPCRequest<SDKTypes.CreateMessageRequest>, SpecTypes.CreateMessageRequest>>;
type _K_InitializeRequest = Assert<AssertExactKeys<WithJSONRPCRequest<SDKTypes.InitializeRequest>, SpecTypes.InitializeRequest>>;
type _K_GetTaskRequest = Assert<AssertExactKeys<WithJSONRPCRequest<SDKTypes.GetTaskRequest>, SpecTypes.GetTaskRequest>>;
type _K_GetTaskPayloadRequest = Assert<
AssertExactKeys<WithJSONRPCRequest<SDKTypes.GetTaskPayloadRequest>, SpecTypes.GetTaskPayloadRequest>
>;
type _K_ListTasksRequest = Assert<AssertExactKeys<WithJSONRPCRequest<SDKTypes.ListTasksRequest>, SpecTypes.ListTasksRequest>>;
type _K_CancelTaskRequest = Assert<AssertExactKeys<WithJSONRPCRequest<SDKTypes.CancelTaskRequest>, SpecTypes.CancelTaskRequest>>;
// -- Name mismatches (2) --
// SDK exports these under different names than the spec.
type _K_CreateMessageResult = Assert<AssertExactKeysWithResultType<SDKTypes.CreateMessageResultWithTools, SpecTypes.CreateMessageResult>>;
type _K_ResourceTemplate = Assert<AssertExactKeys<SDKTypes.ResourceTemplateType, SpecTypes.ResourceTemplate>>;
// Types excluded from the key-parity completeness guard: union types and primitive aliases
// that cannot have meaningful AssertExactKeys assertions.
const KEY_PARITY_EXCLUDED = [
// Union types (15)
'ClientRequest',
'ServerRequest',
'ClientNotification',
'ServerNotification',
'ClientResult',
'ServerResult',
'JSONRPCMessage',
'JSONRPCResponse',
'ContentBlock',
'SamplingMessageContentBlock',
'ElicitRequestParams',
'PrimitiveSchemaDefinition',
'SingleSelectEnumSchema',
'MultiSelectEnumSchema',
'EnumSchema',
// Primitive aliases (6)
'Role',
'LoggingLevel',
'ProgressToken',
'RequestId',
'Cursor',
'TaskStatus'
];
// Generated from the frozen 2025-11-25 release schema by `pnpm run fetch:spec-types 2025-11-25`.
const SPEC_TYPES_FILE = path.resolve(__dirname, '../src/types/spec.types.2025-11-25.ts');
const SDK_TYPES_FILE = path.resolve(__dirname, '../src/types/types.ts');
const MISSING_SDK_TYPES = [
// These are inlined in the SDK:
'Error', // The inner error object of a JSONRPCError
'URLElicitationRequiredError' // In the SDK, but with a custom definition
];
function extractExportedTypes(source: string): string[] {
const matches = [...source.matchAll(/export\s+(?:interface|class|type)\s+(\w+)\b/g)];
return matches.map(m => m[1]!);
}
function extractKeyParityTypes(source: string): string[] {
return [...source.matchAll(/^type _K_(\w+)\s*=/gm)].map(m => m[1]!);
}
describe('Spec Types (2025-11-25)', () => {
const specTypes = extractExportedTypes(fs.readFileSync(SPEC_TYPES_FILE, 'utf8'));
const sdkTypes = extractExportedTypes(fs.readFileSync(SDK_TYPES_FILE, 'utf8'));
const typesToCheck = specTypes.filter(type => !MISSING_SDK_TYPES.includes(type));
it('should define some expected types', () => {
expect(specTypes).toContain('JSONRPCNotification');
expect(specTypes).toContain('ElicitResult');
expect(specTypes).toHaveLength(145);
});
it('should have up to date list of missing sdk types', () => {
for (const typeName of MISSING_SDK_TYPES) {
expect(sdkTypes).not.toContain(typeName);
}
});
it('should have comprehensive compatibility tests', () => {
const missingTests = [];
for (const typeName of typesToCheck) {
if (!sdkTypeChecks[typeName as keyof typeof sdkTypeChecks]) {
missingTests.push(typeName);
}
}
expect(missingTests).toHaveLength(0);
});
it('should have key-parity assertions for all non-excluded compatibility tests', () => {
const thisSource = fs.readFileSync(__filename, 'utf8');
const checked = new Set(extractKeyParityTypes(thisSource));
const excluded = new Set<string>(KEY_PARITY_EXCLUDED);
const missing = Object.keys(sdkTypeChecks).filter(name => !checked.has(name) && !excluded.has(name));
expect(missing).toHaveLength(0);
});
describe('Missing SDK Types', () => {
it.each(MISSING_SDK_TYPES)('%s should not be present in MISSING_SDK_TYPES if it has a compatibility test', type => {
expect(sdkTypeChecks[type as keyof typeof sdkTypeChecks]).toBeUndefined();
});
});
});

View file

@ -0,0 +1,550 @@
/**
* Compares the SDK's types against the upcoming 2026-07-28 schema (spec.types.2026-07-28.ts).
* The frozen-release comparison lives in spec.types.2025-11-25.test.ts.
*
* The SDK does not implement the 2026-07-28 surface yet: every 2026-07-28 type whose shape the SDK
* does not (yet) match is listed in MISSING_SDK_TYPES_2026_07_28 below. Removing a name from
* that list forces a real mutual-assignability check to be added to sdkTypeChecks (the
* completeness tests below fail otherwise) — implementation work burns the list down.
*
* Unlike MISSING_SDK_TYPES in the 2025-11-25 comparison, names in this list may well
* exist in the SDK (e.g. RequestParams) — they are listed because the 2026-07-28 revision changed
* their shape, not necessarily because the SDK lacks them.
*/
import fs from 'node:fs';
import path from 'node:path';
import {
LATEST_PROTOCOL_VERSION,
MISSING_REQUIRED_CLIENT_CAPABILITY,
UNSUPPORTED_PROTOCOL_VERSION
} from '../src/types/spec.types.2026-07-28.js';
import type * as SpecTypes from '../src/types/spec.types.2026-07-28.js';
import type * as SDKTypes from '../src/types/index.js';
import {
CLIENT_CAPABILITIES_META_KEY,
CLIENT_INFO_META_KEY,
LOG_LEVEL_META_KEY,
PROTOCOL_VERSION_META_KEY,
ProtocolErrorCode
} from '../src/types/index.js';
/* eslint-disable @typescript-eslint/no-unused-vars */
// Adds the `jsonrpc` property to a type, to match the on-wire format of notifications.
type WithJSONRPC<T> = T & { jsonrpc: '2.0' };
// Adds the `jsonrpc` and `id` properties to a type, to match the on-wire format of requests.
type WithJSONRPCRequest<T> = T & { jsonrpc: '2.0'; id: SDKTypes.RequestId };
const sdkTypeChecks = {
JSONValue: (sdk: SDKTypes.JSONValue, spec: SpecTypes.JSONValue) => {
sdk = spec;
spec = sdk;
},
JSONObject: (sdk: SDKTypes.JSONObject, spec: SpecTypes.JSONObject) => {
sdk = spec;
spec = sdk;
},
JSONArray: (sdk: SDKTypes.JSONArray, spec: SpecTypes.JSONArray) => {
sdk = spec;
spec = sdk;
},
MetaObject: (sdk: SDKTypes.MetaObject, spec: SpecTypes.MetaObject) => {
sdk = spec;
spec = sdk;
},
// The SDK models the 2026-07-28 revision's required per-request `_meta` envelope as
// RequestMetaEnvelope (the base request schemas stay lenient; envelope
// requiredness is enforced at dispatch). This check also pins the
// *_META_KEY constants: a drifted key name breaks mutual assignability.
RequestMetaObject: (sdk: SDKTypes.RequestMetaEnvelope, spec: SpecTypes.RequestMetaObject) => {
sdk = spec;
spec = sdk;
},
ProgressToken: (sdk: SDKTypes.ProgressToken, spec: SpecTypes.ProgressToken) => {
sdk = spec;
spec = sdk;
},
Cursor: (sdk: SDKTypes.Cursor, spec: SpecTypes.Cursor) => {
sdk = spec;
spec = sdk;
},
Request: (sdk: SDKTypes.Request, spec: SpecTypes.Request) => {
sdk = spec;
spec = sdk;
},
NotificationParams: (sdk: SDKTypes.NotificationParams, spec: SpecTypes.NotificationParams) => {
sdk = spec;
spec = sdk;
},
RequestId: (sdk: SDKTypes.RequestId, spec: SpecTypes.RequestId) => {
sdk = spec;
spec = sdk;
},
JSONRPCRequest: (sdk: SDKTypes.JSONRPCRequest, spec: SpecTypes.JSONRPCRequest) => {
sdk = spec;
spec = sdk;
},
JSONRPCNotification: (sdk: WithJSONRPC<SDKTypes.JSONRPCNotification>, spec: SpecTypes.JSONRPCNotification) => {
sdk = spec;
spec = sdk;
},
JSONRPCErrorResponse: (sdk: SDKTypes.JSONRPCErrorResponse, spec: SpecTypes.JSONRPCErrorResponse) => {
sdk = spec;
spec = sdk;
},
ParseError: (sdk: SDKTypes.ParseError, spec: SpecTypes.ParseError) => {
sdk = spec;
spec = sdk;
},
InvalidRequestError: (sdk: SDKTypes.InvalidRequestError, spec: SpecTypes.InvalidRequestError) => {
sdk = spec;
spec = sdk;
},
MethodNotFoundError: (sdk: SDKTypes.MethodNotFoundError, spec: SpecTypes.MethodNotFoundError) => {
sdk = spec;
spec = sdk;
},
InvalidParamsError: (sdk: SDKTypes.InvalidParamsError, spec: SpecTypes.InvalidParamsError) => {
sdk = spec;
spec = sdk;
},
InternalError: (sdk: SDKTypes.InternalError, spec: SpecTypes.InternalError) => {
sdk = spec;
spec = sdk;
},
CancelledNotificationParams: (sdk: SDKTypes.CancelledNotificationParams, spec: SpecTypes.CancelledNotificationParams) => {
sdk = spec;
spec = sdk;
},
CancelledNotification: (sdk: WithJSONRPC<SDKTypes.CancelledNotification>, spec: SpecTypes.CancelledNotification) => {
sdk = spec;
spec = sdk;
},
ClientCapabilities: (sdk: SDKTypes.ClientCapabilities, spec: SpecTypes.ClientCapabilities) => {
sdk = spec;
spec = sdk;
},
ServerCapabilities: (sdk: SDKTypes.ServerCapabilities, spec: SpecTypes.ServerCapabilities) => {
sdk = spec;
spec = sdk;
},
Icon: (sdk: SDKTypes.Icon, spec: SpecTypes.Icon) => {
sdk = spec;
spec = sdk;
},
Icons: (sdk: SDKTypes.Icons, spec: SpecTypes.Icons) => {
sdk = spec;
spec = sdk;
},
BaseMetadata: (sdk: SDKTypes.BaseMetadata, spec: SpecTypes.BaseMetadata) => {
sdk = spec;
spec = sdk;
},
Implementation: (sdk: SDKTypes.Implementation, spec: SpecTypes.Implementation) => {
sdk = spec;
spec = sdk;
},
ProgressNotificationParams: (sdk: SDKTypes.ProgressNotificationParams, spec: SpecTypes.ProgressNotificationParams) => {
sdk = spec;
spec = sdk;
},
ProgressNotification: (sdk: WithJSONRPC<SDKTypes.ProgressNotification>, spec: SpecTypes.ProgressNotification) => {
sdk = spec;
spec = sdk;
},
ResourceListChangedNotification: (
sdk: WithJSONRPC<SDKTypes.ResourceListChangedNotification>,
spec: SpecTypes.ResourceListChangedNotification
) => {
sdk = spec;
spec = sdk;
},
ResourceUpdatedNotificationParams: (
sdk: SDKTypes.ResourceUpdatedNotificationParams,
spec: SpecTypes.ResourceUpdatedNotificationParams
) => {
sdk = spec;
spec = sdk;
},
ResourceUpdatedNotification: (sdk: WithJSONRPC<SDKTypes.ResourceUpdatedNotification>, spec: SpecTypes.ResourceUpdatedNotification) => {
sdk = spec;
spec = sdk;
},
Resource: (sdk: SDKTypes.Resource, spec: SpecTypes.Resource) => {
sdk = spec;
spec = sdk;
},
ResourceTemplate: (sdk: SDKTypes.ResourceTemplateType, spec: SpecTypes.ResourceTemplate) => {
sdk = spec;
spec = sdk;
},
ResourceContents: (sdk: SDKTypes.ResourceContents, spec: SpecTypes.ResourceContents) => {
sdk = spec;
spec = sdk;
},
TextResourceContents: (sdk: SDKTypes.TextResourceContents, spec: SpecTypes.TextResourceContents) => {
sdk = spec;
spec = sdk;
},
BlobResourceContents: (sdk: SDKTypes.BlobResourceContents, spec: SpecTypes.BlobResourceContents) => {
sdk = spec;
spec = sdk;
},
Prompt: (sdk: SDKTypes.Prompt, spec: SpecTypes.Prompt) => {
sdk = spec;
spec = sdk;
},
PromptArgument: (sdk: SDKTypes.PromptArgument, spec: SpecTypes.PromptArgument) => {
sdk = spec;
spec = sdk;
},
Role: (sdk: SDKTypes.Role, spec: SpecTypes.Role) => {
sdk = spec;
spec = sdk;
},
PromptMessage: (sdk: SDKTypes.PromptMessage, spec: SpecTypes.PromptMessage) => {
sdk = spec;
spec = sdk;
},
ResourceLink: (sdk: SDKTypes.ResourceLink, spec: SpecTypes.ResourceLink) => {
sdk = spec;
spec = sdk;
},
EmbeddedResource: (sdk: SDKTypes.EmbeddedResource, spec: SpecTypes.EmbeddedResource) => {
sdk = spec;
spec = sdk;
},
PromptListChangedNotification: (
sdk: WithJSONRPC<SDKTypes.PromptListChangedNotification>,
spec: SpecTypes.PromptListChangedNotification
) => {
sdk = spec;
spec = sdk;
},
ToolListChangedNotification: (sdk: WithJSONRPC<SDKTypes.ToolListChangedNotification>, spec: SpecTypes.ToolListChangedNotification) => {
sdk = spec;
spec = sdk;
},
ToolAnnotations: (sdk: SDKTypes.ToolAnnotations, spec: SpecTypes.ToolAnnotations) => {
sdk = spec;
spec = sdk;
},
LoggingMessageNotificationParams: (
sdk: SDKTypes.LoggingMessageNotificationParams,
spec: SpecTypes.LoggingMessageNotificationParams
) => {
sdk = spec;
spec = sdk;
},
LoggingMessageNotification: (sdk: WithJSONRPC<SDKTypes.LoggingMessageNotification>, spec: SpecTypes.LoggingMessageNotification) => {
sdk = spec;
spec = sdk;
},
LoggingLevel: (sdk: SDKTypes.LoggingLevel, spec: SpecTypes.LoggingLevel) => {
sdk = spec;
spec = sdk;
},
ToolChoice: (sdk: SDKTypes.ToolChoice, spec: SpecTypes.ToolChoice) => {
sdk = spec;
spec = sdk;
},
Annotations: (sdk: SDKTypes.Annotations, spec: SpecTypes.Annotations) => {
sdk = spec;
spec = sdk;
},
ContentBlock: (sdk: SDKTypes.ContentBlock, spec: SpecTypes.ContentBlock) => {
sdk = spec;
spec = sdk;
},
TextContent: (sdk: SDKTypes.TextContent, spec: SpecTypes.TextContent) => {
sdk = spec;
spec = sdk;
},
ImageContent: (sdk: SDKTypes.ImageContent, spec: SpecTypes.ImageContent) => {
sdk = spec;
spec = sdk;
},
AudioContent: (sdk: SDKTypes.AudioContent, spec: SpecTypes.AudioContent) => {
sdk = spec;
spec = sdk;
},
ToolUseContent: (sdk: SDKTypes.ToolUseContent, spec: SpecTypes.ToolUseContent) => {
sdk = spec;
spec = sdk;
},
ModelPreferences: (sdk: SDKTypes.ModelPreferences, spec: SpecTypes.ModelPreferences) => {
sdk = spec;
spec = sdk;
},
ModelHint: (sdk: SDKTypes.ModelHint, spec: SpecTypes.ModelHint) => {
sdk = spec;
spec = sdk;
},
ResourceTemplateReference: (sdk: SDKTypes.ResourceTemplateReference, spec: SpecTypes.ResourceTemplateReference) => {
sdk = spec;
spec = sdk;
},
PromptReference: (sdk: SDKTypes.PromptReference, spec: SpecTypes.PromptReference) => {
sdk = spec;
spec = sdk;
},
Root: (sdk: SDKTypes.Root, spec: SpecTypes.Root) => {
sdk = spec;
spec = sdk;
},
ElicitRequestFormParams: (sdk: SDKTypes.ElicitRequestFormParams, spec: SpecTypes.ElicitRequestFormParams) => {
sdk = spec;
spec = sdk;
},
ElicitRequestURLParams: (sdk: SDKTypes.ElicitRequestURLParams, spec: SpecTypes.ElicitRequestURLParams) => {
sdk = spec;
spec = sdk;
},
ElicitRequestParams: (sdk: SDKTypes.ElicitRequestParams, spec: SpecTypes.ElicitRequestParams) => {
sdk = spec;
spec = sdk;
},
ElicitRequest: (sdk: SDKTypes.ElicitRequest, spec: SpecTypes.ElicitRequest) => {
sdk = spec;
spec = sdk;
},
PrimitiveSchemaDefinition: (sdk: SDKTypes.PrimitiveSchemaDefinition, spec: SpecTypes.PrimitiveSchemaDefinition) => {
sdk = spec;
spec = sdk;
},
StringSchema: (sdk: SDKTypes.StringSchema, spec: SpecTypes.StringSchema) => {
sdk = spec;
spec = sdk;
},
NumberSchema: (sdk: SDKTypes.NumberSchema, spec: SpecTypes.NumberSchema) => {
sdk = spec;
spec = sdk;
},
BooleanSchema: (sdk: SDKTypes.BooleanSchema, spec: SpecTypes.BooleanSchema) => {
sdk = spec;
spec = sdk;
},
UntitledSingleSelectEnumSchema: (sdk: SDKTypes.UntitledSingleSelectEnumSchema, spec: SpecTypes.UntitledSingleSelectEnumSchema) => {
sdk = spec;
spec = sdk;
},
TitledSingleSelectEnumSchema: (sdk: SDKTypes.TitledSingleSelectEnumSchema, spec: SpecTypes.TitledSingleSelectEnumSchema) => {
sdk = spec;
spec = sdk;
},
SingleSelectEnumSchema: (sdk: SDKTypes.SingleSelectEnumSchema, spec: SpecTypes.SingleSelectEnumSchema) => {
sdk = spec;
spec = sdk;
},
UntitledMultiSelectEnumSchema: (sdk: SDKTypes.UntitledMultiSelectEnumSchema, spec: SpecTypes.UntitledMultiSelectEnumSchema) => {
sdk = spec;
spec = sdk;
},
TitledMultiSelectEnumSchema: (sdk: SDKTypes.TitledMultiSelectEnumSchema, spec: SpecTypes.TitledMultiSelectEnumSchema) => {
sdk = spec;
spec = sdk;
},
MultiSelectEnumSchema: (sdk: SDKTypes.MultiSelectEnumSchema, spec: SpecTypes.MultiSelectEnumSchema) => {
sdk = spec;
spec = sdk;
},
LegacyTitledEnumSchema: (sdk: SDKTypes.LegacyTitledEnumSchema, spec: SpecTypes.LegacyTitledEnumSchema) => {
sdk = spec;
spec = sdk;
},
EnumSchema: (sdk: SDKTypes.EnumSchema, spec: SpecTypes.EnumSchema) => {
sdk = spec;
spec = sdk;
},
ElicitationCompleteNotification: (
sdk: WithJSONRPC<SDKTypes.ElicitationCompleteNotification>,
spec: SpecTypes.ElicitationCompleteNotification
) => {
sdk = spec;
spec = sdk;
},
ClientNotification: (sdk: WithJSONRPC<SDKTypes.ClientNotification>, spec: SpecTypes.ClientNotification) => {
sdk = spec;
spec = sdk;
}
};
// Generated from the 2026-07-28 schema by `pnpm run fetch:spec-types 2026-07-28 <sha>`.
const SPEC_TYPES_FILE = path.resolve(__dirname, '../src/types/spec.types.2026-07-28.ts');
/**
* 2026-07-28 spec types the SDK does not match yet. Spec-implementation work for the
* 2026-07-28 release removes entries from this list as the SDK adopts each shape.
*/
const MISSING_SDK_TYPES_2026_07_28 = [
// Inlined in the SDK (same as the 2025-11-25 comparison):
'Error', // The inner error object of a JSONRPCError
// SEP-2575 per-request envelope: 2026-07-28 requests REQUIRE a `_meta` envelope
// (`io.modelcontextprotocol/protocolVersion`, clientInfo, clientCapabilities). The
// envelope itself is modeled by RequestMetaEnvelope (see sdkTypeChecks above); the
// request shapes below stay here because the SDK wire schemas deliberately keep
// `_meta` lenient — the same schemas parse pre-2026 requests (no envelope) and 2026
// requests, with envelope requiredness enforced per request at dispatch. They burn
// only if the SDK ever models era-specific request types.
'RequestParams',
'PaginatedRequestParams',
'ResourceRequestParams',
'CallToolRequestParams',
'CompleteRequestParams',
'GetPromptRequestParams',
'ReadResourceRequestParams',
'CreateMessageRequestParams',
'PaginatedRequest',
'CallToolRequest',
'CompleteRequest',
'GetPromptRequest',
'ListPromptsRequest',
'ListResourceTemplatesRequest',
'ListResourcesRequest',
'ListRootsRequest',
'ListToolsRequest',
'ReadResourceRequest',
'CreateMessageRequest',
'ClientRequest',
// SEP-2322 (MRTR) → PR for MRTR: 2026-07-28 results carry a required `resultType`
// discriminator. The SDK base result schema carries `resultType` as an optional
// passthrough only (absent means "complete"); per-result modeling lands with MRTR.
'Result',
'EmptyResult',
'PaginatedResult',
'CallToolResult',
'CompleteResult',
'ElicitResult',
'GetPromptResult',
'ListPromptsResult',
'ListResourceTemplatesResult',
'ListResourcesResult',
'ListRootsResult',
'ListToolsResult',
'ReadResourceResult',
'CreateMessageResult',
'ClientResult',
'ServerResult',
'ResultType',
// SEP-2549 cacheable results: `ttlMs`/`cacheScope` caching hints on the list/read
// result shapes → PR for SEP-2549:
'CacheableResult',
// Response envelopes embedding the changed Result shape → PR for MRTR:
'JSONRPCResultResponse',
'JSONRPCResponse',
'JSONRPCMessage',
'CallToolResultResponse',
'CompleteResultResponse',
'GetPromptResultResponse',
'ListPromptsResultResponse',
'ListResourceTemplatesResultResponse',
'ListResourcesResultResponse',
'ListToolsResultResponse',
'ReadResourceResultResponse',
// SEP-2575 sessionless discovery: the SDK ships the wire shapes
// (DiscoverRequestSchema / DiscoverResultSchema), but the 2026-07-28 shapes embed the
// required `_meta` envelope (request) and required `resultType` (result → MRTR PR),
// so they do not match yet; DiscoverResultResponse is a response wrapper (→ MRTR PR):
'DiscoverRequest',
'DiscoverResult',
'DiscoverResultResponse',
// SEP-2567 input requests/responses (new surface) → PR for MRTR:
'InputRequest',
'InputRequests',
'InputRequiredResult',
'InputResponse',
'InputResponseRequestParams',
'InputResponses',
// 2026-07-28 subscriptions surface (new) → PR for subscriptions/listen:
'SubscriptionFilter',
'SubscriptionsAcknowledgedNotification',
'SubscriptionsAcknowledgedNotificationParams',
'SubscriptionsListenRequest',
'SubscriptionsListenRequestParams',
// New typed protocol errors: the SDK ships -32003/-32004 as ProtocolErrorCode
// entries plus the UnsupportedProtocolVersionError class (errors.ts); the spec's
// per-code error *response envelope* interfaces are not modeled as wire types:
'MissingRequiredClientCapabilityError',
'UnsupportedProtocolVersionError',
// Other shapes changed in the 2026-07-28 schema: sampling content changes (SamplingMessage,
// SamplingMessageContentBlock, ToolResultContent) → backchannel PR; open tool
// input/output schema typing (Tool); loosened Notification.params (Notification);
// server notification union, which gains the subscriptions ack (ServerNotification →
// PR for subscriptions/listen):
'SamplingMessage',
'SamplingMessageContentBlock',
'ToolResultContent',
'Tool',
'Notification',
'ServerNotification'
];
function extractExportedTypes(source: string): string[] {
const matches = [...source.matchAll(/export\s+(?:interface|class|type)\s+(\w+)\b/g)];
return matches.map(m => m[1]!);
}
describe('Spec Types (2026-07-28)', () => {
const specTypes = extractExportedTypes(fs.readFileSync(SPEC_TYPES_FILE, 'utf8'));
const typesToCheck = specTypes.filter(type => !MISSING_SDK_TYPES_2026_07_28.includes(type));
it('pins the 2026-07-28 protocol version and the new error codes', () => {
expect(LATEST_PROTOCOL_VERSION).toBe('2026-07-28');
expect(MISSING_REQUIRED_CLIENT_CAPABILITY).toBe(-32003);
expect(UNSUPPORTED_PROTOCOL_VERSION).toBe(-32004);
expect(ProtocolErrorCode.MissingRequiredClientCapability).toBe(MISSING_REQUIRED_CLIENT_CAPABILITY);
expect(ProtocolErrorCode.UnsupportedProtocolVersion).toBe(UNSUPPORTED_PROTOCOL_VERSION);
});
it('pins the per-request _meta envelope keys to the 2026-07-28 schema', () => {
expect(PROTOCOL_VERSION_META_KEY).toBe('io.modelcontextprotocol/protocolVersion');
expect(CLIENT_INFO_META_KEY).toBe('io.modelcontextprotocol/clientInfo');
expect(CLIENT_CAPABILITIES_META_KEY).toBe('io.modelcontextprotocol/clientCapabilities');
expect(LOG_LEVEL_META_KEY).toBe('io.modelcontextprotocol/logLevel');
});
it('should define some expected types', () => {
expect(specTypes).toContain('DiscoverRequest');
expect(specTypes).toContain('InputRequiredResult');
expect(specTypes).toContain('SubscriptionsListenRequest');
expect(specTypes).toHaveLength(150);
});
it('should only allowlist types that exist in the 2026-07-28 schema', () => {
for (const typeName of MISSING_SDK_TYPES_2026_07_28) {
expect(specTypes).toContain(typeName);
}
});
it('should have comprehensive compatibility tests', () => {
const missingTests = [];
for (const typeName of typesToCheck) {
if (!sdkTypeChecks[typeName as keyof typeof sdkTypeChecks]) {
missingTests.push(typeName);
}
}
expect(missingTests).toHaveLength(0);
});
describe('Missing SDK Types', () => {
it.each(MISSING_SDK_TYPES_2026_07_28)(
'%s should not be present in MISSING_SDK_TYPES_2026_07_28 if it has a compatibility test',
type => {
expect(sdkTypeChecks[type as keyof typeof sdkTypeChecks]).toBeUndefined();
}
);
});
});

View file

@ -1,6 +1,8 @@
import {
CallToolRequestSchema,
CallToolResultSchema,
CLIENT_CAPABILITIES_META_KEY,
CLIENT_INFO_META_KEY,
ClientCapabilitiesSchema,
ClientRequestSchema,
CompleteRequestSchema,
@ -8,10 +10,17 @@ import {
CreateMessageRequestSchema,
CreateMessageResultSchema,
CreateMessageResultWithToolsSchema,
DiscoverRequestSchema,
DiscoverResultSchema,
ElicitRequestFormParamsSchema,
EmptyResultSchema,
LATEST_PROTOCOL_VERSION,
LOG_LEVEL_META_KEY,
PromptMessageSchema,
PROTOCOL_VERSION_META_KEY,
RequestMetaEnvelopeSchema,
ResourceLinkSchema,
ResultSchema,
SamplingMessageSchema,
SUPPORTED_PROTOCOL_VERSIONS,
ToolChoiceSchema,
@ -1049,3 +1058,117 @@ describe('2025-11-25 task wire interop (task feature removed; wire types remain)
}
});
});
describe('2026-07-28 wire shapes', () => {
describe('RequestMetaEnvelope', () => {
const envelope = {
[PROTOCOL_VERSION_META_KEY]: '2026-07-28',
[CLIENT_INFO_META_KEY]: { name: 'test-client', version: '1.0.0' },
[CLIENT_CAPABILITIES_META_KEY]: {}
};
test('accepts a complete envelope', () => {
const result = RequestMetaEnvelopeSchema.safeParse(envelope);
expect(result.success).toBe(true);
if (result.success) {
expect(result.data[PROTOCOL_VERSION_META_KEY]).toBe('2026-07-28');
expect(result.data[CLIENT_INFO_META_KEY]).toEqual({ name: 'test-client', version: '1.0.0' });
expect(result.data[CLIENT_CAPABILITIES_META_KEY]).toEqual({});
}
});
test('accepts the optional log level, progress token, and unknown keys', () => {
const result = RequestMetaEnvelopeSchema.safeParse({
...envelope,
[LOG_LEVEL_META_KEY]: 'warning',
progressToken: 'token-1',
'com.example/custom': { anything: true }
});
expect(result.success).toBe(true);
if (result.success) {
expect(result.data[LOG_LEVEL_META_KEY]).toBe('warning');
expect(result.data.progressToken).toBe('token-1');
expect(result.data['com.example/custom']).toEqual({ anything: true });
}
});
test.each([PROTOCOL_VERSION_META_KEY, CLIENT_INFO_META_KEY, CLIENT_CAPABILITIES_META_KEY])(
'rejects an envelope missing %s',
key => {
const incomplete: Record<string, unknown> = { ...envelope };
delete incomplete[key];
expect(RequestMetaEnvelopeSchema.safeParse(incomplete).success).toBe(false);
}
);
test('rejects an invalid log level', () => {
const result = RequestMetaEnvelopeSchema.safeParse({ ...envelope, [LOG_LEVEL_META_KEY]: 'loud' });
expect(result.success).toBe(false);
});
});
describe('DiscoverRequest', () => {
test('parses a discover request with and without params', () => {
expect(DiscoverRequestSchema.safeParse({ method: 'server/discover' }).success).toBe(true);
expect(
DiscoverRequestSchema.safeParse({
method: 'server/discover',
params: { _meta: { [PROTOCOL_VERSION_META_KEY]: '2026-07-28' } }
}).success
).toBe(true);
});
test('rejects other methods', () => {
expect(DiscoverRequestSchema.safeParse({ method: 'initialize' }).success).toBe(false);
});
});
describe('DiscoverResult', () => {
const result = {
supportedVersions: ['2026-07-28'],
capabilities: { tools: { listChanged: true } },
serverInfo: { name: 'test-server', version: '1.0.0' }
};
test('parses a discover result', () => {
const parsed = DiscoverResultSchema.safeParse({ ...result, resultType: 'complete', instructions: 'Use the echo tool.' });
expect(parsed.success).toBe(true);
if (parsed.success) {
expect(parsed.data.supportedVersions).toEqual(['2026-07-28']);
expect(parsed.data.capabilities).toEqual({ tools: { listChanged: true } });
expect(parsed.data.serverInfo).toEqual({ name: 'test-server', version: '1.0.0' });
expect(parsed.data.instructions).toBe('Use the echo tool.');
}
});
test.each(['supportedVersions', 'capabilities', 'serverInfo'])('rejects a discover result missing %s', key => {
const incomplete: Record<string, unknown> = { ...result };
delete incomplete[key];
expect(DiscoverResultSchema.safeParse(incomplete).success).toBe(false);
});
});
describe('Result resultType passthrough', () => {
test('accepts results with and without resultType (absent means "complete")', () => {
const withIt = ResultSchema.safeParse({ resultType: 'complete' });
expect(withIt.success).toBe(true);
if (withIt.success) {
expect(withIt.data.resultType).toBe('complete');
}
const withoutIt = ResultSchema.safeParse({});
expect(withoutIt.success).toBe(true);
if (withoutIt.success) {
expect(withoutIt.data.resultType).toBeUndefined();
}
});
test('rejects a non-string resultType', () => {
expect(ResultSchema.safeParse({ resultType: 42 }).success).toBe(false);
});
test('EmptyResult accepts resultType but still rejects unknown keys', () => {
expect(EmptyResultSchema.safeParse({ resultType: 'complete' }).success).toBe(true);
expect(EmptyResultSchema.safeParse({ unexpected: true }).success).toBe(false);
});
});
});

View file

@ -0,0 +1,44 @@
import { describe, expect, it } from 'vitest';
import { ProtocolErrorCode } from '../../src/types/enums.js';
import { ProtocolError, UnsupportedProtocolVersionError } from '../../src/types/errors.js';
describe('UnsupportedProtocolVersionError', () => {
const data = { supported: ['2025-11-25', '2025-06-18'], requested: '2026-07-28' };
it('carries code -32004 and the supported/requested data', () => {
const error = new UnsupportedProtocolVersionError(data);
expect(error.code).toBe(ProtocolErrorCode.UnsupportedProtocolVersion);
expect(error.code).toBe(-32004);
expect(error.supported).toEqual(['2025-11-25', '2025-06-18']);
expect(error.requested).toBe('2026-07-28');
expect(error.data).toEqual(data);
});
it('defaults the message from the requested version', () => {
const error = new UnsupportedProtocolVersionError(data);
expect(error.message).toBe('Unsupported protocol version: 2026-07-28');
const custom = new UnsupportedProtocolVersionError(data, 'try another version');
expect(custom.message).toBe('try another version');
});
it('is materialized by ProtocolError.fromError', () => {
const error = ProtocolError.fromError(-32004, 'Unsupported protocol version: 2026-07-28', data);
expect(error).toBeInstanceOf(UnsupportedProtocolVersionError);
if (error instanceof UnsupportedProtocolVersionError) {
expect(error.supported).toEqual(['2025-11-25', '2025-06-18']);
expect(error.requested).toBe('2026-07-28');
}
expect(error.message).toBe('Unsupported protocol version: 2026-07-28');
});
it('falls back to a generic ProtocolError when the data is missing or malformed', () => {
for (const malformed of [undefined, {}, { supported: 'not-an-array', requested: '2026-07-28' }, { supported: ['2025-11-25'] }]) {
const error = ProtocolError.fromError(-32004, 'unsupported', malformed);
expect(error).toBeInstanceOf(ProtocolError);
expect(error).not.toBeInstanceOf(UnsupportedProtocolVersionError);
expect(error.code).toBe(-32004);
expect(error.data).toEqual(malformed);
}
});
});

View file

@ -7,12 +7,32 @@ const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const PROJECT_ROOT = join(__dirname, '..');
/**
* The protocol revisions the SDK keeps reference types for:
* - `2025-11-25`: the frozen, released schema.
* - `2026-07-28`: the upcoming protocol revision.
*
* Each is written to `packages/core/src/types/spec.types.<version>.ts`.
*/
const SUPPORTED_VERSIONS = ['2025-11-25', '2026-07-28'] as const;
type SpecVersion = (typeof SUPPORTED_VERSIONS)[number];
/**
* Upstream schema directory per revision. Until the 2026-07-28 revision is
* published, its schema lives in the spec repository's draft directory; this
* mapping drops once `schema/2026-07-28/` exists upstream.
*/
const UPSTREAM_SCHEMA_DIRS: Record<SpecVersion, string> = {
'2025-11-25': '2025-11-25',
'2026-07-28': 'draft'
};
interface GitHubCommit {
sha: string;
}
async function fetchLatestSHA(): Promise<string> {
const url = 'https://api.github.com/repos/modelcontextprotocol/modelcontextprotocol/commits?path=schema/draft/schema.ts&per_page=1';
async function fetchLatestSHA(version: SpecVersion): Promise<string> {
const url = `https://api.github.com/repos/modelcontextprotocol/modelcontextprotocol/commits?path=schema/${UPSTREAM_SCHEMA_DIRS[version]}/schema.ts&per_page=1`;
const response = await fetch(url);
if (!response.ok) {
@ -27,8 +47,8 @@ async function fetchLatestSHA(): Promise<string> {
return commits[0].sha;
}
async function fetchSpecTypes(sha: string): Promise<string> {
const url = `https://raw.githubusercontent.com/modelcontextprotocol/modelcontextprotocol/${sha}/schema/draft/schema.ts`;
async function fetchSpecTypes(version: SpecVersion, sha: string): Promise<string> {
const url = `https://raw.githubusercontent.com/modelcontextprotocol/modelcontextprotocol/${sha}/schema/${UPSTREAM_SCHEMA_DIRS[version]}/schema.ts`;
const response = await fetch(url);
if (!response.ok) {
@ -38,49 +58,70 @@ async function fetchSpecTypes(sha: string): Promise<string> {
return await response.text();
}
async function main() {
try {
// Check if SHA is provided as command line argument
const providedSHA = process.argv[2];
async function updateSpecTypes(version: SpecVersion, providedSHA?: string): Promise<void> {
let sha: string;
if (providedSHA) {
console.log(`[${version}] Using provided SHA: ${providedSHA}`);
sha = providedSHA;
} else {
console.log(`[${version}] Fetching latest commit SHA...`);
sha = await fetchLatestSHA(version);
}
let latestSHA: string;
if (providedSHA) {
console.log(`Using provided SHA: ${providedSHA}`);
latestSHA = providedSHA;
} else {
console.log('Fetching latest commit SHA...');
latestSHA = await fetchLatestSHA();
}
console.log(`[${version}] Fetching schema.ts from commit: ${sha}`);
console.log(`Fetching spec.types.ts from commit: ${latestSHA}`);
const specContent = await fetchSpecTypes(version, sha);
const specContent = await fetchSpecTypes(latestSHA);
// Read header template
const headerTemplate = `/**
// Read header template
const headerTemplate = `/**
* This file is automatically generated from the Model Context Protocol specification.
*
* Source: https://github.com/modelcontextprotocol/modelcontextprotocol
* Pulled from: https://raw.githubusercontent.com/modelcontextprotocol/modelcontextprotocol/main/schema/draft/schema.ts
* Pulled from: https://raw.githubusercontent.com/modelcontextprotocol/modelcontextprotocol/main/schema/{SCHEMA_DIR}/schema.ts
* Last updated from commit: {SHA}
*
* DO NOT EDIT THIS FILE MANUALLY. Changes will be overwritten by automated updates.
* To update this file, run: pnpm run fetch:spec-types
* To update this file, run: pnpm run fetch:spec-types {VERSION}
*/`;
const header = headerTemplate.replace('{SHA}', latestSHA);
const header = headerTemplate.replaceAll('{VERSION}', version).replaceAll('{SCHEMA_DIR}', UPSTREAM_SCHEMA_DIRS[version]).replace('{SHA}', sha);
// Combine header and content
const fullContent = header + specContent;
// Combine header and content
const fullContent = header + specContent;
// Format with prettier using the project's config so the output passes lint
const outputPath = join(PROJECT_ROOT, 'packages', 'core', 'src', 'types', 'spec.types.ts');
const prettierConfig = await prettier.resolveConfig(outputPath);
const formatted = await prettier.format(fullContent, { ...prettierConfig, filepath: outputPath });
// Format with prettier using the project's config so the output passes lint
const outputPath = join(PROJECT_ROOT, 'packages', 'core', 'src', 'types', `spec.types.${version}.ts`);
const prettierConfig = await prettier.resolveConfig(outputPath);
const formatted = await prettier.format(fullContent, { ...prettierConfig, filepath: outputPath });
writeFileSync(outputPath, formatted, 'utf-8');
writeFileSync(outputPath, formatted, 'utf-8');
console.log('Successfully updated packages/core/src/types/spec.types.ts');
console.log(`[${version}] Successfully updated packages/core/src/types/spec.types.${version}.ts`);
}
function isSupportedVersion(value: string): value is SpecVersion {
return (SUPPORTED_VERSIONS as readonly string[]).includes(value);
}
async function main() {
try {
// Usage: fetch-spec-types.ts [version] [sha]
// With no version, all supported versions are fetched at their latest upstream SHA.
const providedVersion = process.argv[2];
const providedSHA = process.argv[3];
if (providedVersion === undefined) {
for (const version of SUPPORTED_VERSIONS) {
await updateSpecTypes(version);
}
return;
}
if (!isSupportedVersion(providedVersion)) {
throw new Error(`Unsupported version "${providedVersion}". Supported versions: ${SUPPORTED_VERSIONS.join(', ')}`);
}
await updateSpecTypes(providedVersion, providedSHA);
} catch (error) {
console.error('Error:', error instanceof Error ? error.message : String(error));
process.exit(1);