Convert MCP Server (SDK v1 → v2)
Port an existing MCP TypeScript server from @modelcontextprotocol/sdk v1.x (single package) to the v2 split-package SDK: @modelcontextprotocol/server, /client, /node, /express, /hono. The v2 surface changes are package split, extra → ctx handler-context mapping, Zod v3→v4 with raw-shape removal, McpError → ProtocolError, method-string request-handler keys, framework adapter packages, and OAuth-router removal.
v2 is currently a pre-release alpha. Latest published: 2.0.0-alpha.2 (verified on npm, 2026-05-09). Most production servers should stay on @modelcontextprotocol/sdk@^1.x and use this skill to plan, test, and stage the migration — not to flip a production switch.
When to use this skill
Trigger on actual migration intent applied to an existing v1 codebase. Italicized phrases below are concrete tells:
- "port my MCP server to v2", "upgrade @modelcontextprotocol/sdk to the new split packages"
- "replace
@modelcontextprotocol/sdk/server/mcp.js imports with @modelcontextprotocol/server"
- "my handlers use
extra.signal/extra.authInfo and I need the v2 equivalent"
- "convert
inputSchema: { name: z.string() } raw shapes to v2", "upgrade Zod v3 to v4 in MCP tools"
- "replace
mcpAuthRouter / requireBearerAuth / OAuthServerProvider"
- "swap
StreamableHTTPServerTransport for NodeStreamableHTTPServerTransport"
- "
McpError/ErrorCode → ProtocolError/ProtocolErrorCode rename"
- "
setRequestHandler(CallToolRequestSchema, ...) → method-string keys"
Detection signals (all should be true)
package.json contains @modelcontextprotocol/sdk (the v1 single package).
- The user states intent to migrate, upgrade, port, or convert — not just maintain v1.
- There is existing handler/transport/auth code to transform — not a greenfield build.
Do NOT use this skill for
- New v1 server (no existing code) → use
build-mcp-server-sdk-v1.
- New v2 server (greenfield on v2 alpha) → use
build-mcp-server-sdk-v2.
- Maintaining or bug-fixing a v1 server with no migration intent → use
build-mcp-server-sdk-v1.
- Already on v2 and just need to extend it → use
build-mcp-server-sdk-v2.
- Using the
mcp-use wrapper (not the official SDK) → use build-mcp-use-server.
Core rules — load-bearing
- Pick a strategy before touching files. Full rewrite, meta-package shim (only if the target alpha actually publishes one), HTTP-layer auth transition, or stay on v1. Verify package availability on npm before committing.
- Never mix v1 and v2 packages in the same module graph except deliberately during a staged migration. Two
McpServer classes from two packages do not interoperate; types silently diverge and instanceof checks break at runtime.
- Pin to exact alpha versions (
@modelcontextprotocol/server@2.0.0-alpha.2). ^ ranges across alphas surface breaking changes mid-migration. Always use --save-exact (or pnpm/yarn equivalents).
- Migrate handler context (
extra → ctx) and schemas (ZodRawShape → z.object) together for any tool you touch. Half-migrated handlers are the single biggest source of runtime errors.
- Replace OAuth deliberately. Server-side OAuth (
mcpAuthRouter, requireBearerAuth, OAuthServerProvider) is removed from v2. If @modelcontextprotocol/server-auth-legacy is published for the target alpha, use it as a transition; otherwise stay on v1 or move auth to the HTTP layer (Bearer middleware, Passport, jose) and forward identity via req.auth.
- Upgrade Node to 20+ and add
"type": "module" to package.json. v2 is ESM-only — CommonJS dual-publish is unsupported.
- Keep a working v1 branch alive until v2 graduates from alpha. v2 is a delivery target; v1 is the running production until then.
v1 → v2 surface map
The most-used renames at a glance. Detailed per-area guides linked below.
| Area |
v1 |
v2 |
| Server class import |
@modelcontextprotocol/sdk/server/mcp.js |
@modelcontextprotocol/server |
| Stdio transport |
@modelcontextprotocol/sdk/server/stdio.js |
@modelcontextprotocol/server |
| HTTP transport |
StreamableHTTPServerTransport from …/server/streamableHttp.js |
NodeStreamableHTTPServerTransport from @modelcontextprotocol/node |
| SSE transport |
SSEServerTransport |
removed (clients must move to Streamable HTTP first) |
| Express adapter |
createMcpExpressApp from SDK subpath |
createMcpExpressApp from @modelcontextprotocol/express |
| Hono adapter |
(none) |
createMcpHonoApp from @modelcontextprotocol/hono |
| Client |
@modelcontextprotocol/sdk/client/index.js |
@modelcontextprotocol/client |
| Errors |
McpError / ErrorCode from …/types.js |
ProtocolError / ProtocolErrorCode from @modelcontextprotocol/server |
| Request-handler key |
setRequestHandler(CallToolRequestSchema, …) |
setRequestHandler("tools/call", …) |
| Zod |
import { z } from "zod" (v3) |
import * as z from "zod/v4" |
| Tool input schema |
inputSchema: { name: z.string() } raw shape |
inputSchema: z.object({ name: z.string() }) full schema |
| Handler signature |
(args, extra) => … |
(args, ctx) => … |
| Auth router |
mcpAuthRouter, requireBearerAuth, OAuthServerProvider |
removed (HTTP-layer auth or transition package) |
extra → ctx (most-frequent moves)
| v1 |
v2 |
extra.signal |
ctx.mcpReq.signal |
extra.requestId |
ctx.mcpReq.id |
extra.sendNotification(n) |
ctx.mcpReq.notify(n) |
extra.sendRequest(r, s) |
ctx.mcpReq.send(r, s) |
extra.authInfo |
ctx.http?.authInfo |
extra.requestInfo |
ctx.http?.req |
extra.closeSSEStream?.() |
ctx.http?.closeSSE?.() |
extra.sessionId |
ctx.sessionId (top-level, unchanged) |
ctx.http? is nullable — stdio transport leaves it undefined. Any code that assumed extra.authInfo was always defined needs an explicit branch.
Workflow
1 — Inventory the v1 server
Read package.json, tsconfig.json, and every file under src/. Record:
- Every
@modelcontextprotocol/sdk/* import path (subpath exports are the migration unit).
- Every
extra.* field accessed in handlers.
- Every transport class instantiated.
- Every Zod schema shape: raw-object shorthand vs full
z.object().
- Every
McpError(ErrorCode.X, …) call site and the codes used.
- Every
setRequestHandler(SomeRequestSchema, …) call.
- Every framework wiring point:
createMcpExpressApp, requireBearerAuth, mcpAuthRouter, custom middleware that depends on the SDK.
For a deterministic first pass, run bash scripts/check-v2-feasibility.sh <project-dir> from this skill directory and read scripts/check-v2-feasibility.md. Use the report to focus the manual inventory; do not treat it as a substitute for reading the code.
2 — Choose the migration strategy
| Strategy |
When |
Effort |
Trade-off |
| Full rewrite |
Small server (≤200 LOC tools, ≤2 transports, no OAuth router) |
Hours |
Cleanest end state, full v2 API access |
| Meta-package shim |
Medium server, many subpath imports, target alpha publishes the shim |
Hours |
Keeps v1 import paths working under v2; defer rewrites tool-by-tool |
| HTTP-layer auth transition |
Production OAuth server using mcpAuthRouter |
Days |
Replace SDK OAuth with app/framework middleware in a separate auth migration |
| Stay on v1 |
OAuth-heavy, large, or alpha-allergic |
Zero |
No code change; revisit when v2 reaches stable |
Read references/guides/migration-strategy.md before committing. Record the choice in the change description.
3 — Rewrite packages and imports
Per references/guides/package-and-imports.md. Smallest unit: one import line at a time.
For direct-package migrations, preview the mechanical import portion with bash scripts/migrate-imports.sh <project-dir> and read scripts/migrate-imports.md. Rerun with --write only after reviewing the dry-run. Do not use it for schema, ctx, auth-router, request-handler-key, or transport-lifecycle rewrites — those need hand edits.
4 — Rewrite schemas
Per references/guides/schema-and-errors.md.
import { z } from "zod" → import * as z from "zod/v4"
- Raw shape
{ name: z.string() } → full schema z.object({ name: z.string() }). v2 rejects raw shapes outright.
- Drop
zod-to-json-schema — v2 emits JSON Schema 2020-12 natively via z.toJSONSchema().
5 — Rewrite handlers (extra → ctx)
Per references/guides/handler-context-mapping.md. Use the mapping table above. No-args tool handler: (extra) => … becomes (ctx) => … — same shape, renamed.
6 — Rewrite errors and request-handler keys
Per references/guides/schema-and-errors.md. McpError / ErrorCode → ProtocolError / ProtocolErrorCode. setRequestHandler(CallToolRequestSchema, …) → setRequestHandler("tools/call", …).
7 — Replace auth
Per references/guides/auth-replacements.md.
- If using
mcpAuthRouter and the target alpha publishes @modelcontextprotocol/server-auth-legacy, keep the v1 router through that transition package; otherwise stay on v1 until auth can move out of the SDK.
- If integrating fresh: do auth at the HTTP layer (Express/Hono middleware) and forward identity into
authInfo via req.auth. The Express adapter passes req.auth through to ctx.http?.authInfo automatically.
- The
better-auth MCP plugin currently targets v1 import paths and is flagged for deprecation — do not adopt it new.
8 — Replace transports and adapters
Per references/guides/transports-and-adapters.md. Use the surface map table above. Hono is new in v2 via @modelcontextprotocol/hono (the official SDK package, not the unrelated community @hono/mcp package).
9 — Validate and stage rollout
Per references/patterns/validation-and-rollback.md.
- Add
"type": "module" to package.json. Bump engines to Node 20+.
- Run type-check first, then the existing unit/integration test suite.
- Smoke-test with
npx @anthropic-ai/mcp-inspector for browser/manual coverage.
- Use
test-by-mcpc-cli for headless CLI smoke/regression checks when mcpc is available.
- Connect at least one real MCP client before production rollout.
- Stage in a non-prod environment for at least one week before flipping production traffic.
- Keep the v1 branch deployable until v2 reaches stable — alpha versions can break.
Quick diff — minimal hello-world before/after
// 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: "my-server", version: "1.0.0" });
server.registerTool("greet", {
inputSchema: { name: z.string() }, // raw shape
}, async ({ name }, extra) => { // (args, extra)
await extra.sendNotification({ method: "...", params: {} });
return { content: [{ type: "text", text: `Hi ${name}` }] };
});
await server.connect(new StdioServerTransport());
// v2
import { McpServer, StdioServerTransport } from "@modelcontextprotocol/server";
import * as z from "zod/v4";
const server = new McpServer({ name: "my-server", version: "1.0.0" });
server.registerTool("greet", {
inputSchema: z.object({ name: z.string() }), // full schema
}, async ({ name }, ctx) => { // (args, ctx)
await ctx.mcpReq.notify({ method: "...", params: {} });
return { content: [{ type: "text" as const, text: `Hi ${name}` }] };
});
await server.connect(new StdioServerTransport());
Decision rules
- Prefer the meta-package shim only when the target alpha actually publishes it; otherwise direct packages or stay on v1.
- Prefer separating auth migration from the SDK port — auth is high-stakes; only use a verified transition package.
- Prefer pinned alpha versions over
^ ranges — alphas can publish breaking changes between any two patches.
- Prefer migrating one handler end-to-end (imports + schema + ctx + errors) over one concern across all handlers — bounds the test surface per PR.
- Treat
ctx.http? as nullable everywhere — stdio leaves it undefined.
Guardrails
- Never run an alpha SDK in production before staging it in a non-prod environment with realistic traffic for at least one week.
- Never mix
@modelcontextprotocol/sdk and @modelcontextprotocol/server in the same compiled bundle without the meta-package shim — TypeScript accepts duplicate types but instanceof checks and class identity break at runtime.
- Never assume
req.auth propagates without explicitly wiring HTTP-layer auth middleware — v2 has no server-side OAuth router.
- Never delete the v1 branch or
package-lock.json until v2 has been stable in production for at least one full release cycle.
- Never
npm install v2 packages without --save-exact.
- Never adopt the
better-auth MCP plugin as a new dependency in a v2 migration — flagged for deprecation, currently targets v1 import paths.
Output contract
When a port finishes, report:
- migration strategy chosen and why
- package/version changes, exact alpha pins, and whether the meta-package shim remains
- handlers/tools migrated, especially schema and
ctx rewrites
- auth path chosen: stay on v1, verified transition package, HTTP-layer auth, or no auth
- transports/adapters changed
- validation rung reached: type-check, unit tests, Inspector,
test-by-mcpc-cli, real client, staging/canary
- rollback status: v1 branch/image/lockfile preserved, or blocker if not verified
- residual risks from v2 alpha status
After the port lands, hand off to build-mcp-server-sdk-v2 for ongoing v2 maintenance.
Reference routing
Use the smallest set relevant to the migration step.
Plan and decide
| Reference |
When to read |
references/guides/migration-strategy.md |
Choosing between full rewrite, meta-package shim, HTTP-layer auth transition, "stay on v1" |
Rewrite mechanics
| Reference |
When to read |
references/guides/package-and-imports.md |
Package split table, import-by-import rewriter, meta-package shim usage |
references/guides/schema-and-errors.md |
Zod v3→v4, raw shapes, JSON Schema dialect, error class rename, request-handler key strings |
references/guides/handler-context-mapping.md |
Full extra → ctx field mapping, no-args handlers, http nullability, new ctx-only methods |
references/guides/transports-and-adapters.md |
Transport renames, Express/Hono adapters, DNS rebinding, hostHeaderValidation |
references/guides/auth-replacements.md |
OAuth-router replacement, custom Bearer/Passport/jose patterns, why not better-auth |
Validate and ship
| Reference |
When to read |
references/patterns/validation-and-rollback.md |
Migration test plan, dual-version coexistence, rollback playbook, alpha-pinning |
Compatibility note
Source-verified against the v1.x branch (latest stable: @modelcontextprotocol/sdk@^1.x) and the v2 alpha packages (@modelcontextprotocol/server@2.0.0-alpha.2, /client@2.0.0-alpha.2, /node@2.0.0-alpha.2, /express@2.0.0-alpha.2, /hono@2.0.0-alpha.2). npm verification on 2026-05-09 found no published @modelcontextprotocol/core, @modelcontextprotocol/sdk@2.0.0-alpha.2, or @modelcontextprotocol/server-auth-legacy; re-check the v2 changelog and npm package availability before each migration sprint.
1---2name: convert-mcp-sdk-v1-to-v23description: Use if porting an MCP TypeScript server from @modelcontextprotocol/sdk v1.x to the v2 SDK.4---56# Convert MCP Server (SDK v1 → v2)78Port an existing MCP TypeScript server from `@modelcontextprotocol/sdk` v1.x (single package) to the v2 split-package SDK: `@modelcontextprotocol/server`, `/client`, `/node`, `/express`, `/hono`. The v2 surface changes are package split, `extra → ctx` handler-context mapping, Zod v3→v4 with raw-shape removal, `McpError → ProtocolError`, method-string request-handler keys, framework adapter packages, and OAuth-router removal.910**v2 is currently a pre-release alpha.** Latest published: `2.0.0-alpha.2` (verified on npm, 2026-05-09). Most production servers should stay on `@modelcontextprotocol/sdk@^1.x` and use this skill to plan, test, and stage the migration — not to flip a production switch.1112## When to use this skill1314Trigger on actual migration intent applied to an existing v1 codebase. Italicized phrases below are concrete tells:1516- *"port my MCP server to v2"*, *"upgrade @modelcontextprotocol/sdk to the new split packages"*17- *"replace `@modelcontextprotocol/sdk/server/mcp.js` imports with `@modelcontextprotocol/server`"*18- *"my handlers use `extra.signal`/`extra.authInfo` and I need the v2 equivalent"*19- *"convert `inputSchema: { name: z.string() }` raw shapes to v2"*, *"upgrade Zod v3 to v4 in MCP tools"*20- *"replace `mcpAuthRouter` / `requireBearerAuth` / `OAuthServerProvider`"*21- *"swap `StreamableHTTPServerTransport` for `NodeStreamableHTTPServerTransport`"*22- *"`McpError`/`ErrorCode` → `ProtocolError`/`ProtocolErrorCode` rename"*23- *"`setRequestHandler(CallToolRequestSchema, ...)` → method-string keys"*2425### Detection signals (all should be true)2627- `package.json` contains `@modelcontextprotocol/sdk` (the v1 single package).28- The user states intent to migrate, upgrade, port, or convert — not just maintain v1.29- There is existing handler/transport/auth code to transform — not a greenfield build.3031### Do NOT use this skill for3233- **New v1 server** (no existing code) → use `build-mcp-server-sdk-v1`.34- **New v2 server** (greenfield on v2 alpha) → use `build-mcp-server-sdk-v2`.35- **Maintaining or bug-fixing a v1 server** with no migration intent → use `build-mcp-server-sdk-v1`.36- **Already on v2** and just need to extend it → use `build-mcp-server-sdk-v2`.37- **Using the `mcp-use` wrapper** (not the official SDK) → use `build-mcp-use-server`.3839## Core rules — load-bearing4041- **Pick a strategy before touching files.** Full rewrite, meta-package shim (only if the target alpha actually publishes one), HTTP-layer auth transition, or stay on v1. Verify package availability on npm before committing.42- **Never mix v1 and v2 packages in the same module graph** except deliberately during a staged migration. Two `McpServer` classes from two packages do not interoperate; types silently diverge and `instanceof` checks break at runtime.43- **Pin to exact alpha versions** (`@modelcontextprotocol/server@2.0.0-alpha.2`). `^` ranges across alphas surface breaking changes mid-migration. Always use `--save-exact` (or pnpm/yarn equivalents).44- **Migrate handler context (`extra → ctx`) and schemas (`ZodRawShape → z.object`) together** for any tool you touch. Half-migrated handlers are the single biggest source of runtime errors.45- **Replace OAuth deliberately.** Server-side OAuth (`mcpAuthRouter`, `requireBearerAuth`, `OAuthServerProvider`) is removed from v2. If `@modelcontextprotocol/server-auth-legacy` is published for the target alpha, use it as a transition; otherwise stay on v1 or move auth to the HTTP layer (Bearer middleware, Passport, jose) and forward identity via `req.auth`.46- **Upgrade Node to 20+ and add `"type": "module"` to `package.json`.** v2 is ESM-only — CommonJS dual-publish is unsupported.47- **Keep a working v1 branch alive until v2 graduates from alpha.** v2 is a delivery target; v1 is the running production until then.4849## v1 → v2 surface map5051The most-used renames at a glance. Detailed per-area guides linked below.5253| Area | v1 | v2 |54|---|---|---|55| Server class import | `@modelcontextprotocol/sdk/server/mcp.js` | `@modelcontextprotocol/server` |56| Stdio transport | `@modelcontextprotocol/sdk/server/stdio.js` | `@modelcontextprotocol/server` |57| HTTP transport | `StreamableHTTPServerTransport` from `…/server/streamableHttp.js` | `NodeStreamableHTTPServerTransport` from `@modelcontextprotocol/node` |58| SSE transport | `SSEServerTransport` | removed (clients must move to Streamable HTTP first) |59| Express adapter | `createMcpExpressApp` from SDK subpath | `createMcpExpressApp` from `@modelcontextprotocol/express` |60| Hono adapter | _(none)_ | `createMcpHonoApp` from `@modelcontextprotocol/hono` |61| Client | `@modelcontextprotocol/sdk/client/index.js` | `@modelcontextprotocol/client` |62| Errors | `McpError` / `ErrorCode` from `…/types.js` | `ProtocolError` / `ProtocolErrorCode` from `@modelcontextprotocol/server` |63| Request-handler key | `setRequestHandler(CallToolRequestSchema, …)` | `setRequestHandler("tools/call", …)` |64| Zod | `import { z } from "zod"` (v3) | `import * as z from "zod/v4"` |65| Tool input schema | `inputSchema: { name: z.string() }` raw shape | `inputSchema: z.object({ name: z.string() })` full schema |66| Handler signature | `(args, extra) => …` | `(args, ctx) => …` |67| Auth router | `mcpAuthRouter`, `requireBearerAuth`, `OAuthServerProvider` | removed (HTTP-layer auth or transition package) |6869## extra → ctx (most-frequent moves)7071| v1 | v2 |72|---|---|73| `extra.signal` | `ctx.mcpReq.signal` |74| `extra.requestId` | `ctx.mcpReq.id` |75| `extra.sendNotification(n)` | `ctx.mcpReq.notify(n)` |76| `extra.sendRequest(r, s)` | `ctx.mcpReq.send(r, s)` |77| `extra.authInfo` | `ctx.http?.authInfo` |78| `extra.requestInfo` | `ctx.http?.req` |79| `extra.closeSSEStream?.()` | `ctx.http?.closeSSE?.()` |80| `extra.sessionId` | `ctx.sessionId` (top-level, unchanged) |8182`ctx.http?` is **nullable** — stdio transport leaves it `undefined`. Any code that assumed `extra.authInfo` was always defined needs an explicit branch.8384## Workflow8586### 1 — Inventory the v1 server8788Read `package.json`, `tsconfig.json`, and every file under `src/`. Record:8990- Every `@modelcontextprotocol/sdk/*` import path (subpath exports are the migration unit).91- Every `extra.*` field accessed in handlers.92- Every transport class instantiated.93- Every Zod schema shape: raw-object shorthand vs full `z.object()`.94- Every `McpError(ErrorCode.X, …)` call site and the codes used.95- Every `setRequestHandler(SomeRequestSchema, …)` call.96- Every framework wiring point: `createMcpExpressApp`, `requireBearerAuth`, `mcpAuthRouter`, custom middleware that depends on the SDK.9798For a deterministic first pass, run `bash scripts/check-v2-feasibility.sh <project-dir>` from this skill directory and read `scripts/check-v2-feasibility.md`. Use the report to focus the manual inventory; do not treat it as a substitute for reading the code.99100### 2 — Choose the migration strategy101102| Strategy | When | Effort | Trade-off |103|---|---|---|---|104| **Full rewrite** | Small server (≤200 LOC tools, ≤2 transports, no OAuth router) | Hours | Cleanest end state, full v2 API access |105| **Meta-package shim** | Medium server, many subpath imports, target alpha publishes the shim | Hours | Keeps v1 import paths working under v2; defer rewrites tool-by-tool |106| **HTTP-layer auth transition** | Production OAuth server using `mcpAuthRouter` | Days | Replace SDK OAuth with app/framework middleware in a separate auth migration |107| **Stay on v1** | OAuth-heavy, large, or alpha-allergic | Zero | No code change; revisit when v2 reaches stable |108109Read `references/guides/migration-strategy.md` before committing. Record the choice in the change description.110111### 3 — Rewrite packages and imports112113Per `references/guides/package-and-imports.md`. Smallest unit: one import line at a time.114115For direct-package migrations, preview the mechanical import portion with `bash scripts/migrate-imports.sh <project-dir>` and read `scripts/migrate-imports.md`. Rerun with `--write` only after reviewing the dry-run. Do not use it for schema, `ctx`, auth-router, request-handler-key, or transport-lifecycle rewrites — those need hand edits.116117### 4 — Rewrite schemas118119Per `references/guides/schema-and-errors.md`.120121- `import { z } from "zod"` → `import * as z from "zod/v4"`122- Raw shape `{ name: z.string() }` → full schema `z.object({ name: z.string() })`. v2 rejects raw shapes outright.123- Drop `zod-to-json-schema` — v2 emits JSON Schema 2020-12 natively via `z.toJSONSchema()`.124125### 5 — Rewrite handlers (extra → ctx)126127Per `references/guides/handler-context-mapping.md`. Use the mapping table above. No-args tool handler: `(extra) => …` becomes `(ctx) => …` — same shape, renamed.128129### 6 — Rewrite errors and request-handler keys130131Per `references/guides/schema-and-errors.md`. `McpError` / `ErrorCode` → `ProtocolError` / `ProtocolErrorCode`. `setRequestHandler(CallToolRequestSchema, …)` → `setRequestHandler("tools/call", …)`.132133### 7 — Replace auth134135Per `references/guides/auth-replacements.md`.136137- If using `mcpAuthRouter` and the target alpha publishes `@modelcontextprotocol/server-auth-legacy`, keep the v1 router through that transition package; otherwise stay on v1 until auth can move out of the SDK.138- If integrating fresh: do auth at the HTTP layer (Express/Hono middleware) and forward identity into `authInfo` via `req.auth`. The Express adapter passes `req.auth` through to `ctx.http?.authInfo` automatically.139- The `better-auth` MCP plugin currently targets v1 import paths and is flagged for deprecation — do not adopt it new.140141### 8 — Replace transports and adapters142143Per `references/guides/transports-and-adapters.md`. Use the surface map table above. Hono is new in v2 via `@modelcontextprotocol/hono` (the official SDK package, not the unrelated community `@hono/mcp` package).144145### 9 — Validate and stage rollout146147Per `references/patterns/validation-and-rollback.md`.148149- Add `"type": "module"` to `package.json`. Bump engines to Node 20+.150- Run type-check first, then the existing unit/integration test suite.151- Smoke-test with `npx @anthropic-ai/mcp-inspector` for browser/manual coverage.152- Use `test-by-mcpc-cli` for headless CLI smoke/regression checks when `mcpc` is available.153- Connect at least one real MCP client before production rollout.154- Stage in a non-prod environment for at least one week before flipping production traffic.155- Keep the v1 branch deployable until v2 reaches stable — alpha versions can break.156157## Quick diff — minimal hello-world before/after158159```typescript160// v1161import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";162import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";163import { z } from "zod";164165const server = new McpServer({ name: "my-server", version: "1.0.0" });166server.registerTool("greet", {167 inputSchema: { name: z.string() }, // raw shape168}, async ({ name }, extra) => { // (args, extra)169 await extra.sendNotification({ method: "...", params: {} });170 return { content: [{ type: "text", text: `Hi ${name}` }] };171});172await server.connect(new StdioServerTransport());173174// v2175import { McpServer, StdioServerTransport } from "@modelcontextprotocol/server";176import * as z from "zod/v4";177178const server = new McpServer({ name: "my-server", version: "1.0.0" });179server.registerTool("greet", {180 inputSchema: z.object({ name: z.string() }), // full schema181}, async ({ name }, ctx) => { // (args, ctx)182 await ctx.mcpReq.notify({ method: "...", params: {} });183 return { content: [{ type: "text" as const, text: `Hi ${name}` }] };184});185await server.connect(new StdioServerTransport());186```187188## Decision rules189190- Prefer the meta-package shim only when the target alpha actually publishes it; otherwise direct packages or stay on v1.191- Prefer separating auth migration from the SDK port — auth is high-stakes; only use a verified transition package.192- Prefer pinned alpha versions over `^` ranges — alphas can publish breaking changes between any two patches.193- Prefer migrating one handler end-to-end (imports + schema + ctx + errors) over one concern across all handlers — bounds the test surface per PR.194- Treat `ctx.http?` as nullable everywhere — stdio leaves it `undefined`.195196## Guardrails197198- Never run an alpha SDK in production before staging it in a non-prod environment with realistic traffic for at least one week.199- Never mix `@modelcontextprotocol/sdk` and `@modelcontextprotocol/server` in the same compiled bundle without the meta-package shim — TypeScript accepts duplicate types but `instanceof` checks and class identity break at runtime.200- Never assume `req.auth` propagates without explicitly wiring HTTP-layer auth middleware — v2 has no server-side OAuth router.201- Never delete the v1 branch or `package-lock.json` until v2 has been stable in production for at least one full release cycle.202- Never `npm install` v2 packages without `--save-exact`.203- Never adopt the `better-auth` MCP plugin as a new dependency in a v2 migration — flagged for deprecation, currently targets v1 import paths.204205## Output contract206207When a port finishes, report:208209- migration strategy chosen and why210- package/version changes, exact alpha pins, and whether the meta-package shim remains211- handlers/tools migrated, especially schema and `ctx` rewrites212- auth path chosen: stay on v1, verified transition package, HTTP-layer auth, or no auth213- transports/adapters changed214- validation rung reached: type-check, unit tests, Inspector, `test-by-mcpc-cli`, real client, staging/canary215- rollback status: v1 branch/image/lockfile preserved, or blocker if not verified216- residual risks from v2 alpha status217218After the port lands, hand off to `build-mcp-server-sdk-v2` for ongoing v2 maintenance.219220## Reference routing221222Use the smallest set relevant to the migration step.223224### Plan and decide225226| Reference | When to read |227|---|---|228| `references/guides/migration-strategy.md` | Choosing between full rewrite, meta-package shim, HTTP-layer auth transition, "stay on v1" |229230### Rewrite mechanics231232| Reference | When to read |233|---|---|234| `references/guides/package-and-imports.md` | Package split table, import-by-import rewriter, meta-package shim usage |235| `references/guides/schema-and-errors.md` | Zod v3→v4, raw shapes, JSON Schema dialect, error class rename, request-handler key strings |236| `references/guides/handler-context-mapping.md` | Full `extra` → `ctx` field mapping, no-args handlers, http nullability, new ctx-only methods |237| `references/guides/transports-and-adapters.md` | Transport renames, Express/Hono adapters, DNS rebinding, hostHeaderValidation |238| `references/guides/auth-replacements.md` | OAuth-router replacement, custom Bearer/Passport/jose patterns, why not better-auth |239240### Validate and ship241242| Reference | When to read |243|---|---|244| `references/patterns/validation-and-rollback.md` | Migration test plan, dual-version coexistence, rollback playbook, alpha-pinning |245246## Compatibility note247248Source-verified against the v1.x branch (latest stable: `@modelcontextprotocol/sdk@^1.x`) and the v2 alpha packages (`@modelcontextprotocol/server@2.0.0-alpha.2`, `/client@2.0.0-alpha.2`, `/node@2.0.0-alpha.2`, `/express@2.0.0-alpha.2`, `/hono@2.0.0-alpha.2`). npm verification on 2026-05-09 found no published `@modelcontextprotocol/core`, `@modelcontextprotocol/sdk@2.0.0-alpha.2`, or `@modelcontextprotocol/server-auth-legacy`; re-check the v2 changelog and npm package availability before each migration sprint.