github modelcontextprotocol/typescript-sdk @modelcontextprotocol/codemod@2.0.0-alpha.2

Minor Changes

  • #2393 e4e8b22 Thanks @felixweinberger! - Three migration-sweep fixes. completable() calls whose first argument is an optional-wrapped schema are rewritten to apply .optional() to the completable(...) result — v2 resolves completion metadata after unwrapping an outer optional wrapper, so the v1 nesting produced empty completion lists without an error; wrapper shapes the codemod cannot invert get an action-required marker instead. Imports of Protocol and mergeCapabilities from v1's shared/protocol.js are no longer rewritten to a member the v2 packages do not export: the symbols are dropped from the rewritten import and an action-required marker explains the replacement (fallbackRequestHandler for unrouted inbound requests; a plain object spread for capability merging). The manifest zod-range warning now describes the symptom by vintage — zod 4.0–4.1 ranges fail type-checking (TS2769), while zod-3 ranges fail type-checking or at the first tools/list depending on the imported zod entry point.

  • #2393 e4e8b22 Thanks @felixweinberger! - Migration-sweep batch. The ErrorCode split now coordinates with the surrounding check: an all-SDK condition's instanceof ProtocolError/McpError guard is rewritten to SdkError, a guard covering both enums gets a marker asking for a split, and an ErrorCode import with no rewritable member access on a v2 specifier is dropped with a marker instead of failing at module link time. Wrapping a raw shape with z.object() adds import { z } from 'zod' when the file has no z value binding (a non-import z binding gets a marker instead). The context-parameter rewrite finds the trailing extra parameter, covering the three-argument registerResource template callback without flagging its variables argument. Resource-server auth helpers routed to the frozen server-legacy copy get a marker on value imports and barrel re-exports (an info note for type-only imports), every rewritten SdkHttpError constructor site gets a marker, and single-argument finishAuth(...) calls in files the run changes get a run-log note (the one-argument URLSearchParams form is valid v2, so the note never re-fires on already-migrated trees). The codemod accepts a single source file as target — source rewrites scope to that file and manifest changes are reported, not applied — and the no-changes summary distinguishes "already on the v2 packages", "still on the v1 SDK under a transform subset", and "no MCP SDK imports found".

  • #2393 e4e8b22 Thanks @felixweinberger! - Overhaul manifest handling. The codemod now discovers workspace-member manifests (npm/yarn/bun workspaces and pnpm-workspace.yaml), writes only the nearest package.json, and reports the exact dependency changes every other affected manifest needs, so you can apply them deliberately. The v2 additions are computed from the post-transform import state of the files each manifest owns, so already-migrated packages still receive the packages their imports need when the v1 dependency is removed; in hoisted monorepos, member usage counts toward the manifest that declares the SDK dependency, with a note naming the contributing members. File collection no longer follows symbolic links (pnpm node_modules layouts contain cycles that previously aborted the run) and honors --ignore patterns during directory descent. Manifests whose zod range cannot satisfy the v2 floor get a warning describing the runtime failure mode. RunnerResult.packageJsonChanges is now an array of per-manifest changes with optional warnings and notes.

Patch Changes

  • #2393 e4e8b22 Thanks @felixweinberger! - Backlog fixes. When the zod import injection fires in a package that declares no zod, the manifest pass adds it (devDependencies when only tests import it) so strict node_modules layouts install cleanly. The ErrorCode-split pairing re-points stale as ProtocolError/as McpError casts bound to subjects whose assertions it moves to SdkError. Handler registration resolves one same-file variable hop (const S = ListToolsRequestSchema) before declaring a schema custom. Shorthand and aliased destructures of SDK dynamic imports rename with the static-import pass. Call-shape assertions pinning a registration schema (expect.objectContaining({ inputSchema: … })) get an advisory. The guide covers dist-text pins (no CJS-resolvable subpaths, content-hashed chunks, changed quote style, ESM-only output).

  • #2393 e4e8b22 Thanks @felixweinberger! - Fixes from migrating two large consumers. Registrations nested inside another handler's body no longer crash the transform with a whole-file rollback (calls process inner-first). Legacy .tool()/.prompt()/.resource() calls migrate without a direct McpServer import when their shape matches the v1 signature AND the receiver is named like an MCP server (server, harness.mcp, this.mockServer); other receivers are left alone, without hard markers, since their type is unknown to the codemod. setRequestHandler/setNotificationHandler with a schema expression first argument get a marker pointing at the typed two-argument or custom three-argument form instead of being skipped silently, and removeRequestHandler/removeNotificationHandler with Schema.shape.method.value arguments rewrite to the method string. Destructured trailing callback parameters only count as the context when their keys look like context members, so template-variable destructures stop collecting false markers. The manifest zod note only appears for manifests that actually take part in the migration.

  • #2393 e4e8b22 Thanks @felixweinberger! - Two long-standing intervention classes now migrate mechanically. .code reads on values an instanceof SdkHttpError check proves — in the same condition or the guarded block — rewrite to .status (v2 carries the HTTP status there; .code is an SdkErrorCode string); unprovable reads keep the existing warning. The context-property remap reaches three shapes the call-site scan missed: functions assigned to fallbackRequestHandler, parameters annotated with a context type directly or via a same-file alias (accesses remap in place, the parameter keeps its name), and contexts forwarded wholesale to helpers, which get an advisory naming the callee.

  • #2286 1823aae Thanks @felixweinberger! - The v1→v2 codemod no longer rewrites taskStore/taskMessageQueue McpServer constructor options into capabilities.tasks — that target does not exist in v2 (the experimental tasks runtime was removed, SEP-2663). The codemod now leaves the code untouched and emits an action-required diagnostic telling migrators to remove the option, matching the removal guidance already given for experimental/tasks imports and the migration guide.

  • #2386 36055d5 Thanks @KKonstantinov! - Preserve a leading #! shebang (and the blank lines after it) when migrating a file. Some transforms drop the shebang because it is leading trivia of the first import they rewrite; the codemod now captures it before transforms and restores it before saving, so CLI packages whose bin points at the migrated entry keep working.

  • #2393 e4e8b22 Thanks @felixweinberger! - The explicit-undefined result-schema removal now requires the same proof as the schema-identifier path: request() calls must carry a provably literal spec method, and callTool() calls whose first argument is a primitive are left alone. Previously, any file importing an MCP package could have the middle argument deleted from an unrelated .request() / .callTool() member call on a non-SDK receiver (e.g. a bespoke end.request('ping', undefined, id) helper), corrupting the call.

  • #2286 1823aae Thanks @felixweinberger! - v1-to-v2: now wraps outputSchema raw shapes with z.object(); importMap covers sdk/server/express.js, sdk/server/middleware/hostHeaderValidation.js, and sdk/client/auth-extensions.js. The unreachable expressMiddleware transform is removed.

  • #2383 9f8ba61 Thanks @felixweinberger! - Keep the result-schema argument on request() calls unless the method is a literal spec method, and keep the generic passthrough ResultSchema even then. Schema-less v2 request() enforces the spec result schema for spec methods and throws a TypeError for non-spec methods, so dropping the schema from a dynamic-method call site (the proxy/forwarder shape, request({ method, params }, ResultSchema)) or from a custom-method call broke the call. callTool() is unaffected — v2 callTool() has no schema parameter.

Don't miss a new typescript-sdk release

NewReleases is sending notifications on new releases.