Skip to content

deps: upgrade zod to v4 and drop deprecated zod-to-json-schema#10535

Open
xtazz wants to merge 5 commits into
firebase:mainfrom
xtazz:zod-v4-migration
Open

deps: upgrade zod to v4 and drop deprecated zod-to-json-schema#10535
xtazz wants to merge 5 commits into
firebase:mainfrom
xtazz:zod-v4-migration

Conversation

@xtazz
Copy link
Copy Markdown

@xtazz xtazz commented May 20, 2026

Summary

  • Upgrades zod from ^3.24.3 to ^4.0.0 and drops the now-deprecated zod-to-json-schema dependency in favor of zod v4's built-in z.toJSONSchema().
  • zod-to-json-schema was deprecated in November 2025; its README explicitly recommends migrating to v4 (https://www.npmjs.com/package/zod-to-json-schema).
  • Designed to be wire-compatible with the previous JSON Schema output that MCP clients consume.

Changes

src/mcp/tool.ts — Replace zodToJsonSchema(schema) with z.toJSONSchema(schema, { target: "draft-7", io: "input" }):

  • target: "draft-7" keeps the previous JSON Schema dialect (zod v4's default is draft-2020-12, which uses constructs cleanSchema() doesn't normalize).
  • io: "input" preserves the prior required[] semantics — without it, fields with .default() would be reported as required to MCP clients (v4 default mode is io: "output").
  • ServerTool default generic changed from ZodTypeAny to z.ZodAny so z.infer<> still resolves to any (v4 made ZodTypeAny's output unknown, which broke heterogeneous ServerTool[] arrays).

src/mcp/index.tsmcpCallTool now coalesces request.params.arguments to {} before invoking tool.fn. The MCP protocol allows clients to omit the arguments field, and v4's stricter input typing made it impossible to keep the existing per-tool = {} destructuring defaults under schemas with required fields. Centralizing the fallback at the dispatcher is equivalent for the two tools that had it and now also guards every other tool against the same runtime hazard.

src/mcp/tools/apphosting/{fetch_logs,list_backends}.ts — Drop the now-redundant = {} parameter defaults (replaced by the dispatcher fallback above).

package.json / npm-shrinkwrap.json — bump zod, remove zod-to-json-schema.

CHANGELOG.md — release note entry.

Verified wire-format compatibility

The combination of target: "draft-7" + io: "input" + the existing cleanSchema() post-processor produces equivalent JSON Schema output to the previous zodToJsonSchema() for the schema patterns used in this repo (z.object, z.string, z.number, z.boolean, z.array, z.enum, z.union, z.literal, .optional, .default, .describe, z.infer, one .refine). No tool uses .nullable(), z.coerce, .brand, z.discriminatedUnion, or other patterns that change shape between emitters.

Test plan

  • npm run build — clean
  • npm run lint:changed-files — 0 errors (2 pre-existing warnings in tool.ts)
  • npm run mocha — all 4382 tests passing
  • npm test (lint:quiet + test:compile + mocha) — green
  • Manually verified io: "input" semantics with a standalone script against node_modules/zod to confirm defaulted fields stay out of required[]
  • CI integration tests will run on PR

zod-to-json-schema was deprecated in November 2025; its README points
users at v4's built-in z.toJSONSchema(). Switch the MCP tool factory
to use the built-in with target: "draft-7" (preserves the JSON Schema
shape that cleanSchema() is tuned for) and io: "input" (preserves the
prior required[] semantics: fields with .default() do NOT appear in
required, matching what zod-to-json-schema emitted).

The ServerTool default generic parameter is changed from ZodTypeAny
to z.ZodAny so that z.infer<> still resolves to any (zod v4 made
ZodTypeAny's output unknown), keeping ServerTool[] arrays of
heterogeneous tool types assignable as before.

mcpCallTool now coalesces request.params.arguments to {} before
invoking tool.fn. The MCP protocol allows clients to omit the
arguments field, and v4's stricter input typing made it impossible
to keep the existing per-tool '= {}' destructuring defaults under
schemas with required fields. The two App Hosting handlers that
carried that defensive default are simplified accordingly.
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request upgrades the zod dependency to version 4 and removes the zod-to-json-schema package, opting instead for the native z.toJSONSchema() method. The changes include updating package.json, CHANGELOG.md, and refactoring src/mcp/tool.ts to use the new schema generation logic. Additionally, src/mcp/index.ts now provides a default empty object for tool arguments, and several tool implementations in the apphosting directory have been updated to remove redundant default parameter assignments. I have no feedback to provide.

Copy link
Copy Markdown

@IzaakGough IzaakGough left a comment

Choose a reason for hiding this comment

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

This looks good overall, but I think it needs a couple of targeted regression tests since this PR is preserving behavior in two pretty subtle places.

  • In src/mcp/tool.ts, we’re swapping zod-to-json-schema for z.toJSONSchema(...) and relying on target: "draft-7" / io: "input" to keep the emitted MCP inputSchema the same. It would be good to add a test that asserts the actual emitted schema for something with .optional() / .default() fields, so we know those don’t start showing up as required.

  • In src/mcp/index.ts, we now normalize missing request.params.arguments to {} before invoking the tool. That seems right, but it’s a shared protocol-level behavior change, so I think it should have a server-level test to prove tools still work when a client omits arguments.

@xtazz
Copy link
Copy Markdown
Author

xtazz commented May 25, 2026

@IzaakGough thanks for the review! Both points addressed.

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented May 26, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
⚠️ Please upload report for BASE (main@d5d9feb). Learn more about missing BASE report.

Additional details and impacted files
@@           Coverage Diff           @@
##             main   #10535   +/-   ##
=======================================
  Coverage        ?   57.15%           
=======================================
  Files           ?      599           
  Lines           ?    38146           
  Branches        ?     7704           
=======================================
  Hits            ?    21802           
  Misses          ?    14552           
  Partials        ?     1792           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

xtazz added 2 commits May 27, 2026 10:20
The committed lockfile pinned ajv-formats' requires.ajv to ^8.0.0, but
npm regenerates ^8.17.1, failing the check-package-lock CI job.
@xtazz
Copy link
Copy Markdown
Author

xtazz commented May 27, 2026

@IzaakGough looks like the vscode_unit tests checks were failing prior to the change. How do we proceed?

@IzaakGough
Copy link
Copy Markdown

@xtazz Thanks for addressing my feedback, looks good!
You're right, those CI checks appear to be unrelated to this PR and I have spotted these failing elsewhere in other PRs. I will approve this for now and look into the CI issue.

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