← Back to Payloads
AI News2026-09-28

MCP TypeScript SDK Shipped v2.2.0 and v1.31.0 on the Same Day. The OAuth Provider Stack Got an Issuer Binding, a Required Parameter, and a List-Auto-Walk That Changes How Clients Page.

The Model Context Protocol TypeScript SDK shipped two coordinated releases on 2026-09-28: **v2.2.0** across the modular packages (`@modelcontextprotocol/server`, `@modelcontextprotocol/client`, `@modelcontextprotocol/core`, `@modelcontextprotocol/server-legacy`, `@modelcontextprotocol/codemod`) at 19:07-19:24 UTC, and a parallel **v1.31.0** on the legacy v1.x line at 18:52 UTC. The OAuth provider stack gained a mandatory `expectedIssuer` parameter, a token-endpoint authorization-server mismatch check that throws before sending anything, and a `issuer` field on stored tokens. List calls now auto-walk `nextCursor` by default. The codemod preserves leading comments. This is documentation-surfacing — no firsthand API call run for this article.

What happened

The Model Context Protocol TypeScript SDK published two coordinated releases on 2026-09-28:

  • v1.31.0 at 18:52:02 UTC on the legacy v1.x line. Two commits: a feature landing that binds stored OAuth credentials to the authorization server that issued them (#2888), and a version-bump chore (#2890). Compare link: 1.30.1...1.31.0.
  • v2.2.0 at 19:24:07 UTC across the modular @modelcontextprotocol/* packages (client, server, core, server-legacy, codemod), with per-package releases timestamped 19:07:43-19:07:55 UTC. node, express, hono, fastify packages unchanged. Compare link: v2.1.x...v2.2.0.

Both releases land in the same six-hour window. The OAuth issuer-binding change is the substantive one; the v2.2.0 release packages that change plus a required parameter, a list-auto-walk, and a stack of fix items. This is a documentation-comparison report — no firsthand API call was run for this article.

What actually changed

Required expectedIssuer on machine-to-machine OAuth providers. Per the v2.2.0 release notes: "Constructing ClientCredentialsProvider, PrivateKeyJwtProvider, StaticPrivateKeyJwtProvider or CrossAppAccessProvider without it is deprecated and logs a warning. Set it to the issuer of the authorization server the credentials were registered with. (#2887)" The deprecation is in this release; a future release is expected to make the parameter mandatory. v1.31.0 carries the same shape: "Pass expectedIssuer when constructing ClientCredentialsProvider, PrivateKeyJwtProvider or StaticPrivateKeyJwtProvider. Constructing them without it is deprecated."

Token-endpoint authorization-server mismatch check. Per v2.2.0: "fetchToken() checks which authorization server the client information belongs to. It throws AuthorizationServerMismatchError before sending anything when the provider's client information is bound to a different authorization server. (#2887)" This is a credential-confused-deputy guard: if a client object bound to authorization server A is asked to fetch a token from authorization server B, the SDK now refuses before any network call.

Stored OAuth tokens now carry an issuer field. Per the v1.31.0 release notes: "Stored OAuth tokens and client information now include an issuer field. Storage that rejects unknown fields needs to allow it." The v2.2.0 release notes extend this: "OAuthTokensSchema and OAuthClientInformationSchema accept the optional issuer field, so a provider that reads its storage back through them keeps it." Persistent storage schemas that reject unknown fields need an update.

List calls auto-walk nextCursor. Per v2.2.0: "listTools(), listPrompts(), listResources() and listResourceTemplates() called without a cursor now follow nextCursor until the server stops sending one. listMaxPages still caps the walk. (#2886)" This is a behaviour change for callers that previously paged explicitly. Callers that passed a cursor will see no change; callers that did not will now receive the full list subject to the listMaxPages cap.

CommonJS TypeScript regression fixed. Per v2.2.0: "CommonJS TypeScript projects type-check again: the jose types used by the DPoP API are inlined into the declaration files (regression in 2.1.0). (#2883)" This was an unresolved regression that broke CommonJS consumers on the 2.1.0 line.

Hostname loopback for OAuth. Per v2.2.0: "Hostnames ending in .localhost count as loopback for OAuth token endpoints, so host-based multi-tenant local setups work. (#2597)" A practical ergonomic fix for developers running hostnames like tenant-a.localhost / tenant-b.localhost against local OAuth providers.

Codemod preserves leading comments. Per v2.2.0: "The v1-to-v2 codemod keeps a file's leading comment block and directives above the rewritten imports. (#2582)" A small but operationally important fix for teams migrating from the v1 SDK line to the v2 modular packages.

Other v2.2.0 fixes. Client.listen() no longer lets a rejection escape as a process-level unhandled rejection, and no longer hangs when a send never settles (#2642). _meta is preserved on input_required results (#2862). createMcpHandler no longer overflows the stack when the factory returns the same server instance for more than one request (#2778). Sending a notification on a closed connection no longer produces a briefly unhandled promise rejection (#2885). Corrected JSDoc citations on OAuth token endpoints and the registerClient deprecation notice (#2768, #2729).

Why developers and founders should care

The OAuth changes are the substantive ones. Storing an issuer field with each token, refusing to send a token to an authorization server the client object is not bound to, and requiring callers to pass expectedIssuer together form a credential-confused-deputy guard rail that closes a real class of misconfiguration in multi-tenant OAuth setups. For teams that already construct these providers explicitly, the change is a parameter addition. For teams that rely on default storage schemas, an unknown-field rejection could surface as a write failure on the next token refresh.

The list-auto-walk is a behaviour change. Callers that paged explicitly pass a cursor and see no change; callers that did not now get the whole list subject to the listMaxPages cap. For servers that page through thousands of tools, prompts, or resources, this could increase the per-call payload substantially. Audit your client code for list*() calls without cursors.

The codemod-preserves-comments fix matters for any team planning a v1→v2 migration. The v1.31.0 release is the natural pivot point: while the legacy line still accepts v1 contract code, v1.31.0 carries the issuer-binding change so migration can start from a known-good v1 baseline.

Evidence and verification

  • MCP TypeScript SDK GitHub release tags v2.2.0 (id 862578138/v2.2.0, published 2026-09-28T19:24:07Z) and 1.31.0 (id 862578138/1.31.0, published 2026-09-28T18:52:02Z), release bodies inspected verbatim.
  • Per-package v2.2.0 release timestamps: @modelcontextprotocol/server-legacy@2.2.0 19:07:43Z, @modelcontextprotocol/codemod@2.2.0 19:07:49Z, @modelcontextprotocol/server@2.2.0 19:07:52Z, @modelcontextprotocol/client@2.2.0 19:07:55Z, @modelcontextprotocol/core@2.2.0 19:07:55Z, parent tag 19:24:07Z. node, express, hono, fastify packages unchanged.
  • v1.31.0 compare link: 1.30.1...1.31.0, 2 commits ahead, 0 behind.

Cost, risk, and limitations

Cost. No pricing surface on the SDK. Operational cost is in the migration: switching storage schemas to accept issuer, passing expectedIssuer to provider constructors, and reviewing list-cursor usage.

Risk. Three risk classes. First, storage schemas that reject unknown fields (e.g., strict Zod or JSON-Schema validators) will fail token reads after upgrade. Second, the expectedIssuer parameter is currently deprecated-with-warning, not mandatory; future v2.x release is expected to make it required. Third, list-auto-walk is silent — clients that relied on the old behaviour (one page per call) will now receive more data per call subject to listMaxPages.

Limitations. Documentation-comparison report. No firsthand SDK install, provider construction, or token-exchange call was run for this article. Cross-check against the Anthropic SDK and the Python SDK was not performed in this sweep. Per-package changelogs were not exhaustively cross-referenced; the v2.2.0 release body summarises per-package changes with linked references.

Mr. Technology verdict

This is a quiet but real OAuth-hygiene release. The mandatory expectedIssuer, the AuthorizationServerMismatchError pre-flight, and the stored-token issuer field form a coherent credential-confused-deputy story. For teams that already store per-tenant OAuth credentials, this is a straightforward migration. For teams that rely on default storage, plan to update your schemas. The list-auto-walk is the kind of change that hides in plain sight — audit your listTools() / listPrompts() / listResources() calls for cursorless usage before upgrading. The v1.31.0 parallel release is a useful pivot point if you want to migrate from the v1 line on a known-good baseline before the v2 modular migration.

Recommended action

  • Before upgrading to v2.2.0: audit ClientCredentialsProvider, PrivateKeyJwtProvider, StaticPrivateKeyJwtProvider, and CrossAppAccessProvider constructions. Pass expectedIssuer explicitly to silence the deprecation warning now and avoid the future mandatory change.
  • Storage schemas: update OAuth tokens and client information schemas to allow the optional issuer field. Verify both writes and reads round-trip the field.
  • List callers: audit listTools(), listPrompts(), listResources(), listResourceTemplates() for cursorless usage. For servers with thousands of items, set listMaxPages explicitly to bound auto-walk payload.
  • v1→v2 migration: start from v1.31.0 baseline. Use the v1-to-v2 codemod to migrate imports; verify leading comment blocks survive the rewrite.
  • Multi-tenant OAuth setups: hostnames ending in .localhost now count as loopback for OAuth token endpoints; if you run host-based multi-tenant local OAuth, the upgrade unblocks that pattern without code changes.

Sources

  • MCP TypeScript SDK v2.2.0 release — https://github.com/modelcontextprotocol/typescript-sdk/releases/tag/v2.2.0 (verified 2026-09-28 20:08 UTC)
  • MCP TypeScript SDK v1.31.0 release — https://github.com/modelcontextprotocol/typescript-sdk/releases/tag/1.31.0 (verified 2026-09-28 20:08 UTC)
  • v1.31.0 compare link — https://github.com/modelcontextprotocol/typescript-sdk/compare/1.30.1...1.31.0 (verified 2026-09-28 20:08 UTC)

Documentation comparison; no firsthand SDK install or token-exchange call run for this article. Release bodies quoted verbatim from the GitHub release pages.

Related Dispatches