This guide covers upgrading from @modelcontextprotocol/sdk (v1.x) to the v2 packages.
It is written for shell-capable agents and humans alike: run the codemod first, then
work through the manual sections for what the codemod can't rewrite.
If you are already on v2 and want to adopt the 2026-07-28 protocol revision, see support-2026-07-28.md instead.
TL;DR — quick path
- Prerequisites. Node.js 20+. v2 is ESM-first but ships a CommonJS build too, so
both
importandrequire('@modelcontextprotocol/…')resolve natively. - Run the codemod.
Run it at the package root (npx @modelcontextprotocol/codemod@latest v1-to-v2 ..), not./src— it also rewritespackage.json, and real projects import the SDK fromtest/,scripts/, and fixtures too. - Grep for markers. Anything the codemod recognized but could not safely rewrite is
marked in place:
grep -rn '@mcp-codemod-error' . - Type-check.
tsc --noEmit(or your build). Remaining errors map to the manual sections below. - Format. The codemod rewrites the AST without reformatting — run your formatter on
the changed files (
prettier --write/eslint --fix/biome format --write); the codemod prints the exact command after it runs. - Run your tests.
Migrating a large codebase gradually instead of in one pass? See Migrating in stages (large codebases).
What the codemod handles
The codemod (@modelcontextprotocol/codemod)
mechanically applies every rename whose mapping is fixed. The mappings are the
source of truth — they live in the codemod package and are not reproduced here:
| Mapping | Source file |
|---|---|
@modelcontextprotocol/sdk/... import paths → v2 packages |
mappings/importMap.ts |
Symbol renames (McpError → ProtocolError, JSONRPCError → JSONRPCErrorResponse, …) |
mappings/symbolMap.ts |
setRequestHandler(Schema, …) → setRequestHandler('method/string', …) |
mappings/schemaToMethodMap.ts |
extra.* → ctx.mcpReq.* / ctx.http?.* property remap |
mappings/contextPropertyMap.ts |
In addition the codemod:
- Updates
package.jsondependencies (@modelcontextprotocol/sdk→ the v2 packages your imports actually use). - Rewrites
.tool()/.prompt()/.resource()toregisterTool/registerPrompt/registerResourceand wrapsinputSchema/outputSchema/argsSchema/uriSchemaraw Zod shapes withz.object(), addingimport { z } from 'zod'when the file has nozbinding. - Drops the result-schema argument from
client.request()/client.callTool()for spec methods. - Routes the spec Zod
*Schemaconstants imported fromsdk/types.jsto@modelcontextprotocol/core(mixed imports are split;.parse()/.safeParse()calls are left untouched). Task-handler schema constants (GetTaskRequestSchemaetc.) used assetRequestHandlerargs are not rewritten — the experimental tasks feature was removed (SEP-2663), so each such registration is marked with an action-required diagnostic instead (see Experimental tasks interception removed). - Renames
ErrorCode→ProtocolErrorCodeand routes the local-only members (RequestTimeout,ConnectionClosed) toSdkErrorCode— rewriting an all-SDK condition'sinstanceof ProtocolErrorguard toSdkError, and marking guards that mix the two enums. - Renames every
StreamableHTTPErrorreference toSdkHttpErrorand adds the import (constructor calls are marked for review — argument shape changed). - Replaces
IsomorphicHeaderswith the Web StandardHeaderstype and drops the import (a warning notesHeadersuses.get()/.set(), not bracket access). - Rewrites
SchemaInput<T>→StandardSchemaWithJSON.InferInput<T>. - Renames
RequestHandlerExtra→ServerContext/ClientContextand theextraparameter toctx. - Rewrites
vi.mock/jest.mockand dynamicimport()paths. - Renames the
ResourceTemplatetype imported from@modelcontextprotocol/sdk/types.jstoResourceTemplateType(the spec wire type). TheResourceTemplateURI-template helper class fromserver/mcp.jskeeps its name and is not renamed. - Drops
@modelcontextprotocol/sdk/server/zod-compat.jsimports. - Inverts optional completable nesting —
completable(schema.optional(), cb)becomescompletable(schema, cb).optional()(see Standard Schema objects); shapes it cannot invert get an@mcp-codemod-errormarker. - Rewrites
Protocol/mergeCapabilitiesimports fromshared/protocol.jsto the client or server package root, like the module's other symbols.
What the codemod does NOT handle
Each of these maps to a manual section below. The codemod marks every site it
recognized but could not safely rewrite with an @mcp-codemod-error comment.
- Node 20 / ESM — pre-flight, not a code rewrite. → Packaging & runtime
- Header-read
.get()rewrite —IsomorphicHeadersis renamed toHeadersandextra.requestInfo?.headers[…]is remapped toctx.http?.req?.headers[…], but converting that bracket access to.get()is manual. (Headers you pass in viarequestInit.headersneed no rewrite — plain objects remain valid.) → HTTP & headers ctx.mcpReq.send()schema-arg drop — the codemod drops the schema arg fromclient.request()/client.callTool()but leaves nestedctx.mcpReq.send()calls alone. → Low-level protocol- OAuth error-class consolidation —
instanceof InvalidGrantError→OAuthError+OAuthErrorCodeis a judgment rewrite. → Auth SdkErrorCodebranch selection — the codemod renamesStreamableHTTPError→SdkHttpError; deciding whichSdkErrorCodebranch a given catch should match is judgment. → Errors- Namespace schema access —
import * as t from '…/types.js'+t.CallToolResultSchema.parse(…)can't be split per-symbol; the codemod flags it action-required — re-import the schema from@modelcontextprotocol/coreby hand. → Types & schemas - Import-less (injected) SDK surfaces — the codemod is import-driven: a file that
receives the SDK surface as a parameter (dependency injection, factory seams) and has
no SDK import is never rewritten, and the v1 idioms there fail at runtime, not
compile time — e.g. the v1 schema-first
setRequestHandler(Schema, …)form throws aTypeErrorat registration. Grep such seams for v1 API tokens beyond import statements (setRequestHandler(,ErrorCode.,extra.) and apply the handler-registration and Errors sections by hand. → Low-level protocol - Behavioral adaptation — list auto-aggregation, capability empties, lazy validator compilation, output-schema validation rules. → Behavioral changes
Manual changes (what the codemod does not handle)
Each subsystem has its own reference file. Open the one that matches the type error, runtime failure or @mcp-codemod-error marker in front of you; most migrations need only a few of them.
| Reference file | Read it when |
|---|---|
| Packaging & runtime | Choosing which v2 packages to install, or dealing with Node/ESM/CommonJS setup, monorepo workspaces, Jest, nested zod copies, migrating in stages, or a library that peer-depends on the SDK |
| Imports & transports | A transport import needs a runtime decision (Node vs web-standard Streamable HTTP) or a stdio import moved to a ./stdio subpath |
Low-level protocol & handler context (ctx) |
Code uses extra / ctx, setRequestHandler / setNotificationHandler, request(), ctx.mcpReq.send() or callTool() |
| Server registration API | Code registers tools, prompts or resources, or passes raw Zod shapes as schemas |
| HTTP & headers | Code reads HTTP request headers |
| Errors | Code throws or catches McpError / ProtocolError, ErrorCode, SdkError or SdkHttpError |
| Auth | Code uses OAuth (auth(), OAuthClientProvider, OAuth error classes) or bearer-token auth |
| Types & schemas | Code imports Zod *Schema constants or removed type aliases, or depends on JSON Schema details |
| Behavioral changes | The code compiles but tests or runtime behavior differ |
| Enhancements | Optional: v2 features worth adopting once the upgrade is done |
| Unchanged APIs | Checking whether an API changed at all before rewriting it |
Need help?
- The codemod's
@mcp-codemod-errormarkers point at every site it could not safely rewrite. - The Troubleshooting page covers common errors and their fixes.
- Runnable examples for every subsystem.
- Open an issue on GitHub.