From 4a0db70a42387eadffe685dbec895d8d4769bf18 Mon Sep 17 00:00:00 2001 From: Felix Weinberger <3823880+felixweinberger@users.noreply.github.com> Date: Mon, 2 Feb 2026 17:10:51 +0000 Subject: [PATCH] docs: add v1 to v2 migration guide (#1438) --- CLAUDE.md | 10 ++ docs/migration-SKILL.md | 216 ++++++++++++++++++++++++++++ docs/migration.md | 303 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 529 insertions(+) create mode 100644 docs/migration-SKILL.md create mode 100644 docs/migration.md diff --git a/CLAUDE.md b/CLAUDE.md index 0f6eaeec..da47e407 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -22,6 +22,16 @@ pnpm --filter @modelcontextprotocol/core test -- path/to/file.test.ts pnpm --filter @modelcontextprotocol/core test -- -t "test name" ``` +## Breaking Changes + +When making breaking changes, document them in **both**: + +- `docs/migration.md` — human-readable guide with before/after code examples +- `docs/migration-SKILL.md` — LLM-optimized mapping tables for mechanical migration + +Include what changed, why, and how to migrate. Search for related sections and group +related changes together rather than adding new standalone sections. + ## Code Style Guidelines - **TypeScript**: Strict type checking, ES modules, explicit return types diff --git a/docs/migration-SKILL.md b/docs/migration-SKILL.md new file mode 100644 index 00000000..66db6d3d --- /dev/null +++ b/docs/migration-SKILL.md @@ -0,0 +1,216 @@ +--- +name: migrate-v1-to-v2 +description: Migrate MCP TypeScript SDK code from v1 (@modelcontextprotocol/sdk) to v2 (@modelcontextprotocol/core, /client, /server). Use when a user asks to migrate, upgrade, or port their MCP TypeScript code from v1 to v2. +--- + +# MCP TypeScript SDK: v1 → v2 Migration + +Apply these changes in order: dependencies → imports → API calls → type aliases. + +## 1. Environment + +- Node.js 20+ required (v18 dropped) +- ESM only (CJS dropped). If the project uses `require()`, convert to `import`/`export` or use dynamic `import()`. + +## 2. Dependencies + +Remove the old package and install only what you need: + +```bash +npm uninstall @modelcontextprotocol/sdk +``` + +| You need | Install | +|----------|---------| +| Client only | `npm install @modelcontextprotocol/client` | +| Server only | `npm install @modelcontextprotocol/server` | +| Server + Node.js HTTP | `npm install @modelcontextprotocol/server @modelcontextprotocol/node` | +| Server + Express | `npm install @modelcontextprotocol/server @modelcontextprotocol/express` | +| Server + Hono | `npm install @modelcontextprotocol/server @modelcontextprotocol/hono` | + +`@modelcontextprotocol/core` is installed automatically as a dependency. + +## 3. Import Mapping + +Replace all `@modelcontextprotocol/sdk/...` imports using this table. + +### Client imports + +| v1 import path | v2 package | +|----------------|------------| +| `@modelcontextprotocol/sdk/client/index.js` | `@modelcontextprotocol/client` | +| `@modelcontextprotocol/sdk/client/auth.js` | `@modelcontextprotocol/client` | +| `@modelcontextprotocol/sdk/client/streamableHttp.js` | `@modelcontextprotocol/client` | +| `@modelcontextprotocol/sdk/client/sse.js` | `@modelcontextprotocol/client` | +| `@modelcontextprotocol/sdk/client/stdio.js` | `@modelcontextprotocol/client` | +| `@modelcontextprotocol/sdk/client/websocket.js` | `@modelcontextprotocol/client` | + +### Server imports + +| v1 import path | v2 package | +|----------------|------------| +| `@modelcontextprotocol/sdk/server/mcp.js` | `@modelcontextprotocol/server` | +| `@modelcontextprotocol/sdk/server/index.js` | `@modelcontextprotocol/server` | +| `@modelcontextprotocol/sdk/server/stdio.js` | `@modelcontextprotocol/server` | +| `@modelcontextprotocol/sdk/server/streamableHttp.js` | `@modelcontextprotocol/node` (class renamed to `NodeStreamableHTTPServerTransport`) | +| `@modelcontextprotocol/sdk/server/sse.js` | REMOVED (migrate to Streamable HTTP) | +| `@modelcontextprotocol/sdk/server/auth/*` | REMOVED (use external auth library) | +| `@modelcontextprotocol/sdk/server/middleware.js` | `@modelcontextprotocol/express` (signature changed, see section 8) | + +### Types / shared imports + +| v1 import path | v2 package | +|----------------|------------| +| `@modelcontextprotocol/sdk/types.js` | `@modelcontextprotocol/core` | +| `@modelcontextprotocol/sdk/shared/protocol.js` | `@modelcontextprotocol/core` | +| `@modelcontextprotocol/sdk/shared/transport.js` | `@modelcontextprotocol/core` | +| `@modelcontextprotocol/sdk/shared/stdio.js` | `@modelcontextprotocol/core` | +| `@modelcontextprotocol/sdk/shared/uriTemplate.js` | `@modelcontextprotocol/core` | +| `@modelcontextprotocol/sdk/shared/auth.js` | `@modelcontextprotocol/core` | + +Notes: +- `@modelcontextprotocol/client` and `@modelcontextprotocol/server` both re-export everything from `@modelcontextprotocol/core`, so you can import types from whichever package you already depend on. +- When multiple v1 imports map to the same v2 package, consolidate them into a single import statement. +- If code imports from `sdk/client/...`, install `@modelcontextprotocol/client`. If from `sdk/server/...`, install `@modelcontextprotocol/server`. If from `sdk/types.js` or `sdk/shared/...` only, install `@modelcontextprotocol/core`. + +## 4. Renamed Symbols + +| v1 symbol | v2 symbol | v2 package | +|-----------|-----------|------------| +| `StreamableHTTPServerTransport` | `NodeStreamableHTTPServerTransport` | `@modelcontextprotocol/node` | + +## 5. Removed / Renamed Type Aliases and Symbols + +| v1 (removed) | v2 (replacement) | +|--------------|------------------| +| `JSONRPCError` | `JSONRPCErrorResponse` | +| `JSONRPCErrorSchema` | `JSONRPCErrorResponseSchema` | +| `isJSONRPCError` | `isJSONRPCErrorResponse` | +| `isJSONRPCResponse` | `isJSONRPCResultResponse` | +| `ResourceReference` | `ResourceTemplateReference` | +| `ResourceReferenceSchema` | `ResourceTemplateReferenceSchema` | +| `IsomorphicHeaders` | REMOVED (use Web Standard `Headers`) | +| `AuthInfo` (from `server/auth/types.js`) | `AuthInfo` (now in `@modelcontextprotocol/core`) | + +All other symbols from `@modelcontextprotocol/sdk/types.js` retain their original names (e.g., `CallToolResultSchema`, `ListToolsResultSchema`, etc.). + +**Unchanged APIs** (only import paths changed): `Client` constructor and methods, `McpServer` constructor, `server.connect()`, `server.close()`, all client transports (`StreamableHTTPClientTransport`, `SSEClientTransport`, `StdioClientTransport`), `StdioServerTransport`, all Zod schemas, all callback return types. + +## 6. McpServer API Changes + +The variadic `.tool()`, `.prompt()`, `.resource()` methods are removed. Use the `register*` methods with a config object. + +### Tools + +```typescript +// v1: server.tool(name, schema, callback) +server.tool('greet', { name: z.string() }, async ({ name }) => { + return { content: [{ type: 'text', text: `Hello, ${name}!` }] }; +}); + +// v1: server.tool(name, description, schema, callback) +server.tool('greet', 'Greet a user', { name: z.string() }, async ({ name }) => { + return { content: [{ type: 'text', text: `Hello, ${name}!` }] }; +}); + +// v2: server.registerTool(name, config, callback) +server.registerTool('greet', { + description: 'Greet a user', + inputSchema: { name: z.string() }, +}, async ({ name }) => { + return { content: [{ type: 'text', text: `Hello, ${name}!` }] }; +}); +``` + +Config object fields: `title?`, `description?`, `inputSchema?`, `outputSchema?`, `annotations?`, `_meta?` + +### Prompts + +```typescript +// v1: server.prompt(name, schema, callback) +server.prompt('summarize', { text: z.string() }, async ({ text }) => { + return { messages: [{ role: 'user', content: { type: 'text', text } }] }; +}); + +// v2: server.registerPrompt(name, config, callback) +server.registerPrompt('summarize', { + argsSchema: { text: z.string() }, +}, async ({ text }) => { + return { messages: [{ role: 'user', content: { type: 'text', text } }] }; +}); +``` + +Config object fields: `title?`, `description?`, `argsSchema?` + +### Resources + +```typescript +// v1: server.resource(name, uri, callback) +server.resource('config', 'config://app', async (uri) => { + return { contents: [{ uri: uri.href, text: '{}' }] }; +}); + +// v2: server.registerResource(name, uri, metadata, callback) +server.registerResource('config', 'config://app', {}, async (uri) => { + return { contents: [{ uri: uri.href, text: '{}' }] }; +}); +``` + +Note: the third argument (`metadata`) is required — pass `{}` if no metadata. + +## 7. Headers API + +Transport constructors and `RequestInfo.headers` now use the Web Standard `Headers` object instead of plain objects. + +```typescript +// v1: plain object, bracket access +headers: { 'Authorization': 'Bearer token' } +extra.requestInfo?.headers['mcp-session-id'] + +// v2: Headers object, .get() access +headers: new Headers({ 'Authorization': 'Bearer token' }) +extra.requestInfo?.headers.get('mcp-session-id') +``` + +## 8. Removed Server Features + +### SSE server transport + +`SSEServerTransport` removed entirely. Migrate to `NodeStreamableHTTPServerTransport` (from `@modelcontextprotocol/node`). Client-side `SSEClientTransport` still available for connecting to legacy servers. + +### Server-side auth + +All server OAuth exports removed: `mcpAuthRouter`, `OAuthServerProvider`, `OAuthTokenVerifier`, `requireBearerAuth`, `authenticateClient`, `ProxyOAuthServerProvider`, `allowedMethods`, and associated types. Use an external auth library (e.g., `better-auth`). See `examples/server/src/` for demos. + +### Host header validation (Express) + +`hostHeaderValidation()` and `localhostHostValidation()` moved from server package to `@modelcontextprotocol/express`. Signature changed: takes `string[]` instead of options object. + +```typescript +// v1 +import { hostHeaderValidation } from '@modelcontextprotocol/sdk/server/middleware.js'; +app.use(hostHeaderValidation({ allowedHosts: ['example.com'] })); + +// v2 +import { hostHeaderValidation } from '@modelcontextprotocol/express'; +app.use(hostHeaderValidation(['example.com'])); +``` + +The server package now exports framework-agnostic alternatives: `validateHostHeader()`, `localhostAllowedHostnames()`, `hostHeaderValidationResponse()`. + +## 9. Client Behavioral Changes + +`Client.listPrompts()`, `listResources()`, `listResourceTemplates()`, `listTools()` now return empty results when the server lacks the corresponding capability (instead of sending the request). Set `enforceStrictCapabilities: true` in `ClientOptions` to throw an error instead. + +## 10. Migration Steps (apply in this order) + +1. Update `package.json`: `npm uninstall @modelcontextprotocol/sdk`, install the appropriate v2 packages +2. Replace all imports from `@modelcontextprotocol/sdk/...` using the import mapping tables (sections 3-4), including `StreamableHTTPServerTransport` → `NodeStreamableHTTPServerTransport` +3. Replace removed type aliases (`JSONRPCError` → `JSONRPCErrorResponse`, etc.) per section 5 +4. Replace `.tool()` / `.prompt()` / `.resource()` calls with `registerTool` / `registerPrompt` / `registerResource` per section 6 +5. Replace plain header objects with `new Headers({...})` and bracket access (`headers['x']`) with `.get()` calls per section 7 +6. If using `hostHeaderValidation` from server, update import and signature per section 8 +7. If using server SSE transport, migrate to Streamable HTTP +8. If using server auth from the SDK, migrate to an external auth library +9. If relying on `listTools()`/`listPrompts()`/etc. throwing on missing capabilities, set `enforceStrictCapabilities: true` +10. Verify: build with `tsc` / run tests diff --git a/docs/migration.md b/docs/migration.md new file mode 100644 index 00000000..9bbb25fc --- /dev/null +++ b/docs/migration.md @@ -0,0 +1,303 @@ +# Migration Guide: v1 to v2 + +This guide covers the breaking changes introduced in v2 of the MCP TypeScript SDK and how to update your code. + +## Overview + +Version 2 of the MCP TypeScript SDK introduces several breaking changes to improve modularity, reduce dependency bloat, and provide a cleaner API surface. The biggest change is the split from a single `@modelcontextprotocol/sdk` package into separate `@modelcontextprotocol/core`, `@modelcontextprotocol/client`, and `@modelcontextprotocol/server` packages. + +## Breaking Changes + +### Package split (monorepo) + +The single `@modelcontextprotocol/sdk` package has been split into three packages: + +| v1 | v2 | +|----|-----| +| `@modelcontextprotocol/sdk` | `@modelcontextprotocol/core` (types, protocol, transports) | +| | `@modelcontextprotocol/client` (client implementation) | +| | `@modelcontextprotocol/server` (server implementation) | + +Remove the old package and install only the packages you need: + +```bash +npm uninstall @modelcontextprotocol/sdk + +# If you only need a client +npm install @modelcontextprotocol/client + +# If you only need a server +npm install @modelcontextprotocol/server + +# Both packages depend on @modelcontextprotocol/core automatically +``` + +Update your imports accordingly: + +**Before (v1):** + +```typescript +import { Client } from '@modelcontextprotocol/sdk/client/index.js'; +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { CallToolResultSchema } from '@modelcontextprotocol/sdk/types.js'; +import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'; +import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'; +import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'; +import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; +``` + +**After (v2):** + +```typescript +import { Client, StreamableHTTPClientTransport, StdioClientTransport } from '@modelcontextprotocol/client'; +import { McpServer, StdioServerTransport } from '@modelcontextprotocol/server'; +import { CallToolResultSchema } from '@modelcontextprotocol/core'; + +// Node.js HTTP server transport is in the @modelcontextprotocol/node package +import { NodeStreamableHTTPServerTransport } from '@modelcontextprotocol/node'; +``` + +Note: `@modelcontextprotocol/client` and `@modelcontextprotocol/server` both re-export everything from `@modelcontextprotocol/core`, so you can import types from whichever package you already depend on. + +### Dropped Node.js 18 and CommonJS + +v2 requires **Node.js 20+** and ships **ESM only** (no more CommonJS builds). + +If your project uses CommonJS (`require()`), you will need to either: +- Migrate to ESM (`import`/`export`) +- Use dynamic `import()` to load the SDK + +### Server decoupled from HTTP frameworks + +The server package no longer depends on Express or Hono. HTTP framework integrations are now separate middleware packages: + +| v1 | v2 | +|----|-----| +| Built into `@modelcontextprotocol/sdk` | `@modelcontextprotocol/node` (Node.js HTTP) | +| | `@modelcontextprotocol/express` (Express) | +| | `@modelcontextprotocol/hono` (Hono) | + +Install the middleware package for your framework: + +```bash +npm install @modelcontextprotocol/node # Node.js native http +npm install @modelcontextprotocol/express # Express +npm install @modelcontextprotocol/hono # Hono +``` + +### `StreamableHTTPServerTransport` renamed + +`StreamableHTTPServerTransport` has been renamed to `NodeStreamableHTTPServerTransport` and moved to `@modelcontextprotocol/node`. + +**Before (v1):** + +```typescript +import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'; + +const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID() }); +``` + +**After (v2):** + +```typescript +import { NodeStreamableHTTPServerTransport } from '@modelcontextprotocol/node'; + +const transport = new NodeStreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID() }); +``` + +### Server-side SSE transport removed + +The SSE transport has been removed from the server. Servers should migrate to Streamable HTTP. The client-side SSE transport remains available for connecting to legacy SSE servers. + +### Server auth removed + +Server-side OAuth/auth has been removed entirely from the SDK. This includes `mcpAuthRouter`, `OAuthServerProvider`, `OAuthTokenVerifier`, `requireBearerAuth`, `authenticateClient`, `ProxyOAuthServerProvider`, `allowedMethods`, and all associated types. + +Use a dedicated auth library (e.g., `better-auth`) or a full Authorization Server instead. See the [examples](../examples/server/src/) for a working demo with `better-auth`. + +Note: `AuthInfo` has moved from `server/auth/types.ts` to `@modelcontextprotocol/core` (it's now a core type). + +### `Headers` object instead of plain objects + +Transport APIs and `RequestInfo.headers` now use the Web Standard `Headers` object instead of plain `Record` (`IsomorphicHeaders` has been removed). + +This affects both transport constructors and request handler code that reads headers: + +**Before (v1):** + +```typescript +// Transport headers +const transport = new StreamableHTTPClientTransport(url, { + requestInit: { + headers: { + 'Authorization': 'Bearer token', + 'X-Custom': 'value', + }, + }, +}); + +// Reading headers in a request handler +const sessionId = extra.requestInfo?.headers['mcp-session-id']; +``` + +**After (v2):** + +```typescript +// Transport headers +const transport = new StreamableHTTPClientTransport(url, { + requestInit: { + headers: new Headers({ + 'Authorization': 'Bearer token', + 'X-Custom': 'value', + }), + }, +}); + +// Reading headers in a request handler +const sessionId = extra.requestInfo?.headers.get('mcp-session-id'); +``` + +### `McpServer.tool()`, `.prompt()`, `.resource()` removed + +The deprecated variadic-overload methods have been removed. Use `registerTool`, `registerPrompt`, and `registerResource` instead. These use an explicit config object rather than positional arguments. + +**Before (v1):** + +```typescript +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; + +const server = new McpServer({ name: 'demo', version: '1.0.0' }); + +// Tool with schema +server.tool('greet', { name: z.string() }, async ({ name }) => { + return { content: [{ type: 'text', text: `Hello, ${name}!` }] }; +}); + +// Tool with description +server.tool('greet', 'Greet a user', { name: z.string() }, async ({ name }) => { + return { content: [{ type: 'text', text: `Hello, ${name}!` }] }; +}); + +// Prompt +server.prompt('summarize', { text: z.string() }, async ({ text }) => { + return { messages: [{ role: 'user', content: { type: 'text', text: `Summarize: ${text}` } }] }; +}); + +// Resource +server.resource('config', 'config://app', async (uri) => { + return { contents: [{ uri: uri.href, text: '{}' }] }; +}); +``` + +**After (v2):** + +```typescript +import { McpServer } from '@modelcontextprotocol/server'; + +const server = new McpServer({ name: 'demo', version: '1.0.0' }); + +// Tool with schema +server.registerTool('greet', { inputSchema: { name: z.string() } }, async ({ name }) => { + return { content: [{ type: 'text', text: `Hello, ${name}!` }] }; +}); + +// Tool with description +server.registerTool('greet', { description: 'Greet a user', inputSchema: { name: z.string() } }, async ({ name }) => { + return { content: [{ type: 'text', text: `Hello, ${name}!` }] }; +}); + +// Prompt +server.registerPrompt('summarize', { argsSchema: { text: z.string() } }, async ({ text }) => { + return { messages: [{ role: 'user', content: { type: 'text', text: `Summarize: ${text}` } }] }; +}); + +// Resource +server.registerResource('config', 'config://app', {}, async (uri) => { + return { contents: [{ uri: uri.href, text: '{}' }] }; +}); +``` + +### Host header validation moved + +Express-specific middleware (`hostHeaderValidation()`, `localhostHostValidation()`) moved from the server package to `@modelcontextprotocol/express`. The server package now exports framework-agnostic functions instead: `validateHostHeader()`, `localhostAllowedHostnames()`, `hostHeaderValidationResponse()`. + +**Before (v1):** + +```typescript +import { hostHeaderValidation } from '@modelcontextprotocol/sdk/server/middleware.js'; +app.use(hostHeaderValidation({ allowedHosts: ['example.com'] })); +``` + +**After (v2):** + +```typescript +import { hostHeaderValidation } from '@modelcontextprotocol/express'; +app.use(hostHeaderValidation(['example.com'])); +``` + +Note: the v2 signature takes a plain `string[]` instead of an options object. + +### Client list methods return empty results for missing capabilities + +`Client.listPrompts()`, `listResources()`, `listResourceTemplates()`, and `listTools()` now return empty results when the server didn't advertise the corresponding capability, instead of sending the request. This respects the MCP spec's capability negotiation. + +To restore v1 behavior (throw an error when capabilities are missing), set `enforceStrictCapabilities: true`: + +```typescript +const client = new Client({ name: 'my-client', version: '1.0.0' }, { + enforceStrictCapabilities: true, +}); +``` + +### Removed type aliases and deprecated exports + +The following deprecated type aliases have been removed from `@modelcontextprotocol/core`: + +| Removed | Replacement | +|---------|-------------| +| `JSONRPCError` | `JSONRPCErrorResponse` | +| `JSONRPCErrorSchema` | `JSONRPCErrorResponseSchema` | +| `isJSONRPCError` | `isJSONRPCErrorResponse` | +| `isJSONRPCResponse` | `isJSONRPCResultResponse` | +| `ResourceReferenceSchema` | `ResourceTemplateReferenceSchema` | +| `ResourceReference` | `ResourceTemplateReference` | +| `IsomorphicHeaders` | Use Web Standard `Headers` | +| `AuthInfo` (from `server/auth/types.js`) | `AuthInfo` (now in `@modelcontextprotocol/core`) | + +All other types and schemas exported from `@modelcontextprotocol/sdk/types.js` retain their original names in `@modelcontextprotocol/core`. + +**Before (v1):** + +```typescript +import { JSONRPCError, ResourceReference, isJSONRPCError } from '@modelcontextprotocol/sdk/types.js'; +``` + +**After (v2):** + +```typescript +import { JSONRPCErrorResponse, ResourceTemplateReference, isJSONRPCErrorResponse } from '@modelcontextprotocol/core'; +``` + +## Unchanged APIs + +The following APIs are unchanged between v1 and v2 (only the import paths changed): + +- `Client` constructor and all client methods (`connect`, `callTool`, `listTools`, `listPrompts`, `listResources`, `readResource`, etc.) +- `McpServer` constructor, `server.connect(transport)`, `server.close()` +- `Server` (low-level) constructor and all methods +- `StreamableHTTPClientTransport`, `SSEClientTransport`, `StdioClientTransport` constructors and options +- `StdioServerTransport` constructor and options +- All Zod schemas and type definitions from `types.ts` (except the aliases listed above) +- Tool, prompt, and resource callback return types + +## Using an LLM to migrate your code + +An LLM-optimized version of this guide is available at [`docs/migration-SKILL.md`](migration-SKILL.md). It contains dense mapping tables designed for tools like Claude Code to mechanically apply all the changes described above. You can paste it into your LLM context or load it as a skill. + +## Need Help? + +If you encounter issues during migration: + +1. Check the [FAQ](faq.md) for common questions about v2 changes +2. Review the [examples](https://github.com/modelcontextprotocol/typescript-sdk/tree/main/examples) for updated usage patterns +3. Open an issue on [GitHub](https://github.com/modelcontextprotocol/typescript-sdk/issues) if you find a bug or need further assistance