Skip to content

Conversation

felixweinberger
Copy link
Contributor

@felixweinberger felixweinberger commented Oct 16, 2025

Summary

Upgrades the SDK from Zod v3 to v4 through a systematic, auditable migration process.

Motivation and Context

Zod v4 was released with significant improvements including native JSON Schema support, better type inference, and a more stable API. This upgrade:

  • Removes dependency on unmaintained zod-to-json-schema library
  • Uses native Zod v4 APIs for JSON Schema conversion
  • Updates to latest validation library with bug fixes and performance improvements

NOTE: last commit is just fixing lint errors - recommend reviewing commits individually.

Key Implementation Detail

The Completable rewrite (commit f46f2ac) deserves special attention. This implements an immutable wrapper pattern that:

  • Creates new schema objects via Object.create() (doesn't mutate originals)
  • Follows Zod's immutability principles (all methods create new instances)
  • No reliance on internal/unstable APIs (only uses public _def property)
  • Maintains _def === _zod.def invariant required by Zod v4
  • Preserves all properties including getters (e.g., description)
  • Future-proof against Zod internal changes

How Has This Been Tested?

✅ All 755 tests pass unchanged (no test modifications required)
✅ Build succeeds with 0 errors (down from 29 compilation errors)
✅ Lint passes (eslint + prettier)
✅ True drop-in replacement - full behavioral preservation

Commits are organized for easy review:

  • Each logical change in a separate commit
  • Automated changes separated from manual fixes
  • Clear commit messages explaining rationale

Breaking Changes

None.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

Migration path: Since we bundle Zod as a dependency (not peerDependency), users get the upgrade automatically when updating the SDK. No code changes required in user applications.

Automated transformations applied via zod-v3-to-v4:
- String validators: z.string().url() → z.url()
- Number validators: z.number().int() → z.int()
- Object methods: .passthrough() → z.looseObject(), .strict() → z.strictObject()
- Schema composition: .merge() → .extend() where applicable
- Error messages: { message } → { error }
- Defaults: .default() → .prefault()
- Object stripping: .strip() removed/consolidated
Remove the external zod-to-json-schema library in preparation
for migrating to Zod v4's native toJSONSchema() method.
Replace zodToJsonSchema() calls with Zod v4's native z.toJSONSchema() method.
Use default options (JSON Schema Draft 2020-12) for simplicity and standard compliance.

Removed unnecessary options:
- target: 'openapi-3.0' - default draft-2020-12 is appropriate for MCP
- strictUnions: true - option doesn't exist in Zod v4's native implementation
Replace removed/renamed types with v4 equivalents:
- AnyZodObject → ZodObject<any>
- z.objectOutputType<> → z.infer<ZodObject<>>
- Remove ZodTypeDef import (will be addressed with PromptArgsRawShape fix)

This resolves 4 compilation errors (Category B & C).
Replace generic ZodType<string, ZodTypeDef, string> with concrete ZodString type.
This fixes compatibility with Zod v4's internal type system changes.

Resolves 8 compilation errors (Category D & F):
- 5 errors in mcp.ts type constraints
- 3 cascading errors in example servers
The previous class-based approach that extended ZodType no longer works in
Zod v4 due to architectural changes. This implements an immutable wrapper
approach that creates a new schema object with completion metadata while
preserving all validation behavior.

- Creates new schema via Object.create() + property copying
- Adds completion metadata to new _def object (not mutating shared state)
- Maintains _def === _zod.def invariant
- Preserves all schema properties including getters
- Follows Zod's immutability pattern

Also updates mcp.ts to check _def.typeName instead of instanceof.
All 755 tests pass.
Extracts the intersection type to a named CompletableSchema<T> type alias,
making the function signature more readable and reusable.
- Remove unused ZodTypeAny import
- Replace any with ZodRawShape in RegisteredTool interface
- Apply prettier formatting to modified files
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.

1 participant