diff --git a/.changeset/migration-doc-overhaul.md b/.changeset/migration-doc-overhaul.md new file mode 100644 index 000000000..4c9a2848b --- /dev/null +++ b/.changeset/migration-doc-overhaul.md @@ -0,0 +1,7 @@ +--- +'@modelcontextprotocol/server': patch +'@modelcontextprotocol/client': patch +--- + +Overhaul v1→v2 migration docs: lead with the "just bump `@modelcontextprotocol/sdk` to ^2" path, add prerequisite callouts (zod ^4.2.0, `moduleResolution: bundler|nodenext`, bun cache), and fill mapping-table gaps (`ResourceTemplateType`, `OAuthError.code`, `parseJSONRPCMessage`, +`specTypeSchema`, `ZodRawShapeCompat`, `roots/list`/`tasks/*`/`completion/complete` method strings, `InMemoryTransport`, `callToolStream`, custom-method handlers, transitive-v1-dep guidance). diff --git a/docs/migration-SKILL.md b/docs/migration-SKILL.md index a37b5e206..0e75afd35 100644 --- a/docs/migration-SKILL.md +++ b/docs/migration-SKILL.md @@ -1,20 +1,25 @@ --- 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. +description: Migrate MCP TypeScript SDK code from v1 (@modelcontextprotocol/sdk) to v2 (@modelcontextprotocol/sdk@^2, or the split /client and /server packages). 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. +**Shortest path for most servers:** bump `@modelcontextprotocol/sdk` to `^2.0.0` and stop. v1 import paths and APIs continue to work as `@deprecated` aliases (IDE strikethrough, no runtime warnings) that forward to the new implementation. If the user only wants to "get on v2", do that, run the build, and +report any IDE-flagged `@deprecated` usages as optional follow-ups. + +Apply the rest of this guide when the user wants a **full migration** to the new split packages with no `@deprecated` usages. Order: environment → 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()`. +- `tsconfig.json` must use `"moduleResolution": "bundler"`, `"nodenext"`, or `"node16"`. Legacy `"node"` / `"node10"` cannot resolve v2's `exports`-only packages and fails with `TS2307`. Do **not** add `main`/`types` fields to work around this — fix the tsconfig. +- **Zod must be `^4.2.0`** (if used). zod 3.x and 4.0–4.1 lack `~standard.jsonSchema` and crash at runtime on `tools/list` even though typecheck passes. Import from `'zod'` or `'zod/v4'`. ## 2. Dependencies -Remove the old package and install only what you need: +Either keep `@modelcontextprotocol/sdk@^2` (meta-package, simplest), **or** remove it and install the split packages: ```bash npm uninstall @modelcontextprotocol/sdk @@ -28,7 +33,7 @@ npm uninstall @modelcontextprotocol/sdk | Server + Express | `npm install @modelcontextprotocol/server @modelcontextprotocol/express` | | Server + Hono | `npm install @modelcontextprotocol/server @modelcontextprotocol/hono` | -`@modelcontextprotocol/core` is installed automatically as a dependency. +`@modelcontextprotocol/core` is an internal package — never install or import it directly. Its contents are bundled into `@modelcontextprotocol/client` and `@modelcontextprotocol/server`, which re-export everything you need. ## 3. Import Mapping @@ -36,13 +41,13 @@ 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` | +| 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` | REMOVED (use Streamable HTTP or stdio; implement `Transport` for custom needs) | ### Server imports @@ -53,24 +58,27 @@ Replace all `@modelcontextprotocol/sdk/...` imports using this table. | `@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`) OR `@modelcontextprotocol/server` (web-standard `WebStandardStreamableHTTPServerTransport` for Cloudflare Workers, Deno, etc.) | -| `@modelcontextprotocol/sdk/server/sse.js` | REMOVED (migrate to Streamable HTTP) | -| `@modelcontextprotocol/sdk/server/auth/*` | REMOVED (use external auth library) | +| `@modelcontextprotocol/sdk/server/sse.js` | REMOVED (migrate to Streamable HTTP; deprecated copy at `@modelcontextprotocol/node/sse` for proxy/bridge use) | +| `@modelcontextprotocol/sdk/server/auth/*` | REMOVED (use external auth library; `requireBearerAuth`/`mcpAuthMetadataRouter` available in `@modelcontextprotocol/express`) | +| `@modelcontextprotocol/sdk/server/auth/errors.js` | `@modelcontextprotocol/client` or `/server` (see OAuth error consolidation in section 5) | | `@modelcontextprotocol/sdk/server/middleware.js` | `@modelcontextprotocol/express` (signature changed, see section 8) | +| `@modelcontextprotocol/sdk/server/express.js` | `@modelcontextprotocol/express` | ### Types / shared imports -| v1 import path | v2 package | -| ------------------------------------------------- | ---------------------------- | +| v1 import path | v2 package | +| ------------------------------------------------- | ---------------------------------------------------------------- | | `@modelcontextprotocol/sdk/types.js` | `@modelcontextprotocol/client` or `@modelcontextprotocol/server` | | `@modelcontextprotocol/sdk/shared/protocol.js` | `@modelcontextprotocol/client` or `@modelcontextprotocol/server` | | `@modelcontextprotocol/sdk/shared/transport.js` | `@modelcontextprotocol/client` or `@modelcontextprotocol/server` | | `@modelcontextprotocol/sdk/shared/uriTemplate.js` | `@modelcontextprotocol/client` or `@modelcontextprotocol/server` | | `@modelcontextprotocol/sdk/shared/auth.js` | `@modelcontextprotocol/client` or `@modelcontextprotocol/server` | | `@modelcontextprotocol/sdk/shared/stdio.js` | `@modelcontextprotocol/client` or `@modelcontextprotocol/server` | +| `@modelcontextprotocol/sdk/inMemory.js` | `@modelcontextprotocol/client` or `@modelcontextprotocol/server` | Notes: -- `@modelcontextprotocol/client` and `@modelcontextprotocol/server` both re-export shared types from `@modelcontextprotocol/core`, so import from whichever package you already depend on. Do not import from `@modelcontextprotocol/core` directly — it is an internal package. +- `@modelcontextprotocol/client` and `@modelcontextprotocol/server` both re-export the shared types, so import from whichever package you already depend on. Do not import from `@modelcontextprotocol/core` directly — it is an internal package. - When multiple v1 imports map to the same v2 package, consolidate them into a single import statement. ## 4. Renamed Symbols @@ -79,26 +87,38 @@ Notes: | ------------------------------- | ----------------------------------- | ---------------------------- | | `StreamableHTTPServerTransport` | `NodeStreamableHTTPServerTransport` | `@modelcontextprotocol/node` | -## 5. Removed / Renamed Type Aliases and Symbols - -| v1 (removed) | v2 (replacement) | -| ---------------------------------------- | -------------------------------------------------------- | -| `JSONRPCError` | `JSONRPCErrorResponse` | -| `JSONRPCErrorSchema` | `JSONRPCErrorResponseSchema` | -| `isJSONRPCError` | `isJSONRPCErrorResponse` | -| `isJSONRPCResponse` (deprecated in v1) | `isJSONRPCResultResponse` (**not** v2's new `isJSONRPCResponse`, which correctly matches both result and error) | -| `ResourceReference` | `ResourceTemplateReference` | -| `ResourceReferenceSchema` | `ResourceTemplateReferenceSchema` | -| `IsomorphicHeaders` | REMOVED (use Web Standard `Headers`) | -| `AuthInfo` (from `server/auth/types.js`) | `AuthInfo` (now re-exported by `@modelcontextprotocol/client` and `@modelcontextprotocol/server`) | -| `McpError` | `ProtocolError` | -| `ErrorCode` | `ProtocolErrorCode` | -| `ErrorCode.RequestTimeout` | `SdkErrorCode.RequestTimeout` | -| `ErrorCode.ConnectionClosed` | `SdkErrorCode.ConnectionClosed` | -| `StreamableHTTPError` | REMOVED (use `SdkError` with `SdkErrorCode.ClientHttp*`) | -| `WebSocketClientTransport` | REMOVED (use `StreamableHTTPClientTransport` or `StdioClientTransport`) | - -All other **type** symbols from `@modelcontextprotocol/sdk/types.js` retain their original names. **Zod schemas** (e.g., `CallToolResultSchema`, `ListToolsResultSchema`) are no longer part of the public API — they are internal to the SDK. For runtime validation, use type guard functions like `isCallToolResult` instead of `CallToolResultSchema.safeParse()`. +## 5. Deprecated Type Aliases and Symbols + +The v1 names below are still exported as `@deprecated` aliases. Migrate to the v2 names when convenient. + +| v1 (`@deprecated`) | v2 (replacement) | +| -------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | +| `JSONRPCError` | `JSONRPCErrorResponse` | +| `JSONRPCErrorSchema` | `JSONRPCErrorResponseSchema` | +| `isJSONRPCError` | `isJSONRPCErrorResponse` | +| `isJSONRPCResponse` (deprecated in v1) | `isJSONRPCResultResponse` (**not** v2's new `isJSONRPCResponse`, which correctly matches both result and error) | +| `ResourceReference` | `ResourceTemplateReference` | +| `ResourceReferenceSchema` | `ResourceTemplateReferenceSchema` | +| `ResourceTemplate` (the protocol _type_) | `ResourceTemplateType` (`ResourceTemplate` is now the server template _class_) | +| `IsomorphicHeaders` | `Headers` (Web Standard) | +| `RequestInfo` (custom SDK type) | `Request` (Web Standard, via `ctx.http?.req`) | +| `ZodRawShapeCompat` | `StandardSchemaWithJSON` | +| `AuthInfo` (from `server/auth/types.js`) | `AuthInfo` (now re-exported by `@modelcontextprotocol/client` and `@modelcontextprotocol/server`) | +| `OAuthProtectedResourceMetadata` (from `shared/auth.js`) | `OAuthProtectedResourceMetadata` (re-exported by `@modelcontextprotocol/client` / `server`) | +| `OAuthError#errorCode` (instance prop) | `OAuthError#code` | +| `JSONRPCMessageSchema.parse(raw)` | `parseJSONRPCMessage(raw)` | +| `Schema.parse(v)` (any spec type) | `specTypeSchema('').parse(v)` / `isSpecType('', v)` | +| `McpError` | `ProtocolError` | +| `ErrorCode` | `ProtocolErrorCode` | +| `ErrorCode.RequestTimeout` | `SdkErrorCode.RequestTimeout` | +| `ErrorCode.ConnectionClosed` | `SdkErrorCode.ConnectionClosed` | +| `StreamableHTTPError` | `SdkError` with `SdkErrorCode.ClientHttp*` | + +`WebSocketClientTransport` is **removed** (no deprecated alias). Use `StreamableHTTPClientTransport` or `StdioClientTransport`. + +All other **type** symbols from `@modelcontextprotocol/sdk/types.js` retain their original names. **Zod schemas** (e.g., `CallToolResultSchema`, `ListToolsResultSchema`, `OAuthTokensSchema`) are no longer part of the public API. For runtime validation: use +`parseJSONRPCMessage(raw)` for transport framing, `isCallToolResult(v)` for the common case, or `specTypeSchema('')` / `isSpecType('', v)` for any other spec type. The raw Zod constants remain available at `@modelcontextprotocol/server/zod-schemas` as a compatibility +escape hatch. ### Error class changes @@ -204,13 +224,14 @@ if (error instanceof OAuthError && error.code === OAuthErrorCode.InvalidClient) ``` **Unchanged APIs** (only import paths changed): `Client` constructor and most methods, `McpServer` constructor, `server.connect()`, `server.close()`, all client transports (`StreamableHTTPClientTransport`, `SSEClientTransport`, `StdioClientTransport`), `StdioServerTransport`, all -Zod schemas, all callback return types. Note: `callTool()` and `request()` signatures changed (schema parameter removed, see section 11). +protocol **type** definitions, all callback return types. Note: `callTool()` and `request()` accept the result-schema parameter as a supported overload (alternative to the method-string form — see section 11). Zod **schema constants** are available via the `@deprecated` `/zod-schemas` subpath (see section 5). ## 6. McpServer API Changes -The variadic `.tool()`, `.prompt()`, `.resource()` methods are removed. Use the `register*` methods with a config object. +The variadic `.tool()`, `.prompt()`, `.resource()` methods are `@deprecated` (still work; forward to `register*`). For a full migration, switch to the `register*` methods with a config object. -**IMPORTANT**: v2 requires schema objects implementing [Standard Schema](https://standardschema.dev/) — raw shapes like `{ name: z.string() }` are no longer supported. Wrap with `z.object()` (Zod v4), or use ArkType's `type({...})`, or Valibot. For raw JSON Schema, wrap with `fromJsonSchema(schema)` from `@modelcontextprotocol/server` (validator defaults automatically; pass an explicit validator for custom configurations). Applies to `inputSchema`, `outputSchema`, and `argsSchema`. +**IMPORTANT**: v2 requires schema objects implementing [Standard Schema](https://standardschema.dev/) — raw shapes like `{ name: z.string() }` are no longer supported. Wrap with `z.object()` (Zod v4), or use ArkType's `type({...})`, or Valibot. For raw JSON Schema, wrap with +`fromJsonSchema(schema)` from `@modelcontextprotocol/server` (validator defaults automatically; pass an explicit validator for custom configurations). Applies to `inputSchema`, `outputSchema`, and `argsSchema`. ### Tools @@ -280,20 +301,20 @@ Note: the third argument (`metadata`) is required — pass `{}` if no metadata. ### Schema Migration Quick Reference -| v1 (raw shape) | v2 (Standard Schema object) | -|----------------|-----------------| -| `{ name: z.string() }` | `z.object({ name: z.string() })` | +| v1 (raw shape) | v2 (Standard Schema object) | +| ---------------------------------- | -------------------------------------------- | +| `{ name: z.string() }` | `z.object({ name: z.string() })` | | `{ count: z.number().optional() }` | `z.object({ count: z.number().optional() })` | | `{}` (empty) | `z.object({})` | | `undefined` (no schema) | `undefined` or omit the field | -### Removed core exports +### Deprecated core exports -| Removed from `@modelcontextprotocol/core` | Replacement | -|---|---| -| `schemaToJson(schema)` | `standardSchemaToJsonSchema(schema)` | -| `parseSchemaAsync(schema, data)` | `validateStandardSchema(schema, data)` | -| `SchemaInput` | `StandardSchemaWithJSON.InferInput` | +| v1 export (`@deprecated`) | Replacement | +| ------------------------------------------------------------------------------------ | ----------------------------------------- | +| `schemaToJson(schema)` | `standardSchemaToJsonSchema(schema)` | +| `parseSchemaAsync(schema, data)` | `validateStandardSchema(schema, data)` | +| `SchemaInput` | `StandardSchemaWithJSON.InferInput` | | `getSchemaShape`, `getSchemaDescription`, `isOptionalSchema`, `unwrapOptionalSchema` | none (internal Zod introspection helpers) | ## 7. Headers API @@ -311,15 +332,15 @@ ctx.http?.req?.headers.get('mcp-session-id') new URL(ctx.http?.req?.url).searchParams.get('debug') ``` -## 8. Removed Server Features +## 8. Relocated Server Features ### SSE server transport -`SSEServerTransport` removed entirely. Migrate to `NodeStreamableHTTPServerTransport` (from `@modelcontextprotocol/node`). Client-side `SSEClientTransport` still available for connecting to legacy servers. +`SSEServerTransport` is `@deprecated` and now lives under `@modelcontextprotocol/node/sse`. Prefer `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 +Server OAuth has been split: Resource-Server helpers (`requireBearerAuth`, `mcpAuthMetadataRouter`, `getOAuthProtectedResourceMetadataUrl`, `OAuthTokenVerifier`) are now first-class in `@modelcontextprotocol/express`. The Authorization-Server implementation (`mcpAuthRouter`, `ProxyOAuthServerProvider`, `authenticateClient`, etc.) moved to the frozen `@modelcontextprotocol/server-auth-legacy` package — prefer an external IdP for new code. See `examples/server/src/` for demos. ### Host header validation (Express) @@ -373,10 +394,22 @@ Schema to method string mapping: | `PromptListChangedNotificationSchema` | `'notifications/prompts/list_changed'` | | `ProgressNotificationSchema` | `'notifications/progress'` | | `CancelledNotificationSchema` | `'notifications/cancelled'` | +| `RootsListChangedNotificationSchema` | `'notifications/roots/list_changed'` | +| `ListRootsRequestSchema` | `'roots/list'` | +| `CompleteRequestSchema` | `'completion/complete'` | +| `SubscribeRequestSchema` | `'resources/subscribe'` | +| `UnsubscribeRequestSchema` | `'resources/unsubscribe'` | +| `ListResourceTemplatesRequestSchema` | `'resources/templates/list'` | +| `GetTaskRequestSchema` | `'tasks/get'` | +| `GetTaskPayloadRequestSchema` | `'tasks/result'` | +| `ElicitationCompleteNotificationSchema` | `'notifications/elicitation/complete'` | | `InitializedNotificationSchema` | `'notifications/initialized'` | Request/notification params remain fully typed. Remove unused schema imports after migration. +For **vendor-prefixed custom methods**, pass an explicit schema as the second argument: `server.setNotificationHandler('x-myorg/heartbeat', z.object({...}), handler)`. Do **not** add custom params to a spec method name — v2 validates against the spec schema and strips unknown +fields. + ## 10. Request Handler Context Types `RequestHandlerExtra` → structured context types with nested groups. Rename `extra` → `ctx` in all handler callbacks. @@ -407,9 +440,9 @@ Request/notification params remain fully typed. Remove unused schema imports aft | `ctx.mcpReq.elicitInput(params, options?)` | Elicit user input (form or URL) | `server.elicitInput(...)` from within handler | | `ctx.mcpReq.requestSampling(params, options?)` | Request LLM sampling from client | `server.createMessage(...)` from within handler | -## 11. Schema parameter removed from `request()`, `send()`, and `callTool()` +## 11. Schema parameter on `request()` and `callTool()` is now optional -`Protocol.request()`, `BaseContext.mcpReq.send()`, and `Client.callTool()` no longer take a Zod result schema argument. The SDK resolves the schema internally from the method name. +`Protocol.request()`, `BaseContext.mcpReq.send()`, and `Client.callTool()` still accept a result schema argument, but for spec methods it is optional — the SDK resolves the schema internally from the method name. The schema argument remains the supported form for custom (non-spec) methods. `client.experimental.tasks.callToolStream()` no longer takes a schema argument. ```typescript // v1: schema required @@ -418,46 +451,47 @@ const result = await client.request({ method: 'tools/call', params: { ... } }, C const elicit = await ctx.mcpReq.send({ method: 'elicitation/create', params: { ... } }, ElicitResultSchema); const tool = await client.callTool({ name: 'my-tool', arguments: {} }, CompatibilityCallToolResultSchema); -// v2: no schema argument +// v2: schema optional for spec methods const result = await client.request({ method: 'tools/call', params: { ... } }); const elicit = await ctx.mcpReq.send({ method: 'elicitation/create', params: { ... } }); const tool = await client.callTool({ name: 'my-tool', arguments: {} }); ``` -| v1 call | v2 call | -| ------------------------------------------------------------ | ---------------------------------- | -| `client.request(req, ResultSchema)` | `client.request(req)` | -| `client.request(req, ResultSchema, options)` | `client.request(req, options)` | -| `ctx.mcpReq.send(req, ResultSchema)` | `ctx.mcpReq.send(req)` | -| `ctx.mcpReq.send(req, ResultSchema, options)` | `ctx.mcpReq.send(req, options)` | -| `client.callTool(params, CompatibilityCallToolResultSchema)` | `client.callTool(params)` | -| `client.callTool(params, schema, options)` | `client.callTool(params, options)` | +| v1 call | v2 call | +| -------------------------------------------------------------------------- | ------------------------------------------------------------ | +| `client.request(req, ResultSchema)` | `client.request(req)` | +| `client.request(req, ResultSchema, options)` | `client.request(req, options)` | +| `client.experimental.tasks.callToolStream(params, ResultSchema, options?)` | `client.experimental.tasks.callToolStream(params, options?)` | +| `ctx.mcpReq.send(req, ResultSchema)` | `ctx.mcpReq.send(req)` | +| `ctx.mcpReq.send(req, ResultSchema, options)` | `ctx.mcpReq.send(req, options)` | +| `client.callTool(params, CompatibilityCallToolResultSchema)` | `client.callTool(params)` | +| `client.callTool(params, schema, options)` | `client.callTool(params, options)` | Remove unused schema imports: `CallToolResultSchema`, `CompatibilityCallToolResultSchema`, `ElicitResultSchema`, `CreateMessageResultSchema`, etc., when they were only used in `request()`/`send()`/`callTool()` calls. If `CallToolResultSchema` was used for **runtime validation** (not just as a `request()` argument), replace with the `isCallToolResult` type guard: -| v1 pattern | v2 replacement | -| --------------------------------------------------- | -------------------------- | -| `CallToolResultSchema.safeParse(value).success` | `isCallToolResult(value)` | -| `CallToolResultSchema.parse(value)` | Use `isCallToolResult(value)` then cast, or use `CallToolResult` type | +| v1 pattern | v2 replacement | +| ----------------------------------------------- | --------------------------------------------------------------------- | +| `CallToolResultSchema.safeParse(value).success` | `isCallToolResult(value)` | +| `CallToolResultSchema.parse(value)` | Use `isCallToolResult(value)` then cast, or use `CallToolResult` type | ## 12. Experimental: `TaskCreationParams.ttl` no longer accepts `null` `TaskCreationParams.ttl` changed from `z.union([z.number(), z.null()]).optional()` to `z.number().optional()`. Per the MCP spec, `null` TTL (unlimited lifetime) is only valid in server responses (`Task.ttl`), not in client requests. Omit `ttl` to let the server decide. -| v1 | v2 | -|---|---| -| `task: { ttl: null }` | `task: {}` (omit ttl) | +| v1 | v2 | +| ---------------------- | ---------------------------------- | +| `task: { ttl: null }` | `task: {}` (omit ttl) | | `task: { ttl: 60000 }` | `task: { ttl: 60000 }` (unchanged) | Type changes in handler context: -| Type | v1 | v2 | -|---|---|---| -| `TaskContext.requestedTtl` | `number \| null \| undefined` | `number \| undefined` | +| Type | v1 | v2 | +| ------------------------------------------- | ----------------------------- | --------------------- | +| `TaskContext.requestedTtl` | `number \| null \| undefined` | `number \| undefined` | | `CreateTaskServerContext.task.requestedTtl` | `number \| null \| undefined` | `number \| undefined` | -| `TaskServerContext.task.requestedTtl` | `number \| null \| undefined` | `number \| undefined` | +| `TaskServerContext.task.requestedTtl` | `number \| null \| undefined` | `number \| undefined` | > These task APIs are `@experimental` and may change without notice. @@ -487,16 +521,14 @@ new McpServer( new McpServer({ name: 'server', version: '1.0.0' }, {}); ``` -Access validators explicitly: -- Runtime-aware default: `import { DefaultJsonSchemaValidator } from '@modelcontextprotocol/server/_shims';` -- AJV (Node.js): `import { AjvJsonSchemaValidator } from '@modelcontextprotocol/server';` -- CF Worker: `import { CfWorkerJsonSchemaValidator } from '@modelcontextprotocol/server/validators/cf-worker';` +Access validators via `_shims` export: `import { DefaultJsonSchemaValidator } from '@modelcontextprotocol/server/_shims';` ## 15. Migration Steps (apply in this order) +0. **Shortest path:** bump `@modelcontextprotocol/sdk` to `^2.0.0`, ensure `zod@^4.2.0` and `moduleResolution: bundler|nodenext`, run the build. If it passes, you are on v2; any IDE-flagged `@deprecated` usages are optional follow-ups. Stop here unless a full migration is requested. 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 +3. Replace `@deprecated` type aliases (`JSONRPCError` → `JSONRPCErrorResponse`, etc.) per section 5 4. Replace `.tool()` / `.prompt()` / `.resource()` calls with `registerTool` / `registerPrompt` / `registerResource` per section 6 5. **Wrap all raw Zod shapes with `z.object()`**: Change `inputSchema: { name: z.string() }` → `inputSchema: z.object({ name: z.string() })`. Same for `outputSchema` in tools and `argsSchema` in prompts. 6. Replace plain header objects with `new Headers({...})` and bracket access (`headers['x']`) with `.get()` calls per section 7 diff --git a/docs/migration.md b/docs/migration.md index 7cb7d58f6..fca0f48f6 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -1,39 +1,113 @@ # 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. +> **Note:** This guide describes the v2.0.0 release as a whole. The compatibility shims it references land across the [v2-bc PR series](https://github.com/modelcontextprotocol/typescript-sdk/pulls?q=is%3Apr+label%3Av2-bc); on any individual PR's branch some referenced symbols may not yet exist. + +This guide covers the changes introduced in v2 of the MCP TypeScript SDK and how to update your code. + +## TL;DR — most servers just bump the version + +For **most MCP servers**, upgrading to v2 is a one-line change: + +```diff +- "@modelcontextprotocol/sdk": "^1.0.0" ++ "@modelcontextprotocol/sdk": "^2.0.0" +``` + +In v2, `@modelcontextprotocol/sdk` is a meta-package that re-exports the new split packages at the v1 import paths. v1 APIs (deep-import paths like `@modelcontextprotocol/sdk/server/mcp.js`, variadic `server.tool()`, `McpError`, `RequestHandlerExtra`, etc.) continue to work as +**`@deprecated` aliases** — your IDE will show strikethrough and a hover note pointing at the new API, but there are no runtime warnings. You can ship on v2 immediately and migrate the deprecated usages at your own pace. + +Read on if you want to: + +- Migrate to the new split packages (`@modelcontextprotocol/server`, `@modelcontextprotocol/client`) for smaller bundles +- Adopt the new API surface (no `@deprecated` strikethrough) +- Understand a specific breaking change + +> **Clients and frameworks** (host applications, custom transports, proxies) typically need a few more changes than servers — see the [Runtime validation](#zod-schemas-are-no-longer-exported-runtime-validation), [Custom methods](#custom-method-names-and-extending-spec-methods), +> and [Server auth](#server-auth-removed) sections. + +## Prerequisites + +Before upgrading, check these three environment requirements. They are the most common source of confusing errors during migration. + +### Zod must be `^4.2.0` + +If you use Zod for tool/prompt schemas, you must be on **`zod@^4.2.0` or later**, and import from the `zod` (or `zod/v4`) entry point. + +Older Zod versions (3.x, or 4.0.0–4.1.x) do **not** implement the `~standard.jsonSchema` property the SDK uses to render `inputSchema` for `tools/list`. This passes type-checking but **crashes at runtime** when a client calls `tools/list`. + +```jsonc +// package.json +"dependencies": { + "zod": "^4.2.0" +} +``` + +```typescript +// Either of these works on zod@^4.2.0: +import * as z from 'zod'; +import * as z from 'zod/v4'; + +// This does NOT work (zod 3.x has no Standard Schema support): +import * as z from 'zod/v3'; +``` + +### TypeScript `moduleResolution` must be `bundler`, `nodenext`, or `node16` + +v2 packages ship ESM-only with an `exports` map and no top-level `main`/`types` fields. TypeScript's legacy `"moduleResolution": "node"` (or `"node10"`) cannot resolve them and fails with `TS2307: Cannot find module '@modelcontextprotocol/server'`. + +Update your `tsconfig.json`: + +```jsonc +{ + "compilerOptions": { + "moduleResolution": "bundler" // or "nodenext" / "node16" + } +} +``` + +> We deliberately do **not** ship `main`/`types` fallback fields — testing showed it suppresses TypeScript's helpful "consider updating moduleResolution" diagnostic and replaces it with dozens of misleading transitive errors. + +### Testing locally with bun: clear the cache + +If you are testing a local v2 build via `file:` tarballs, note that **bun caches `file:` dependencies by filename** and ignores content changes. Re-packing the SDK without bumping the version installs a stale tarball. + +```bash +bun pm cache rm && bun install --force +``` + +npm and pnpm do not have this issue. ## 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. +Version 2 of the MCP TypeScript SDK introduces several changes to improve modularity, reduce dependency bloat, and provide a cleaner API surface. The biggest structural change is the split into separate `@modelcontextprotocol/client` and `@modelcontextprotocol/server` packages, +with `@modelcontextprotocol/sdk` remaining as a meta-package that re-exports both. ## Breaking Changes ### Package split (monorepo) -The single `@modelcontextprotocol/sdk` package has been split into three packages: +The single `@modelcontextprotocol/sdk` package has been split. `@modelcontextprotocol/sdk` still exists as a **meta-package** (the recommended default — just bump the version), but you can now also install only the half you need: -| 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: +| v1 | v2 | +| --------------------------- | ----------------------------------------------------------------------------- | +| `@modelcontextprotocol/sdk` | `@modelcontextprotocol/sdk` (meta-package, re-exports everything below) | +| | `@modelcontextprotocol/client` (client implementation) | +| | `@modelcontextprotocol/server` (server implementation) | +| | `@modelcontextprotocol/node` / `express` / `hono` / `fastify` (HTTP adapters) | ```bash -npm uninstall @modelcontextprotocol/sdk +# Easiest: keep the meta-package +npm install @modelcontextprotocol/sdk@^2 -# If you only need a client +# Or install only what you need 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: +> `@modelcontextprotocol/core` is an **internal** package — never install or import it directly. Its contents are **bundled into** (not depended on by) `@modelcontextprotocol/client` and `@modelcontextprotocol/server`, so it never appears in your `node_modules`. Everything you +> need is re-exported from the client/server packages. + +If you switch to the split packages, update your imports accordingly: **Before (v1):** @@ -57,7 +131,7 @@ import { McpServer, StdioServerTransport, WebStandardStreamableHTTPServerTranspo import { NodeStreamableHTTPServerTransport } from '@modelcontextprotocol/node'; ``` -Note: `@modelcontextprotocol/client` and `@modelcontextprotocol/server` both re-export shared types from `@modelcontextprotocol/core`, so you can import types and error classes from whichever package you already depend on. Do not import from `@modelcontextprotocol/core` directly — it is an internal package. +Note: `@modelcontextprotocol/client` and `@modelcontextprotocol/server` both re-export the shared types, so you can import types and error classes from whichever package you already depend on. Do not import from `@modelcontextprotocol/core` directly — it is an internal package. ### Dropped Node.js 18 and CommonJS @@ -86,9 +160,9 @@ npm install @modelcontextprotocol/express # Express npm install @modelcontextprotocol/hono # Hono ``` -### `StreamableHTTPServerTransport` renamed +### `StreamableHTTPServerTransport` → `NodeStreamableHTTPServerTransport` -`StreamableHTTPServerTransport` has been renamed to `NodeStreamableHTTPServerTransport` and moved to `@modelcontextprotocol/node`. +The class is now `NodeStreamableHTTPServerTransport` in `@modelcontextprotocol/node`. The old name `StreamableHTTPServerTransport` is still exported from `@modelcontextprotocol/node` as a `@deprecated` alias. **Before (v1):** @@ -110,6 +184,40 @@ const transport = new NodeStreamableHTTPServerTransport({ sessionIdGenerator: () 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. +**Before (v1):** + +```typescript +import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js'; + +let transport: SSEServerTransport; + +app.get('/sse', async (req, res) => { + transport = new SSEServerTransport('/messages', res); + await server.connect(transport); +}); +app.post('/messages', async (req, res) => { + await transport.handlePostMessage(req, res); +}); +``` + +**After (v2, stateless):** + +```typescript +import { NodeStreamableHTTPServerTransport } from '@modelcontextprotocol/node'; + +app.all('/mcp', async (req, res) => { + const transport = new NodeStreamableHTTPServerTransport({ sessionIdGenerator: undefined }); + await server.connect(transport); + await transport.handleRequest(req, res); +}); +``` + +With `sessionIdGenerator: undefined` the transport runs in stateless mode, so creating a fresh instance per request is correct. For stateful sessions, the transport must be created once per session and stored in a `Map` keyed by session +ID — see `examples/server/src/simpleStreamableHttp.ts` for the full pattern. + +> **Proxy/bridge use case:** If you were using `SSEServerTransport` to bridge a stdio server to a browser (the inspector pattern), a deprecated copy is available at `@modelcontextprotocol/node/sse` for transitional use. New code should expose Streamable HTTP and let the browser +> client open the SSE stream itself. + ### `WebSocketClientTransport` removed `WebSocketClientTransport` has been removed. WebSocket is not a spec-defined MCP transport, and keeping it in the SDK encouraged transport proliferation without a conformance baseline. @@ -140,7 +248,7 @@ Note: `AuthInfo` has moved from `server/auth/types.ts` to the core types and is ### `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). +Transport APIs and `RequestInfo.headers` now use the Web Standard `Headers` object instead of plain `Record`. The `IsomorphicHeaders` type is still exported as a `@deprecated` alias for `Headers`. This affects both transport constructors and request handler code that reads headers: @@ -182,9 +290,9 @@ const url = new URL(ctx.http!.req!.url); const debug = url.searchParams.get('debug'); ``` -### `McpServer.tool()`, `.prompt()`, `.resource()` removed +### `McpServer.tool()`, `.prompt()`, `.resource()` deprecated -The deprecated variadic-overload methods have been removed. Use `registerTool`, `registerPrompt`, and `registerResource` instead. These use an explicit config object rather than positional arguments. +These variadic methods are still available as `@deprecated` overloads that forward to `registerTool`/`registerPrompt`/`registerResource`. The new methods use an explicit config object rather than positional arguments. Migrate when convenient: **Before (v1):** @@ -294,11 +402,11 @@ This applies to: **Removed Zod-specific helpers** from `@modelcontextprotocol/core` (use Standard Schema equivalents): -| Removed | Replacement | -|---|---| -| `schemaToJson(schema)` | `standardSchemaToJsonSchema(schema)` | -| `parseSchemaAsync(schema, data)` | `validateStandardSchema(schema, data)` | -| `SchemaInput` | `StandardSchemaWithJSON.InferInput` | +| Removed | Replacement | +| ------------------------------------------------------------------------------------ | ----------------------------------------------------------------- | +| `schemaToJson(schema)` | `standardSchemaToJsonSchema(schema)` | +| `parseSchemaAsync(schema, data)` | `validateStandardSchema(schema, data)` | +| `SchemaInput` | `StandardSchemaWithJSON.InferInput` | | `getSchemaShape`, `getSchemaDescription`, `isOptionalSchema`, `unwrapOptionalSchema` | No replacement — these are now internal Zod introspection helpers | ### Host header validation moved @@ -381,11 +489,21 @@ Common method string replacements: | `ToolListChangedNotificationSchema` | `'notifications/tools/list_changed'` | | `ResourceListChangedNotificationSchema` | `'notifications/resources/list_changed'` | | `PromptListChangedNotificationSchema` | `'notifications/prompts/list_changed'` | - -### `Protocol.request()`, `ctx.mcpReq.send()`, and `Client.callTool()` no longer take a schema parameter - -The public `Protocol.request()`, `BaseContext.mcpReq.send()`, and `Client.callTool()` methods no longer accept a Zod result schema argument. The SDK now resolves the correct result schema internally based on the method name. This means you no longer need to import result schemas -like `CallToolResultSchema` or `ElicitResultSchema` when making requests. +| `RootsListChangedNotificationSchema` | `'notifications/roots/list_changed'` | +| `ListRootsRequestSchema` | `'roots/list'` | +| `CompleteRequestSchema` | `'completion/complete'` | +| `SubscribeRequestSchema` | `'resources/subscribe'` | +| `UnsubscribeRequestSchema` | `'resources/unsubscribe'` | +| `ListResourceTemplatesRequestSchema` | `'resources/templates/list'` | +| `GetTaskRequestSchema` | `'tasks/get'` | +| `GetTaskPayloadRequestSchema` | `'tasks/result'` | +| `ElicitationCompleteNotificationSchema` | `'notifications/elicitation/complete'` | +| `InitializedNotificationSchema` | `'notifications/initialized'` | + +### `Protocol.request()` and `Client.callTool()` schema parameter is now optional + +The public `Protocol.request()`, `BaseContext.mcpReq.send()`, and `Client.callTool()` methods still accept a result schema argument, but for spec methods it is optional — the SDK resolves the correct schema internally from the method name. You no longer need to import result +schemas like `CallToolResultSchema` or `ElicitResultSchema` when making requests. The schema argument remains the supported form for custom (non-spec) methods. **`client.request()` — Before (v1):** @@ -440,6 +558,24 @@ const result = await client.callTool({ name: 'my-tool', arguments: {} }, Compati const result = await client.callTool({ name: 'my-tool', arguments: {} }); ``` +**`client.experimental.tasks.callToolStream()` — Before (v1):** + +```typescript +import { CallToolResultSchema } from '@modelcontextprotocol/sdk/types.js'; + +for await (const event of client.experimental.tasks.callToolStream({ name: 'my-tool', arguments: {} }, CallToolResultSchema)) { + // ... +} +``` + +**After (v2):** + +```typescript +for await (const event of client.experimental.tasks.callToolStream({ name: 'my-tool', arguments: {} })) { + // ... +} +``` + The return type is now inferred from the method name via `ResultTypeMap`. For example, `client.request({ method: 'tools/call', ... })` returns `Promise`. If you were using `CallToolResultSchema` for **runtime validation** (not just in `request()`/`callTool()` calls), use the new `isCallToolResult` type guard instead: @@ -447,13 +583,59 @@ If you were using `CallToolResultSchema` for **runtime validation** (not just in ```typescript // v1: runtime validation with Zod schema import { CallToolResultSchema } from '@modelcontextprotocol/sdk/types.js'; -if (CallToolResultSchema.safeParse(value).success) { /* ... */ } +if (CallToolResultSchema.safeParse(value).success) { + /* ... */ +} // v2: use the type guard import { isCallToolResult } from '@modelcontextprotocol/client'; -if (isCallToolResult(value)) { /* ... */ } +if (isCallToolResult(value)) { + /* ... */ +} +``` + +### Zod schemas are no longer exported (runtime validation) + +v1 exported every protocol Zod schema (`CallToolResultSchema`, `JSONRPCMessageSchema`, `OAuthTokensSchema`, etc.) as part of the public API. v2 exports only the **TypeScript types** — the Zod schemas are now internal implementation details. This affects code that did runtime +validation at trust boundaries (custom transports, OAuth response parsing, error-data inspection). + +| v1 pattern | v2 replacement | +| ------------------------------------------------------------------- | ------------------------------------------------ | +| `JSONRPCMessageSchema.parse(raw)` | `parseJSONRPCMessage(raw)` | +| `CallToolResultSchema.safeParse(v).success` | `isCallToolResult(v)` | +| `Schema.safeParse(v).success` (any spec type) | `isSpecType('', v)` | +| `Schema.parse(v)` (any spec type) | `specTypeSchema('').parse(v)` | +| `OAuthTokensSchema.parse(body)` / `OAuthMetadataSchema.parse(body)` | `specTypeSchema('OAuthTokens').parse(body)` etc. | + +`parseJSONRPCMessage`, `isSpecType`, and `specTypeSchema` are exported from both `@modelcontextprotocol/client` and `@modelcontextprotocol/server`. + +If you need direct access to the Zod constants (e.g., to extend or compose them), they remain available at the `@modelcontextprotocol/server/zod-schemas` subpath as a compatibility escape hatch. + +### Custom method names and extending spec methods + +v1 let you register a handler for **any** method string by passing a custom Zod schema. In v2, `setRequestHandler('method', handler)` and `setNotificationHandler('method', handler)` are typed against the closed set of spec method names. + +To register a handler for a **vendor-prefixed custom method**, pass an explicit schema as the second argument: + +```typescript +server.setNotificationHandler('x-myorg/heartbeat', z.object({ ts: z.number() }), notification => { + // notification.params is typed as { ts: number } +}); ``` +> **Warning:** Do **not** add custom fields to a _spec_ method's params (e.g., adding `{ tabId, image }` to `'notifications/message'`). v2 validates incoming spec messages against the spec schema, so unknown fields are stripped before your handler sees them. Use a vendor-prefixed +> method name instead. + +### Transitive dependencies still on v1 + +If a package you depend on (e.g., a shared MCP utilities library or framework adapter) still vends types from `@modelcontextprotocol/sdk@^1`, you will see structural type errors where v1 and v2 types meet (`Server` is not assignable to `Server`, `Transport` not assignable to +`Transport`). + +There is no SDK-side fix for this. Either: + +- Upgrade the transitive dependency to v2 first, or +- Add a type assertion (`as unknown as Transport`) at the boundary until it is upgraded. + ### 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. @@ -469,40 +651,45 @@ const client = new Client( ); ``` -### `InMemoryTransport` removed from public API - -`InMemoryTransport` has been removed from the public API surface. It was previously used for in-process client-server connections and testing. +### `InMemoryTransport` import path -For **testing**, import it directly from the internal core package: +`InMemoryTransport` is used for testing client/server interactions within a single process. It is re-exported from both `@modelcontextprotocol/server` and `@modelcontextprotocol/client`. ```typescript // v1 import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js'; -// v2 (testing only — @modelcontextprotocol/core is internal, not for production use) -import { InMemoryTransport } from '@modelcontextprotocol/core'; +// v2 +import { InMemoryTransport } from '@modelcontextprotocol/server'; ``` -For **production in-process connections**, use `StreamableHTTPClientTransport` with a local server URL, or connect client and server via paired streams. +For **production in-process connections**, prefer `StreamableHTTPClientTransport` with a local server URL. ### 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` (see note below) | -| `ResourceReferenceSchema` | `ResourceTemplateReferenceSchema` | -| `ResourceReference` | `ResourceTemplateReference` | -| `IsomorphicHeaders` | Use Web Standard `Headers` | -| `AuthInfo` (from `server/auth/types.js`) | `AuthInfo` (now re-exported by `@modelcontextprotocol/client` and `@modelcontextprotocol/server`) | - -All other types and schemas exported from `@modelcontextprotocol/sdk/types.js` retain their original names — import them from `@modelcontextprotocol/client` or `@modelcontextprotocol/server`. - -> **Note on `isJSONRPCResponse`:** v1's `isJSONRPCResponse` was a deprecated alias that only checked for *result* responses (it was equivalent to `isJSONRPCResultResponse`). v2 removes the deprecated alias and introduces a **new** `isJSONRPCResponse` with corrected semantics — it checks for *any* response (either result or error). If you are migrating v1 code that used `isJSONRPCResponse`, rename it to `isJSONRPCResultResponse` to preserve the original behavior. Use the new `isJSONRPCResponse` only when you want to match both result and error responses. +| Removed | Replacement | +| -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | +| `JSONRPCError` | `JSONRPCErrorResponse` | +| `JSONRPCErrorSchema` | `JSONRPCErrorResponseSchema` | +| `isJSONRPCError` | `isJSONRPCErrorResponse` | +| `isJSONRPCResponse` | `isJSONRPCResultResponse` (see note below) | +| `ResourceReferenceSchema` | `ResourceTemplateReferenceSchema` | +| `ResourceReference` | `ResourceTemplateReference` | +| `ResourceTemplate` (the protocol _type_) | `ResourceTemplateType` (the name `ResourceTemplate` is now the server-side template _class_) | +| `IsomorphicHeaders` | Use Web Standard `Headers` | +| `RequestInfo` (custom SDK type) | Use Web Standard `Request` (via `ctx.http?.req`) | +| `FetchLike` | `FetchLike` (unchanged, but now imported from `@modelcontextprotocol/client` / `server`) | +| `ZodRawShapeCompat` | `StandardSchemaWithJSON` (the constraint type for `inputSchema`/`argsSchema`) | +| `AuthInfo` (from `server/auth/types.js`) | `AuthInfo` (now re-exported by `@modelcontextprotocol/client` and `@modelcontextprotocol/server`) | +| `OAuthProtectedResourceMetadata` (from `shared/auth.js`) | `OAuthProtectedResourceMetadata` (now re-exported by `@modelcontextprotocol/client` and `@modelcontextprotocol/server`) | + +All other **types** exported from `@modelcontextprotocol/sdk/types.js` retain their original names — import them from `@modelcontextprotocol/client` or `@modelcontextprotocol/server`. **Zod schemas** (`*Schema` constants) are no longer part of the public API — see +[Zod schemas are no longer exported](#zod-schemas-are-no-longer-exported-runtime-validation). + +> **Note on `isJSONRPCResponse`:** v1's `isJSONRPCResponse` was a deprecated alias that only checked for _result_ responses (it was equivalent to `isJSONRPCResultResponse`). v2 removes the deprecated alias and introduces a **new** `isJSONRPCResponse` with corrected semantics — it +> checks for _any_ response (either result or error). If you are migrating v1 code that used `isJSONRPCResponse`, rename it to `isJSONRPCResultResponse` to preserve the original behavior. Use the new `isJSONRPCResponse` only when you want to match both result and error responses. **Before (v1):** @@ -518,7 +705,7 @@ import { JSONRPCErrorResponse, ResourceTemplateReference, isJSONRPCErrorResponse ### Request handler context types -The `RequestHandlerExtra` type has been replaced with a structured context type hierarchy using nested groups: +The handler context is now a structured type hierarchy with nested groups. The v1 `RequestHandlerExtra` type and its flat fields (`extra.signal`, `extra.sendRequest`, etc.) are still available as `@deprecated` aliases that point at the nested locations: | v1 | v2 | | ---------------------------------------- | ---------------------------------------------------------------------- | @@ -530,7 +717,7 @@ The `RequestHandlerExtra` type has been replaced with a structured context type | `extra.sendRequest(...)` | `ctx.mcpReq.send(...)` | | `extra.sendNotification(...)` | `ctx.mcpReq.notify(...)` | | `extra.authInfo` | `ctx.http?.authInfo` | -| `extra.requestInfo` | `ctx.http?.req` (standard Web `Request`, only on `ServerContext`) | +| `extra.requestInfo` | `ctx.http?.req` (standard Web `Request`, only on `ServerContext`) | | `extra.closeSSEStream` | `ctx.http?.closeSSE` (only on `ServerContext`) | | `extra.closeStandaloneSSEStream` | `ctx.http?.closeStandaloneSSE` (only on `ServerContext`) | | `extra.sessionId` | `ctx.sessionId` | @@ -597,7 +784,7 @@ These replace the pattern of calling `server.sendLoggingMessage()`, `server.crea The SDK now distinguishes between two types of errors: -1. **`ProtocolError`** (renamed from `McpError`): Protocol errors that cross the wire as JSON-RPC error responses +1. **`ProtocolError`**: Protocol errors that cross the wire as JSON-RPC error responses. The v1 names `McpError` and `ErrorCode` are still exported as `@deprecated` aliases for `ProtocolError`/`ProtocolErrorCode`. 2. **`SdkError`**: Local SDK errors that never cross the wire (timeouts, connection issues, capability checks) #### Renamed exports @@ -665,9 +852,9 @@ The new `SdkErrorCode` enum contains string-valued codes for local SDK errors: | `SdkErrorCode.ClientHttpFailedToOpenStream` | Failed to open SSE stream | | `SdkErrorCode.ClientHttpFailedToTerminateSession` | Failed to terminate session | -#### `StreamableHTTPError` removed +#### `StreamableHTTPError` deprecated -The `StreamableHTTPError` class has been removed. HTTP transport errors are now thrown as `SdkError` with specific `SdkErrorCode` values that provide more granular error information: +`StreamableHTTPError` is now a subclass of `SdkError` (so `instanceof SdkError` and `instanceof StreamableHTTPError` both work). HTTP transport errors carry specific `SdkErrorCode` values for granular handling. Note: `error.code` is now an `SdkErrorCode` string; the HTTP status is on `error.status`. **Before (v1):** @@ -726,6 +913,20 @@ The new design: The OAuth error classes have been consolidated into a single `OAuthError` class with an `OAuthErrorCode` enum. +#### `errorCode` property renamed to `code` + +The `OAuthError` instance property `errorCode` has been renamed to `code` for consistency with `ProtocolError` and `SdkError`: + +```typescript +// v1 +if (err instanceof OAuthError && err.errorCode === 'invalid_token') { ... } + +// v2 +if (err instanceof OAuthError && err.code === OAuthErrorCode.InvalidToken) { ... } +``` + +A deprecated `errorCode` getter remains for backwards compatibility and emits a one-time warning. + #### Removed classes The following individual error classes have been removed in favor of `OAuthError` with the appropriate code: @@ -797,7 +998,8 @@ try { ### Experimental: `TaskCreationParams.ttl` no longer accepts `null` -The `ttl` field in `TaskCreationParams` (used when requesting the server to create a task) no longer accepts `null`. Per the MCP spec, `null` TTL (meaning unlimited lifetime) is only valid in server responses (`Task.ttl`), not in client requests. Clients should omit `ttl` to let the server decide the lifetime. +The `ttl` field in `TaskCreationParams` (used when requesting the server to create a task) no longer accepts `null`. Per the MCP spec, `null` TTL (meaning unlimited lifetime) is only valid in server responses (`Task.ttl`), not in client requests. Clients should omit `ttl` to let +the server decide the lifetime. This also narrows the type of `requestedTtl` in `TaskContext`, `CreateTaskServerContext`, and `TaskServerContext` from `number | null | undefined` to `number | undefined`. @@ -876,10 +1078,8 @@ const server = new McpServer( You can still explicitly override the validator if needed: ```typescript -// Runtime-aware default (auto-selects AjvJsonSchemaValidator or CfWorkerJsonSchemaValidator) import { DefaultJsonSchemaValidator } from '@modelcontextprotocol/server/_shims'; - -// Specific validators +// or import { AjvJsonSchemaValidator } from '@modelcontextprotocol/server'; import { CfWorkerJsonSchemaValidator } from '@modelcontextprotocol/server/validators/cf-worker'; ``` @@ -888,12 +1088,12 @@ import { CfWorkerJsonSchemaValidator } from '@modelcontextprotocol/server/valida The following APIs are unchanged between v1 and v2 (only the import paths changed): -- `Client` constructor and most client methods (`connect`, `listTools`, `listPrompts`, `listResources`, `readResource`, etc.) — note: `callTool()` signature changed (schema parameter removed) +- `Client` constructor and most client methods (`connect`, `listTools`, `listPrompts`, `listResources`, `readResource`, etc.) — note: `callTool()`'s result-schema parameter is now optional (the schema is resolved internally) - `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) +- All protocol **type definitions** from `types.ts` (except the aliases listed above). Zod **schema constants** are no longer exported — see [Zod schemas are no longer exported](#zod-schemas-are-no-longer-exported-runtime-validation). - Tool, prompt, and resource callback return types ## Using an LLM to migrate your code