diff --git a/.changeset/abort-handlers-on-close.md b/.changeset/abort-handlers-on-close.md deleted file mode 100644 index c09d8b5aac..0000000000 --- a/.changeset/abort-handlers-on-close.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': patch ---- - -Abort in-flight request handlers when the connection closes. Previously, request handlers would continue running after the transport disconnected, wasting resources and preventing proper cleanup. Also fixes `InMemoryTransport.close()` firing `onclose` twice on the initiating side. diff --git a/.changeset/add-connect-prior.md b/.changeset/add-connect-prior.md deleted file mode 100644 index add9553e50..0000000000 --- a/.changeset/add-connect-prior.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/client': minor ---- - -Add `connect(transport, { prior: DiscoverResult })` for zero-round-trip reconnect (the gateway / distributed-client pattern). Supplying a previously-obtained `DiscoverResult` skips the `server/discover` probe: on a 2026-era server `connect()` sends nothing on the wire and `callTool()` etc. work immediately. Pair with the new `client.getDiscoverResult()` (populated by the `'auto'`-mode probe, by `client.discover()`, and by `connect({ prior })` itself) — the value round-trips through `JSON.stringify`, so a gateway can probe once, persist the blob, and feed it to every worker. Only reuse a persisted `DiscoverResult` across clients that present the same authorization context as the client that obtained it. diff --git a/.changeset/add-consumer-sse-e2e.md b/.changeset/add-consumer-sse-e2e.md deleted file mode 100644 index 76f256aad8..0000000000 --- a/.changeset/add-consumer-sse-e2e.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/test-e2e': patch ---- - -Add consumer-sourced e2e requirements (behaviors real SDK dependents rely on) and run the interaction matrix over the legacy HTTP+SSE transport, with known failures recording where v2 intentionally differs. diff --git a/.changeset/add-core-public-package.md b/.changeset/add-core-public-package.md deleted file mode 100644 index 23cb56cb21..0000000000 --- a/.changeset/add-core-public-package.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/core': minor ---- - -Add `@modelcontextprotocol/core`: the public home for the MCP specification and OAuth/OpenID Zod schemas. It bundles the SDK's internal schema definitions and re-exports only the `*Schema` values, so consumers can validate protocol payloads (`Schema.parse(value)` / `.safeParse(value)`) without depending on a package's internal barrel. Alongside the spec schemas it also re-exports the auth schemas v1 exposed from `@modelcontextprotocol/sdk/shared/auth.js` (e.g. `OAuthTokensSchema`, `OAuthMetadataSchema`, `IdJagTokenExchangeResponseSchema`). Spec types, error classes, enums, and guards continue to live on `@modelcontextprotocol/server` and `@modelcontextprotocol/client`. diff --git a/.changeset/add-e2e-test-suite.md b/.changeset/add-e2e-test-suite.md deleted file mode 100644 index 8359cea2ae..0000000000 --- a/.changeset/add-e2e-test-suite.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/test-e2e': patch ---- - -Add the end-to-end behavior test suite as a workspace package: a requirements manifest covering protocol-visible SDK behavior across the in-memory, stdio, and Streamable HTTP transports, ported from the v1.x branch and extended with coverage for v2 features. diff --git a/.changeset/add-fastify-middleware.md b/.changeset/add-fastify-middleware.md deleted file mode 100644 index 9a5cfbf3fd..0000000000 --- a/.changeset/add-fastify-middleware.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/fastify': minor ---- - -Add Fastify middleware adapter for MCP servers, following the same pattern as the Express and Hono adapters. diff --git a/.changeset/add-hono-peer-dep.md b/.changeset/add-hono-peer-dep.md deleted file mode 100644 index 25f90bbd83..0000000000 --- a/.changeset/add-hono-peer-dep.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/node': patch ---- - -Add missing `hono` peer dependency to `@modelcontextprotocol/node`. The package already depends on `@hono/node-server` which requires `hono` at runtime, but `hono` was only listed in the workspace root, not as a peer dependency of the package itself. diff --git a/.changeset/add-request-state-codec.md b/.changeset/add-request-state-codec.md deleted file mode 100644 index 1dcc71d748..0000000000 --- a/.changeset/add-request-state-codec.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/server': minor ---- - -Add `createRequestStateCodec({ key, ttlSeconds?, bind? })`, an opt-in HMAC-SHA256 sealing helper for the multi-round-trip `requestState`: `mint` seals a JSON-serializable payload (with TTL and optional context binding) and `verify` drops directly into `ServerOptions.requestState.verify`. WebCrypto-based and runtime-neutral; verification is fail-closed and constant-time. The `ServerOptions.requestState.verify` hook's return type is widened to `unknown | Promise` so the codec's `verify` is directly assignable; the seam captures the hook's resolved value (the decoded payload) and hands it to handlers via the typed `ctx.mcpReq.requestState()` accessor. diff --git a/.changeset/add-resource-size-field.md b/.changeset/add-resource-size-field.md deleted file mode 100644 index 07d06b436d..0000000000 --- a/.changeset/add-resource-size-field.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': patch ---- - -Add missing `size` field to `ResourceSchema` to match the MCP specification diff --git a/.changeset/add-sdk-http-error.md b/.changeset/add-sdk-http-error.md deleted file mode 100644 index 8f591c1b23..0000000000 --- a/.changeset/add-sdk-http-error.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': minor -'@modelcontextprotocol/client': minor ---- - -Add `SdkHttpError` subclass with typed `.status` / `.statusText` accessors for HTTP transport failures. `StreamableHTTPClientTransport` now throws `SdkHttpError` (which extends `SdkError`) for non-OK HTTP responses; `SSEClientTransport` throws `SdkHttpError` for 401-after-reauth (circuit breaker). diff --git a/.changeset/add-server-legacy-package.md b/.changeset/add-server-legacy-package.md deleted file mode 100644 index 881827c0af..0000000000 --- a/.changeset/add-server-legacy-package.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/server-legacy': minor ---- - -Add @modelcontextprotocol/server-legacy package with frozen v1 SSE transport and OAuth Authorization Server helpers for migration from v1 to v2. diff --git a/.changeset/add-version-negotiation-option.md b/.changeset/add-version-negotiation-option.md deleted file mode 100644 index a1433b61a4..0000000000 --- a/.changeset/add-version-negotiation-option.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -'@modelcontextprotocol/client': minor -'@modelcontextprotocol/core-internal': minor ---- - -Add opt-in protocol version negotiation on `ClientOptions.versionNegotiation`. The default is unchanged: without the option (or with `mode: 'legacy'`) the client performs today's 2025 connect sequence byte-identically. `mode: 'auto'` probes the server with `server/discover` at -connect time and conservatively falls back to the plain legacy `initialize` handshake on the same connection unless the outcome is definitive modern evidence (with a supported-versions list that has no 2025-era entry there is nothing to fall back to, and connect rejects -with a typed error instead); a network outage rejects with a typed connect error, and a probe timeout is transport-aware — on stdio it indicates -a legacy server and falls back to `initialize` on the same stream, on HTTP it rejects with a typed timeout error. -`mode: { pin: '' }` negotiates exactly the pinned modern revision with no fallback. Probe policy lives under `probe: { timeoutMs? }` — the probe inherits the standard request timeout. The probe's `MCP-Protocol-Version`/`Mcp-Method` headers derive from the probe -message body; the transport version slot is never touched during negotiation, so legacy-era traffic carries zero 2026 headers by construction. Adds the `SdkErrorCode.EraNegotiationFailed` code for negotiation-phase connect failures. diff --git a/.changeset/auth-dcr-hygiene.md b/.changeset/auth-dcr-hygiene.md deleted file mode 100644 index b37808f011..0000000000 --- a/.changeset/auth-dcr-hygiene.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@modelcontextprotocol/client': minor -'@modelcontextprotocol/core-internal': minor ---- - -Dynamic Client Registration hygiene for the 2026-07-28 authorization requirements (SEP-837, SEP-2207). New `resolveClientMetadata(provider)` reads `provider.clientMetadata` and applies the spec defaults — `application_type` derived from the redirect URIs (loopback or custom scheme → `'native'`, otherwise `'web'`), `grant_types: ['authorization_code', 'refresh_token']` when omitted — and `auth()` feeds the resolved document to DCR only (scope selection still reads the raw consumer-supplied `clientMetadata` so statically-registered/CIMD clients are not pushed into `offline_access` + `prompt=consent`); consumer-set values are never overwritten. DCR rejection now throws the new `RegistrationRejectedError` carrying the HTTP status, raw body, and submitted metadata — **breaking for direct `registerClient()` callers**: rejection no longer throws `OAuthError`, so update `instanceof` checks. `OAuthClientMetadata` gains a typed `application_type?: string` field (expected `'native'` / `'web'`; tolerant on parse). `OAuthErrorCode` adds `InvalidRedirectUri`. The token-exchange, refresh, and Cross-App Access (`requestJwtAuthorizationGrant` / `exchangeJwtAuthGrant`) paths now throw the new `InsecureTokenEndpointError` for a non-`https:` token endpoint (`localhost` / `127.0.0.1` / `::1` exempt), and `auth()` surfaces it on the refresh branch instead of silently re-authorizing. diff --git a/.changeset/auth-iss-server-and-overload.md b/.changeset/auth-iss-server-and-overload.md deleted file mode 100644 index 7e07db3f35..0000000000 --- a/.changeset/auth-iss-server-and-overload.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@modelcontextprotocol/client': minor -'@modelcontextprotocol/server-legacy': minor ---- - -SEP-2468 follow-up: `transport.finishAuth()` gains a `URLSearchParams` overload (preferred) that extracts `code`/`iss`, validates `iss` first, and on mismatch throws a sanitized `IssuerMismatchError` (no callback `error_description` text); callers remain responsible for `state`. **Behavior change for `@modelcontextprotocol/server-legacy`:** `mcpAuthRouter` now advertises `authorization_response_iss_parameter_supported` (default `true`; `ProxyOAuthServerProvider` reports `false`) and the bundled authorize handler appends `iss` (RFC 9207) to every `res.redirect(...)` your `OAuthServerProvider.authorize()` issues to the client's `redirect_uri`. If your provider redirects another way (`res.writeHead`, a separate consent-page response, or a standalone `authorizationHandler({provider})` without `issuerUrl`), append `params.issuer` as `iss` yourself or set `authorizationResponseIssParameterSupported: false` — otherwise RFC 9207-compliant clients (including this SDK) will reject the callback. diff --git a/.changeset/auth-iss-validation.md b/.changeset/auth-iss-validation.md deleted file mode 100644 index f6008b6a7a..0000000000 --- a/.changeset/auth-iss-validation.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': minor -'@modelcontextprotocol/client': minor ---- - -Implement RFC 9207 / RFC 8414 §3.3 OAuth issuer validation (SEP-2468). `discoverAuthorizationServerMetadata()` now rejects metadata whose `issuer` does not match the discovery URL (opt out via `skipIssuerValidation` / `AuthOptions.skipIssuerMetadataValidation` — security-weakening). `auth()`, `exchangeAuthorization()`, `fetchToken()`, and `transport.finishAuth(code, iss?)` now validate the authorization-callback `iss` against the recorded issuer before redeeming the code; new `IssuerMismatchError` and `validateAuthorizationResponseIssuer()` are exported. diff --git a/.changeset/auth-sep-2352-credential-isolation.md b/.changeset/auth-sep-2352-credential-isolation.md deleted file mode 100644 index 550d98c52a..0000000000 --- a/.changeset/auth-sep-2352-credential-isolation.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/client': minor ---- - -Per-authorization-server credential isolation (SEP-2352). `auth()` now stamps an `issuer` field onto every value it passes to `saveTokens()` / `saveClientInformation()` and threads `{ issuer }` to `tokens()` / `clientInformation()`; on read, a stored credential whose stamp names a different authorization server is treated as `undefined`, so a `client_id` / `refresh_token` issued by one AS is never sent to another. Providers that round-trip stored values verbatim are protected with no code change; multi-AS providers may key storage on `ctx.issuer`. New `AuthorizationServerMismatchError` (callback-leg gate). `OAuthClientProvider.saveAuthorizationServerUrl()` / `authorizationServerUrl()` are deprecated (still written, never read). `ClientCredentialsProvider`, `PrivateKeyJwtProvider`, `StaticPrivateKeyJwtProvider`, and `CrossAppAccessProvider` gain `expectedIssuer` and no longer define `saveClientInformation()`. diff --git a/.changeset/auth-surface-delta.md b/.changeset/auth-surface-delta.md deleted file mode 100644 index b1908834ca..0000000000 --- a/.changeset/auth-surface-delta.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@modelcontextprotocol/client': minor -'@modelcontextprotocol/core-internal': minor ---- - -Add the public surface for the 2026-07-28 authorization requirements. New `AuthOptions` type names the `auth()` options object and adds `iss` and `skipIssuerMetadataValidation` fields. `OAuthClientProvider.clientInformation()` / `.saveClientInformation()` / `.tokens()` / `.saveTokens()` accept an optional `OAuthClientInformationContext` carrying the authorization server's `issuer` so providers can key persisted credentials per authorization server. New `StoredOAuthTokens` / `StoredOAuthClientInformation` aliases add an `issuer` stamp field on top of the wire types (kept off the wire schemas so an authorization server cannot populate it) and become the parameter/return types of the credential methods. New `OAuthClientFlowError` base class in `authErrors.ts` for the flow-specific error classes that follow. All changes are additive — existing `OAuthClientProvider` implementations compile unchanged; the new fields are inert until the behavior changes that follow wire them up. diff --git a/.changeset/bound-resumability-version-gates.md b/.changeset/bound-resumability-version-gates.md deleted file mode 100644 index edd8dcd24d..0000000000 --- a/.changeset/bound-resumability-version-gates.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@modelcontextprotocol/server': patch ---- - -Bound the protocol-version checks gating SSE resumability behavior (priming events and `closeSSEStream` callbacks) in the Streamable HTTP server transport. Previously an open-ended `>= '2025-11-25'` comparison let unknown future protocol version strings from an `initialize` -request body enable this behavior; the version must now also be one of the transport's supported protocol versions. Behavior for all currently supported protocol versions is unchanged. diff --git a/.changeset/brave-lions-glow.md b/.changeset/brave-lions-glow.md deleted file mode 100644 index 5871838994..0000000000 --- a/.changeset/brave-lions-glow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/node': patch ---- - -Prevent Hono from overriding global Response object by passing `overrideGlobalObjects: false` to `getRequestListener()`. This fixes compatibility with frameworks like Next.js whose response classes extend the native Response. diff --git a/.changeset/busy-rice-smoke.md b/.changeset/busy-rice-smoke.md deleted file mode 100644 index 69badd88cf..0000000000 --- a/.changeset/busy-rice-smoke.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@modelcontextprotocol/client': patch -'@modelcontextprotocol/server': patch ---- - -tasks - disallow requesting a null TTL diff --git a/.changeset/busy-weeks-hang.md b/.changeset/busy-weeks-hang.md deleted file mode 100644 index 0a8801eb37..0000000000 --- a/.changeset/busy-weeks-hang.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': patch -'@modelcontextprotocol/server': patch ---- - -Fix ReDoS vulnerability in UriTemplate regex patterns (CVE-2026-0621) diff --git a/.changeset/cacheable-result-cache-fields.md b/.changeset/cacheable-result-cache-fields.md deleted file mode 100644 index 67c0c0a207..0000000000 --- a/.changeset/cacheable-result-cache-fields.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': minor -'@modelcontextprotocol/server': minor ---- - -Results of the cacheable 2026-07-28 operations (`tools/list`, `prompts/list`, `resources/list`, `resources/templates/list`, `resources/read`, `server/discover`) now always carry the revision's required `ttlMs`/`cacheScope` fields when served on that revision, defaulting to `ttlMs: 0` / `cacheScope: 'private'`. Servers can configure the emitted values with the new `ServerOptions.cacheHints` option (per operation) and the new `cacheHint` member of the `registerResource` config (per resource); resolution is per field, most specific author first: cache fields returned by a handler win over the per-resource hint, which wins over the per-operation hint, and configured hints are validated at construction/registration time (`RangeError` on invalid values). Responses on 2025-era connections are unchanged and never carry these fields. Note for untyped callers: `registerResource` now interprets a `cacheHint` key in its config object — it is validated and kept out of the resource's list metadata, where it was previously passed through as ordinary metadata. diff --git a/.changeset/cfworker-out-of-barrel.md b/.changeset/cfworker-out-of-barrel.md deleted file mode 100644 index f9e366acfe..0000000000 --- a/.changeset/cfworker-out-of-barrel.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@modelcontextprotocol/server': patch -'@modelcontextprotocol/client': patch ---- - -Stop bundling `@cfworker/json-schema` into the main package barrel. Previously `CfWorkerJsonSchemaValidator` was re-exported from the core internal barrel, so tsdown inlined the `@cfworker/json-schema` dependency into every consumer's bundle even when it was never used. The named validator classes are now reachable only via the explicit `@modelcontextprotocol/{client,server}/validators/{ajv,cf-worker}` subpaths and the runtime `_shims` conditional, so consumers that import only from the root entry point no longer ship the validator dep. diff --git a/.changeset/client-honor-cache-hints.md b/.changeset/client-honor-cache-hints.md deleted file mode 100644 index 2a7659db63..0000000000 --- a/.changeset/client-honor-cache-hints.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@modelcontextprotocol/client': minor ---- - -`Client` now **honours** the server-stamped SEP-2549 `ttlMs`/`cacheScope` cache hints on the cacheable verbs (`listTools()`, `listPrompts()`, `listResources()`, `listResourceTemplates()`, `readResource()`): a still-fresh held entry is served without a round trip. New `CacheableRequestOptions.cacheMode` (`'use'` — the default; `'refresh'` — always fetch and re-store; `'bypass'` — fetch without consulting or writing the cache) gives per-call control. The behaviour is opt-in by hint: a server that sends `ttlMs: 0` (the conservative default this SDK's server stamps) sees byte-identical behaviour — every call fetches. - -Entries are automatically scoped by connected-server identity (derived from `serverInfo` after connect, encoded collision-free via `JSON.stringify`); `ClientOptions.cachePartition` is the opaque per-principal slot for `'private'`-scoped entries — set it to your principal identifier (e.g. the auth subject) when one `responseCacheStore` backs several principals. With the default `''` every entry lives at the connected server's shared partition (the safe single-tenant posture). `ClientOptions.defaultCacheTtlMs` (default `0`) supplies the TTL when a result lacks one (e.g. a legacy-era response); the server-supplied `ttlMs` is clamped at 24 h (`MAX_CACHE_TTL_MS`). The list verbs always store the aggregate (so `callTool`'s mirroring/output-validation index keeps working at any TTL); `readResource` stores only when the resolved TTL is positive. `notifications/resources/updated` evicts the cached `resources/read` body for that URI. `ResponseCacheStore` gained `delete(key)`; `InMemoryResponseCacheStore` is now bounded (`{ maxEntries }`, default 512, oldest-first eviction). New exports: `CacheMode`, `CacheableRequestOptions`, `InMemoryResponseCacheStoreOptions`, `MAX_CACHE_TTL_MS`. diff --git a/.changeset/client-http-stream-close-cancel.md b/.changeset/client-http-stream-close-cancel.md deleted file mode 100644 index bca1c6d19f..0000000000 --- a/.changeset/client-http-stream-close-cancel.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': minor -'@modelcontextprotocol/client': minor ---- - -Client request cancellation on a 2026-07-28 Streamable HTTP connection now closes that request's SSE response stream — the spec cancellation signal — instead of POSTing `notifications/cancelled`. Cancellation on a 2025-era connection, and on stdio at any era, still sends `notifications/cancelled` as before. Adds the optional `Transport.hasPerRequestStream` capability flag (set on `StreamableHTTPClientTransport`) for the protocol layer to route the per-transport cancel path. diff --git a/.changeset/client-modern-era-inbound-drop.md b/.changeset/client-modern-era-inbound-drop.md deleted file mode 100644 index 846bcd0581..0000000000 --- a/.changeset/client-modern-era-inbound-drop.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@modelcontextprotocol/client': patch ---- - -Drop inbound JSON-RPC requests on connections that negotiated the 2026-07-28 draft revision instead of answering them: the modern era has no server→client request channel (server-initiated interactions are carried in `input_required` results), and the stdio transport forbids the -client from writing JSON-RPC responses. Dropped requests are surfaced via `onerror`. Legacy-era connections, responses, and notifications are unchanged. diff --git a/.changeset/client-response-cache-substrate.md b/.changeset/client-response-cache-substrate.md deleted file mode 100644 index fc24016dc6..0000000000 --- a/.changeset/client-response-cache-substrate.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@modelcontextprotocol/client': major ---- - -`Client.listTools()` / `listPrompts()` / `listResources()` / `listResourceTemplates()` now **auto-aggregate every page** when called without a `cursor` and return the complete result with `nextCursor: undefined` (matching the C#, Java, and mcp.d SDKs). Pass an explicit `{ cursor }` string to fetch a single page; the per-page path is unchanged. Existing manual pagination loops keep working — the first iteration returns everything and the loop exits — but can be deleted. The aggregated result is written to the new pluggable `ResponseCacheStore` (default: a fresh per-instance `InMemoryResponseCacheStore`); a `ClientResponseCache` collaborator owns the eviction-generation guard and the derived `tools/list` index that `callTool`'s output validation and SEP-2243 `Mcp-Param-*` mirroring read. New exports: `ResponseCacheStore`, `CacheKey`, `CacheEntry`, `CacheScope`, `MaybePromise`, `InMemoryResponseCacheStore`; new `ClientOptions.responseCacheStore` / `ClientOptions.listMaxPages` (caps the auto-aggregate walk at 64 pages by default; throws `SdkError` with `SdkErrorCode.ListPaginationExceeded` on overrun so a partial aggregate is never cached). The store interface is async-ready (`MaybePromise<…>`); the in-memory default stays synchronous. Entries are automatically scoped by the connected server's identity and (when set) the consumer-supplied `cachePartition`, so a shared store does not collide across servers or principals; evictions are likewise scoped to the connected server's partitions. - -**Behavior change (every era):** output-schema validator compilation is now lazy — validators are compiled on the first `callTool()` against the cached `tools/list` entry, not eagerly inside `listTools()`. `listTools()` no longer throws on an uncompilable `outputSchema` (every tool stays listed; the compile failure is captured per-tool); calling `callTool()` on the affected tool throws `ProtocolError(InvalidParams, "Tool 'X' has an invalid outputSchema: …")` before the request is sent — output-schema validation is never silently skipped. A pluggable `jsonSchemaValidator` provider therefore observes compilation at `callTool` time, not `listTools` time. The legacy-era `listTools()` path is unchanged at the wire level but is observably different at the validator-lifecycle level. diff --git a/.changeset/codec-era-gates.md b/.changeset/codec-era-gates.md deleted file mode 100644 index e3bb78c5ec..0000000000 --- a/.changeset/codec-era-gates.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': minor -'@modelcontextprotocol/client': minor -'@modelcontextprotocol/server': minor ---- - -Add `SdkErrorCode.MethodNotSupportedByProtocolVersion`: a typed local error raised before anything reaches the transport when a spec method is sent toward a peer whose negotiated protocol version's wire era does not define it (for example `tasks/get` toward a 2026-07-28 peer). The protocol layer now resolves a per-era wire codec from the connection's negotiated protocol version (instance state on `Client`/`Server`, with the legacy era as the pre-negotiation default) and resolves per-method schemas at dispatch time instead of registration time; an edge classification on an inbound message is validated against that instance era, and a mismatch is rejected as an entry/routing error. Behavior on existing (2025-era) connections is unchanged. diff --git a/.changeset/codec-split-wire-break.md b/.changeset/codec-split-wire-break.md deleted file mode 100644 index 3f24d5de30..0000000000 --- a/.changeset/codec-split-wire-break.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': major -'@modelcontextprotocol/client': major -'@modelcontextprotocol/server': major ---- - -Split the wire layer into per-era codecs and make protocol-revision deletions physical. Deliberate wire/schema behavior changes (see docs/migration/support-2026-07-28.md "Per-era wire codecs"): - -- `resultType` is no longer modeled by any neutral wire schema: `EmptyResultSchema` (strict) now rejects `{resultType}` bodies; on 2025-era connections a foreign `resultType` is stripped before validation instead of rejected; the member exists only inside the 2026-era codec, which requires it. -- `CallToolResult.content` / `ToolResultContent.content` are required at the wire boundary (`content.default([])` removed): handler results without `content` are rejected with `-32602` instead of silently defaulted, and content-less wire results fail the client parse loudly. -- Custom (3-arg) handlers now receive `_meta` minus the reserved envelope keys instead of having it deleted before params validation. -- `specTypeSchemas` re-scoped to the neutral model: result validators no longer accept `resultType`; task message-type validators and `RequestMetaEnvelope` left the public set (`SpecTypeName` narrowed). -- Role aggregate types/schemas (`ClientRequest`, `ServerResult`, …) no longer carry task vocabulary; the deprecated `Task*` types remain importable unchanged. -- Era-mismatched spec methods fail physically: inbound era-deleted methods get `-32601` even with a handler registered; outbound sends throw `SdkErrorCode.MethodNotSupportedByProtocolVersion` locally. -- Value guards (`isCallToolResult`, …) are documented as neutral-shape consumer checks, not wire validators. diff --git a/.changeset/codemod-backlog-batch.md b/.changeset/codemod-backlog-batch.md deleted file mode 100644 index fa0929f6f5..0000000000 --- a/.changeset/codemod-backlog-batch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/codemod': patch ---- - -Backlog fixes. When the zod import injection fires in a package that declares no zod, the manifest pass adds it (devDependencies when only tests import it) so strict node_modules layouts install cleanly. The ErrorCode-split pairing re-points stale `as ProtocolError`/`as McpError` casts bound to subjects whose assertions it moves to `SdkError`. Handler registration resolves one same-file variable hop (`const S = ListToolsRequestSchema`) before declaring a schema custom. Shorthand and aliased destructures of SDK dynamic imports rename with the static-import pass. Call-shape assertions pinning a registration schema (`expect.objectContaining({ inputSchema: … })`) get an advisory. The guide covers dist-text pins (no CJS-resolvable subpaths, content-hashed chunks, changed quote style, ESM-only output). diff --git a/.changeset/codemod-bigpatient-batch.md b/.changeset/codemod-bigpatient-batch.md deleted file mode 100644 index 0d9173e417..0000000000 --- a/.changeset/codemod-bigpatient-batch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/codemod': patch ---- - -Fixes from migrating two large consumers. Registrations nested inside another handler's body no longer crash the transform with a whole-file rollback (calls process inner-first). Legacy `.tool()`/`.prompt()`/`.resource()` calls migrate without a direct `McpServer` import when their shape matches the v1 signature AND the receiver is named like an MCP server (`server`, `harness.mcp`, `this.mockServer`); other receivers are left alone, without hard markers, since their type is unknown to the codemod. `setRequestHandler`/`setNotificationHandler` with a schema _expression_ first argument get a marker pointing at the typed two-argument or custom three-argument form instead of being skipped silently, and `removeRequestHandler`/`removeNotificationHandler` with `Schema.shape.method.value` arguments rewrite to the method string. Destructured trailing callback parameters only count as the context when their keys look like context members, so template-variable destructures stop collecting false markers. The manifest zod note only appears for manifests that actually take part in the migration. diff --git a/.changeset/codemod-completable-protocol.md b/.changeset/codemod-completable-protocol.md deleted file mode 100644 index 6b140df0ad..0000000000 --- a/.changeset/codemod-completable-protocol.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/codemod': minor ---- - -Three migration-sweep fixes. `completable()` calls whose first argument is an optional-wrapped schema are rewritten to apply `.optional()` to the `completable(...)` result — v2 resolves completion metadata after unwrapping an outer optional wrapper, so the v1 nesting produced empty completion lists without an error; wrapper shapes the codemod cannot invert get an action-required marker instead. Imports of `Protocol` and `mergeCapabilities` from v1's `shared/protocol.js` are no longer rewritten to a member the v2 packages do not export: the symbols are dropped from the rewritten import and an action-required marker explains the replacement (`fallbackRequestHandler` for unrouted inbound requests; a plain object spread for capability merging). The manifest zod-range warning now describes the symptom by vintage — zod 4.0–4.1 ranges fail type-checking (TS2769), while zod-3 ranges fail type-checking or at the first `tools/list` depending on the imported zod entry point. diff --git a/.changeset/codemod-context-and-status.md b/.changeset/codemod-context-and-status.md deleted file mode 100644 index 8c7722f378..0000000000 --- a/.changeset/codemod-context-and-status.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/codemod': patch ---- - -Two long-standing intervention classes now migrate mechanically. `.code` reads on values an `instanceof SdkHttpError` check proves — in the same condition or the guarded block — rewrite to `.status` (v2 carries the HTTP status there; `.code` is an `SdkErrorCode` string); unprovable reads keep the existing warning. The context-property remap reaches three shapes the call-site scan missed: functions assigned to `fallbackRequestHandler`, parameters annotated with a context type directly or via a same-file alias (accesses remap in place, the parameter keeps its name), and contexts forwarded wholesale to helpers, which get an advisory naming the callee. diff --git a/.changeset/codemod-core-routing.md b/.changeset/codemod-core-routing.md deleted file mode 100644 index 624a847238..0000000000 --- a/.changeset/codemod-core-routing.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/codemod': minor ---- - -Route v1 `@modelcontextprotocol/sdk/types.js` schema imports to the new `@modelcontextprotocol/core` package. The `*Schema` Zod constants now migrate as a behavior-preserving import-path swap — `Schema.parse(value)` / `.safeParse(value)` keep working — while spec types, error classes, enums, and guards continue to resolve to `@modelcontextprotocol/client` / `@modelcontextprotocol/server` by context. A single `import { CallToolResult, CallToolResultSchema } from '.../types.js'` is split accordingly. The v1 OAuth/OpenID `*Schema` constants imported from `@modelcontextprotocol/sdk/shared/auth.js` are routed to `@modelcontextprotocol/core` the same way (their auth TYPES keep resolving to `client` / `server`). The previous `specSchemaAccess` transform (which rewrote `.parse()` into `specTypeSchemas.X['~standard'].validate(...)`) is removed. diff --git a/.changeset/codemod-flag-removed-task-options.md b/.changeset/codemod-flag-removed-task-options.md deleted file mode 100644 index 7eec3cf127..0000000000 --- a/.changeset/codemod-flag-removed-task-options.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/codemod': patch ---- - -The v1→v2 codemod no longer rewrites `taskStore`/`taskMessageQueue` McpServer constructor options into `capabilities.tasks` — that target does not exist in v2 (the experimental tasks runtime was removed, SEP-2663). The codemod now leaves the code untouched and emits an action-required diagnostic telling migrators to remove the option, matching the removal guidance already given for `experimental/tasks` imports and the migration guide. diff --git a/.changeset/codemod-infer-project-type.md b/.changeset/codemod-infer-project-type.md deleted file mode 100644 index 1fa114c775..0000000000 --- a/.changeset/codemod-infer-project-type.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/codemod': patch ---- - -Infer client/server project type from source for v1 projects. A project being migrated still declares the single v1 `@modelcontextprotocol/sdk` dependency, so detecting the project type from `package.json` came back "unknown" and every file importing only shared protocol symbols defaulted to `@modelcontextprotocol/server` with an action-required warning. The codemod now scans the source for quoted `@modelcontextprotocol/sdk/client/…` and `…/server/…` import specifiers to infer the type (both → "both", one → that side, neither → "unknown"), routing shared symbols to the installed package and replacing the spurious warnings with at most an info note for genuinely ambiguous "both" projects. diff --git a/.changeset/codemod-longtail-batch.md b/.changeset/codemod-longtail-batch.md deleted file mode 100644 index f36b0fb5f4..0000000000 --- a/.changeset/codemod-longtail-batch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/codemod': minor ---- - -Migration-sweep batch. The ErrorCode split now coordinates with the surrounding check: an all-SDK condition's `instanceof ProtocolError`/`McpError` guard is rewritten to `SdkError`, a guard covering both enums gets a marker asking for a split, and an `ErrorCode` import with no rewritable member access on a v2 specifier is dropped with a marker instead of failing at module link time. Wrapping a raw shape with `z.object()` adds `import { z } from 'zod'` when the file has no `z` value binding (a non-import `z` binding gets a marker instead). The context-parameter rewrite finds the trailing `extra` parameter, covering the three-argument `registerResource` template callback without flagging its `variables` argument. Resource-server auth helpers routed to the frozen server-legacy copy get a marker on value imports and barrel re-exports (an info note for type-only imports), every rewritten `SdkHttpError` constructor site gets a marker, and single-argument `finishAuth(...)` calls in files the run changes get a run-log note (the one-argument `URLSearchParams` form is valid v2, so the note never re-fires on already-migrated trees). The codemod accepts a single source file as target — source rewrites scope to that file and manifest changes are reported, not applied — and the no-changes summary distinguishes "already on the v2 packages", "still on the v1 SDK under a transform subset", and "no MCP SDK imports found". diff --git a/.changeset/codemod-manifest-handling.md b/.changeset/codemod-manifest-handling.md deleted file mode 100644 index 0466916b4d..0000000000 --- a/.changeset/codemod-manifest-handling.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/codemod': minor ---- - -Overhaul manifest handling. The codemod now discovers workspace-member manifests (npm/yarn/bun `workspaces` and `pnpm-workspace.yaml`), writes only the nearest `package.json`, and reports the exact dependency changes every other affected manifest needs, so you can apply them deliberately. The v2 additions are computed from the post-transform import state of the files each manifest owns, so already-migrated packages still receive the packages their imports need when the v1 dependency is removed; in hoisted monorepos, member usage counts toward the manifest that declares the SDK dependency, with a note naming the contributing members. File collection no longer follows symbolic links (pnpm `node_modules` layouts contain cycles that previously aborted the run) and honors `--ignore` patterns during directory descent. Manifests whose `zod` range cannot satisfy the v2 floor get a warning describing the runtime failure mode. `RunnerResult.packageJsonChanges` is now an array of per-manifest changes with optional `warnings` and `notes`. diff --git a/.changeset/codemod-preserve-shebang.md b/.changeset/codemod-preserve-shebang.md deleted file mode 100644 index 4ce0d6b343..0000000000 --- a/.changeset/codemod-preserve-shebang.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/codemod': patch ---- - -Preserve a leading `#!` shebang (and the blank lines after it) when migrating a file. Some transforms drop the shebang because it is leading trivia of the first import they rewrite; the codemod now captures it before transforms and restores it before saving, so CLI packages whose `bin` points at the migrated entry keep working. diff --git a/.changeset/codemod-resolve-legacy-imports.md b/.changeset/codemod-resolve-legacy-imports.md deleted file mode 100644 index 45bfbe7260..0000000000 --- a/.changeset/codemod-resolve-legacy-imports.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/codemod': minor ---- - -Codemod now resolves SSE server and OAuth auth imports to @modelcontextprotocol/server-legacy sub-paths instead of removing them. An info diagnostic suggests eventual migration to v2 equivalents. diff --git a/.changeset/codemod-schema-drop-receivers.md b/.changeset/codemod-schema-drop-receivers.md deleted file mode 100644 index e1a1ff2aa5..0000000000 --- a/.changeset/codemod-schema-drop-receivers.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/codemod': patch ---- - -The explicit-`undefined` result-schema removal now requires the same proof as the schema-identifier path: `request()` calls must carry a provably literal spec method, and `callTool()` calls whose first argument is a primitive are left alone. Previously, any file importing an MCP package could have the middle argument deleted from an unrelated `.request()` / `.callTool()` member call on a non-SDK receiver (e.g. a bespoke `end.request('ping', undefined, id)` helper), corrupting the call. diff --git a/.changeset/codemod-streamablehttperror-sdkhttperror.md b/.changeset/codemod-streamablehttperror-sdkhttperror.md deleted file mode 100644 index 6f9585dc51..0000000000 --- a/.changeset/codemod-streamablehttperror-sdkhttperror.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/codemod': patch ---- - -The v1→v2 codemod now migrates the removed `StreamableHTTPError` to `SdkHttpError` (instead of the base `SdkError`), matching the shipped error type and the migration guide. Diagnostics now point at the typed `error.status` / `error.statusText` accessors and note that unexpected-content-type responses are thrown as the base `SdkError`. diff --git a/.changeset/codemod-task-handler-methods.md b/.changeset/codemod-task-handler-methods.md deleted file mode 100644 index 140805338f..0000000000 --- a/.changeset/codemod-task-handler-methods.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/codemod': patch ---- - -Emit a dedicated action-required diagnostic for v1 task-handler registrations (`setRequestHandler(GetTaskRequestSchema, …)`, `setNotificationHandler(TaskStatusNotificationSchema, …)`, and the other `tasks/*` schemas). The experimental tasks feature was removed in v2 (SEP-2663) and the `tasks/*` method strings are excluded from the typed `RequestMethod` / `NotificationMethod` surface, so these registrations are **not** rewritten to method-string form — the codemod marks each site with an `@mcp-codemod-error` comment pointing at the migration guide's tasks-removed section instead. diff --git a/.changeset/codemod-v1-to-v2-gaps.md b/.changeset/codemod-v1-to-v2-gaps.md deleted file mode 100644 index d712b55cf2..0000000000 --- a/.changeset/codemod-v1-to-v2-gaps.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/codemod': patch ---- - -v1-to-v2: now wraps `outputSchema` raw shapes with `z.object()`; importMap covers `sdk/server/express.js`, `sdk/server/middleware/hostHeaderValidation.js`, and `sdk/client/auth-extensions.js`. The unreachable `expressMiddleware` transform is removed. diff --git a/.changeset/core-subscription-schemas.md b/.changeset/core-subscription-schemas.md deleted file mode 100644 index d4cc1cea3d..0000000000 --- a/.changeset/core-subscription-schemas.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -'@modelcontextprotocol/core': major ---- - -Align the published schema set with the 2026-07-28 surface: removes the -`RequestMetaEnvelopeSchema` export; adds `SubscriptionFilterSchema`, the -`SubscriptionsListen*` request/result schemas, and the -`SubscriptionsAcknowledged*` notification schemas. The bundled schemas now -match `@modelcontextprotocol/client` and `@modelcontextprotocol/server` of -the same release. diff --git a/.changeset/create-mcp-handler-legacy-revision.md b/.changeset/create-mcp-handler-legacy-revision.md deleted file mode 100644 index 4212856377..0000000000 --- a/.changeset/create-mcp-handler-legacy-revision.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'@modelcontextprotocol/server': minor ---- - -Revise `createMcpHandler`'s legacy handling (a behavior change to the unreleased entry). The entry now serves 2025-era (non-envelope) traffic **by default** through per-request stateless serving from the same factory — `legacy: 'stateless'` is the default rather than an -opt-in — and the strict, modern-only posture is selected with the new `legacy: 'reject'` value (the earlier alpha's default). The handler-valued `legacy` option (bring-your-own legacy serving) is removed: existing legacy deployments (for example a sessionful streamable -HTTP wiring) keep serving 2025 traffic by routing in user land with the new `isLegacyRequest(request, parsedBody?)` export, which runs the entry's own classification step — it returns `true` only for requests with no per-request `_meta` envelope claim, while malformed or -incomplete modern claims are NOT legacy and must be routed to the modern handler, which answers them with the documented validation errors. The predicate classifies a clone, so the routed request body stays readable. `legacyStatelessFallback` remains exported as a -standalone fetch-shaped handler with the same stateless serving as the default. diff --git a/.changeset/create-mcp-handler.md b/.changeset/create-mcp-handler.md deleted file mode 100644 index 83906108be..0000000000 --- a/.changeset/create-mcp-handler.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -'@modelcontextprotocol/server': minor ---- - -Add `createMcpHandler(factory, { legacy?, onerror?, responseMode? })`, an HTTP entry point that serves the 2026-07-28 draft revision per request: each envelope-carrying request is classified once, served on a fresh instance from the factory bound to the claimed revision, -and answered with a JSON body or a lazily-upgraded SSE stream. 2025-era serving is selected with the `legacy` option (`'stateless'` — the default — for per-request stateless serving via the existing streamable HTTP transport, `'reject'` for a modern-only strict endpoint -that answers 2025-era requests with the unsupported-protocol-version error naming its supported revisions). The handler is a web-standard `{ fetch, close, notify, bus }` object: `fetch(request, { authInfo?, parsedBody? })` is the only request face (Node frameworks wrap it with -`toNodeHandler(handler)` from `@modelcontextprotocol/node`), and `close()` tears down in-flight modern exchanges. Also exported: `legacyStatelessFallback` (the same stateless legacy serving as a standalone fetch-shaped handler), the `PerRequestHTTPServerTransport` single-exchange transport and the -`classifyInboundRequest` classifier for hand-wired compositions, and the supporting types. `responseMode: 'json'` never streams and drops mid-call notifications (progress, logging and other related messages emitted before the result); listen-class subscription streams are -always served over SSE. The entry performs no Origin/Host validation (use the middleware packages) and no token verification — `authInfo` is pass-through and never derived from request headers. diff --git a/.changeset/custom-methods-minimal.md b/.changeset/custom-methods-minimal.md deleted file mode 100644 index 103332b166..0000000000 --- a/.changeset/custom-methods-minimal.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': minor -'@modelcontextprotocol/client': minor -'@modelcontextprotocol/server': minor ---- - -Add custom (non-spec) method support: a 3-arg `setRequestHandler(method, schemas, handler)` / `setNotificationHandler(method, schemas, handler)` form for vendor-prefixed methods, and a `request(req, resultSchema)` overload (also on `ctx.mcpReq.send`) for typed custom-method results. Spec-method calls are unchanged. - -Response result-schema validation failure now rejects with `SdkError(InvalidResult)` instead of a raw `ZodError`. Adds `SdkErrorCode.InvalidResult`. diff --git a/.changeset/cyan-cycles-pump.md b/.changeset/cyan-cycles-pump.md deleted file mode 100644 index 0f2008a3a1..0000000000 --- a/.changeset/cyan-cycles-pump.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/server': patch ---- - -missing change for fix(client): replace body.cancel() with text() to prevent hanging diff --git a/.changeset/deprecate-client-identity-accessors.md b/.changeset/deprecate-client-identity-accessors.md deleted file mode 100644 index 8b73104076..0000000000 --- a/.changeset/deprecate-client-identity-accessors.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@modelcontextprotocol/server': patch ---- - -Deprecate `Server.getClientCapabilities()`, `Server.getClientVersion()` and `Server.getNegotiatedProtocolVersion()` in favor of the per-request handler context: on 2026-07-28 requests the validated `_meta` envelope carries the client's identity (`ctx.mcpReq.envelope`), -and instances serving that revision through `createMcpHandler` are backfilled per request so the accessors keep answering. Behavior on 2025-era connections is unchanged; the accessors remain functional. diff --git a/.changeset/dist-types-skiplibcheck.md b/.changeset/dist-types-skiplibcheck.md deleted file mode 100644 index 65bdc0e653..0000000000 --- a/.changeset/dist-types-skiplibcheck.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@modelcontextprotocol/server': patch -'@modelcontextprotocol/client': patch -'@modelcontextprotocol/node': patch ---- - -Fix the published declaration files for consumers compiling with `skipLibCheck: false`: the bundled `.d.mts` no longer leaves a dangling `URIComponent` reference (ajv's published types import it from `fast-uri`, whose export-assigned namespace the dts bundler cannot link — the type is now inlined via a dts-only path mapping), and no longer imports `json-schema-typed` from an undeclared dependency (it is inlined via `dts.resolve`). `@modelcontextprotocol/node` and `@modelcontextprotocol/server` drop stale `typesVersions` entries pointing at subpaths that never shipped. Package READMEs note that TypeScript >=6.0 requires `"types": ["node"]` since the published declarations reference `Buffer`. diff --git a/.changeset/draft-spec-non-sep-conformance.md b/.changeset/draft-spec-non-sep-conformance.md deleted file mode 100644 index d57d1f34d6..0000000000 --- a/.changeset/draft-spec-non-sep-conformance.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'@modelcontextprotocol/server': patch -'@modelcontextprotocol/client': patch ---- - -Non-SEP draft spec conformance fixes - -- `McpServer` now eagerly installs list/read/call handlers for every primitive capability (`tools`, `resources`, `prompts`) declared in `ServerOptions.capabilities`. Per the draft spec, a server that declares a capability MUST respond to its list method (potentially with an empty result) instead of returning "Method not found". Previously, handlers were only installed lazily on first registration, so a server constructed with e.g. `capabilities: { tools: {} }` and zero registered tools answered `tools/list` with `-32601`. Low-level `Server` users remain responsible for registering handlers for declared capabilities (documented on `ServerOptions.capabilities`). -- Fixed pagination doc examples on `Client.listTools`/`listPrompts`/`listResources` to loop `while (cursor !== undefined)` instead of `while (cursor)` — per the draft spec, clients MUST NOT treat an empty-string cursor as the end of results. diff --git a/.changeset/drop-zod-peer-dep.md b/.changeset/drop-zod-peer-dep.md deleted file mode 100644 index c5e283274a..0000000000 --- a/.changeset/drop-zod-peer-dep.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'@modelcontextprotocol/client': patch -'@modelcontextprotocol/server': patch ---- - -Drop `zod` from `peerDependencies` (kept as direct dependency) - -Since Standard Schema support landed, `zod` is purely an internal runtime dependency used for protocol message parsing. User-facing schemas (`registerTool`, `registerPrompt`) accept any Standard Schema library. `zod` remains in `dependencies` and auto-installs; users no longer -need to install it alongside the SDK. diff --git a/.changeset/envelope-auto-emission.md b/.changeset/envelope-auto-emission.md deleted file mode 100644 index b8e9caf9d3..0000000000 --- a/.changeset/envelope-auto-emission.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -'@modelcontextprotocol/client': minor -'@modelcontextprotocol/core-internal': minor ---- - -Per-request `_meta` envelope auto-emission on modern-era connections: once a client negotiates a 2026-07-28+ protocol revision (via `versionNegotiation: { mode: 'auto' }` or `{ pin }`), it automatically attaches the reserved protocol-version / client-info / client-capabilities -`_meta` keys to every outgoing request and notification — you no longer set the envelope by hand. User-supplied `_meta` keys take precedence over the auto-attached ones; the auto-attached client-capabilities reflect what the client actually registered. Legacy-era connections -(the default, and the `'auto'`-mode fallback) never gain these keys, so 2025-era outbound traffic is byte-identical to before. - -Adds `Client.getProtocolEra()` (`'legacy' | 'modern' | undefined`), the `ProtocolEra` type, `Client.setVersionNegotiation()` for configuring negotiation pre-connect on an already-constructed instance, and the `probe.maxRetries` knob (default `0`) which governs probe-timeout -re-sends only — the spec-mandated `-32022` corrective continuation is never counted against it. The `versionNegotiation` default remains `'legacy'`: absent (or `mode: 'legacy'`), `connect()` runs the plain 2025 sequence, byte-identical to a v1.x client. diff --git a/.changeset/export-inmemory-transport.md b/.changeset/export-inmemory-transport.md deleted file mode 100644 index cc1d8f7307..0000000000 --- a/.changeset/export-inmemory-transport.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@modelcontextprotocol/server': patch -'@modelcontextprotocol/client': patch ---- - -Export `InMemoryTransport` for in-process testing. diff --git a/.changeset/expose-auth-server-discovery.md b/.changeset/expose-auth-server-discovery.md deleted file mode 100644 index 443dce893d..0000000000 --- a/.changeset/expose-auth-server-discovery.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -'@modelcontextprotocol/client': minor ---- - -Add `discoverOAuthServerInfo()` function and unified discovery state caching for OAuth - -- New `discoverOAuthServerInfo(serverUrl)` export that performs RFC 9728 protected resource metadata discovery followed by authorization server metadata discovery in a single call. Use this for operations like token refresh and revocation that need the authorization server URL outside of `auth()`. -- New `OAuthDiscoveryState` type and optional `OAuthClientProvider` methods `saveDiscoveryState()` / `discoveryState()` allow providers to persist all discovery results (auth server URL, resource metadata URL, resource metadata, auth server metadata) across sessions. This avoids redundant discovery requests and handles browser redirect scenarios where discovery state would otherwise be lost. -- New `'discovery'` scope for `invalidateCredentials()` to clear cached discovery state. -- New `OAuthServerInfo` type exported for the return value of `discoverOAuthServerInfo()`. diff --git a/.changeset/expose-icons-on-tools-and-prompts.md b/.changeset/expose-icons-on-tools-and-prompts.md deleted file mode 100644 index 3a8bb38ce7..0000000000 --- a/.changeset/expose-icons-on-tools-and-prompts.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/server': minor ---- - -Support `icons` on the high-level `McpServer.registerTool()` and `registerPrompt()` config objects (and on their `update()` methods). Tool and prompt icons now surface in `tools/list` and `prompts/list`. Resource, resource-template, and server-info (`Implementation`) icons already passed through. This closes the gap with the MCP spec, which allows `icons` on tools, resources, resource templates, and prompts. diff --git a/.changeset/express-resource-server-auth.md b/.changeset/express-resource-server-auth.md deleted file mode 100644 index a9e16227bd..0000000000 --- a/.changeset/express-resource-server-auth.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/express': minor ---- - -Add OAuth Resource-Server glue to the Express adapter: `requireBearerAuth` middleware (token verification + RFC 6750 `WWW-Authenticate` challenges), `mcpAuthMetadataRouter` (serves RFC 9728 Protected Resource Metadata and mirrors RFC 8414 AS metadata at the resource origin), the `getOAuthProtectedResourceMetadataUrl` helper, and the `OAuthTokenVerifier` interface. These restore the v1 `src/server/auth` Resource-Server pieces as first-class v2 API so MCP servers can plug into an external Authorization Server with a few lines of Express wiring. diff --git a/.changeset/fast-dragons-lead.md b/.changeset/fast-dragons-lead.md deleted file mode 100644 index 731aaa37e6..0000000000 --- a/.changeset/fast-dragons-lead.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -'@modelcontextprotocol/express': patch -'@modelcontextprotocol/fastify': patch -'@modelcontextprotocol/hono': patch -'@modelcontextprotocol/node': patch -'@modelcontextprotocol/client': patch -'@modelcontextprotocol/server': patch ---- - -tsdown exports resolution fix diff --git a/.changeset/finish-sdkerror-capability.md b/.changeset/finish-sdkerror-capability.md deleted file mode 100644 index 8e8b0bca31..0000000000 --- a/.changeset/finish-sdkerror-capability.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': patch -'@modelcontextprotocol/client': patch -'@modelcontextprotocol/server': patch ---- - -Convert remaining capability-assertion throws to `SdkError(SdkErrorCode.CapabilityNotSupported, ...)`. Follow-up to #1454 which missed `Client.assertCapability()`, the task capability helpers in `experimental/tasks/helpers.ts`, and the sampling/elicitation capability checks in `experimental/tasks/server.ts`. diff --git a/.changeset/fix-abort-listener-leak.md b/.changeset/fix-abort-listener-leak.md deleted file mode 100644 index 0a87e559c7..0000000000 --- a/.changeset/fix-abort-listener-leak.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': patch ---- - -Consolidate per-request cleanup in `_requestWithSchema` into a single `.finally()` block. This fixes an abort signal listener leak (listeners accumulated when a caller reused one `AbortSignal` across requests) and two cases where `_responseHandlers` entries leaked on send-failure paths. diff --git a/.changeset/fix-conformance-server-leak.md b/.changeset/fix-conformance-server-leak.md deleted file mode 100644 index 7b655bc475..0000000000 --- a/.changeset/fix-conformance-server-leak.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/test-conformance': patch ---- - -Fix the server conformance script leaking the test server process: the cleanup trap killed the npx wrapper while the actual server kept listening on port 3000, making later runs silently test stale code or hang forever in the readiness loop. The script now spawns the server directly with `node --import tsx`, refuses to start while the port is taken, and bounds each readiness probe; both test servers report `EADDRINUSE` with an actionable message, and the plain `test:conformance:client` script works again (`--suite core`, required since conformance 0.2.0-alpha.1). diff --git a/.changeset/fix-oauth-5xx-discovery.md b/.changeset/fix-oauth-5xx-discovery.md deleted file mode 100644 index 0b2e05ae70..0000000000 --- a/.changeset/fix-oauth-5xx-discovery.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/client': patch ---- - -Continue OAuth metadata discovery on 502 (Bad Gateway) responses, matching the existing behavior for 4xx. This fixes MCP servers behind reverse proxies that return 502 for path-aware metadata URLs. Other 5xx errors still throw to avoid retrying against overloaded servers. diff --git a/.changeset/fix-onerror-callbacks.md b/.changeset/fix-onerror-callbacks.md deleted file mode 100644 index 4ca4e72e4b..0000000000 --- a/.changeset/fix-onerror-callbacks.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/server': patch ---- - -Fix transport errors being silently swallowed by adding missing `onerror` callback invocations before all `createJsonErrorResponse` calls in `WebStandardStreamableHTTPServerTransport`. This ensures errors like parse failures, invalid headers, and session validation errors are properly reported via the `onerror` callback. diff --git a/.changeset/fix-server-protocol-version.md b/.changeset/fix-server-protocol-version.md deleted file mode 100644 index 5344b1429e..0000000000 --- a/.changeset/fix-server-protocol-version.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/server': patch ---- - -fix(server): propagate negotiated protocol version to transport in \_oninitialize diff --git a/.changeset/fix-stdio-epipe-crash.md b/.changeset/fix-stdio-epipe-crash.md deleted file mode 100644 index 456a8c22f8..0000000000 --- a/.changeset/fix-stdio-epipe-crash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/server': patch ---- - -Handle stdout errors (e.g. EPIPE) in `StdioServerTransport` gracefully instead of crashing. When the client disconnects abruptly, the transport now catches the stdout error, surfaces it via `onerror`, and closes. diff --git a/.changeset/fix-stdio-windows-hide.md b/.changeset/fix-stdio-windows-hide.md deleted file mode 100644 index 7b0db2743e..0000000000 --- a/.changeset/fix-stdio-windows-hide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/client': patch ---- - -Always set `windowsHide` when spawning stdio server processes on Windows, not just in Electron environments. Prevents unwanted console windows in non-Electron Windows applications. diff --git a/.changeset/fix-streamable-close-reentrant.md b/.changeset/fix-streamable-close-reentrant.md deleted file mode 100644 index 9f0bd76493..0000000000 --- a/.changeset/fix-streamable-close-reentrant.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/server': patch ---- - -Prevent stack overflow in StreamableHTTPServerTransport.close() with re-entrant guard diff --git a/.changeset/fix-streamable-http-error-response.md b/.changeset/fix-streamable-http-error-response.md deleted file mode 100644 index 1de5839d3f..0000000000 --- a/.changeset/fix-streamable-http-error-response.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/client': patch ---- - -Fix StreamableHTTPClientTransport to handle error responses in SSE streams diff --git a/.changeset/fix-transport-exact-optional-property-types.md b/.changeset/fix-transport-exact-optional-property-types.md deleted file mode 100644 index 57b4d00cd5..0000000000 --- a/.changeset/fix-transport-exact-optional-property-types.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': patch ---- - -Add explicit `| undefined` to optional properties on the `Transport` interface and `TransportSendOptions` (`onclose`, `onerror`, `onmessage`, `sessionId`, `setProtocolVersion`, `setSupportedProtocolVersions`, `onresumptiontoken`). - -This fixes TS2420 errors for consumers using `exactOptionalPropertyTypes: true` without `skipLibCheck`, where the emitted `.d.ts` for implementing classes included `| undefined` but the interface did not. - -Workaround for older SDK versions: enable `skipLibCheck: true` in your tsconfig. diff --git a/.changeset/fix-unknown-tool-protocol-error.md b/.changeset/fix-unknown-tool-protocol-error.md deleted file mode 100644 index e6a0e0d32f..0000000000 --- a/.changeset/fix-unknown-tool-protocol-error.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': minor -'@modelcontextprotocol/server': major ---- - -Fix error handling for unknown tools and resources per MCP spec. - -**Tools:** Unknown or disabled tool calls now return JSON-RPC protocol errors with -code `-32602` (InvalidParams) instead of `CallToolResult` with `isError: true`. -Callers who checked `result.isError` for unknown tools should catch rejected promises instead. - -**Resources:** Added `ProtocolErrorCode.ResourceNotFound` (`-32002`) as receive-tolerated -vocabulary. The wire code emitted for an unknown `resources/read` URI is `-32602` -(Invalid Params) — see the `resource-not-found-32602` changeset. diff --git a/.changeset/fix-validate-client-metadata-url.md b/.changeset/fix-validate-client-metadata-url.md deleted file mode 100644 index a460fca4c9..0000000000 --- a/.changeset/fix-validate-client-metadata-url.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'@modelcontextprotocol/client': minor ---- - -Add `validateClientMetadataUrl()` utility for early validation of `clientMetadataUrl` - -Exports a `validateClientMetadataUrl()` function that `OAuthClientProvider` implementations -can call in their constructors to fail fast on invalid URL-based client IDs, instead of -discovering the error deep in the auth flow. diff --git a/.changeset/funky-baths-attack.md b/.changeset/funky-baths-attack.md deleted file mode 100644 index 8f7e20f73b..0000000000 --- a/.changeset/funky-baths-attack.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@modelcontextprotocol/node': patch -'@modelcontextprotocol/test-integration': patch -'@modelcontextprotocol/server': patch -'@modelcontextprotocol/core-internal': patch ---- - -remove deprecated .tool, .prompt, .resource method signatures diff --git a/.changeset/gentle-planets-rest.md b/.changeset/gentle-planets-rest.md deleted file mode 100644 index 21255a6b6d..0000000000 --- a/.changeset/gentle-planets-rest.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@modelcontextprotocol/server': patch ---- - -Add `| undefined` to optional callback and function properties on `WebStandardStreamableHTTPServerTransportOptions` (`sessionIdGenerator`, `onsessioninitialized`, `onsessionclosed`) and corresponding private fields. - -This fixes TS2430 errors for consumers using `exactOptionalPropertyTypes: true` without `skipLibCheck`, where optional properties with function types need explicit `| undefined` to match their emitted declarations. diff --git a/.changeset/handler-drop-node-face.md b/.changeset/handler-drop-node-face.md deleted file mode 100644 index 6442e14148..0000000000 --- a/.changeset/handler-drop-node-face.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'@modelcontextprotocol/server': major -'@modelcontextprotocol/node': minor ---- - -`createMcpHandler` now returns a web-standards-only `{ fetch, close, notify, bus }` handler — the shape Workers/Bun/Deno expect from `export default`. The duck-typed `.node(req, res, parsedBody?)` face is removed; Node frameworks (Express, Fastify, plain `node:http`) wrap the -handler once with the new `toNodeHandler(handler, { onerror? })` exported from `@modelcontextprotocol/node`, which converts the Node request to a web-standard `Request`, calls `handler.fetch`, and writes the `Response` back honoring write backpressure. The optional `onerror` -receives the adapter-level error fallback (request conversion / `handler.fetch` throw) before the `500` response is written, restoring observability parity with the removed `.node` face. `NodeIncomingMessageLike` and `NodeServerResponseLike` move from -`@modelcontextprotocol/server` to `@modelcontextprotocol/node`. diff --git a/.changeset/heavy-walls-swim.md b/.changeset/heavy-walls-swim.md deleted file mode 100644 index 7a09cda0dc..0000000000 --- a/.changeset/heavy-walls-swim.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/server': patch ---- - -reverting application/json in notifications diff --git a/.changeset/hide-wire-only-members.md b/.changeset/hide-wire-only-members.md deleted file mode 100644 index 082ca73312..0000000000 --- a/.changeset/hide-wire-only-members.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': major -'@modelcontextprotocol/client': major -'@modelcontextprotocol/server': major ---- - -Hide wire-only protocol members from the public surface, at the type level and at runtime. `resultType` (the 2026-07-28 result discrimination field) is no longer declared on any public result type — the wire schemas keep parsing it, and the client funnel now consumes it raw-first: `'complete'` results are stripped to the public shape and any other kind (e.g. `input_required`) rejects with the new `SdkErrorCode.UnsupportedResultType` instead of masking into an empty success. The reserved `_meta` envelope keys are lifted out of inbound requests and notifications before handlers run, and the multi-round-trip retry fields (`inputResponses`, `requestState`) out of inbound requests only (the spec reserves those names on client-initiated requests; notification params keep them), so handler params keep the 2025-era shape; for requests the lifted material surfaces at `ctx.mcpReq.envelope`, `ctx.mcpReq.inputResponses`, and `ctx.mcpReq.requestState` (notifications have no ctx — their lifted envelope keys are not surfaced). High-level client/server methods now return the named public result types (`Promise` etc.). Task wire vocabulary stays importable but is `@deprecated` and excluded from the typed method maps (`RequestMethod`/`RequestTypeMap`/`ResultTypeMap`/`NotificationTypeMap`), and `callTool` is typed as plain `CallToolResult`. See docs/migration/support-2026-07-28.md "Wire-only members hidden from public types". diff --git a/.changeset/hono-peer-optional.md b/.changeset/hono-peer-optional.md deleted file mode 100644 index 2f6a3ef5f1..0000000000 --- a/.changeset/hono-peer-optional.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/node': patch ---- - -Mark `hono` peer dependency as optional. `@modelcontextprotocol/node` only uses `getRequestListener` from `@hono/node-server` (Node HTTP ↔ Web Standard conversion), which does not require the `hono` framework at runtime. Consumers no longer need to install `hono` to use `NodeStreamableHTTPServerTransport`. Note: `@hono/node-server` itself still declares `hono` as a hard peer, so package managers may emit a warning; this is upstream and harmless for `getRequestListener`-only usage. diff --git a/.changeset/idjag-spec-type-export.md b/.changeset/idjag-spec-type-export.md deleted file mode 100644 index 8fb5816036..0000000000 --- a/.changeset/idjag-spec-type-export.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@modelcontextprotocol/client': minor -'@modelcontextprotocol/server': minor ---- - -Add the v2 `IdJagTokenExchangeResponse` type to the public API and register its schema as an MCP spec type. `IdJagTokenExchangeResponse` is now exported from `@modelcontextprotocol/client` and `@modelcontextprotocol/server`, `'IdJagTokenExchangeResponse'` joins the `SpecTypeName` union, and `isSpecType.IdJagTokenExchangeResponse(value)` / `specTypeSchemas.IdJagTokenExchangeResponse` validate it by name — matching how the OAuth/OpenID auth schemas are already exposed. The Zod schema itself, `IdJagTokenExchangeResponseSchema`, is available from `@modelcontextprotocol/core`. diff --git a/.changeset/is-legacy-request-doc-lead.md b/.changeset/is-legacy-request-doc-lead.md deleted file mode 100644 index a3e2c7983e..0000000000 --- a/.changeset/is-legacy-request-doc-lead.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/server': patch ---- - -`isLegacyRequest` docs: lead with the single-argument form. `isLegacyRequest(request)` is the whole API — the body is read from an internal clone, so the request you pass stays readable for whichever handler you route it to. `parsedBody` is an optional perf escape for a body you already hold parsed (and the way in for an already-consumed stream, e.g. behind `express.json()`), not a required companion. Documentation only; no behavior change. diff --git a/.changeset/legacy-input-required-shim.md b/.changeset/legacy-input-required-shim.md deleted file mode 100644 index 61bcb33e12..0000000000 --- a/.changeset/legacy-input-required-shim.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -'@modelcontextprotocol/server': minor -'@modelcontextprotocol/core-internal': minor ---- - -Serve `input_required` handlers on 2025-era connections: the legacy shim (on by default) converts each embedded request of an `input_required` return into a real server→client request (`elicitation/create`, `sampling/createMessage`, `roots/list`) over the live session — stamped with the originating request's id for stream association — and re-enters the handler with the collected `inputResponses` until a final result. Handlers are written once in the 2026 `inputRequired(...)` style and serve both eras; the previous loud `-32603` failure remains available via `ServerOptions.inputRequired.legacyShim: false`. Knobs: `inputRequired.maxRounds` (default 8) and `inputRequired.roundTimeoutMs` (default 600 000 ms per leg; legs carry a progressToken so a client reporting progress mid-leg resets the leg timeout). Semantics mirror the modern client driver exactly: per-round replaced `inputResponses`, byte-exact `requestState` echo with the verify hook running every round, paced requestState-only rounds, and elicitation accepted content passed through UNVALIDATED (the handler validates via the schema-aware `acceptedContent` overload, exactly as on the 2026 era). URL-mode legs synthesize the `elicitationId` the 2025-11-25 wire requires. Failures surface per family (`tools/call` → `isError` tool result; `prompts/get` / `resources/read` → JSON-RPC error); stateless legacy HTTP degrades to a clean capability refusal; the shim emits no progress of its own (the originating progressToken is the handler's single must-increase stream — the shim never adds a second author to it). - -`ctx.mcpReq.requestState` is now a typed accessor: `ctx.mcpReq.requestState()` returns the payload the configured `requestState.verify` hook resolved with (e.g. `createRequestStateCodec.verify` — the hook's return value is now load-bearing; verifiers that are not also decoders should resolve `undefined`), the raw wire string when no hook is configured, or `undefined` when the round carried no state. Code that read the property directly becomes a call: `ctx.mcpReq.requestState` → `ctx.mcpReq.requestState()`. Note the member is now always present (a function), so truthiness no longer means "has state", and it is dropped by JSON serialization of the context. - -New typed readers for `inputResponses`, exported from `@modelcontextprotocol/server`: a schema-aware `acceptedContent(responses, key, schema)` overload (validates untrusted accepted content against any synchronous Standard Schema) and `inputResponse(responses, key)` (discriminated `missing | elicit | sampling | roots` view, for decline/cancel detection and the non-elicitation kinds). Content conveniences like text extraction stay in application code as one-liners over the discriminated view. diff --git a/.changeset/legacy-module-resolution-types.md b/.changeset/legacy-module-resolution-types.md deleted file mode 100644 index c12cb839db..0000000000 --- a/.changeset/legacy-module-resolution-types.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -'@modelcontextprotocol/client': patch -'@modelcontextprotocol/server': patch -'@modelcontextprotocol/node': patch -'@modelcontextprotocol/express': patch -'@modelcontextprotocol/fastify': patch -'@modelcontextprotocol/hono': patch ---- - -Add top-level `types` field (and `typesVersions` on client/server for their subpath exports) so consumers on legacy `moduleResolution: "node"` can resolve type declarations. The `exports` map remains the source of truth for `nodenext`/`bundler` resolution. The `typesVersions` map includes entries for subpaths added by sibling PRs in this series (`zod-schemas`, `stdio`); those entries are no-ops until the corresponding `dist/*.d.mts` files exist. diff --git a/.changeset/lucky-poets-refuse.md b/.changeset/lucky-poets-refuse.md deleted file mode 100644 index 92f0052858..0000000000 --- a/.changeset/lucky-poets-refuse.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/codemod': patch ---- - -Keep the result-schema argument on `request()` calls unless the method is a literal spec method, and keep the generic passthrough `ResultSchema` even then. Schema-less v2 `request()` enforces the spec result schema for spec methods and throws a `TypeError` for non-spec methods, so dropping the schema from a dynamic-method call site (the proxy/forwarder shape, `request({ method, params }, ResultSchema)`) or from a custom-method call broke the call. `callTool()` is unaffected — v2 `callTool()` has no schema parameter. diff --git a/.changeset/migration-doc-findings.md b/.changeset/migration-doc-findings.md deleted file mode 100644 index ac8f1e824d..0000000000 --- a/.changeset/migration-doc-findings.md +++ /dev/null @@ -1,4 +0,0 @@ ---- ---- - -Docs-only: migration-guide corrections from real v1-to-v2 migrations. No package changes. diff --git a/.changeset/missing-client-capability-error.md b/.changeset/missing-client-capability-error.md deleted file mode 100644 index 7f40814001..0000000000 --- a/.changeset/missing-client-capability-error.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': minor -'@modelcontextprotocol/client': minor -'@modelcontextprotocol/server': minor ---- - -Add `MissingRequiredClientCapabilityError`, the typed error class for the 2026-07-28 `-32021` protocol error (processing a request requires a capability the client did not declare). Its `data.requiredCapabilities` lists the missing capabilities and `ProtocolError.fromError` recognizes the code/data shape. The 2026-07-28 HTTP entry gains a pre-dispatch gate that refuses a request requiring an undeclared client capability with this error and HTTP status `400`; no method served on the 2026-07-28 registry currently carries such a requirement, so observable behavior is unchanged until methods with capability requirements exist. diff --git a/.changeset/mrtr-client-engine.md b/.changeset/mrtr-client-engine.md deleted file mode 100644 index 3330480f42..0000000000 --- a/.changeset/mrtr-client-engine.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': minor -'@modelcontextprotocol/client': minor ---- - -Add the client side of multi round-trip requests (protocol revision 2026-07-28, SEP-2322). The neutral `InputRequest`/`InputResponse`/`InputRequests`/`InputResponses`/`InputRequiredResult` types and the `isInputRequiredResult()` guard ship as the neutral surface (the -`inputRequired()` builder family and the `acceptedContent()` reader are exported by the server package as part of the server-side change); the 2026-07-28 wire codec models the in-band vocabulary (embedded requests and bare responses) and the retry-channel request fields. On the -client, an `input_required` answer to `tools/call`, `prompts/get`, or `resources/read` on a 2026-07-28 connection is now fulfilled automatically by default: the embedded requests are dispatched to the client's already-registered elicitation/sampling/roots handlers, and the -original call is retried with the collected `inputResponses`, a byte-exact echo of the opaque `requestState`, and a fresh request id, up to `inputRequired.maxRounds` rounds (default 10; exhaustion raises a typed `InputRequiredRoundsExceeded` error carrying the last result). -`client.callTool()` and its siblings keep returning their plain result types. `ClientOptions.inputRequired` (`autoFulfill`, `maxRounds`) configures the driver; manual mode is `autoFulfill: false` plus the per-call `allowInputRequired: true` request option and the -`withInputRequired()` schema wrapper. Retried requests surface their `inputResponses` to server handlers as bare response objects — entries in a wrapped `{method, result}` shape are dropped and reported via `ctx.mcpReq.droppedInputResponseKeys`. 2025-era behavior is unchanged: -the legacy wire has no `input_required` vocabulary and the legacy server-to-client request flow is untouched. diff --git a/.changeset/mrtr-server-seam.md b/.changeset/mrtr-server-seam.md deleted file mode 100644 index 9285837356..0000000000 --- a/.changeset/mrtr-server-seam.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': minor -'@modelcontextprotocol/server': minor ---- - -Add the server side of multi round-trip requests (protocol revision 2026-07-28, SEP-2322). Handlers for `tools/call`, `prompts/get`, and `resources/read` can return the value built by `inputRequired()` (exported from the server package together with `acceptedContent()`) -to request additional client input in-band; the structured-content requirement and the tools/call result-schema validation are skipped for that return, the encode seam emits it as `resultType: 'input_required'`, and the handler reads the responses on re-entry from -`ctx.mcpReq.inputResponses` (with non-bare entries reported via `ctx.mcpReq.droppedInputResponseKeys`). The seam re-checks the at-least-one rule for hand-built results, checks every embedded request against the capabilities the client declared on that request's envelope -(answering the typed `-32021` error on violation), and fails loudly — never emitting a mis-typed result — when an input-required value is returned from any other method. Toward a 2025-era request the return is served by the default-on legacy shim (real server→client requests plus handler re-entry); the loud failure for that case remains available via `ServerOptions.inputRequired.legacyShim: false`. A `UrlElicitationRequiredError` escaping a handler on a 2026-era request -fails as an internal error with a clear steer to `inputRequired.elicitUrl(...)`, so the `-32042` error never reaches the 2026-07-28 wire; 2025-era serving keeps today's `-32042` behavior -exactly. The typed local error raised when push-style server-to-client request APIs are used while serving a 2026-era request now steers to `inputRequired(...)`. Tool, prompt, and resource callback types accept the new return alongside their existing result types; 2025-era -wire behavior is unchanged. An optional `ServerOptions.requestState.verify` hook lets a server integrity-check the echoed `requestState` before the handler runs — a throw answers the wire-level `-32602` Invalid Params error with `data.reason: 'invalid_request_state'`; the SDK provides no default verification. diff --git a/.changeset/node-export-to-web-request.md b/.changeset/node-export-to-web-request.md deleted file mode 100644 index c061b40446..0000000000 --- a/.changeset/node-export-to-web-request.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/node': minor ---- - -Export `toWebRequest(req, parsedBody?, options?)` — the Node `IncomingMessage` → web-standard `Request` conversion `toNodeHandler` already performs internally. Use it to feed `isLegacyRequest()` (or `handler.fetch()`) from a hand-wired Node/Express `(req, res)` handler instead of assembling a `globalThis.Request` from `req.headers` by hand. When a body parser already consumed the Node stream (`express.json()`), pass the parsed value as `parsedBody`; pass `options.signal` to tie the constructed request to client disconnect, the way `toNodeHandler` does. diff --git a/.changeset/node-forward-supported-versions.md b/.changeset/node-forward-supported-versions.md deleted file mode 100644 index 413f53fde4..0000000000 --- a/.changeset/node-forward-supported-versions.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@modelcontextprotocol/node': patch ---- - -Forward `setSupportedProtocolVersions` from `NodeStreamableHTTPServerTransport` to the wrapped Web Standard transport. Previously a server's `supportedProtocolVersions` option never reached the Node adapter's `MCP-Protocol-Version` header validation, which silently kept -validating against the default version list. diff --git a/.changeset/oauth-error-http200.md b/.changeset/oauth-error-http200.md deleted file mode 100644 index 1ce4fdd9ed..0000000000 --- a/.changeset/oauth-error-http200.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@modelcontextprotocol/client': patch ---- - -Fix OAuth error handling for servers returning errors with HTTP 200 status - -Some OAuth servers (e.g., GitHub) return error responses with HTTP 200 status instead of 4xx. The SDK now checks for an `error` field in the JSON response before attempting to parse it as tokens, providing users with meaningful error messages. diff --git a/.changeset/odd-forks-enjoy.md b/.changeset/odd-forks-enjoy.md deleted file mode 100644 index b9b6261831..0000000000 --- a/.changeset/odd-forks-enjoy.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@modelcontextprotocol/client': patch ---- - -fix(client): append custom Accept headers to spec-required defaults in StreamableHTTPClientTransport - -Custom Accept headers provided via `requestInit.headers` are now appended to the spec-mandated Accept types instead of being overwritten. This ensures the required media types (`application/json, text/event-stream` for POST; `text/event-stream` for GET SSE) are always present while allowing users to include additional types for proxy/gateway routing. diff --git a/.changeset/origin-validation-middleware.md b/.changeset/origin-validation-middleware.md deleted file mode 100644 index 7f484d7412..0000000000 --- a/.changeset/origin-validation-middleware.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -'@modelcontextprotocol/server': minor -'@modelcontextprotocol/express': minor -'@modelcontextprotocol/hono': minor -'@modelcontextprotocol/fastify': minor -'@modelcontextprotocol/node': minor ---- - -Add Origin header validation alongside the existing Host header validation. The server package gains framework-agnostic helpers (`validateOriginHeader`, `localhostAllowedOrigins`, `originValidationResponse`); the Express, Hono and Fastify adapters gain `originValidation` / -`localhostOriginValidation` middleware and a new `allowedOrigins` option on their app factories, which now arm Origin validation by default for localhost-class binds (mirroring the Host validation ladder; the 0.0.0.0-without-allowlist warning is unchanged). Requests -without an `Origin` header pass — non-browser MCP clients are unaffected — while a present `Origin` that is not allowed or cannot be parsed (including the opaque `null` origin) is rejected with `403`. The Node adapter ships `hostHeaderValidation` / `originValidation` -request guards for plain `node:http` servers, which previously had no validation helpers. diff --git a/.changeset/pin-modern-rejection-codes.md b/.changeset/pin-modern-rejection-codes.md deleted file mode 100644 index c131e8ea73..0000000000 --- a/.changeset/pin-modern-rejection-codes.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': patch -'@modelcontextprotocol/server': patch ---- - -Pin the modern (2026-07-28) HTTP serving path's rejection codes to the assignments the published conformance suite asserts: a header/body cross-check mismatch (`MCP-Protocol-Version` or `Mcp-Method` disagreeing with the request body) is now rejected with `-32020` (HeaderMismatch), and a request whose protocol-version header names a modern revision but whose body is missing the `_meta` envelope (or its required protocol-version key) is rejected with `-32602` invalid params naming the missing key(s). Both keep HTTP 400. These cells previously emitted a provisional `-32004` while the upstream error-code discussion was open. The envelope-less rejection on a modern-only endpoint (`-32022` with the supported-versions list), the 2025-era serving paths, and the client-side probe handling are unchanged. diff --git a/.changeset/pre.json b/.changeset/pre.json index d5343b66c8..48e2caa708 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -1,170 +1,28 @@ { - "mode": "pre", - "tag": "beta", - "initialVersions": { - "@modelcontextprotocol/eslint-config": "2.0.0", - "@modelcontextprotocol/tsconfig": "2.0.0", - "@modelcontextprotocol/vitest-config": "2.0.0", - "@modelcontextprotocol/examples": "2.0.0-alpha.0", - "@mcp-examples/client-quickstart": "2.0.0-alpha.0", - "@mcp-examples/server-quickstart": "2.0.0-alpha.0", - "@mcp-examples/shared": "2.0.0-alpha.0", - "@modelcontextprotocol/client": "2.0.0-alpha.4", - "@modelcontextprotocol/codemod": "2.0.0-alpha.2", - "@modelcontextprotocol/core": "2.0.0-alpha.2", - "@modelcontextprotocol/core-internal": "2.0.0-alpha.3", - "@modelcontextprotocol/express": "2.0.0-alpha.4", - "@modelcontextprotocol/fastify": "2.0.0-alpha.4", - "@modelcontextprotocol/hono": "2.0.0-alpha.4", - "@modelcontextprotocol/node": "2.0.0-alpha.4", - "@modelcontextprotocol/server": "2.0.0-alpha.4", - "@modelcontextprotocol/server-legacy": "2.0.0-alpha.4", - "@modelcontextprotocol/test-conformance": "2.0.0-alpha.1", - "@modelcontextprotocol/test-e2e": "2.0.0-alpha.1", - "@modelcontextprotocol/test-helpers": "2.0.0-alpha.0", - "@modelcontextprotocol/test-integration": "2.0.0-alpha.1" - }, - "changesets": [ - "abort-handlers-on-close", - "add-connect-prior", - "add-consumer-sse-e2e", - "add-core-public-package", - "add-e2e-test-suite", - "add-fastify-middleware", - "add-hono-peer-dep", - "add-request-state-codec", - "add-resource-size-field", - "add-sdk-http-error", - "add-server-legacy-package", - "add-version-negotiation-option", - "auth-dcr-hygiene", - "auth-iss-server-and-overload", - "auth-iss-validation", - "auth-sep-2352-credential-isolation", - "auth-surface-delta", - "bound-resumability-version-gates", - "brave-lions-glow", - "busy-rice-smoke", - "busy-weeks-hang", - "cacheable-result-cache-fields", - "cfworker-out-of-barrel", - "client-honor-cache-hints", - "client-http-stream-close-cancel", - "client-modern-era-inbound-drop", - "client-response-cache-substrate", - "codec-era-gates", - "codec-split-wire-break", - "codemod-backlog-batch", - "codemod-bigpatient-batch", - "codemod-completable-protocol", - "codemod-context-and-status", - "codemod-core-routing", - "codemod-flag-removed-task-options", - "codemod-infer-project-type", - "codemod-longtail-batch", - "codemod-manifest-handling", - "codemod-preserve-shebang", - "codemod-resolve-legacy-imports", - "codemod-schema-drop-receivers", - "codemod-streamablehttperror-sdkhttperror", - "codemod-task-handler-methods", - "codemod-v1-to-v2-gaps", - "core-subscription-schemas", - "create-mcp-handler-legacy-revision", - "create-mcp-handler", - "custom-methods-minimal", - "cyan-cycles-pump", - "deprecate-client-identity-accessors", - "dist-types-skiplibcheck", - "draft-spec-non-sep-conformance", - "drop-zod-peer-dep", - "envelope-auto-emission", - "export-inmemory-transport", - "expose-auth-server-discovery", - "expose-icons-on-tools-and-prompts", - "express-resource-server-auth", - "fast-dragons-lead", - "finish-sdkerror-capability", - "fix-abort-listener-leak", - "fix-conformance-server-leak", - "fix-oauth-5xx-discovery", - "fix-onerror-callbacks", - "fix-server-protocol-version", - "fix-stdio-epipe-crash", - "fix-stdio-windows-hide", - "fix-streamable-close-reentrant", - "fix-streamable-http-error-response", - "fix-transport-exact-optional-property-types", - "fix-unknown-tool-protocol-error", - "fix-validate-client-metadata-url", - "funky-baths-attack", - "gentle-planets-rest", - "handler-drop-node-face", - "heavy-walls-swim", - "hide-wire-only-members", - "hono-peer-optional", - "idjag-spec-type-export", - "is-legacy-request-doc-lead", - "legacy-input-required-shim", - "legacy-module-resolution-types", - "lucky-poets-refuse", - "migration-doc-findings", - "missing-client-capability-error", - "mrtr-client-engine", - "mrtr-server-seam", - "node-export-to-web-request", - "node-forward-supported-versions", - "oauth-error-http200", - "odd-forks-enjoy", - "origin-validation-middleware", - "pin-modern-rejection-codes", - "protocol-pre-aborted-signal-wrap", - "quick-islands-occur", - "reconnection-scheduler", - "register-rawshape-compat", - "remove-websocket-transport", - "resource-not-found-32602", - "respect-capability-negotiation", - "restore-task-wire-types", - "rich-hounds-report", - "schema-object-type-for-unions", - "sep-2106-dialect-posture", - "sep-2243-mcp-param-client", - "sep-2243-mcp-param-server", - "sep-2243-std-header-server", - "sep-2350-scope-step-up", - "sep-2577-deprecate-runtime-apis", - "sep-2577-deprecate-type-stacks", - "sep-2663-tasks-removal", - "sep-414-trace-context-meta-keys", - "server-ctx-log-request-related", - "server-serve-stdio", - "server-streamablehttp-store-first", - "shy-times-learn", - "spec-2907-error-code-renumber", - "spec-anchor-repin-2fb207da", - "spec-corpus-and-leak-net", - "spec-reference-types-2026-07-28", - "spec-type-schema", - "spec-types-2026-repin", - "spotty-cats-tickle", - "stdio-max-buffer-size", - "stdio-skip-non-json", - "stdio-subpath-export", - "subscriptions-listen-client", - "subscriptions-listen-result", - "subscriptions-listen-server", - "support-standard-json-schema", - "tame-camels-greet", - "tender-snails-fold", - "token-provider-composable-auth", - "twelve-dodos-taste", - "use-scopes-supported-in-dcr", - "wire-public-separation", - "wire-server-discover", - "workerd-shim-vendors-cfworker", - "wraphandler-hook", - "zod-json-schema-compat", - "zod-jsonschema-fallback" - ] + "mode": "pre", + "tag": "beta", + "initialVersions": { + "@modelcontextprotocol/eslint-config": "2.0.0", + "@modelcontextprotocol/tsconfig": "2.0.0", + "@modelcontextprotocol/vitest-config": "2.0.0", + "@modelcontextprotocol/examples": "2.0.0-alpha.0", + "@mcp-examples/client-quickstart": "2.0.0-alpha.0", + "@mcp-examples/server-quickstart": "2.0.0-alpha.0", + "@mcp-examples/shared": "2.0.0-alpha.0", + "@modelcontextprotocol/client": "2.0.0-alpha.4", + "@modelcontextprotocol/codemod": "2.0.0-alpha.2", + "@modelcontextprotocol/core": "2.0.0-alpha.2", + "@modelcontextprotocol/core-internal": "2.0.0-alpha.3", + "@modelcontextprotocol/express": "2.0.0-alpha.4", + "@modelcontextprotocol/fastify": "2.0.0-alpha.4", + "@modelcontextprotocol/hono": "2.0.0-alpha.4", + "@modelcontextprotocol/node": "2.0.0-alpha.4", + "@modelcontextprotocol/server": "2.0.0-alpha.4", + "@modelcontextprotocol/server-legacy": "2.0.0-alpha.4", + "@modelcontextprotocol/test-conformance": "2.0.0-alpha.1", + "@modelcontextprotocol/test-e2e": "2.0.0-alpha.1", + "@modelcontextprotocol/test-helpers": "2.0.0-alpha.0", + "@modelcontextprotocol/test-integration": "2.0.0-alpha.1" + }, + "changesets": [] } diff --git a/.changeset/protocol-pre-aborted-signal-wrap.md b/.changeset/protocol-pre-aborted-signal-wrap.md deleted file mode 100644 index 80b31f5136..0000000000 --- a/.changeset/protocol-pre-aborted-signal-wrap.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': patch ---- - -`Protocol.request()` now rejects with `SdkError(RequestTimeout, reason)` when called with an already-aborted signal, matching in-flight aborts. Previously the raw `signal.reason` was thrown. diff --git a/.changeset/quick-islands-occur.md b/.changeset/quick-islands-occur.md deleted file mode 100644 index b229b82c36..0000000000 --- a/.changeset/quick-islands-occur.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -'@modelcontextprotocol/express': patch -'@modelcontextprotocol/hono': patch -'@modelcontextprotocol/node': patch -'@modelcontextprotocol/client': patch -'@modelcontextprotocol/server': patch -'@modelcontextprotocol/core-internal': patch ---- - -remove npm references, use pnpm diff --git a/.changeset/reconnection-scheduler.md b/.changeset/reconnection-scheduler.md deleted file mode 100644 index add9bd6fc1..0000000000 --- a/.changeset/reconnection-scheduler.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/client': minor ---- - -Add `reconnectionScheduler` option to `StreamableHTTPClientTransport`. Lets non-persistent environments (serverless, mobile, desktop sleep/wake) override the default `setTimeout`-based SSE reconnection scheduling. The scheduler may return a cancel function that is invoked on `transport.close()`. diff --git a/.changeset/register-rawshape-compat.md b/.changeset/register-rawshape-compat.md deleted file mode 100644 index 84fccdd726..0000000000 --- a/.changeset/register-rawshape-compat.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': patch -'@modelcontextprotocol/server': patch ---- - -`registerTool`/`registerPrompt` accept a raw Zod shape (`{ field: z.string() }`) for `inputSchema`/`outputSchema`/`argsSchema` in addition to a wrapped Standard Schema. Raw shapes are auto-wrapped with `z.object()`. The raw-shape overloads are `@deprecated`; prefer wrapping with `z.object()`. - -Also widens the `completable()` constraint from `StandardSchemaWithJSON` to `StandardSchemaV1` so v1's `completable(z.string(), fn)` continues to work. diff --git a/.changeset/remove-websocket-transport.md b/.changeset/remove-websocket-transport.md deleted file mode 100644 index a36102da49..0000000000 --- a/.changeset/remove-websocket-transport.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/client': major ---- - -Remove `WebSocketClientTransport`. WebSocket is not a spec-defined transport; use stdio or Streamable HTTP. The `Transport` interface remains exported for custom implementations. See #142. diff --git a/.changeset/resource-not-found-32602.md b/.changeset/resource-not-found-32602.md deleted file mode 100644 index e4f613117f..0000000000 --- a/.changeset/resource-not-found-32602.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': minor -'@modelcontextprotocol/server': major -'@modelcontextprotocol/client': minor ---- - -`resources/read` for an unknown URI now answers with JSON-RPC error code `-32602` -(Invalid Params) on every protocol revision, with `error.data.uri` echoing the -requested URI. The 2026-07-28 specification requires `-32602`; the v1.x SDK already -emitted `-32602` on earlier revisions, so v1.x peers see no change. - -This supersedes an interim `-32002` emission that shipped in earlier v2 alphas. The -era-aware encode seam (`WireCodec.encodeErrorCode`) maps any handler-thrown `-32002` -to `-32602` on the wire; note that a `-32002` thrown without `data.uri` is emitted as -a bare `-32602` and is no longer recognizable as resource-not-found — throw -`ResourceNotFoundError` (or include `data: { uri }`) to preserve the classification. - -`ProtocolErrorCode.ResourceNotFound` (`-32002`) remains importable as receive-tolerated -vocabulary; clients should accept both `-32602` and `-32002` from peers (the -specification's backwards-compatibility clause). The new typed `ResourceNotFoundError` -class carries `data.uri`, and `ProtocolError.fromError` reconstructs it from a `-32602` -only when `error.data` is exactly `{ uri: string }` (and nothing else), and from a -legacy `-32002` whenever `data.uri` is a string; a bare `-32002` without `data.uri` -stays a generic `ProtocolError`. diff --git a/.changeset/respect-capability-negotiation.md b/.changeset/respect-capability-negotiation.md deleted file mode 100644 index 6a42cf6076..0000000000 --- a/.changeset/respect-capability-negotiation.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -'@modelcontextprotocol/client': patch ---- - -Respect capability negotiation in list methods by returning empty lists when server lacks capability - -The Client now returns empty lists instead of sending requests to servers that don't advertise the corresponding capability: - -- `listPrompts()` returns `{ prompts: [] }` if server lacks prompts capability -- `listResources()` returns `{ resources: [] }` if server lacks resources capability -- `listResourceTemplates()` returns `{ resourceTemplates: [] }` if server lacks resources capability -- `listTools()` returns `{ tools: [] }` if server lacks tools capability - -This respects the MCP spec requirement that "Both parties SHOULD respect capability negotiation" and avoids unnecessary server warnings and traffic. The existing `enforceStrictCapabilities` option continues to throw errors when set to `true`. diff --git a/.changeset/restore-task-wire-types.md b/.changeset/restore-task-wire-types.md deleted file mode 100644 index 05c0875364..0000000000 --- a/.changeset/restore-task-wire-types.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': minor -'@modelcontextprotocol/server': minor -'@modelcontextprotocol/client': minor ---- - -Restore the 2025-11-25 task wire types that were removed together with the task feature: the task schemas and inferred types, task members of the request/result/notification unions, the `task` request-params augmentation, the `tasks` capability key, the `isTaskAugmentedRequestParams` guard, and `RELATED_TASK_META_KEY`. The task feature itself remains removed — servers do not advertise the `tasks` capability and inbound `tasks/*` requests receive `-32601` — but the wire surface stays so SDKs interoperate cleanly with peers on the 2025-11-25 revision. diff --git a/.changeset/rich-hounds-report.md b/.changeset/rich-hounds-report.md deleted file mode 100644 index b05e7c4656..0000000000 --- a/.changeset/rich-hounds-report.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -'@modelcontextprotocol/express': patch -'@modelcontextprotocol/hono': patch -'@modelcontextprotocol/node': patch -'@modelcontextprotocol/client': patch -'@modelcontextprotocol/server': patch -'@modelcontextprotocol/core-internal': patch ---- - -clean up package manager usage, all pnpm diff --git a/.changeset/schema-object-type-for-unions.md b/.changeset/schema-object-type-for-unions.md deleted file mode 100644 index d307f2af58..0000000000 --- a/.changeset/schema-object-type-for-unions.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': patch ---- - -Ensure `standardSchemaToJsonSchema` emits `type: "object"` at the root, fixing discriminated-union tool/prompt schemas that previously produced `{oneOf: [...]}` without the MCP-required top-level type. Also throws a clear error when given an explicitly non-object schema (e.g. `z.string()`). Fixes #1643. diff --git a/.changeset/sep-2106-dialect-posture.md b/.changeset/sep-2106-dialect-posture.md deleted file mode 100644 index 5328276841..0000000000 --- a/.changeset/sep-2106-dialect-posture.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': minor -'@modelcontextprotocol/client': major -'@modelcontextprotocol/server': major ---- - -SEP-1613 / SEP-2106 (JSON Schema 2020-12 posture): the Node default JSON Schema validator is now `Ajv2020` (true draft 2020-12) instead of the draft-07 `Ajv` class — `$defs`/`prefixItems`/`unevaluatedProperties`/`dependentRequired` are now enforced where they were previously silently ignored; to opt back, construct the draft-07 instance with the v1 defaults — `const ajv = new Ajv({ strict: false, validateFormats: true, validateSchema: false, allErrors: true }); addFormats(ajv);` — and pass `new AjvJsonSchemaValidator(ajv)`. Schemas declaring a `$schema` other than 2020-12 are rejected with a clear error rather than mis-validating. `outputSchema` may now have a non-object root and `CallToolResult.structuredContent` is widened to `unknown` (a deliberate source-level break for typed consumers — see the migration guide for the narrowing pattern). Toward 2025-era clients McpServer wraps a non-object `outputSchema` (and the matching `structuredContent`) in a `{result: …}` envelope so the tool stays callable, with same-document `$ref`/`$dynamicRef` pointers rewritten to keep resolving — low-level `Server` users (those bypassing `McpServer` and registering a `tools/call` handler directly) get the same wrap by routing the result through the new `Server.projectCallToolResult(result, advertisedOutputSchema)`. Independently, on every era (the SEP's MUST applies regardless of client version), McpServer auto-appends a `TextContent` JSON serialisation when a handler returns non-object `structuredContent` without its own text block. The `structuredContent` presence check is `!== undefined` (not falsy) on both sides. Thanks @mattzcarey (#2249). diff --git a/.changeset/sep-2243-mcp-param-client.md b/.changeset/sep-2243-mcp-param-client.md deleted file mode 100644 index ffb90b1d1c..0000000000 --- a/.changeset/sep-2243-mcp-param-client.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': minor -'@modelcontextprotocol/client': minor ---- - -SEP-2243 `Mcp-Param-*` client-side mirroring (protocol revision 2026-07-28). On a 2026-07-28 connection over Streamable HTTP, `Client.callTool()` now mirrors tool arguments designated with `x-mcp-header` in the tool's `inputSchema` into `Mcp-Param-{Name}` HTTP headers (with the spec's `=?base64?…?=` sentinel encoding for values that are not safe plain-ASCII field values), and on a non-stdio modern connection `Client.listTools()` (and the client's internal `tools/list` cache) exclude tool definitions whose `x-mcp-header` declarations violate the spec's constraints, logging a warning naming the tool and the reason. The legacy-era `callTool` and `listTools` paths are unchanged at the wire level. Browser environments skip mirroring (dynamically named headers cannot be statically allow-listed for credentialed CORS); a conforming SEP-2243 server will reject a `tools/call` whose body carries a non-null value for an `x-mcp-header` parameter when the matching header is absent, so calling such a tool with that argument from a browser is a known limitation. New `CallToolRequestOptions.toolDefinition` lets callers supply the tool definition directly so mirroring and output-schema validation can run without a prior `tools/list`. `TransportSendOptions.headers` is added (additive, optional) for per-request HTTP headers; the Streamable HTTP transport skips reserved standard/auth header names (`authorization`, `mcp-protocol-version`, `mcp-method`, `mcp-name`, `mcp-session-id`, `content-type`); transports that share a single channel (stdio, in-memory) ignore it. - -The Streamable HTTP transport now emits the `Mcp-Name` standard header on every modern-enveloped request (`params.name` for `tools/call`/`prompts/get`, `params.uri` for `resources/read`), sentinel-encoded. - -**Behavior change (modern era only):** on a modern-enveloped request the Streamable HTTP transport now surfaces an HTTP `400` whose body is a well-formed JSON-RPC error response addressed to the pending request id in-band as a `ProtocolError` (instead of `SdkHttpError`), so the `HEADER_MISMATCH` recovery retry can fire. Legacy-era exchanges are unchanged. diff --git a/.changeset/sep-2243-mcp-param-server.md b/.changeset/sep-2243-mcp-param-server.md deleted file mode 100644 index fc0316cfb7..0000000000 --- a/.changeset/sep-2243-mcp-param-server.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/server': minor ---- - -SEP-2243 `Mcp-Param-*` server-side validation (protocol revision 2026-07-28). On the modern (2026-07-28) serving path, `createMcpHandler` now validates `Mcp-Param-{Name}` headers against the named tool's `x-mcp-header` declarations and the body `arguments` before dispatch: a missing header for a present body value, a header that decodes to a different value than the body, or an invalid `=?base64?…?=` sentinel is rejected with `400 Bad Request` and JSON-RPC `-32020` (`HeaderMismatch`) — the same shape the existing standard-header cross-checks emit. A `null`/absent body value passes regardless of any header (the spec's "server MUST NOT expect the header" rows). `McpServer.registerTool` now warns at registration time when an `x-mcp-header` declaration violates the spec's constraints. The 2025-era serving paths and the low-level `Server` factory shape are unchanged. diff --git a/.changeset/sep-2243-std-header-server.md b/.changeset/sep-2243-std-header-server.md deleted file mode 100644 index eb8bc03fd9..0000000000 --- a/.changeset/sep-2243-std-header-server.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': minor -'@modelcontextprotocol/server': minor ---- - -SEP-2243 standard-header server-side validation (protocol revision 2026-07-28). On the modern (2026-07-28) serving path, `createMcpHandler` now enforces the required `Mcp-Method` and `Mcp-Name` standard request headers in addition to the existing `MCP-Protocol-Version` and `Mcp-Method` cross-checks: a modern request without an `Mcp-Method` header, a `tools/call` / `prompts/get` / `resources/read` request without an `Mcp-Name` header, an `Mcp-Name` header carrying an invalid `=?base64?…?=` sentinel, and an `Mcp-Name` header whose (decoded) value disagrees with the body's `params.name` / `params.uri` are all rejected with `400 Bad Request` and JSON-RPC `-32020` (`HeaderMismatch`). The 2025-era serving paths are unchanged. - -New public surface: - -- `@modelcontextprotocol/server`: the `mcpNameHeader` field on `InboundHttpRequest`, and the `'standard-header-validation'` member of `InboundValidationRung` (with `client-capabilities` / `param-header-validation` renumbered). diff --git a/.changeset/sep-2350-scope-step-up.md b/.changeset/sep-2350-scope-step-up.md deleted file mode 100644 index e2155b53e4..0000000000 --- a/.changeset/sep-2350-scope-step-up.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/client': minor ---- - -SEP-2350 scope step-up: on `403 insufficient_scope`, `StreamableHTTPClientTransport` now re-authorizes with the **union** of the previously-requested and challenged scopes (`computeScopeUnion`), bypassing the refresh-token branch when the union is a strict superset of the current token's granted scope (`isStrictScopeSuperset`, `AuthOptions.forceReauthorization`). New `onInsufficientScope: 'reauthorize' | 'throw'` (default `'reauthorize'`) and `maxStepUpRetries` (default 1) on `StreamableHTTPClientTransportOptions`; `'throw'` raises the new `InsufficientScopeError`. The GET listen-stream open path now applies the same step-up handling. The previous verbatim-header retry guard is replaced by the bounded per-send counter. diff --git a/.changeset/sep-2577-deprecate-runtime-apis.md b/.changeset/sep-2577-deprecate-runtime-apis.md deleted file mode 100644 index e042112d08..0000000000 --- a/.changeset/sep-2577-deprecate-runtime-apis.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': patch -'@modelcontextprotocol/server': patch -'@modelcontextprotocol/client': patch ---- - -Mark the roots, sampling, and logging runtime APIs as `@deprecated` per SEP-2577 (deprecated as of protocol version 2026-07-28; functional for at least twelve months). Annotates `Server.createMessage`/`listRoots`/`sendLoggingMessage`, `McpServer.sendLoggingMessage`, `Client.setLoggingLevel`/`sendRootsListChanged`, the `ServerContext.mcpReq.log`/`requestSampling` helpers, and the `roots`/`sampling`/`logging` capability schema fields. JSDoc/docs only — no behavior change. diff --git a/.changeset/sep-2577-deprecate-type-stacks.md b/.changeset/sep-2577-deprecate-type-stacks.md deleted file mode 100644 index 7471457f7a..0000000000 --- a/.changeset/sep-2577-deprecate-type-stacks.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': patch -'@modelcontextprotocol/client': patch ---- - -Complete the SEP-2577 `@deprecated` sweep on the public type surface (SEP-2596 Tier-1 obligation). Marks the full Logging type stack (`LoggingLevel`, `SetLevelRequest`, `LoggingMessageNotification` and params), the full Sampling type stack (`CreateMessageRequest`/`Result`, `SamplingMessage`, `ModelPreferences`/`ModelHint`, `ToolChoice`, `ToolUseContent`/`ToolResultContent`, and the `includeContext` enum values), the full Roots type stack (`Root`, `ListRootsRequest`/`Result`, `RootsListChangedNotification`), and `registerClient` (Dynamic Client Registration; prefer Client ID Metadata Documents per SEP-991). Mirrors the markers already present on the per-revision reference types. JSDoc only — wire behavior is unchanged; everything remains fully functional during the deprecation window (at least twelve months). diff --git a/.changeset/sep-2663-tasks-removal.md b/.changeset/sep-2663-tasks-removal.md deleted file mode 100644 index 6da4fba840..0000000000 --- a/.changeset/sep-2663-tasks-removal.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': major -'@modelcontextprotocol/server': major -'@modelcontextprotocol/client': major ---- - -SEP-2663: remove 2025-11 experimental tasks (TaskManager, experimental.tasks.\* accessors). Tasks are now Extensions Track. diff --git a/.changeset/sep-414-trace-context-meta-keys.md b/.changeset/sep-414-trace-context-meta-keys.md deleted file mode 100644 index 1281c9dafb..0000000000 --- a/.changeset/sep-414-trace-context-meta-keys.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': patch ---- - -Add reserved trace context `_meta` key constants (`TRACEPARENT_META_KEY`, `TRACESTATE_META_KEY`, `BAGGAGE_META_KEY`) per SEP-414, plus docs and a passthrough regression test. The spec reserves the unprefixed `_meta` keys `traceparent`, `tracestate`, and `baggage` (W3C Trace Context / W3C Baggage formats) for distributed tracing; the SDK passes them through untouched. diff --git a/.changeset/server-ctx-log-request-related.md b/.changeset/server-ctx-log-request-related.md deleted file mode 100644 index 8bc91794ea..0000000000 --- a/.changeset/server-ctx-log-request-related.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@modelcontextprotocol/server': patch ---- - -`ctx.mcpReq.log()` now emits its `notifications/message` notification request-related (like progress and `ctx.mcpReq.notify`), so handler-emitted log messages are delivered when the server is hosted per request via `createMcpHandler` instead of being silently dropped. On a 2026-07-28 request the level filter consults the per-request `_meta` `io.modelcontextprotocol/logLevel` key (the modern equivalent of `logging/setLevel`); per the spec, an absent key means no `notifications/message` is sent for that request. - -**2025-era delivery-channel change (spec-conformance correction).** On a 2025-era sessionful Streamable HTTP transport, handler-emitted log messages now ride the per-request POST response stream instead of the standalone GET stream. This is a correction to the 2025-11-25 specification: `docs/specification/2025-11-25/basic/transports.mdx` §"Sending Messages to the Server" item 6 says JSON-RPC messages on the POST response stream SHOULD relate to the originating client request, and §"Listening for Messages from the Server" item 4 says messages on the GET stream SHOULD be unrelated to any concurrently-running client request — so a log emitted from a handler context belongs on the POST stream. Clients reading handler logs off the standalone GET stream will now see them on the per-request POST stream instead. The eventStore-resumable case (a log emitted after `closeSSE()` while the client has not yet reconnected) is handled by the store-first persistence behavior in `WebStandardStreamableHTTPServerTransport.send()`. The session-scoped `Server.sendLoggingMessage()` API is unchanged. diff --git a/.changeset/server-serve-stdio.md b/.changeset/server-serve-stdio.md deleted file mode 100644 index d7331aaaeb..0000000000 --- a/.changeset/server-serve-stdio.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -'@modelcontextprotocol/server': minor ---- - -Add `serveStdio(factory, options?)` (exported from `@modelcontextprotocol/server/stdio`), the connection-pinned stdio entry point for serving the 2026-07-28 draft revision on long-lived connections. The entry owns the transport and the era decision: the client's opening -exchange selects the era (a 2025 `initialize` handshake, 2026-07-28 per-request `_meta` envelope traffic, or a `server/discover` probe followed by either), and ONE instance from the factory is pinned to the connection and serves only that era — mirroring how -`createMcpHandler` classifies each HTTP request before constructing an instance. 2025-era openings are served by default; `legacy: 'reject'` answers them with the unsupported-protocol-version error naming the supported modern revisions instead. A `transport` option -accepts a bring-your-own `StdioServerTransport` (for example over a Unix domain socket); `onerror` reports out-of-band errors; the returned handle's `close()` tears the connection down. - -Removed: `ServerOptions.eraSupport` (introduced in an earlier 2.0 alpha, never in a stable release). A hand-constructed `Server`/`McpServer` serves only the 2025-era protocol it was written for; serving the 2026-07-28 revision always goes through a serving entry. Migrate -`new McpServer(info, { eraSupport: 'dual-era' })` + `connect(new StdioServerTransport())` to `serveStdio(() => new McpServer(info))`, and `eraSupport: 'modern'` to `serveStdio(factory, { legacy: 'reject' })`. diff --git a/.changeset/server-streamablehttp-store-first.md b/.changeset/server-streamablehttp-store-first.md deleted file mode 100644 index cae12269c8..0000000000 --- a/.changeset/server-streamablehttp-store-first.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/server': patch ---- - -`WebStandardStreamableHTTPServerTransport`: request-related events (progress, `ctx.mcpReq.notify`, handler-emitted log) and the final response are now persisted to the configured `eventStore` whenever the request is in flight, regardless of whether a live SSE writer currently exists — mirroring the standalone-SSE path's store-first semantics. This fixes the `closeSSE()` poll-and-replay drop (events emitted after `closeSSE()` were previously silently lost) and aligns with the 2025-11-25 specification ("disconnection SHOULD NOT be interpreted as the client cancelling its request"). When an `eventStore` is configured, a final response sent while no per-request stream is connected is stored for replay and returns cleanly instead of throwing "No connection established"; a `Last-Event-ID` reconnect after the request has been retired replays the stored response and then closes the resumed stream. When no `eventStore` is configured, the same condition is surfaced via `onerror` (the response is undeliverable) and the request id is retired. diff --git a/.changeset/shy-times-learn.md b/.changeset/shy-times-learn.md deleted file mode 100644 index 24d47206eb..0000000000 --- a/.changeset/shy-times-learn.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@modelcontextprotocol/node': patch -'@modelcontextprotocol/test-integration': patch -'@modelcontextprotocol/server': patch -'@modelcontextprotocol/core-internal': patch ---- - -deprecated .tool, .prompt, .resource method removal diff --git a/.changeset/spec-2907-error-code-renumber.md b/.changeset/spec-2907-error-code-renumber.md deleted file mode 100644 index a58191756b..0000000000 --- a/.changeset/spec-2907-error-code-renumber.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': minor -'@modelcontextprotocol/client': minor -'@modelcontextprotocol/server': minor ---- - -Align the 2026-07-28 protocol error codes to the spec renumber: `HeaderMismatch` is now `-32020` (was `-32001`), `MissingRequiredClientCapability` is now `-32021` (was `-32003`), and `UnsupportedProtocolVersion` is now `-32022` (was `-32004`). These codes are part of the draft 2026-07-28 protocol revision only and have never appeared on a 2025-era wire — the 2025 serving paths and the SDK-conventional `-32001` (`Session not found`) on the stateful Streamable HTTP transport are unchanged. `ProtocolErrorCode.MissingRequiredClientCapability`, `ProtocolErrorCode.UnsupportedProtocolVersion`, the `HEADER_MISMATCH_ERROR_CODE` constant, and the `HEADER_MISMATCH` / `MISSING_REQUIRED_CLIENT_CAPABILITY` / `UNSUPPORTED_PROTOCOL_VERSION` spec-type constants now carry the renumbered values; the `UnsupportedProtocolVersionError` and `MissingRequiredClientCapabilityError` classes (and `ProtocolError.fromError` recognition) follow. The client probe classifier recognizes `-32022` for the corrective continuation and the SEP-2243 one-refresh-on-miss retry triggers on `-32020`. diff --git a/.changeset/spec-anchor-repin-2fb207da.md b/.changeset/spec-anchor-repin-2fb207da.md deleted file mode 100644 index cdb774acad..0000000000 --- a/.changeset/spec-anchor-repin-2fb207da.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': patch -'@modelcontextprotocol/client': patch -'@modelcontextprotocol/server': patch ---- - -Re-pin the 2026-07-28 draft references (spec reference types, vendored schema.json twins, example corpus) to the latest spec commit and align the 2026-era wire surface with it. Deliberate 2026-era wire behavior changes (the released 2025-11-25 surface is untouched): - -- `notifications/elicitation/complete` is no longer part of the 2026-07-28 wire registry (the draft removed the notification together with `elicitationId` on URL-mode elicitation). On connections negotiated at 2026-07-28, sending it — including via `Server.createElicitationCompletionNotifier()` — now fails locally with `SdkErrorCode.MethodNotSupportedByProtocolVersion`, and inbound copies are dropped as unknown notifications. Both remain fully supported on 2025-11-25. -- `notifications/cancelled` on 2026-era connections now parses with a revision-exact schema that requires `requestId` (the draft made it required); the notification `_meta` shape types the `io.modelcontextprotocol/subscriptionId` key. 2025-era parsing is unchanged. -- The error code `-32001` emitted for HTTP header/body mismatches is now defined by the draft schema (`HEADER_MISMATCH`); the emitted behavior is unchanged (documentation only). - -No public API surface changes; the regenerated reference artifacts are internal/test-only. diff --git a/.changeset/spec-corpus-and-leak-net.md b/.changeset/spec-corpus-and-leak-net.md deleted file mode 100644 index 9e8ebbbc54..0000000000 --- a/.changeset/spec-corpus-and-leak-net.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': patch ---- - -Test-only hardening, no runtime changes: a spec example corpus harness (the draft revision's 86 example directories vendored from the specification repository plus a frozen hand-built 2025-11-25 corpus, with rejection-side fixtures routed through real dispatch), a cross-bundle typed-error recognition guard, and extended end-to-end draft-vocabulary leak coverage for hosted transports, SSE streams, and compatibility fallback paths. diff --git a/.changeset/spec-reference-types-2026-07-28.md b/.changeset/spec-reference-types-2026-07-28.md deleted file mode 100644 index ea3c5c0fda..0000000000 --- a/.changeset/spec-reference-types-2026-07-28.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': 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 `-32022` error, the `-32021` code constant, and a `resultType` passthrough on the base result. Types and constants only — no behavior changes. diff --git a/.changeset/spec-type-schema.md b/.changeset/spec-type-schema.md deleted file mode 100644 index 7bcc977e42..0000000000 --- a/.changeset/spec-type-schema.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@modelcontextprotocol/client': minor -'@modelcontextprotocol/server': minor ---- - -Export `isSpecType` and `specTypeSchemas` records for runtime validation of any MCP spec type by name. `isSpecType.ContentBlock(value)` is a type predicate; `specTypeSchemas.ContentBlock` is a `StandardSchemaV1Sync` validator — `validate()` returns the result synchronously. Guards are standalone functions, so `arr.filter(isSpecType.ContentBlock)` works. Also export the `SpecTypeName`, `SpecTypes`, and `StandardSchemaV1Sync` types. diff --git a/.changeset/spec-types-2026-repin.md b/.changeset/spec-types-2026-repin.md deleted file mode 100644 index 32eea35562..0000000000 --- a/.changeset/spec-types-2026-repin.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': patch -'@modelcontextprotocol/client': patch -'@modelcontextprotocol/server': patch ---- - -Internal: regenerate the 2026-07-28 spec reference types from the latest draft schema (`DiscoverResult` now extends `CacheableResult`; `ElicitationCompleteNotificationParams` extracted as a named interface) and document the anchor lifecycle policy. Released-revision spec-type generation is now pinned to a fixed spec commit; draft anchors keep floating via the nightly refresh PRs. No public API or runtime behavior changes. diff --git a/.changeset/spotty-cats-tickle.md b/.changeset/spotty-cats-tickle.md deleted file mode 100644 index 502130dc52..0000000000 --- a/.changeset/spotty-cats-tickle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/client': minor ---- - -The client credentials providers now support scopes being added to the token request. diff --git a/.changeset/stdio-max-buffer-size.md b/.changeset/stdio-max-buffer-size.md deleted file mode 100644 index 080a6ae81c..0000000000 --- a/.changeset/stdio-max-buffer-size.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': patch -'@modelcontextprotocol/client': patch -'@modelcontextprotocol/server': patch ---- - -Add a configurable `maxBufferSize` (default 10 MB) to the stdio transports. When a single message would push the read buffer past the limit, the transport now emits an `onerror` and closes instead of growing the buffer unbounded. Configure via `new StdioClientTransport({ ..., maxBufferSize })` or `new StdioServerTransport(stdin, stdout, { maxBufferSize })`. The default is exported from `@modelcontextprotocol/client` / `@modelcontextprotocol/server` as `STDIO_DEFAULT_MAX_BUFFER_SIZE`. diff --git a/.changeset/stdio-skip-non-json.md b/.changeset/stdio-skip-non-json.md deleted file mode 100644 index 7c70e412f3..0000000000 --- a/.changeset/stdio-skip-non-json.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': patch ---- - -`ReadBuffer.readMessage()` now silently skips non-JSON lines instead of throwing `SyntaxError`. This prevents noisy `onerror` callbacks when hot-reload tools (tsx, nodemon) write debug output like "Gracefully restarting..." to stdout. Lines that parse as JSON but fail JSONRPC schema validation still throw. diff --git a/.changeset/stdio-subpath-export.md b/.changeset/stdio-subpath-export.md deleted file mode 100644 index b0893194a7..0000000000 --- a/.changeset/stdio-subpath-export.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@modelcontextprotocol/client': minor -'@modelcontextprotocol/server': minor ---- - -Move stdio transports to a `./stdio` subpath export. Import `StdioClientTransport`, `getDefaultEnvironment`, `DEFAULT_INHERITED_ENV_VARS`, and `StdioServerParameters` from `@modelcontextprotocol/client/stdio`, and `StdioServerTransport` from `@modelcontextprotocol/server/stdio`. The `@modelcontextprotocol/client` root entry no longer pulls in `node:child_process`, `node:stream`, or `cross-spawn`, fixing bundling for browser and Cloudflare Workers targets; the `@modelcontextprotocol/server` root entry drops its `node:stream` reference. Node.js, Bun, and Deno consumers update the import path; runtime behavior is unchanged. diff --git a/.changeset/subscriptions-listen-client.md b/.changeset/subscriptions-listen-client.md deleted file mode 100644 index db7b1fb4eb..0000000000 --- a/.changeset/subscriptions-listen-client.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': minor -'@modelcontextprotocol/client': minor ---- - -`Client.listen(filter)` opens a `subscriptions/listen` stream on a 2026-07-28-era connection, resolving once the server's acknowledged notification arrives with an `McpSubscription { honoredFilter, close(), closed }`. `closed` is a `Promise<'local' | 'remote'>` that resolves exactly once when the subscription terminates (`'local'` = you called `close()`; `'remote'` = the server cancelled, the stream ended, or the transport dropped — re-listen if you still want events) and never rejects. Change notifications delivered on the stream dispatch to the existing `setNotificationHandler` registrations — the same handlers the 2025-era unsolicited notifications fire on a legacy connection — so `listen()` is era-transparent for consumers that already register those. `close()` aborts the listen request's stream (where the transport supports it) and sends `notifications/cancelled` referencing the listen id — both, on every transport; no automatic re-listen. On a 2025-era connection `listen()` throws a typed `MethodNotSupportedByProtocolVersion` steering to `resources/subscribe` and `ClientOptions.listChanged`. `ClientOptions.listChanged` now auto-opens a listen stream on a modern connection — the filter is the intersection of the configured sub-options and the server-advertised `listChanged` capabilities; auto-open is skipped (`client.autoOpenedSubscription` stays `undefined`) when that intersection is empty; otherwise the auto-opened subscription is exposed at `client.autoOpenedSubscription`. `TransportSendOptions` gains `requestSignal` (per-request abort) and `onRequestStreamEnd` (fires when a per-request response stream ends or errors for any non-deliberate reason) on the Streamable HTTP transport. diff --git a/.changeset/subscriptions-listen-result.md b/.changeset/subscriptions-listen-result.md deleted file mode 100644 index 60117a325b..0000000000 --- a/.changeset/subscriptions-listen-result.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': minor -'@modelcontextprotocol/server': minor -'@modelcontextprotocol/client': minor ---- - -`subscriptions/listen` graceful close: per spec PR #2953, a server-side graceful close (`createMcpHandler` / `serveStdio` `close()`) now emits the empty `subscriptions/listen` JSON-RPC result (the new `SubscriptionsListenResult` — `_meta` carries the subscriptionId) before closing the stream, replacing the previous server-originated `notifications/cancelled`. On the client, `McpSubscription.closed` now resolves `'graceful'` for this signal (added alongside `'local'` and `'remote'`); a stream close without a result remains `'remote'` (unexpected disconnect). diff --git a/.changeset/subscriptions-listen-server.md b/.changeset/subscriptions-listen-server.md deleted file mode 100644 index 70462dea60..0000000000 --- a/.changeset/subscriptions-listen-server.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': minor -'@modelcontextprotocol/server': minor ---- - -`subscriptions/listen` (SEP-1865) is served by both serving entries on protocol revision 2026-07-28. The entry owns ack-first, per-stream filtering, subscription-id stamping, keepalive (HTTP), the pre-ack `-32603` capacity guard, and teardown (HTTP stream close; one -`notifications/cancelled` per subscription on stdio). `server/discover` now advertises `listChanged`/`subscribe` capability bits — the rider that suppressed them until listen was served is discharged. - -Under `createMcpHandler` the consumer's factory **is** constructed for `subscriptions/listen` (a capabilities-only probe so the acknowledged filter reflects what the server advertises; the instance is never connected and is closed immediately). Per-request authorization performed inside the factory therefore sees listen requests; token verification still belongs at the middleware layer mounted in front of the entry. - -New public surface: - -- `@modelcontextprotocol/server`: `ServerEventBus`, `ServerEvent`, `ServerNotifier` (types); `InMemoryServerEventBus` (class). -- `McpHttpHandler` gains `.notify` (`ServerNotifier`: `toolsChanged()`, `promptsChanged()`, `resourcesChanged()`, `resourceUpdated(uri)`) and `.bus` (the `ServerEventBus` listen streams subscribe to). -- `CreateMcpHandlerOptions` gains `bus?: ServerEventBus` (an in-process `InMemoryServerEventBus` is created when omitted), `maxSubscriptions?: number` (default 1024), and `keepAliveMs?: number` (default 15000). -- `ServeStdioOptions` gains `maxSubscriptions?: number` (default 1024). On a modern-pinned connection `serveStdio` routes the pinned instance's existing `send*ListChanged()` calls onto active subscriptions; legacy connections are unchanged. -- `@modelcontextprotocol/server`: `SUBSCRIPTION_ID_META_KEY` (const); `SubscriptionFilter`, `SubscriptionsListenRequest`, `SubscriptionsListenRequestParams`, `SubscriptionsAcknowledgedNotification`, `SubscriptionsAcknowledgedNotificationParams` (types). diff --git a/.changeset/support-standard-json-schema.md b/.changeset/support-standard-json-schema.md deleted file mode 100644 index a8856479ba..0000000000 --- a/.changeset/support-standard-json-schema.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': minor -'@modelcontextprotocol/server': minor -'@modelcontextprotocol/client': minor ---- - -Support Standard Schema for tool and prompt schemas - -Tool and prompt registration now accepts any schema library that implements the [Standard Schema spec](https://standardschema.dev/): Zod v4, Valibot, ArkType, and others. `RegisteredTool.inputSchema`, `RegisteredTool.outputSchema`, and `RegisteredPrompt.argsSchema` now use `StandardSchemaWithJSON` (requires both `~standard.validate` and `~standard.jsonSchema`) instead of the Zod-specific `AnySchema` type. - -**Zod v4 schemas continue to work unchanged** — Zod v4 implements the required interfaces natively. - -```typescript -import { type } from 'arktype'; - -server.registerTool( - 'greet', - { - inputSchema: type({ name: 'string' }) - }, - async ({ name }) => ({ content: [{ type: 'text', text: `Hello, ${name}!` }] }) -); -``` - -For raw JSON Schema (e.g. TypeBox output), use the new `fromJsonSchema` adapter: - -```typescript -import { fromJsonSchema } from '@modelcontextprotocol/server'; - -server.registerTool( - 'greet', - { - inputSchema: fromJsonSchema({ type: 'object', properties: { name: { type: 'string' } } }) - }, - handler -); -``` - -**Breaking changes:** - -- `experimental.tasks.getTaskResult()` no longer accepts a `resultSchema` parameter. Returns `GetTaskPayloadResult` (a loose `Result`); cast to the expected type at the call site. -- Removed unused exports from `@modelcontextprotocol/core-internal`: `SchemaInput`, `schemaToJson`, `parseSchemaAsync`, `getSchemaShape`, `getSchemaDescription`, `isOptionalSchema`, `unwrapOptionalSchema`. Use the new `standardSchemaToJsonSchema` and `validateStandardSchema` instead. -- `completable()` remains Zod-specific (it relies on Zod's `.shape` introspection). diff --git a/.changeset/tame-camels-greet.md b/.changeset/tame-camels-greet.md deleted file mode 100644 index 5f9c1d1c59..0000000000 --- a/.changeset/tame-camels-greet.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'@modelcontextprotocol/client': patch ---- - -Don't swallow fetch `TypeError` as CORS in non-browser environments. Network errors -(DNS resolution failure, connection refused, invalid URL) in Node.js and Cloudflare -Workers now propagate from OAuth discovery instead of being silently misattributed -to CORS and returning `undefined`. This surfaces the real error to callers rather -than masking it as "metadata not found." diff --git a/.changeset/tender-snails-fold.md b/.changeset/tender-snails-fold.md deleted file mode 100644 index 138596950c..0000000000 --- a/.changeset/tender-snails-fold.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@modelcontextprotocol/client': patch -'@modelcontextprotocol/server': patch ---- - -Initial 2.0.0-alpha.0 client and server package diff --git a/.changeset/token-provider-composable-auth.md b/.changeset/token-provider-composable-auth.md deleted file mode 100644 index f5c064e7f1..0000000000 --- a/.changeset/token-provider-composable-auth.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -'@modelcontextprotocol/client': minor ---- - -Add `AuthProvider` for composable bearer-token auth; transports adapt `OAuthClientProvider` automatically - -- New `AuthProvider` interface: `{ token(): Promise; onUnauthorized?(ctx): Promise }`. Transports call `token()` before every request and `onUnauthorized()` on 401 (then retry once). -- Transport `authProvider` option now accepts `AuthProvider | OAuthClientProvider`. OAuth providers are adapted internally via `adaptOAuthProvider()` — no changes needed to existing `OAuthClientProvider` implementations. -- For simple bearer tokens (API keys, gateway-managed tokens, service accounts): `{ authProvider: { token: async () => myKey } }` — one-line object literal, no class. -- New `adaptOAuthProvider(provider)` export for explicit adaptation. -- New `handleOAuthUnauthorized(provider, ctx)` helper — the standard OAuth `onUnauthorized` behavior. -- New `isOAuthClientProvider()` type guard. -- New `UnauthorizedContext` type. -- Exported previously-internal auth helpers for building custom flows: `applyBasicAuth`, `applyPostAuth`, `applyPublicAuth`, `executeTokenRequest`. - -Transports are simplified internally — ~50 lines of inline OAuth orchestration (auth() calls, WWW-Authenticate parsing, circuit-breaker state) moved into the adapter's `onUnauthorized()` implementation. `OAuthClientProvider` itself is unchanged. diff --git a/.changeset/twelve-dodos-taste.md b/.changeset/twelve-dodos-taste.md deleted file mode 100644 index 3f523fad00..0000000000 --- a/.changeset/twelve-dodos-taste.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/express': patch ---- - -Add jsonLimit option to createMcpExpressApp diff --git a/.changeset/use-scopes-supported-in-dcr.md b/.changeset/use-scopes-supported-in-dcr.md deleted file mode 100644 index d40da05f0e..0000000000 --- a/.changeset/use-scopes-supported-in-dcr.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -'@modelcontextprotocol/client': minor ---- - -Apply resolved scope consistently to both DCR and the authorization URL (SEP-835) - -When `scopes_supported` is present in the protected resource metadata (`/.well-known/oauth-protected-resource`), the SDK already uses it as the default scope for the authorization URL. This change applies the same resolved scope to the dynamic client registration request body, ensuring both use a consistent value. - -- `registerClient()` now accepts an optional `scope` parameter that overrides `clientMetadata.scope` in the registration body. -- `auth()` now computes the resolved scope once (WWW-Authenticate → PRM `scopes_supported` → `clientMetadata.scope`) and passes it to both DCR and the authorization request. diff --git a/.changeset/wire-public-separation.md b/.changeset/wire-public-separation.md deleted file mode 100644 index 5e88cf5895..0000000000 --- a/.changeset/wire-public-separation.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': patch -'@modelcontextprotocol/client': patch -'@modelcontextprotocol/server': patch ---- - -Freeze the per-era wire schemas as self-contained copies decoupled from the public types layer, and convert `WireCodec` to a function-only interface. Two small spec-conformance fixes ride along with the otherwise-pure refactor: - -- The 2026 wire-true `resultType` member now defaults to `'complete'` when absent (the spec's receiver-side back-compat rule); the inbound `decodeResult` step continues to require it. The `server/discover` result accepts absent or malformed `ttlMs`/`cacheScope` (falling back to `0`/`'private'` per the spec's receiver leniency in caching.mdx) so the version-negotiation probe classifier stays behavior-neutral. Other cacheable result schemas are unchanged here; general receiver leniency for those belongs to the response-cache surface. -- The sampling `hasTools` discriminant now keys on `tools || toolChoice` (previously `tools` only), aligning the client and server selection of the with-tools result variant with `clientCapabilityRequirements`. diff --git a/.changeset/wire-server-discover.md b/.changeset/wire-server-discover.md deleted file mode 100644 index ea8eaea832..0000000000 --- a/.changeset/wire-server-discover.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': minor -'@modelcontextprotocol/server': minor -'@modelcontextprotocol/client': minor ---- - -Wire `server/discover` (protocol revision 2026-07-28) into the typed request funnel and serve it era-aware. The request joins `ClientRequestSchema`/`ServerResultSchema`/`ResultTypeMap` (per-era availability stays with the wire registries: only the 2026-era registry serves -it), and `Client.discover()` issues it as a typed request on 2026-era connections. A `Server` whose `supportedProtocolVersions` list carries a modern (2026-07-28+) revision installs the `server/discover` handler, advertising ONLY its modern revisions and excluding the -listChanged/subscribe-class capabilities until the `subscriptions/listen` flow ships; servers with today's default list are unchanged and keep answering `-32601`. The `initialize` handshake is now era-aware in the other direction: its accept check and counter-offer consult -only the legacy subset of the supported versions — a 2026-era revision is never negotiated via `initialize` — so a 2025-era client can never be offered a 2026 version string; with the default list this is byte-identical to previous behavior. Serving the 2026 revision to -ordinary HTTP/stdio traffic arrives with an upcoming server-side entry point: today the negotiation surface is client-side, and `mode: 'auto'` falls back cleanly against current SDK servers. diff --git a/.changeset/workerd-shim-vendors-cfworker.md b/.changeset/workerd-shim-vendors-cfworker.md deleted file mode 100644 index 38c8222913..0000000000 --- a/.changeset/workerd-shim-vendors-cfworker.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': minor -'@modelcontextprotocol/client': patch -'@modelcontextprotocol/server': patch ---- - -Bundle automatic JSON Schema validator defaults in `@modelcontextprotocol/client` and `@modelcontextprotocol/server` runtime shims. - -Client and server pick the right validator automatically based on the runtime: the Node shim uses AJV, the browser/workerd shim uses `@cfworker/json-schema`. Both backends are bundled into the shim chunks that select them, so the default code path needs no extra installs — `import { McpServer } from '@modelcontextprotocol/server'` does not pull `ajv` or `@cfworker/json-schema` into the root entry chunk. - -The named validator classes remain part of the public surface for consumers who want to customize the built-in backend (pre-register schemas by `$id`, register custom AJV formats, switch dialects, change `@cfworker/json-schema` draft). They are exposed through explicit subpaths so they do not bloat the root index chunk: - -- `import { AjvJsonSchemaValidator } from '@modelcontextprotocol/{client,server}/validators/ajv'` -- `import { CfWorkerJsonSchemaValidator } from '@modelcontextprotocol/{client,server}/validators/cf-worker'` - -Importing from one of these subpaths means the corresponding peer dep (`ajv` + `ajv-formats`, or `@cfworker/json-schema`) must be in your `package.json`. The shim keeps its own vendored copy for the default path, so a project can use the subpath in some files and rely on the default in others. - -The `jsonSchemaValidator` interface remains the public extension point for replacing validation entirely with a custom implementation. diff --git a/.changeset/wraphandler-hook.md b/.changeset/wraphandler-hook.md deleted file mode 100644 index 114ebf0332..0000000000 --- a/.changeset/wraphandler-hook.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': patch -'@modelcontextprotocol/client': patch -'@modelcontextprotocol/server': patch ---- - -refactor: subclasses override `_wrapHandler` hook instead of redeclaring `setRequestHandler`. diff --git a/.changeset/zod-json-schema-compat.md b/.changeset/zod-json-schema-compat.md deleted file mode 100644 index b3180fb06b..0000000000 --- a/.changeset/zod-json-schema-compat.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': patch ---- - -Allow additional JSON Schema properties in elicitInput's requestedSchema type by adding .catchall(z.unknown()), matching the pattern used by inputSchema. This fixes type incompatibility when using Zod v4's .toJSONSchema() output which includes extra properties like $schema and additionalProperties. diff --git a/.changeset/zod-jsonschema-fallback.md b/.changeset/zod-jsonschema-fallback.md deleted file mode 100644 index a02662da12..0000000000 --- a/.changeset/zod-jsonschema-fallback.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@modelcontextprotocol/core-internal': patch -'@modelcontextprotocol/server': patch -'@modelcontextprotocol/client': patch ---- - -Fix runtime crash on `tools/list` when a tool's `inputSchema` comes from zod 4.0–4.1. The SDK requires `~standard.jsonSchema` (StandardJSONSchemaV1, added in zod 4.2.0); previously a missing `jsonSchema` crashed at `undefined[io]`. `standardSchemaToJsonSchema` now detects zod 4 schemas lacking `jsonSchema` and falls back to the SDK-bundled `z.toJSONSchema()`, emitting a one-time console warning. zod 3 schemas (which the bundled zod 4 converter cannot introspect) and non-zod schema libraries without `jsonSchema` get a clear error pointing to `fromJsonSchema()`. The workspace zod catalog is also bumped to `^4.2.0`.