diff --git a/.changeset/cancelled-request-id-zero.md b/.changeset/cancelled-request-id-zero.md deleted file mode 100644 index 13cc84332d..0000000000 --- a/.changeset/cancelled-request-id-zero.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': patch -'@modelcontextprotocol/client': patch -'@modelcontextprotocol/server': patch ---- - -Treat request id `0` as a real id. Two guards tested a `RequestId` for truthiness, so the legal JSON-RPC ids `0` and `''` were read as absent. Id `0` is not a corner case: the outbound request counter is zero-based, so it is the first id every peer assigns, which on the server→client leg is the first `sampling/createMessage`, `elicitation/create`, or `roots/list` a server sends. - -- `notifications/cancelled` carrying id `0` was ignored, and the in-flight handler ran to completion with its `AbortSignal` never fired. -- A notification sent with `relatedRequestId: 0` wrongly passed the debounce gate (for methods opted into `debouncedNotificationMethods`). Because the pending set is keyed by method alone, a second such notification in the same tick was silently dropped rather than sent. - -Absent is now the only value that means "no id". diff --git a/.changeset/no-cancel-notification-for-initialize.md b/.changeset/no-cancel-notification-for-initialize.md deleted file mode 100644 index b7acd086d3..0000000000 --- a/.changeset/no-cancel-notification-for-initialize.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': patch -'@modelcontextprotocol/client': patch -'@modelcontextprotocol/server': patch ---- - -Stop sending `notifications/cancelled` for the `initialize` handshake. The spec is explicit that a client MUST NOT attempt to cancel its `initialize` request, but the outbound cancel path fired for any in-flight request: aborting the `AbortSignal` passed to `connect()`, or letting the handshake hit its timeout, put a forbidden cancellation on the wire naming the initialize request id. - -The local behaviour is unchanged — the caller's promise still rejects with the same abort/timeout error, and `connect()` still tears the connection down. Only the wire notification is suppressed. Every other method keeps the existing cancellation path. diff --git a/.changeset/require-protocol-version-header-on-modern-post.md b/.changeset/require-protocol-version-header-on-modern-post.md deleted file mode 100644 index a120853db7..0000000000 --- a/.changeset/require-protocol-version-header-on-modern-post.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': patch -'@modelcontextprotocol/server': patch ---- - -Reject a modern (2026-07-28) POST that omits the required `MCP-Protocol-Version` header. - -`createMcpHandler` accepted a request whose body carried a valid per-request `_meta` -envelope but whose `MCP-Protocol-Version` header was absent: the request was classified -modern, dispatched, and answered `200` — tool handlers ran. Only the _mismatch_ case -(header present, disagreeing with the body) was rejected, so of the standard headers -SEP-2243 requires on a modern POST, presence was enforced for `Mcp-Method` (and for -`Mcp-Name` on the methods that mirror `params.name` / `params.uri`) but not for -`MCP-Protocol-Version`. - -Such a request is now refused with `400 Bad Request` and JSON-RPC `-32020` -(`HeaderMismatch`), matching the shape the sibling missing-header cells already emit and -echoing the request id — per the Streamable HTTP spec, which requires the header on every -POST and lists a missing required standard header as a `HeaderMismatch` failure. The -spec's allowance to treat a header-less request as `2025-03-26` is available only to a -server that also serves pre-2025-06-18 clients, and permits routing it to _legacy_ -handling — never serving it as 2026-07-28; under `legacy: 'reject'` the requirement is -unconditional. - -Era classification is deliberately unchanged and stays body-primary: a proxy that strips -the header still must not change the era, so such a request is still _classified_ modern -and is refused one rung later, at `standard-header-validation` — the same rung that -already answers a missing `Mcp-Method`. Legacy-era traffic is untouched, notifications -are unaffected, body-less `GET` / `DELETE` session operations are method-routed before -any header validation, and stdio serving (which has no HTTP headers) is not involved. - -Clients built with this SDK always send the header, so no first-party client is affected; -hand-rolled clients that omitted it must add it. diff --git a/packages/client/CHANGELOG.md b/packages/client/CHANGELOG.md index 5f3dea3b2d..2167875fcb 100644 --- a/packages/client/CHANGELOG.md +++ b/packages/client/CHANGELOG.md @@ -1,5 +1,22 @@ # @modelcontextprotocol/client +## 2.0.1 + +### Patch Changes + +- [#2654](https://github.com/modelcontextprotocol/typescript-sdk/pull/2654) [`03842cd`](https://github.com/modelcontextprotocol/typescript-sdk/commit/03842cd9cae9a9b142c77d2fb65e829fc4e03eab) Thanks [@pshah19](https://github.com/pshah19)! - Treat request id `0` as a real id. Two guards tested a `RequestId` for truthiness, so the legal JSON-RPC ids `0` and `''` were read as absent. Id `0` is not a corner case: the outbound request counter is zero-based, so it is the first id every peer assigns, which on the server→client leg is the first `sampling/createMessage`, `elicitation/create`, or `roots/list` a server sends. + - `notifications/cancelled` carrying id `0` was ignored, and the in-flight handler ran to completion with its `AbortSignal` never fired. + - A notification sent with `relatedRequestId: 0` wrongly passed the debounce gate (for methods opted into `debouncedNotificationMethods`). Because the pending set is keyed by method alone, a second such notification in the same tick was silently dropped rather than sent. + + Absent is now the only value that means "no id". + +- [#2668](https://github.com/modelcontextprotocol/typescript-sdk/pull/2668) [`3e90449`](https://github.com/modelcontextprotocol/typescript-sdk/commit/3e90449fd52997da43b79a536d2c19c446603cc7) Thanks [@KKonstantinov](https://github.com/KKonstantinov)! - Stop sending `notifications/cancelled` for the `initialize` handshake. The spec is explicit that a client MUST NOT attempt to cancel its `initialize` request, but the outbound cancel path fired for any in-flight request: aborting the `AbortSignal` passed to `connect()`, or letting the handshake hit its timeout, put a forbidden cancellation on the wire naming the initialize request id. + + The local behaviour is unchanged — the caller's promise still rejects with the same abort/timeout error, and `connect()` still tears the connection down. Only the wire notification is suppressed. Every other method keeps the existing cancellation path. + +- Updated dependencies []: + - @modelcontextprotocol/core@2.0.1 + ## 2.0.0 ### Minor Changes diff --git a/packages/client/package.json b/packages/client/package.json index 3ebfde64d3..378957e1f3 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/client", - "version": "2.0.0", + "version": "2.0.1", "description": "Model Context Protocol implementation for TypeScript - Client package", "license": "MIT", "author": "Anthropic, PBC (https://anthropic.com)", diff --git a/packages/codemod/CHANGELOG.md b/packages/codemod/CHANGELOG.md index 3506a1e5f9..ba6fad1e02 100644 --- a/packages/codemod/CHANGELOG.md +++ b/packages/codemod/CHANGELOG.md @@ -1,5 +1,7 @@ # @modelcontextprotocol/codemod +## 2.0.1 + ## 2.0.0 ### Patch Changes diff --git a/packages/codemod/package.json b/packages/codemod/package.json index 09cabe0bcf..bd71d9980c 100644 --- a/packages/codemod/package.json +++ b/packages/codemod/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/codemod", - "version": "2.0.0", + "version": "2.0.1", "description": "Codemod to migrate MCP TypeScript SDK code from v1 to v2", "license": "MIT", "author": "Anthropic, PBC (https://anthropic.com)", diff --git a/packages/core-internal/CHANGELOG.md b/packages/core-internal/CHANGELOG.md index 450c293bf3..26e6672acf 100644 --- a/packages/core-internal/CHANGELOG.md +++ b/packages/core-internal/CHANGELOG.md @@ -1,5 +1,51 @@ # @modelcontextprotocol/core-internal +## 2.0.1 + +### Patch Changes + +- [#2654](https://github.com/modelcontextprotocol/typescript-sdk/pull/2654) [`03842cd`](https://github.com/modelcontextprotocol/typescript-sdk/commit/03842cd9cae9a9b142c77d2fb65e829fc4e03eab) Thanks [@pshah19](https://github.com/pshah19)! - Treat request id `0` as a real id. Two guards tested a `RequestId` for truthiness, so the legal JSON-RPC ids `0` and `''` were read as absent. Id `0` is not a corner case: the outbound request counter is zero-based, so it is the first id every peer assigns, which on the server→client leg is the first `sampling/createMessage`, `elicitation/create`, or `roots/list` a server sends. + - `notifications/cancelled` carrying id `0` was ignored, and the in-flight handler ran to completion with its `AbortSignal` never fired. + - A notification sent with `relatedRequestId: 0` wrongly passed the debounce gate (for methods opted into `debouncedNotificationMethods`). Because the pending set is keyed by method alone, a second such notification in the same tick was silently dropped rather than sent. + + Absent is now the only value that means "no id". + +- [#2668](https://github.com/modelcontextprotocol/typescript-sdk/pull/2668) [`3e90449`](https://github.com/modelcontextprotocol/typescript-sdk/commit/3e90449fd52997da43b79a536d2c19c446603cc7) Thanks [@KKonstantinov](https://github.com/KKonstantinov)! - Stop sending `notifications/cancelled` for the `initialize` handshake. The spec is explicit that a client MUST NOT attempt to cancel its `initialize` request, but the outbound cancel path fired for any in-flight request: aborting the `AbortSignal` passed to `connect()`, or letting the handshake hit its timeout, put a forbidden cancellation on the wire naming the initialize request id. + + The local behaviour is unchanged — the caller's promise still rejects with the same abort/timeout error, and `connect()` still tears the connection down. Only the wire notification is suppressed. Every other method keeps the existing cancellation path. + +- [#2590](https://github.com/modelcontextprotocol/typescript-sdk/pull/2590) [`75dc7ea`](https://github.com/modelcontextprotocol/typescript-sdk/commit/75dc7ea6e2913e1ac37d4f06eec62cd5cfac9e7a) Thanks [@davidpavlovschi](https://github.com/davidpavlovschi)! - Reject a modern (2026-07-28) POST that omits the required `MCP-Protocol-Version` header. + + `createMcpHandler` accepted a request whose body carried a valid per-request `_meta` + envelope but whose `MCP-Protocol-Version` header was absent: the request was classified + modern, dispatched, and answered `200` — tool handlers ran. Only the _mismatch_ case + (header present, disagreeing with the body) was rejected, so of the standard headers + SEP-2243 requires on a modern POST, presence was enforced for `Mcp-Method` (and for + `Mcp-Name` on the methods that mirror `params.name` / `params.uri`) but not for + `MCP-Protocol-Version`. + + Such a request is now refused with `400 Bad Request` and JSON-RPC `-32020` + (`HeaderMismatch`), matching the shape the sibling missing-header cells already emit and + echoing the request id — per the Streamable HTTP spec, which requires the header on every + POST and lists a missing required standard header as a `HeaderMismatch` failure. The + spec's allowance to treat a header-less request as `2025-03-26` is available only to a + server that also serves pre-2025-06-18 clients, and permits routing it to _legacy_ + handling — never serving it as 2026-07-28; under `legacy: 'reject'` the requirement is + unconditional. + + Era classification is deliberately unchanged and stays body-primary: a proxy that strips + the header still must not change the era, so such a request is still _classified_ modern + and is refused one rung later, at `standard-header-validation` — the same rung that + already answers a missing `Mcp-Method`. Legacy-era traffic is untouched, notifications + are unaffected, body-less `GET` / `DELETE` session operations are method-routed before + any header validation, and stdio serving (which has no HTTP headers) is not involved. + + Clients built with this SDK always send the header, so no first-party client is affected; + hand-rolled clients that omitted it must add it. + +- Updated dependencies []: + - @modelcontextprotocol/core@2.0.1 + ## 2.0.0 ### Minor Changes diff --git a/packages/core-internal/package.json b/packages/core-internal/package.json index 44a4be1cd3..63719b3c2b 100644 --- a/packages/core-internal/package.json +++ b/packages/core-internal/package.json @@ -1,7 +1,7 @@ { "name": "@modelcontextprotocol/core-internal", "private": true, - "version": "2.0.0", + "version": "2.0.1", "description": "Model Context Protocol implementation for TypeScript - Core package", "license": "MIT", "author": "Anthropic, PBC (https://anthropic.com)", diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index d109bed7a6..0f75b3e2ae 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -1,5 +1,7 @@ # @modelcontextprotocol/core +## 2.0.1 + ## 2.0.0 ### Minor Changes diff --git a/packages/core/package.json b/packages/core/package.json index 0e02ff8b1e..520aa1d4b1 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/core", - "version": "2.0.0", + "version": "2.0.1", "description": "Model Context Protocol for TypeScript — public Zod schemas (spec + OAuth/OpenID)", "license": "MIT", "author": "Anthropic, PBC (https://anthropic.com)", diff --git a/packages/server-legacy/CHANGELOG.md b/packages/server-legacy/CHANGELOG.md index b3d95738d1..e0cdee5a64 100644 --- a/packages/server-legacy/CHANGELOG.md +++ b/packages/server-legacy/CHANGELOG.md @@ -1,5 +1,12 @@ # @modelcontextprotocol/server-legacy +## 2.0.1 + +### Patch Changes + +- Updated dependencies []: + - @modelcontextprotocol/core@2.0.1 + ## 2.0.0 ### Minor Changes diff --git a/packages/server-legacy/package.json b/packages/server-legacy/package.json index 9ef13dcff7..4c7ce80d95 100644 --- a/packages/server-legacy/package.json +++ b/packages/server-legacy/package.json @@ -1,7 +1,7 @@ { "name": "@modelcontextprotocol/server-legacy", "private": false, - "version": "2.0.0", + "version": "2.0.1", "description": "Frozen v1 SSE transport and OAuth Authorization Server helpers for the Model Context Protocol TypeScript SDK. Deprecated; use StreamableHTTP and a dedicated OAuth server in production.", "deprecated": "This package is a frozen copy of v1's SSE transport and OAuth Authorization Server helpers for migration purposes only. Use StreamableHTTP from @modelcontextprotocol/server and a dedicated OAuth server in production. Will not receive new features.", "license": "MIT", diff --git a/packages/server/CHANGELOG.md b/packages/server/CHANGELOG.md index c24a3604e5..21f9f8575c 100644 --- a/packages/server/CHANGELOG.md +++ b/packages/server/CHANGELOG.md @@ -1,5 +1,51 @@ # @modelcontextprotocol/server +## 2.0.1 + +### Patch Changes + +- [#2654](https://github.com/modelcontextprotocol/typescript-sdk/pull/2654) [`03842cd`](https://github.com/modelcontextprotocol/typescript-sdk/commit/03842cd9cae9a9b142c77d2fb65e829fc4e03eab) Thanks [@pshah19](https://github.com/pshah19)! - Treat request id `0` as a real id. Two guards tested a `RequestId` for truthiness, so the legal JSON-RPC ids `0` and `''` were read as absent. Id `0` is not a corner case: the outbound request counter is zero-based, so it is the first id every peer assigns, which on the server→client leg is the first `sampling/createMessage`, `elicitation/create`, or `roots/list` a server sends. + - `notifications/cancelled` carrying id `0` was ignored, and the in-flight handler ran to completion with its `AbortSignal` never fired. + - A notification sent with `relatedRequestId: 0` wrongly passed the debounce gate (for methods opted into `debouncedNotificationMethods`). Because the pending set is keyed by method alone, a second such notification in the same tick was silently dropped rather than sent. + + Absent is now the only value that means "no id". + +- [#2668](https://github.com/modelcontextprotocol/typescript-sdk/pull/2668) [`3e90449`](https://github.com/modelcontextprotocol/typescript-sdk/commit/3e90449fd52997da43b79a536d2c19c446603cc7) Thanks [@KKonstantinov](https://github.com/KKonstantinov)! - Stop sending `notifications/cancelled` for the `initialize` handshake. The spec is explicit that a client MUST NOT attempt to cancel its `initialize` request, but the outbound cancel path fired for any in-flight request: aborting the `AbortSignal` passed to `connect()`, or letting the handshake hit its timeout, put a forbidden cancellation on the wire naming the initialize request id. + + The local behaviour is unchanged — the caller's promise still rejects with the same abort/timeout error, and `connect()` still tears the connection down. Only the wire notification is suppressed. Every other method keeps the existing cancellation path. + +- [#2590](https://github.com/modelcontextprotocol/typescript-sdk/pull/2590) [`75dc7ea`](https://github.com/modelcontextprotocol/typescript-sdk/commit/75dc7ea6e2913e1ac37d4f06eec62cd5cfac9e7a) Thanks [@davidpavlovschi](https://github.com/davidpavlovschi)! - Reject a modern (2026-07-28) POST that omits the required `MCP-Protocol-Version` header. + + `createMcpHandler` accepted a request whose body carried a valid per-request `_meta` + envelope but whose `MCP-Protocol-Version` header was absent: the request was classified + modern, dispatched, and answered `200` — tool handlers ran. Only the _mismatch_ case + (header present, disagreeing with the body) was rejected, so of the standard headers + SEP-2243 requires on a modern POST, presence was enforced for `Mcp-Method` (and for + `Mcp-Name` on the methods that mirror `params.name` / `params.uri`) but not for + `MCP-Protocol-Version`. + + Such a request is now refused with `400 Bad Request` and JSON-RPC `-32020` + (`HeaderMismatch`), matching the shape the sibling missing-header cells already emit and + echoing the request id — per the Streamable HTTP spec, which requires the header on every + POST and lists a missing required standard header as a `HeaderMismatch` failure. The + spec's allowance to treat a header-less request as `2025-03-26` is available only to a + server that also serves pre-2025-06-18 clients, and permits routing it to _legacy_ + handling — never serving it as 2026-07-28; under `legacy: 'reject'` the requirement is + unconditional. + + Era classification is deliberately unchanged and stays body-primary: a proxy that strips + the header still must not change the era, so such a request is still _classified_ modern + and is refused one rung later, at `standard-header-validation` — the same rung that + already answers a missing `Mcp-Method`. Legacy-era traffic is untouched, notifications + are unaffected, body-less `GET` / `DELETE` session operations are method-routed before + any header validation, and stdio serving (which has no HTTP headers) is not involved. + + Clients built with this SDK always send the header, so no first-party client is affected; + hand-rolled clients that omitted it must add it. + +- Updated dependencies []: + - @modelcontextprotocol/core@2.0.1 + ## 2.0.0 ### Minor Changes diff --git a/packages/server/package.json b/packages/server/package.json index f481e019e7..44d1ada738 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/server", - "version": "2.0.0", + "version": "2.0.1", "description": "Model Context Protocol implementation for TypeScript - Server package", "license": "MIT", "author": "Anthropic, PBC (https://anthropic.com)",