deps: upgrade zod to v4 and drop deprecated zod-to-json-schema#10535
deps: upgrade zod to v4 and drop deprecated zod-to-json-schema#10535xtazz wants to merge 5 commits into
Conversation
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.
There was a problem hiding this comment.
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.
IzaakGough
left a comment
There was a problem hiding this comment.
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 swappingzod-to-json-schemaforz.toJSONSchema(...)and relying ontarget: "draft-7"/io: "input"to keep the emitted MCPinputSchemathe 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 missingrequest.params.argumentsto{}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 omitsarguments.
# Conflicts: # CHANGELOG.md
|
@IzaakGough thanks for the review! Both points addressed. |
Codecov Report✅ All modified and coverable lines are covered by tests. 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. 🚀 New features to boost your workflow:
|
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.
# Conflicts: # CHANGELOG.md
|
@IzaakGough looks like the vscode_unit tests checks were failing prior to the change. How do we proceed? |
|
@xtazz Thanks for addressing my feedback, looks good! |
Summary
zodfrom^3.24.3to^4.0.0and drops the now-deprecatedzod-to-json-schemadependency in favor of zod v4's built-inz.toJSONSchema().zod-to-json-schemawas deprecated in November 2025; its README explicitly recommends migrating to v4 (https://www.npmjs.com/package/zod-to-json-schema).Changes
src/mcp/tool.ts— ReplacezodToJsonSchema(schema)withz.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 constructscleanSchema()doesn't normalize).io: "input"preserves the priorrequired[]semantics — without it, fields with.default()would be reported as required to MCP clients (v4 default mode isio: "output").ServerTooldefault generic changed fromZodTypeAnytoz.ZodAnysoz.infer<>still resolves toany(v4 madeZodTypeAny's outputunknown, which broke heterogeneousServerTool[]arrays).src/mcp/index.ts—mcpCallToolnow coalescesrequest.params.argumentsto{}before invokingtool.fn. The MCP protocol allows clients to omit theargumentsfield, 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 existingcleanSchema()post-processor produces equivalent JSON Schema output to the previouszodToJsonSchema()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— cleannpm run lint:changed-files— 0 errors (2 pre-existing warnings intool.ts)npm run mocha— all 4382 tests passingnpm test(lint:quiet + test:compile + mocha) — greenio: "input"semantics with a standalone script againstnode_modules/zodto confirm defaulted fields stay out ofrequired[]