Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/spec-reference-types-2026-07-28.md
Original file line number Diff line number Diff line change
@@ -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.
14 changes: 7 additions & 7 deletions .github/workflows/update-spec-types.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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

Expand All @@ -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
Expand Down
3 changes: 2 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion REVIEW.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion common/eslint-config/eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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'],
Expand Down
3 changes: 3 additions & 0 deletions packages/codemod/src/generated/specSchemaMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ export const SPEC_SCHEMA_NAMES: ReadonlySet<string> = new Set([
'CreateMessageResultWithToolsSchema',
'CreateTaskResultSchema',
'CursorSchema',
'DiscoverRequestSchema',
'DiscoverResultSchema',
'ElicitRequestFormParamsSchema',
'ElicitRequestParamsSchema',
'ElicitRequestSchema',
Expand Down Expand Up @@ -112,6 +114,7 @@ export const SPEC_SCHEMA_NAMES: ReadonlySet<string> = new Set([
'ReadResourceResultSchema',
'RelatedTaskMetadataSchema',
'RequestIdSchema',
'RequestMetaEnvelopeSchema',
'RequestMetaSchema',
'RequestSchema',
'ResourceContentsSchema',
Expand Down
6 changes: 5 additions & 1 deletion packages/core/src/exports/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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 {
Expand Down
33 changes: 33 additions & 0 deletions packages/core/src/types/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down
10 changes: 10 additions & 0 deletions packages/core/src/types/enums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
38 changes: 37 additions & 1 deletion packages/core/src/types/errors.ts
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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);
}
Expand All @@ -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;
}
}
97 changes: 95 additions & 2 deletions packages/core/src/types/schemas.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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()
});

/**
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down
Loading
Loading