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
95 changes: 0 additions & 95 deletions .github/workflows/update-spec-types.yml

This file was deleted.

4 changes: 2 additions & 2 deletions REVIEW.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ review rounds and grows over time.
## Checklist

**Protocol & spec**
- Types match [`schema.ts`](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/schema/draft/schema.ts) exactly (optional vs required fields)
- Types match the schema for the protocol revision being changed (`schema/<revision>/schema.ts` for released revisions; `schema/draft/schema.ts` only for unreleased work)
- Correct `ProtocolError` codes (enum `ProtocolErrorCode`); HTTP status codes match spec (e.g., 404 vs 410)
- Works for both stdio and Streamable HTTP transports — no transport-specific assumptions
- Cross-SDK consistency: check what `python-sdk` does for the same feature
Expand Down Expand Up @@ -61,7 +61,7 @@ When verifying spec compliance, consult the spec directly rather than relying on

- MCP documentation server: `https://modelcontextprotocol.io/mcp`
- Full spec text (single file, LLM-friendly): `https://modelcontextprotocol.io/llms-full.txt` — fetch to a temp file and grep for the relevant section
- Schema source of truth: [`schema.ts`](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/schema/draft/schema.ts)
- Schema source of truth: the revision-matched `schema.ts` in `modelcontextprotocol/modelcontextprotocol` (`schema/<revision>/schema.ts` for released revisions; `schema/draft/schema.ts` only for unreleased work)

## Recurring Catches

Expand Down
12 changes: 5 additions & 7 deletions packages/core-internal/src/types/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,17 @@ They are reference-only test oracles: the comparison suites in `packages/core-in
## Lifecycle policy

1. **Released revisions are frozen.** Once a protocol revision is published under `schema/<revision>/` in the spec repository, its anchor regenerates only from the pinned spec commit recorded in `RELEASED_REVISION_PINS` (`scripts/fetch-spec-types.ts`) — never from the latest
upstream commit. Moving that pin, including the freeze of a newly published revision (when its generation source switches from `schema/draft/` to `schema/<revision>/`), must land in the same commit that retargets the nightly update workflow
(`.github/workflows/update-spec-types.yml`), so the anchor and the automation that maintains it can never disagree about the source of truth.
upstream commit. Moving that pin, including the freeze of a newly published revision (when its generation source switches from `schema/draft/` to `schema/<revision>/`), must land atomically with every checked-in oracle derived from that revision.

2. **Draft anchors float only via reviewed refresh PRs.** The anchor for an unreleased revision tracks the spec repository's `schema/draft/schema.ts`. The nightly workflow regenerates it from the latest upstream commit and, when the result differs from what is checked in, opens
(or updates) a refresh PR. Manual refreshes follow the same path: regenerate, then propose the diff in a PR.
2. **Draft anchors float only while they have an explicit consumer.** An unreleased revision may track the spec repository's `schema/draft/schema.ts`, but refreshes must go through reviewed PRs and update all derived oracles atomically. No currently supported revision uses this mode.

3. **The bot proposes; it never auto-merges.** Automated refreshes always go through a pull request that a maintainer reviews and merges. No automation pushes anchor changes directly to `main` or merges its own PRs. A refresh PR that breaks the comparison suites is the desired
signal — it is fixed in that PR, not bypassed.

4. **Generated twins update atomically with their anchor.** If artifacts derived from an anchor (for example vendored JSON schemas or generated validators) are checked into this repository, any refresh that changes the anchor must regenerate those artifacts in the same commit.
The anchor and its derived twins must never be out of sync at any commit on `main`.

**This clause is OPERATIVE.** The vendored twins are the per-revision `schema.json` copies under `packages/core-internal/test/corpus/schema-twins/` (`<revision>.schema.json` + `manifest.json` recording the source commit and content hashes). They are TEST-ONLY oracles consumed by the
schema-twin conformance lock (`test/wire/schemaTwinConformance.test.ts`) — never bundled, never imported by runtime code, and the JSON Schema engines stay optional peer dependencies. A refresh of `spec.types.<revision>.ts` must copy the matching upstream
`schema/<dir>/schema.json` (same spec commit) over the twin and update `manifest.json` in the same commit; the spec example corpus manifest (`test/corpus/fixtures/<revision>/manifest.json`) records its own source commit and follows the same atomicity rule when the examples
**This clause is OPERATIVE.** The vendored twins are the per-revision `schema.json` copies under `packages/core-internal/test/corpus/schema-twins/` (`<revision>.schema.json` + `manifest.json` recording each twin's source commit and content hashes). They are TEST-ONLY oracles consumed by the
schema-twin conformance lock (`test/wire/schemaTwinConformance.test.ts`) — never bundled, never imported by runtime code, and the JSON Schema engines stay optional peer dependencies. A refresh of `spec.types.<revision>.ts` must refresh the matching upstream
`schema/<dir>/schema.json` from that twin's recorded source commit and update `manifest.json` in the same change; the spec example corpus manifest (`test/corpus/fixtures/<revision>/manifest.json`) records its own source commit and follows the same atomicity rule when the examples
are re-vendored. The conformance lock failing after an anchor-only refresh is the desired loud signal of a missed twin update.
29 changes: 21 additions & 8 deletions packages/core-internal/src/types/spec.types.2026-07-28.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
* 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
* Last updated from commit: 71e306956a4959c9655e5036be215d41986596e6
* Pulled from: https://raw.githubusercontent.com/modelcontextprotocol/modelcontextprotocol/main/schema/2026-07-28/schema.ts
* Last updated from commit: 271ecc9accafdd9b83a3c869fa67c22953b2af80
*
* DO NOT EDIT THIS FILE MANUALLY. Changes will be overwritten by automated updates.
* To update this file, run: pnpm run fetch:spec-types 2026-07-28
Expand Down Expand Up @@ -55,7 +55,7 @@ export const JSONRPC_VERSION = '2.0';
* - Unless empty, MUST start and end with an alphanumeric character (`[a-z0-9A-Z]`).
* - Interior characters may be alphanumeric, hyphens (`-`), underscores (`_`), or dots (`.`).
*
* @see [General fields: `_meta`](/specification/draft/basic/index#meta) for more details.
* @see [General fields: `_meta`](/specification/2026-07-28/basic/index#meta) for more details.
* @category Common Types
*/
export type MetaObject = Record<string, unknown>;
Expand All @@ -64,7 +64,7 @@ export type MetaObject = Record<string, unknown>;
* Extends {@link MetaObject} with additional request-specific fields. All key naming rules from `MetaObject` apply.
*
* @see {@link MetaObject} for key naming rules and reserved prefixes.
* @see [General fields: `_meta`](/specification/draft/basic/index#meta) for more details.
* @see [General fields: `_meta`](/specification/2026-07-28/basic/index#meta) for more details.
* @category Common Types
*/
export interface RequestMetaObject extends MetaObject {
Expand Down Expand Up @@ -121,7 +121,7 @@ export interface RequestMetaObject extends MetaObject {
* Extends {@link MetaObject} with additional notification-specific fields. All key naming rules from `MetaObject` apply.
*
* @see {@link MetaObject} for key naming rules and reserved prefixes.
* @see [General fields: `_meta`](/specification/draft/basic/index#meta) for more details.
* @see [General fields: `_meta`](/specification/2026-07-28/basic/index#meta) for more details.
* @category Common Types
*/
export interface NotificationMetaObject extends MetaObject {
Expand All @@ -144,7 +144,7 @@ export interface NotificationMetaObject extends MetaObject {
* Extends {@link MetaObject} with additional result-specific fields. All key naming rules from `MetaObject` apply.
*
* @see {@link MetaObject} for key naming rules and reserved prefixes.
* @see [General fields: `_meta`](/specification/draft/basic/index#meta) for more details.
* @see [General fields: `_meta`](/specification/2026-07-28/basic/index#meta) for more details.
* @category Common Types
*/
export interface ResultMetaObject extends MetaObject {
Expand Down Expand Up @@ -1317,7 +1317,7 @@ export interface SubscriptionsListenRequest extends JSONRPCRequest {
* @see {@link MetaObject} for key naming rules and reserved prefixes.
* @category `subscriptions/listen`
*/
export interface SubscriptionsListenResultMeta extends ResultMetaObject {
export interface SubscriptionsListenResultMetaObject extends ResultMetaObject {
/**
* Identifies the subscription stream this response closes, so the client can
* correlate it with the originating subscription — mirroring the same key on
Expand All @@ -1341,7 +1341,20 @@ export interface SubscriptionsListenResultMeta extends ResultMetaObject {
* @category `subscriptions/listen`
*/
export interface SubscriptionsListenResult extends Result {
_meta: SubscriptionsListenResultMeta;
_meta: SubscriptionsListenResultMetaObject;
}

/**
* A successful response from the server for a {@link SubscriptionsListenRequest | subscriptions/listen}
* request, sent when the server tears the subscription down gracefully.
*
* @example Subscription closed gracefully response
* {@includeCode ./examples/SubscriptionsListenResultResponse/listen-closed-response.json}
*
* @category `subscriptions/listen`
*/
export interface SubscriptionsListenResultResponse extends JSONRPCResultResponse {
result: SubscriptionsListenResult;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1070,7 +1070,7 @@ function build() {
const SubscriptionsListenRequestSchema = wireRequest('subscriptions/listen', subscriptionsListenParamsShape);

/**
* Anchor SubscriptionsListenResultMeta — required subscriptionId stamp on
* Anchor SubscriptionsListenResultMetaObject — required subscriptionId stamp on
* the graceful-close result. Extends `ResultMetaObject` since spec PR
* #3002 (composed, so the serverInfo key and its leniency stay single-sourced).
*/
Expand Down Expand Up @@ -1276,6 +1276,7 @@ function build() {
const ReadResourceResultResponseSchema = wireResultResponse(z.union([ReadResourceResultSchema, InputRequiredResultSchema]));
const CompleteResultResponseSchema = wireResultResponse(CompleteResultSchema);
const DiscoverResultResponseSchema = wireResultResponse(DiscoverResultSchema);
const SubscriptionsListenResultResponseSchema = wireResultResponse(SubscriptionsListenResultSchema);

return {
JSONValueSchema,
Expand Down Expand Up @@ -1411,7 +1412,8 @@ function build() {
ListResourceTemplatesResultResponseSchema,
ReadResourceResultResponseSchema,
CompleteResultResponseSchema,
DiscoverResultResponseSchema
DiscoverResultResponseSchema,
SubscriptionsListenResultResponseSchema
};
}

Expand Down
1 change: 1 addition & 0 deletions packages/core-internal/src/wire/rev2026-07-28/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,3 +156,4 @@ export const ListResourceTemplatesResultResponseSchema = s.ListResourceTemplates
export const ReadResourceResultResponseSchema = s.ReadResourceResultResponseSchema;
export const CompleteResultResponseSchema = s.CompleteResultResponseSchema;
export const DiscoverResultResponseSchema = s.DiscoverResultResponseSchema;
export const SubscriptionsListenResultResponseSchema = s.SubscriptionsListenResultResponseSchema;
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"jsonrpc": "2.0",
"id": "listen-1",
"result": {
"resultType": "complete",
"_meta": {
"io.modelcontextprotocol/subscriptionId": "listen-1"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
"revision": "2026-07-28",
"source": {
"repo": "modelcontextprotocol/modelcontextprotocol",
"path": "schema/draft/examples",
"commit": "71e306956a4959c9655e5036be215d41986596e6"
"path": "schema/2026-07-28/examples",
"commit": "271ecc9accafdd9b83a3c869fa67c22953b2af80"
},
"regenerate": "pnpm fetch:spec-examples --spec-dir <spec-checkout> # or [sha] to fetch from GitHub",
"directoryCount": 87,
"fileCount": 128,
"directoryCount": 88,
"fileCount": 129,
"directories": {
"AudioContent": [
"audio-wav-content.json"
Expand Down Expand Up @@ -273,6 +273,9 @@
"SubscriptionsListenResult": [
"listen-closed.json"
],
"SubscriptionsListenResultResponse": [
"listen-closed-response.json"
],
"TextContent": [
"text-content.json"
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3400,7 +3400,7 @@
"description": "The response to a {@link SubscriptionsListenRequestsubscriptions/listen}\nrequest, signalling that the subscription has ended gracefully (for example,\nduring server shutdown). Because the listen stream is long-lived, this result\nis sent only when the server tears the subscription down; an abrupt transport\nclose carries no response. The result body is otherwise empty.",
"properties": {
"_meta": {
"$ref": "#/$defs/SubscriptionsListenResultMeta"
"$ref": "#/$defs/SubscriptionsListenResultMetaObject"
},
"resultType": {
"description": "Indicates the type of the result, which allows the client to determine\nhow to parse the result object.\n\nServers implementing this protocol version MUST include this field.\nFor backward compatibility, when a client receives a result from a\nserver implementing an earlier protocol version (which does not include\n`resultType`), the client MUST treat the absent field as `\"complete\"`.",
Expand All @@ -3413,7 +3413,7 @@
],
"type": "object"
},
"SubscriptionsListenResultMeta": {
"SubscriptionsListenResultMetaObject": {
"description": "Extends {@link ResultMetaObject} with the subscription-stream identifier carried by a\n{@link SubscriptionsListenResult}. All key naming rules from `MetaObject` apply.",
"properties": {
"io.modelcontextprotocol/serverInfo": {
Expand All @@ -3430,6 +3430,27 @@
],
"type": "object"
},
"SubscriptionsListenResultResponse": {
"description": "A successful response from the server for a {@link SubscriptionsListenRequestsubscriptions/listen}\nrequest, sent when the server tears the subscription down gracefully.",
"properties": {
"id": {
"$ref": "#/$defs/RequestId"
},
"jsonrpc": {
"const": "2.0",
"type": "string"
},
"result": {
"$ref": "#/$defs/SubscriptionsListenResult"
}
},
"required": [
"id",
"jsonrpc",
"result"
],
"type": "object"
},
"TextContent": {
"description": "Text provided to or from an LLM.",
"properties": {
Expand Down
Loading
Loading