Skip to content

feat: zod v4 #869

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open

feat: zod v4 #869

wants to merge 10 commits into from

Conversation

dclark27
Copy link

@dclark27 dclark27 commented Aug 12, 2025

Upgrade to Zod v4 for compatibility with v3 and v4.

  • Replaces zod-to-json-schema with a temporary utility inside of mcp.ts until zod-to-json-schema supports v4.
  • Runs https://github.com/nicoespeon/zod-v3-to-v4 and then manual changes after the fact to clean up any remaining issues.
  • Includes a small zod-to-json schema patch since that does not support zod 4 yet, should be replaced once released
  • Small tweak to jest.config to get things running again

Note, it wasn't clear which formatter to use so some commas changed and some lines I did not intend to.

Motivation and Context

This has been a blocking issue for my team and many others. When versions mismatch across dependencies with older Zod versions, memory leaks become present and can slow or stall development.

This also will help unblock downstream dependencies, such as with vercel's AI SDK and mcp-tools.

#494
vercel/ai#7160
vercel/mcp-adapter#93

How Has This Been Tested?

  • Ran the full test suite locally:
    • 34 test suites, 669 tests, all passing (no linter errors introduced)
  • Validated scenarios:
    • Tools with input schema: names and types appear as expected
    • Tools with output schema: JSON schema produced and used for server-side validation
    • Optional fields correctly omitted from required, enums produce { type: "string", enum: [...] }
  • Install to my Next.js app that spins up a remote MCP with tools and was able to successfully call them
  • Nested objects supported recursively
  • Platform: macOS, Node >= 18

Breaking Changes

Users will need to use Zod 3.25.76+ or Zod 4 and import from either /v3 or /v4.

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

@dclark27 dclark27 requested review from a team as code owners August 12, 2025 17:43
@dclark27 dclark27 requested review from ochafik and domdomegg August 12, 2025 17:43
@dclark27 dclark27 changed the title Zod v4 feat: zod v4 Aug 12, 2025
enum?: string[];
};

function zodToJsonSchema(schema: ZodObject<ZodRawShape>): JsonSchema {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still need to think about this PR more broadly, e.g. what the impacts of this potentially breaking change will be.

But I think for this we could use https://zod.dev/json-schema instead?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Happy to give it a shot today!

Copy link
Author

@dclark27 dclark27 Aug 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@domdomegg
So I definitely just had a misunderstanding of how the new toJSONSchema method from Zod 4 worked -- just updated and stripped out the custom code in favor of that. Tests passed without issue. Check it out!

@ihrpr
Copy link
Contributor

ihrpr commented Aug 13, 2025

Thanks for working on this. We are preparing for Typescript V2, this potentially can be included there

@ihrpr ihrpr added this to the v2 milestone Aug 13, 2025
@dclark27
Copy link
Author

Thanks for working on this. We are preparing for Typescript V2, this potentially can be included there

Let me know if I can help! @ihrpr

@@ -25,8 +25,8 @@ const getServer = () => {
'start-notification-stream',
'Starts sending periodic notifications',
{
interval: z.number().describe('Interval in milliseconds between notifications').default(1000),
count: z.number().describe('Number of notifications to send').default(10),
interval: z.number().describe('Interval in milliseconds between notifications').prefault(1000),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is prefault preferred in this case?
as far as I understand from https://zod.dev/v4/changelog#default-updates it's about expected input type (in their example it's a string) and output type (in their example the string goes through transform to output a number)

looks like here default will work as well since input type is same as output type - number

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah we can use default! let me get it updated. the util I used defaults to prefault to preserve default functionality so I left it as is.

@dclark27
Copy link
Author

dclark27 commented Aug 19, 2025

@avivnakar / all

Updated with main, resolved conflicts, reverted to use default over prefault. I've been running since this PR went live by publishing behind our org and it's been lovely!

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.

4 participants