docs(migration): align four passages with the implementation (#2388)

This commit is contained in:
Felix Weinberger 2026-06-29 16:32:33 +01:00 • committed by GitHub
commit 7c42d476cd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 21 additions and 16 deletions

View file

@ -302,10 +302,13 @@ and the multi-round-trip retry fields (`inputResponses`, `requestState`).
- **`resultType` is gone from every public result type** (`Result`, `CallToolResult`,
`GetPromptResult`, …). The wire schemas keep parsing it, and the protocol layer
consumes it before results reach your code.
- **`DiscoverResult` strips its cache fields too.** `ttlMs` / `cacheScope` on
`server/discover` are wire-only — consumed by the client's response-cache layer and
absent from the public `DiscoverResult` type returned by `getDiscoverResult()`.
Tooling that displays the server's advertised cache policy must parse raw frames.
- **`DiscoverResult` hides its cache fields at the type level only.** `ttlMs` /
`cacheScope` on `server/discover` are read by the client's response-cache layer and
are absent from the public `DiscoverResult` type returned by `getDiscoverResult()` —
but they are not removed at runtime: the returned object still carries both, readable
via a cast. The wire parse defaults absent or malformed hints to `0` / `'private'`,
so only tooling that must distinguish an omitted hint from an advertised default
needs raw frames.
- **High-level methods return the named public types** (`client.callTool()` →
`Promise<CallToolResult>`, etc.). Handler return positions are unaffected.
- **Reserved envelope keys and retry fields appear in no public params/result type.**

View file

@ -393,9 +393,11 @@ prefer wrapping explicitly. Zod v4, ArkType, and Valibot all implement the spec.
**Zod v3 is no longer supported** (v1 peer was `^3.25 || ^4.0`). Check the **declared
range** in your `package.json`, not just the installed version: a zod-3 range that
satisfied the v1 peer installs and typechecks cleanly under v2 and only fails at
runtime, when the first registration throws — under a spawning harness that surfaces
as an opaque child exit two hops from the cause. A Zod v3 schema
hard-errors with a pointer at `fromJsonSchema()`. Zod **≥4.2.0** self-converts via
runtime — and quietly: registration swallows the conversion failure, the server starts
and connects normally, and the first `tools/list` (so `client.listTools()`) answers
with an error pointing at `fromJsonSchema()` while the process keeps running. (Only the
deprecated unwrapped raw-shape form with zod-3 field values throws at registration,
with a message pointing at `zod/v4`.) Zod **≥4.2.0** self-converts via
`~standard.jsonSchema` — the supported path. Zod **4.0–4.1** lacks it, so the SDK falls
back to its bundled Zod's `z.toJSONSchema()` with a one-time `[mcp-sdk]` console
warning; and because `.describe()` field descriptions live in the _authoring_ Zod's
@ -798,13 +800,12 @@ same handling as the POST send path.
methods plus `tokens()` / `clientInformation()`. On read, a stored value whose `issuer`
names a different AS is treated as `undefined` and the flow re-registers / re-authorizes.
**Round-trip the stored object verbatim and you're protected** — single-slot storage
works. The failure modes differ: a stamp naming a **different** AS reads back as
`undefined` and the flow re-registers / re-authorizes. A **missing** stamp (a
`saveTokens()` that rebuilds the object field-by-field and drops `issuer`, or
pre-upgrade storage) is used **as-is** with a `[mcp-sdk]` console warning — SEP-2352
isolation is silently inactive for that read; `auth()` re-stamps on first use where the
provider can persist it. If you see that warning repeatedly, your provider is not
round-tripping the stored object. To hold credentials for several authorization servers at once, key your storage
works. Dropping the stamp is easy to miss: a `saveTokens()` implementation that
rebuilds the object field-by-field and drops `issuer` leaves the value unstamped —
reads still succeed and refresh keeps working, the per-AS issuer check simply does not
apply to that credential, and every read logs an `[mcp-sdk]` warning (`auth()`
re-stamps on first use where the provider can persist it). If you see that warning
repeating after upgrading, check this first. To hold credentials for several authorization servers at once, key your storage
on `ctx.issuer` (treat **`ctx === undefined` as "return the most-recently-saved token
set"** — the transport's per-request `Authorization: Bearer` read calls `tokens()` with
no `ctx`). New TypeScript-only aliases `StoredOAuthTokens` / `StoredOAuthClientInformation`
@ -998,8 +999,9 @@ rewrite required unless noted.
#### Client connection & dispatch
- **`connect()` skips the `initialize` handshake when the transport already exposes a
`sessionId`** — it assumes it is reconnecting to an existing session (v1 always
initialized). A custom or test transport that sets `sessionId` at construction
`sessionId`** — it assumes it is reconnecting to an existing session (unchanged from
v1.x, where the same guard has existed since 1.10.0; recorded here because the
far-away symptom keeps surprising migrators). A custom or test transport that sets `sessionId` at construction
silently skips initialization: `getServerCapabilities()` stays `undefined` and the
list verbs return empty results. Expose `sessionId` only after the first request has
been sent.