Tool pages / MCP TypeScript SDK

MCP TypeScript SDK 2.3.1 and 1.32.1: latest versions, API diff and gotchas

Latest: MCP TypeScript SDK 2.3.1, released (release notes).

Last verified 2026-10-07 against 2.3.1. Page updated . Also as Markdown and JSON.

The latest MCP TypeScript SDK is 2.3.1 (@modelcontextprotocol/server and @modelcontextprotocol/client), released 2026-10-05 11:54 UTC; the v1.x line @modelcontextprotocol/sdk got 1.32.1 the same day. Install with npm i @modelcontextprotocol/server@latest or npm i @modelcontextprotocol/client@latest; migrate from v1 with npx @modelcontextprotocol/codemod@latest v1-to-v2 .. v2 implements the 2026-07-28 MCP revision, but a Client still connects with the 2025 initialize handshake unless you pass versionNegotiation: { mode: 'auto' }. Since 2.3.0 and 1.32.0 the HTTP client transports refuse cross-origin redirects, and since 2.3.0 an McpServer that is already connected rejects a second connect().

How do I install or upgrade to MCP TypeScript SDK 2.3.1?

v2 server (Node.js 20 or later):

npm i @modelcontextprotocol/server@latest zod@^4.2.0
npm ls @modelcontextprotocol/server   # @modelcontextprotocol/server@2.3.1

v2 client:

npm i @modelcontextprotocol/client@latest

Migrate a v1 project to v2 (run at the package root):

npx @modelcontextprotocol/codemod@latest v1-to-v2 .
grep -rn '@mcp-codemod-error' .
npx tsc --noEmit

Optional v2 framework adapters (versioned separately: node 2.1.1, express 2.0.2, hono 2.0.2, fastify 2.0.1):

npm i @modelcontextprotocol/node        # IncomingMessage/ServerResponse
npm i @modelcontextprotocol/express express
npm i @modelcontextprotocol/hono hono
npm i @modelcontextprotocol/fastify fastify

Stay on v1.x (maintenance line):

npm i @modelcontextprotocol/sdk@^1.32.1 zod

MCP TypeScript SDK key facts

What changed in the last 5 MCP TypeScript SDK releases?

MCP TypeScript SDK 2.3.1 (2026-10-05 11:54 UTC)

  • requireBearerAuth in @modelcontextprotocol/server-legacy takes the optional expectedResource (token audience check) that @modelcontextprotocol/server 2.3.0 added; off unless set. (source)
  • The npm pages of @modelcontextprotocol/server and client open with a note linking the docs, the migration guide and the issue form. node, express, hono, fastify unchanged. (source)

MCP TypeScript SDK 1.32.1 (2026-10-05 11:42 UTC)

  • v1.x (@modelcontextprotocol/sdk): documentation only. The README now points at v2 and states that v1.x implements the spec up to 2025-11-25 and will not get 2026-07-28. (source)

MCP TypeScript SDK 2.3.0 (2026-10-02 17:55 UTC)

  • One server per request: Server.connect() rejects while the instance is already connected, and a stateless Streamable HTTP transport handles one request. Create the McpServer inside the request handler or the createMcpHandler factory. (source)
  • HTTP client transports follow a redirect only within the same origin; set redirectPolicy: 'follow' or use the final URL. (source)
  • New opt-in options: maxToolInputElements on McpServer; expectedResource on requireBearerAuth / verifyBearerToken (upgrade @modelcontextprotocol/express with the server). (source)
  • The client requires eventsource-parser 3.0.8 or later; a 50 MB tool result in one SSE event now arrives in under a second instead of about 13 seconds. (source)
  • prompts/get without arguments is validated as {}, so prompts whose arguments are all optional work. (source)

MCP TypeScript SDK 1.32.0 (2026-10-02 17:28 UTC)

  • v1.x: HTTP client transports follow redirects only within the endpoint's origin (redirectPolicy: 'follow' restores the old behaviour). (source)
  • v1.x: maxToolInputElements option on McpServer. (source)
  • v1.x: expectedResource option on requireBearerAuth. (source)
  • v1.x: tasks of the in-memory task store stay within the session that created them. (source)
  • v1.x: tools/call and prompts/get requests that omit arguments are accepted. (source)

MCP TypeScript SDK 2.2.0 (2026-09-28 19:24 UTC)

  • Pass expectedIssuer to ClientCredentialsProvider, PrivateKeyJwtProvider, StaticPrivateKeyJwtProvider and CrossAppAccessProvider; omitting it is deprecated and logs a warning. fetchToken() throws AuthorizationServerMismatchError when stored client information belongs to another authorization server. (source)
  • listTools(), listPrompts(), listResources() and listResourceTemplates() without a cursor now follow nextCursor to the end; listMaxPages caps the walk. (source)
  • CommonJS TypeScript projects type-check again (the jose types are inlined; regression in 2.1.0). (source)

Which MCP TypeScript SDK exports and members were added or removed?

MCP TypeScript SDK 2.2.0 → 2.3.1

2 exports added, 0 removed, 4 exports with public members added or removed; 17 declarations changed in any way.

Method: bin/tool-snapshot installed both versions in a throwaway Linux sandbox and listed every export of every package entry point from the type declarations with the TypeScript compiler API (1079 and 1081 exported symbols), then bin/tool-diff compared them. In the tables, "command" is an exported symbol and "flags" are its public members (function parameters in parentheses).

Exports added:

  • @modelcontextprotocol/server McpServerOptions
  • @modelcontextprotocol/server VerifyBearerTokenOptions
ExportMembers addedMembers removed
@modelcontextprotocol/client SSEClientTransportOptionsredirectPolicynone
@modelcontextprotocol/client StreamableHTTPClientTransportOptionsredirectPolicynone
@modelcontextprotocol/server-legacy BearerAuthMiddlewareOptionsexpectedResourcenone
@modelcontextprotocol/server-legacy/auth BearerAuthMiddlewareOptionsexpectedResourcenone
  • v2 line: @modelcontextprotocol/server, client, core and server-legacy at the latest patch of the last two minors (2.2.0 and 2.3.1). 2.3.1 itself changed no export; the members above arrived in 2.3.0.
  • McpServerOptions is ServerOptions & { maxToolInputElements?: number }; the McpServer constructor now takes it. VerifyBearerTokenOptions adds expectedResource?: URL and is the new parameter type of requireBearerAuth and verifyBearerToken.
  • redirectPolicy?: 'same-origin' | 'follow' on both client HTTP transports is the switch for the redirect change below. The declaration text of Server.connect() now says it "rejects while this instance is connected to another one".
  • Protocol constants read at runtime are unchanged in both versions: LATEST_PROTOCOL_VERSION = 2025-11-25, SUPPORTED_PROTOCOL_VERSIONS = 2025-11-25, 2025-06-18, 2025-03-26, 2024-11-05, 2024-10-07. The 2026-07-28 revision is negotiated separately (versionNegotiation, see the gotchas).

MCP TypeScript SDK 1.31.0 → 1.32.1

6 exports added, 0 removed, 3 exports with public members added or removed; 18 declarations changed in any way.

Method: bin/tool-snapshot installed both versions in a throwaway Linux sandbox and listed every export of every package entry point from the type declarations with the TypeScript compiler API (1044 and 1050 exported symbols), then bin/tool-diff compared them. In the tables, "command" is an exported symbol and "flags" are its public members (function parameters in parentheses).

Exports added:

  • @modelcontextprotocol/sdk/server/mcp.js McpServerOptions
  • @modelcontextprotocol/sdk/shared/transport.js fetchLeavingRedirects
  • @modelcontextprotocol/sdk/shared/transport.js fetchWithinOrigin
  • @modelcontextprotocol/sdk/shared/transport.js followWithinOrigin
  • @modelcontextprotocol/sdk/shared/transport.js isWithinOrigin
  • @modelcontextprotocol/sdk/shared/transport.js unfollowedRedirect
ExportMembers addedMembers removed
@modelcontextprotocol/sdk/client/sse.js SSEClientTransportOptionsredirectPolicynone
@modelcontextprotocol/sdk/client/streamableHttp.js StreamableHTTPClientTransportOptionsredirectPolicynone
@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js BearerAuthMiddlewareOptionsexpectedResourcenone
  • v1.x line: @modelcontextprotocol/sdk 1.31.0 and 1.32.1, every exports entry point including each ./* deep path (69 entry points). 1.32.1 itself changed no export; the changes arrived in 1.32.0.
  • The same three options as v2 2.3.0 were backported: redirectPolicy on StreamableHTTPClientTransportOptions and SSEClientTransportOptions, maxToolInputElements (new McpServerOptions), expectedResource on requireBearerAuth. The five new shared/transport.js functions are the redirect helpers.
  • InMemoryTaskStore methods now use their sessionId parameter (it was _sessionId, unused): tasks stay in the session that created them (PR #2925).
  • Protocol constants are the same as v2's: LATEST_PROTOCOL_VERSION = 2025-11-25. v1.x does not implement 2026-07-28 and will not.

What breaks when upgrading MCP TypeScript SDK, and how do I migrate?

VersionChangeMigration
2.3.0Server.connect() rejects while the instance is connected to another transport; a stateless Streamable HTTP transport serves one request. A server instance shared across requests or connections now fails.Create the McpServer (and its transport) per request, or return a fresh instance from the createMcpHandler / serveStdio factory. Call close() before reconnecting an instance.
2.3.0 and 1.32.0The Streamable HTTP and SSE client transports follow a redirect only when it stays on the same origin (scheme, host and port; http to https on the same host is allowed). In browsers a redirected request fails unless the option is set.Configure the final URL as the endpoint, or pass redirectPolicy: 'follow' to StreamableHTTPClientTransport / SSEClientTransport.
2.2.0Machine-to-machine OAuth providers without expectedIssuer are deprecated (warning). fetchToken() throws AuthorizationServerMismatchError before sending anything when the stored client information is bound to another authorization server. List calls without a cursor now return the whole list.Set expectedIssuer to the issuer of the authorization server the credentials were registered with. Pass an explicit cursor or listMaxPages if you relied on one page.
1.31.0v1.x: stored OAuth tokens and client information include an issuer field; constructing ClientCredentialsProvider, PrivateKeyJwtProvider or StaticPrivateKeyJwtProvider without expectedIssuer is deprecated.Let your token storage accept the issuer field; pass expectedIssuer.
2.0.0@modelcontextprotocol/sdk was split into @modelcontextprotocol/server and @modelcontextprotocol/client (plus core and optional node, express, hono, fastify adapters). Deep @modelcontextprotocol/sdk/...js imports do not exist in v2; stdio transports moved to ./stdio subpaths; SSEServerTransport and WebSocketClientTransport were removed; Node.js 20 or later is required.npx @modelcontextprotocol/codemod@latest v1-to-v2 . at the package root, then fix what grep -rn '@mcp-codemod-error' . and tsc --noEmit report. A frozen v1 SSE server transport is in @modelcontextprotocol/server-legacy/sse.
2.0.0Zod 3 is no longer supported (v1's peer range was ^3.25 || ^4.0). Schemas take any Standard Schema that can produce JSON Schema; raw { field: z.string() } shapes are deprecated.Upgrade to zod ^4.2.0 and wrap shapes in z.object(), or pass plain JSON Schema through fromJsonSchema().

What MCP TypeScript SDK errors did we reproduce, and how do I fix them?

A v2 `Client` talks the 2025 protocol unless you opt in, even to a v2 server (MCP TypeScript SDK 2.3.1)

// client 2.3.1 over stdio to a server built with @modelcontextprotocol/sdk 1.32.1 and one built with @modelcontextprotocol/server 2.3.1 (serveStdio)
new Client(info)                                                  // default
new Client(info, { versionNegotiation: { mode: 'auto' } })
new Client(info, { versionNegotiation: { mode: { pin: '2026-07-28' } } })
console.log(client.getProtocolEra())
server sdk 1.32.1 | default | era=legacy | Hello, Ada
server sdk 1.32.1 | mode: 'auto' | era=legacy | Hello, Ada
server sdk 1.32.1 | pin: '2026-07-28' | SdkError ERA_NEGOTIATION_FAILED: Version negotiation failed: the server did not offer pinned protocol version 2026-07-28 via server/discover (no fallback in pin mode)
server 2.3.1 (serveStdio) | default | era=legacy | Hello, Ada
server 2.3.1 (serveStdio) | mode: 'auto' | era=modern | Hello, Ada
server 2.3.1 (serveStdio) | pin: '2026-07-28' | era=modern | Hello, Ada

Fix: Pass versionNegotiation: { mode: 'auto' } to reach 2026-07-28 servers and fall back to initialize for older ones. Use a pin only where a 2025 connection is unacceptable: it never falls back. The docs advise against 'auto' as the default of a spawn-per-invocation stdio CLI (each probe spawns an extra server process). (source)

`client.listen()` fails on a 2025-era connection (MCP TypeScript SDK 2.3.1)

const c = new Client({ name: 'probe', version: '1.0.0' });   // default negotiation
await c.connect(new StdioClientTransport({ command: 'node', args: ['srv-v2.mjs'] }));
await c.listen({ resourceSubscriptions: ['file:///logs/app.log'] });
SdkError METHOD_NOT_SUPPORTED_BY_PROTOCOL_VERSION: subscriptions/listen requires a 2026-07-28-era connection (negotiated: 2025-11-25). On a 2025-era connection, change notifications are delivered unsolicited: use ClientOptions.listChanged and resources/subscribe instead.

Fix: Connect with versionNegotiation: { mode: 'auto' } to a server that serves 2026-07-28, or use ClientOptions.listChanged and resources/subscribe on a 2025-era connection. The error is raised locally; nothing is sent. (source)

Zod 3 schemas register fine, then `tools/list` fails (MCP TypeScript SDK 2.3.1)

npm i @modelcontextprotocol/server@2.3.1 @modelcontextprotocol/client@2.3.1 zod@^3.25.0   # installs zod 3.25.76
# import { z } from 'zod'; server.registerTool('greet', { inputSchema: z.object({ name: z.string() }) }, ...)
# then client.listTools(); second run with a raw shape { name: z.string() }
registerTool (z.object): ok
connect: ok
listTools | ProtocolError -32603: Schema appears to be from zod 3, which the SDK cannot convert to JSON Schema. Upgrade to zod >=4.2.0, or wrap your JSON Schema with fromJsonSchema().

registerTool (raw shape) | TypeError: Raw-shape inputSchema/outputSchema/argsSchema fields must be Zod v4 schemas. Got a Zod v3 field schema. Import from `zod/v4` (or upgrade your zod import), or wrap with `z.object({...})` yourself.

Fix: Upgrade to zod ^4.2.0 (check the declared range in package.json, not just the installed version) or wrap plain JSON Schema with fromJsonSchema(). The upgrade guide says such code type-checks cleanly; in our run tsc 6.0.3 with strict rejected the z.object() call with error TS2769: No overload matches this call (Property 'jsonSchema' is missing), so a type check may catch it first. (source)

`StdioServerTransport` and `SSEServerTransport` are not exported from `@modelcontextprotocol/server` (MCP TypeScript SDK 2.3.1)

import { SSEServerTransport } from '@modelcontextprotocol/server';
import { StdioServerTransport } from '@modelcontextprotocol/server';
# npx tsc --noEmit --module nodenext --moduleResolution nodenext --strict imports.mts; node imports-rt.mjs
imports.mts(2,10): error TS2305: Module '"@modelcontextprotocol/server"' has no exported member 'SSEServerTransport'.
imports.mts(3,10): error TS2305: Module '"@modelcontextprotocol/server"' has no exported member 'StdioServerTransport'.

SyntaxError: The requested module '@modelcontextprotocol/server' does not provide an export named 'StdioServerTransport'

Fix: import { StdioServerTransport, serveStdio } from '@modelcontextprotocol/server/stdio' (client side: @modelcontextprotocol/client/stdio). For SSE, serve Streamable HTTP instead; the frozen v1 copy is import { SSEServerTransport } from '@modelcontextprotocol/server-legacy/sse'. (source)

`import ... from '@modelcontextprotocol/sdk'` (v1.x package root) fails (MCP TypeScript SDK 1.32.1)

import * as sdk from '@modelcontextprotocol/sdk';   # tsc --module nodenext, then node
v1-root.mts(2,22): error TS2307: Cannot find module '@modelcontextprotocol/sdk' or its corresponding type declarations.

Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/root/v1/node_modules/@modelcontextprotocol/sdk/dist/esm/index.js' imported from /root/v1/v1-root.mjs

Fix: v1.x has no root module: exports["."] points at dist/esm/index.js, which none of the seven published tarballs we checked (1.0.0 to 1.32.1) contains. Import deep paths with .js: @modelcontextprotocol/sdk/server/mcp.js, @modelcontextprotocol/sdk/server/stdio.js, @modelcontextprotocol/sdk/client/index.js, @modelcontextprotocol/sdk/types.js. (source)

Reusing one `McpServer` for a second connection now rejects (MCP TypeScript SDK 2.3.1)

const server = new McpServer({ name: 's', version: '1.0.0' });
await server.connect(a);
await server.connect(b);   // a, b: InMemoryTransport halves
server 2.2.0: second connect() resolved (no error)
server 2.3.1: second connect() | SdkError: Use a separate Server instance per connection: this instance is already connected to a transport. To connect it to a new transport, call close() first.

Fix: Build a fresh McpServer per connection or per HTTP request (the createMcpHandler / serveStdio factory does this); call close() before reusing an instance. (source)

HTTP client stops at a redirect to another origin (MCP TypeScript SDK 2.3.1)

# :3200 answers every request with 307 to http://127.0.0.1:3100/mcp (createMcpHandler, server 2.3.1)
await client.connect(new StreamableHTTPClientTransport(new URL('http://127.0.0.1:3200/mcp')));
@modelcontextprotocol/client 2.2.0: connected through the redirect, tool says: Hello, Ada
@modelcontextprotocol/client 2.3.1: SdkHttpError CLIENT_HTTP_NOT_IMPLEMENTED: Error POSTing to endpoint: Redirect to http://127.0.0.1:3100/mcp not followed; use that URL as the endpoint if it is the intended server (redirectPolicy: 'same-origin')
@modelcontextprotocol/sdk 1.31.0: connected through the redirect, tool says: Hello, Ada
@modelcontextprotocol/sdk 1.32.1: StreamableHTTPError 307: Streamable HTTP error: Error POSTing to endpoint: Redirect to http://127.0.0.1:3100/mcp not followed (redirectPolicy: 'same-origin')

Fix: Use the final URL as the endpoint, or opt back in with new StreamableHTTPClientTransport(url, { redirectPolicy: 'follow' }). A different port is a different origin, so local proxies and dev servers on another port trip this too. (source)

MCP TypeScript SDK release history by month

2026-10: v2 2.3.0 to 2.3.1; v1.x 1.32.0 to 1.32.1 (4 versions to 2026-10-07)

  • 2.3.0 and 1.32.0 (2026-10-02): same-origin redirects only, maxToolInputElements, expectedResource; 2.3.0 also one server instance per connection. (source)
  • 2.3.1 and 1.32.1 (2026-10-05): expectedResource in server-legacy; v1.x README states its 2025-11-25 limit. (source)

2026-09: v2 2.1.0 to 2.2.0; v1.x 1.30.1 to 1.31.0 (4 versions)

  • 2.1.0 (2026-09-23): request-time OAuth scope challenges (scopeChallenge, requireScopes) on the server; DPoP sender-constrained tokens on the client. (source)
  • 1.30.1 (2026-09-23): HTTP request bodies read with a size limit and JSON-RPC batch length bounded. (source)
  • 2.2.0 and 1.31.0 (2026-09-28): OAuth credentials bound to the issuing authorization server (expectedIssuer); v2 list calls follow nextCursor. (source)

2026-08: no releases

  • No package was published in August 2026; the next releases were 2.1.0 and 1.30.1 on 2026-09-23. (source)

2026-07: v2 2.0.0-beta.2 to 2.0.0 (stable 2026-07-27); v1.x 1.30.0

  • 2.0.0 (2026-07-27): split packages @modelcontextprotocol/server / client / core, Standard Schema, serveStdio() and createMcpHandler(), the 2026-07-28 revision, and the v1-to-v2 codemod. (source)
  • 1.30.0 (2026-07-27): stdio buffer limit, SSE keep-alive frames on the Streamable HTTP server transport, Content-Type parsed by media type, Zod 3.25 method literals. (source)

2026-06: v2 prereleases only: 2.0.0-alpha.3, 2.0.0-alpha.4, 2.0.0-beta.1

  • 2.0.0-beta.1 (2026-06-30): first v2 beta, built for the 2026-07-28 spec; passes the MCP conformance suite except tasks. (source)

2026-05: no releases

  • No release in May 2026. The last v1 release before it was 1.29.0 (2026-03-30); v2 2.0.0-alpha.2 came out on 2026-04-01. (source)

Sources

Last verified: 2026-10-07.