fix: default Zod v4 JSON Schema target to draft-2020-12 - #2653
Open
amitfin wants to merge 1 commit into
Open
Conversation
🦋 Changeset detectedLatest commit: cdc0b89 The changes in this PR will be included in the next version bump. Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
commit: |
mapMiniTarget defaulted to 'draft-7' when no target was supplied, so every tool schema generated from a Zod v4 schema advertised "$schema": "http://json-schema.org/draft-07/schema#". The unrecognised-target fallback returned 'draft-7' as well; both now return 'draft-2020-12' so the two agree. Three things say 2020-12 is the right default: - spec.types.ts documents Tool.outputSchema as defaulting to JSON Schema 2020-12 when no explicit $schema is provided - Zod v4's own toJSONSchema default target is draft-2020-12; the compat layer was overriding it to an older dialect - the v2 SDK already uses draft-2020-12 (JSON_SCHEMA_CONVERSION_TARGET in packages/core-internal) Explicit targets, including 'draft-7', are unaffected. The Zod v3 branch still emits draft-07 because the vendored zod-to-json-schema has no 2020-12 target; a test documents that asymmetry. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
amitfin
force-pushed
the
fix/default-json-schema-dialect-2020-12
branch
from
August 12, 2026 16:05
2e712c3 to
cdc0b89
Compare
2 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
mapMiniTargetinsrc/server/zod-json-schema-compat.tsdefaults to'draft-7'when notargetis supplied. Neither call site insrc/server/mcp.tspasses one (lines 151 and 165 pass onlystrictUnions/pipeStrategy), andtargetis never exposed throughregisterTool. So every tool schema generated from a Zod v4 schema advertises:This change makes the default
'draft-2020-12', and updates the unrecognised-target fallback on the last line to match — it also returned'draft-7', so the two now agree:function mapMiniTarget(t: CommonOpts['target'] | undefined): 'draft-7' | 'draft-2020-12' { - if (!t) return 'draft-7'; + if (!t) return 'draft-2020-12'; if (t === 'jsonSchema7' || t === 'draft-7') return 'draft-7'; if (t === 'jsonSchema2019-09' || t === 'draft-2020-12') return 'draft-2020-12'; - return 'draft-7'; // fallback + return 'draft-2020-12'; // fallback }The fallback is unreachable through the
CommonOptstype — all four accepted values are handled by the two explicit branches — but it is reachable from JavaScript callers passing an off-type value, where silently downgrading the dialect is the same surprise this PR is fixing.Why 2020-12 is the right default
The spec says so.
src/spec.types.ts, onTool.outputSchema:The SDK currently contradicts the spec types vendored in the same tree.
Zod already defaults to it. Zod v4's
toJSONSchemadefault target isdraft-2020-12— verified on zod 4.4.3:The compat layer was overriding Zod's own default to an older dialect.
v2 already made this call. On
main,packages/core-internal/src/util/standardSchema.ts:170:This aligns
v1.xwith a decision already taken for v2.Real-world impact
This surfaced as a client compatibility break. Claude Desktop began rejecting draft-07
outputSchemadocuments outright:The failure happens at tool registration, so every tool on an affected server becomes unusable, and the server process is never contacted.
mongodb-mcp-serveris one instance (mongodb-js/mongodb-mcp-server#1427, anthropics/claude-code#86142), but because draft-07 comes from this SDK's default rather than from server code, any SDK-based server on Zod v4 declaringoutputSchemais affected, and no server-side option exists to opt out.To be clear: that client is too strict — draft-07 is valid JSON Schema, and the v2 SDK's own validator accepts draft-07, draft-06, 2019-09 and 2020-12. This PR isn't a workaround for that bug; it's aligning the default with the spec, Zod, and v2. It happens to also unblock affected users.
Scope and compatibility
target: 'draft-7'and'jsonSchema7'still produce draft-07; the mapping is untouched.zod-to-json-schema, whose targets arejsonSchema7/jsonSchema2019-09/openApi3— there is no 2020-12 target available. A test documents this v3/v4 asymmetry so it reads as intentional.$schemais absent.If you'd prefer a narrower change, the alternative is passing
target: 'draft-2020-12'only at theoutputSchemacall site (src/server/mcp.ts:165), leavinginputSchemaon draft-07. I went with the shared default since the asymmetry seemed harder to justify than the alignment. Happy to switch.Testing
New file
test/server/zod-json-schema-compat.test.ts(6 tests):'draft-7'/'jsonSchema7'→ draft-07 (opt-in preserved)'draft-2020-12'/'jsonSchema2019-09'→ 2020-12tools/listoverInMemoryTransportasserting bothinputSchema.$schemaandoutputSchema.$schemaare 2020-12Full suite: 1645 tests / 53 files passing,
npm run lintclean. No existing test asserted the draft-07 dialect.