Skip to content

Conversation

@mattzcarey
Copy link
Contributor

@mattzcarey mattzcarey commented Feb 3, 2026

Summary

This PR removes Zod v3 support from the SDK. Users must now use Zod v4.0 or later.

Key changes:

  • Remove all Zod v3 compatibility code (zodCompat.ts, zodJsonSchemaCompat.ts)
  • Add new simplified schema.ts utilities using native Zod v4 APIs
  • Remove zod-to-json-schema dependency (Zod v4 has built-in z.toJSONSchema())
  • Require full Zod schemas instead of raw shapes for inputSchema/argsSchema
  • Simplify internal tool/prompt handler execution with closure-based factories

Breaking Changes

1. Zod v4 required

Users must upgrade to Zod v4.0 or later and import from zod/v4:

// Before
import { z } from 'zod';

// After
import * as z from 'zod/v4';

2. Raw shapes no longer supported

Tool inputSchema and prompt argsSchema must now be full Zod schemas:

// Before (v1) - raw shape worked
server.tool('greet', { name: z.string() }, async ({ name }) => { ... });

// After (v2) - must use z.object()
server.registerTool('greet', {
  inputSchema: z.object({ name: z.string() })
}, async ({ name }) => { ... });

// For tools with no parameters
server.registerTool('ping', {
  inputSchema: z.object({})
}, async () => { ... });

3. Removed deprecated exports

  • completable() function removed from @modelcontextprotocol/server (use Completable.string() etc.)

Internal Changes

Replaced zodCompat.ts with schema.ts

The 275-line compatibility layer was replaced with a ~95-line utility file using native Zod v4 APIs:

  • AnySchema = z.core.$ZodType
  • AnyObjectSchema = z.core.$ZodObject
  • schemaToJson() wraps z.toJSONSchema()
  • parseSchema() / parseSchemaAsync() wrap z.safeParse() / z.safeParseAsync()
  • getSchemaShape(), isOptionalSchema(), unwrapOptionalSchema() for schema introspection

Simplified tool/prompt execution

  • Added createToolExecutor() closure factory that captures schema types at registration time
  • Added createPromptHandler() closure factory for prompts
  • Removed runtime type casts from handler execution paths
  • executeToolHandler() reduced from ~30 lines to a single tool.executor(args, extra) call

Removed dependencies

  • zod-to-json-schema removed (Zod v4 has built-in JSON Schema conversion)
  • Removed zod/v3 from workspace dependencies

Files Changed

  • Deleted: packages/core/src/util/zodCompat.ts (275 lines)
  • Deleted: packages/core/src/util/zodJsonSchemaCompat.ts (53 lines)
  • Deleted: test/helpers/src/fixtures/zodTestMatrix.ts (v3/v4 test matrix)
  • Added: packages/core/src/util/schema.ts (94 lines)
  • Modified: packages/server/src/server/mcp.ts (major refactor)
  • Modified: All examples and tests to use zod/v4 imports

Test Plan

  • pnpm check:all passes (typecheck + lint)
  • pnpm test:all passes (485+ tests)
  • Conformance tests pass

🤖 Generated with Claude Code

@mattzcarey mattzcarey requested a review from a team as a code owner February 3, 2026 10:51
@changeset-bot
Copy link

changeset-bot bot commented Feb 3, 2026

⚠️ No Changeset found

Latest commit: d05b6bf

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@pkg-pr-new
Copy link

pkg-pr-new bot commented Feb 3, 2026

Open in StackBlitz

@modelcontextprotocol/client

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/client@1460

@modelcontextprotocol/server

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/server@1460

@modelcontextprotocol/express

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/express@1460

@modelcontextprotocol/hono

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/hono@1460

@modelcontextprotocol/node

npm i https://pkg.pr.new/modelcontextprotocol/typescript-sdk/@modelcontextprotocol/node@1460

commit: d05b6bf

@mattzcarey mattzcarey changed the title feat!: drop Zod v3 support feat: drop Zod v3 support Feb 3, 2026
@mattzcarey mattzcarey force-pushed the drop-zod-v3-support branch 4 times, most recently from 258d6f2 to 466d704 Compare February 3, 2026 18:38
KKonstantinov
KKonstantinov previously approved these changes Feb 3, 2026
BREAKING CHANGE: This SDK now requires Zod v4.0 or later.

- Remove all Zod v3 imports, types, and runtime detection
- Simplify `SchemaOutput<S>` to use direct indexed access `S['_zod']['output']`
- Remove `zod-to-json-schema` dependency (z4-mini has built-in JSON schema support)
- Add closure-based handlers for prompts and tools to eliminate runtime type casts
- Simplify `executeToolHandler` and `handleAutomaticTaskPolling`
- Remove v3 test blocks from integration tests
- Remove ZodOptional import from 'zod' in mcp.ts, use new unwrapOptional helper
- Add unwrapOptional() function to zodCompat.ts for v4 optional handling
- Update isZodSchemaInstance() to only check for v4's _zod property
- Remove outdated v3/v4 comments
- Simplify test files: remove describe.each(zodTestMatrix) pattern
- Tests now import z directly from 'zod/v4'
- Clean up zodTestMatrix.ts (no longer needed)
- Consolidate task store validation in createToolExecutor (remove duplication)
- Add @deprecated JSDoc to legacy completable exports (McpZodTypeKind, CompletableDef, unwrapCompletable)
Remove unused legacy exports:
- unwrapCompletable function
- McpZodTypeKind enum
- CompletableDef interface
- Remove unused getLiteralValue() function
- Remove v3 fallback .parse check in normalizeObjectSchema()
- Remove unused value property from ZodV4Internal interface
- Remove ZodV4Internal interface (no longer accessing _zod.def)
- Use public .type property instead of _zod.def.type
- Use public .shape property instead of _zod.def.shape
- Use public .unwrap() method instead of _zod.def.innerType
- Simplify safeParse/safeParseAsync to use native schema methods
- Replace zodCompat.ts and zodJsonSchemaCompat.ts with simplified schema.ts
- Use native Zod v4 APIs: z.safeParse, z.safeParseAsync, z.toJSONSchema
- Remove raw shape support - all schemas must now use z.object({...})
- Add utility functions: schemaToJson, parseSchema, parseSchemaAsync
- Update all tools/prompts in tests and examples to use z.object()
- Update migration guides to document Zod schema requirement
- Replace z.safeParse with parseSchema in client.ts and server.ts
- Remove direct zod/v4 imports from client.ts and server.ts
- Update schema.ts to use public Zod v4 APIs (schema.type, schema.def)
  instead of internal ._zod properties
- Remove `& Result` type intersections from protocol and experimental tasks client
- Relax ResponseMessage generic constraints to accept any type (defaulting to Result)
- Standardize zod imports to use `import * as z from 'zod/v4'` consistently
…asts

- getRequestSchema now returns z.ZodType<RequestTypeMap[M]>
- getNotificationSchema now returns z.ZodType<NotificationTypeMap[M]>
- Removes casts from setRequestHandler and setNotificationHandler in protocol.ts
- Added JSDoc explaining why the internal cast is necessary
…ToolStream

- AnyObjectSchema now uses z.core.$ZodObject (not just $ZodType)
- Remove CompatibilityCallToolResultSchema from callToolStream
- Simplify callToolStream to use concrete CallToolResultSchema
- Update example files to match new API
- Eliminates all type casts in experimental tasks client
Reverts the type parameter from `T = Result` back to `T extends Result`
for ResultMessage, ResponseMessage, and takeResult to maintain proper
type constraints.
The v2 examples incorrectly showed raw shapes instead of z.object()
wrappers. Updated to be consistent with the documented requirement
that v2 requires full Zod schemas.
When using registerToolTask with a two-argument handler (args, extra),
an inputSchema must be provided. Without it, the executor calls the
handler with only one argument, causing extra to be undefined.

This fixes all remaining test failures after the rebase.
- Remove unused CallToolResultSchema import from simpleTaskInteractiveClient.ts
- Apply prettier formatting to test files
…ndlers

Tools using `(_args, extra)` handler signature require inputSchema to be
defined, otherwise extra is undefined due to how the executor passes arguments.
@mattzcarey mattzcarey changed the title feat: drop Zod v3 support feat!: drop Zod v3 support Feb 3, 2026
Raw shapes (e.g., `{ prompt: z.string() }`) are no longer supported -
must use full Zod schemas (`z.object({ prompt: z.string() })`).
@mattzcarey mattzcarey merged commit be5670f into main Feb 3, 2026
14 checks passed
@mattzcarey mattzcarey deleted the drop-zod-v3-support branch February 3, 2026 19:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants