Skip to content

TypeScript SDK v1 to v2

Spec
2025-11-25 → 2026-07-28
Evidence
E2Reproducedarm64
TypeScript SDK v1
@modelcontextprotocol/sdk@1.32.2
TypeScript SDK (server)
@modelcontextprotocol/server@2.4.0
TypeScript SDK (client)
@modelcontextprotocol/client@2.4.0
Verified

Version 2 of the TypeScript SDK splits the single @modelcontextprotocol/sdk package into separate packages, requires zod 4, and adds the entry points that serve the 2026-07-28 protocol. Version 1 (@modelcontextprotocol/sdk@1.32.2) is still published, and v1 clients and servers keep working with v2 ones over the 2025-11-25 protocol, so you can migrate one side at a time.

v1 v2
Package @modelcontextprotocol/sdk @modelcontextprotocol/server, @modelcontextprotocol/client, plus adapters such as @modelcontextprotocol/node and @modelcontextprotocol/express
Imports …/sdk/server/mcp.js, …/sdk/server/stdio.js @modelcontextprotocol/server, @modelcontextprotocol/server/stdio
Schemas zod 3 or 4; raw shapes like { a: z.number() } zod 4.2 or later (any Standard Schema library); wrap with z.object()
Start a stdio server await server.connect(new StdioServerTransport()) serveStdio(() => createServer())
Start an HTTP server StreamableHTTPServerTransport createMcpHandler(() => createServer())
Protocol Up to 2025-11-25 2025-11-25 and 2026-07-28
Node.js 20 or later

We wrote a small server against @modelcontextprotocol/sdk 1.32.2 and ported it.

Before (v1):

import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';
const server = new McpServer({ name: 'notes', version: '1.0.0' });
server.registerTool(
'add',
{
description: 'Add two numbers.',
inputSchema: { a: z.number(), b: z.number() },
outputSchema: { sum: z.number() },
},
async ({ a, b }) => {
const output = { sum: a + b };
return { content: [{ type: 'text', text: JSON.stringify(output) }], structuredContent: output };
},
);
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('notes: ready on stdio');

After (v2):

import { McpServer } from '@modelcontextprotocol/server';
import { serveStdio } from '@modelcontextprotocol/server/stdio';
import * as z from 'zod';
serveStdio(() => {
const server = new McpServer({ name: 'notes', version: '2.0.0' });
server.registerTool(
'add',
{
description: 'Add two numbers.',
inputSchema: z.object({ a: z.number(), b: z.number() }),
outputSchema: z.object({ sum: z.number() }),
},
async ({ a, b }) => {
const output = { sum: a + b };
return { content: [{ type: 'text', text: JSON.stringify(output) }], structuredContent: output };
},
);
return server;
});
console.error('notes: ready on stdio');

Three changes: the imports, z.object() around the schemas, and building the server inside a factory passed to serveStdio(). The tool code itself did not change.

The SDK team publishes a codemod. Run it at the package root, not in src/, because it also edits package.json:

TerminalRewrite imports and schemas
Terminal window
npx -y @modelcontextprotocol/codemod@2.4.0 v1-to-v2 .
grep -rn '@mcp-codemod-error' .

On our v1 example it rewrote the imports, wrapped both raw schemas in z.object(), replaced @modelcontextprotocol/sdk with @modelcontextprotocol/server in package.json, and printed this warning:

Warning: zod range '3.25.76' cannot satisfy v2's floor: zod >=4.2.0 is required.

It did not change server.connect(transport). That is deliberate, and it matters for the next two sections.

If you ignore the zod warning, the server starts and even answers tools/call. It fails on tools/list, which is the first thing a client does:

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().

Fix: npm install zod@^4.2.0. The v2 packages depend on zod 4 themselves, so a project that pins zod 3 ends up with two copies.

Trap 2: connect() stays on the old protocol

Section titled “Trap 2: connect() stays on the old protocol”

StdioServerTransport still exists in v2, and server.connect(new StdioServerTransport()) still runs. But it only speaks 2025-11-25. We connected the TypeScript client 2.4.0 with versionNegotiation: { mode: 'auto' } to each version of the server:

Server Negotiated
v1, connect() 2025-11-25
v2 after the codemod, connect() 2025-11-25
v2, serveStdio() 2026-07-28

So after the codemod, move the entry point to serveStdio() yourself. For HTTP servers the equivalent is createMcpHandler(); see Migrate from 2025-11-25 to 2026-07-28.

On the client side the package is @modelcontextprotocol/client. Two things to know:

  • The v2 client still uses the 2025-11-25 handshake by default. Set versionNegotiation: { mode: 'auto' } to use 2026-07-28 when the server supports it.
  • client.callTool() no longer takes a result schema argument for spec methods; the codemod removes it.

Sources

MCPDev is community documentation. It is not affiliated with the Model Context Protocol project or with the vendors of the clients it covers.