[SEP-1613 regression] tools/list still emits draft-07 in 1.29.0 — `target` option missing from mcp.js `toJsonSchemaCompat` calls
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 78/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- typescript
- Domain
- api, backend-api-design
Research direction
Start with the two toJsonSchemaCompat call sites in dist/esm/server/mcp.js and read mapMiniTarget in dist/esm/server/zod-json-schema-compat.js to confirm the default behavior. Reproduce tools/list with a Zod v4 tuple schema, then verify both inputSchema and outputSchema advertise the 2020-12 schema and tuple fields use prefixItems.
Written by the indexing model from the issue text.
Description
Describe the bug
SEP-1613 (tracked in #1057, closed 2025-11-21) was supposed to make JSON Schema 2020-12 the default dialect for tool inputSchema / outputSchema emission in tools/list. In @modelcontextprotocol/sdk@1.29.0, tools/list still emits draft-07 because mcp.js calls toJsonSchemaCompat(...) without passing a target option, and toJsonSchemaCompat's mapMiniTarget(undefined) falls back to 'draft-7'.
This appears to be a regression / partial implementation of SEP-1613; the SDK has the wiring for target: 'draft-2020-12' in zod-json-schema-compat.js, but the call sites in mcp.js don't pass it.
Impact
Every server built on @modelcontextprotocol/sdk since SEP-1613 closed advertises draft-07 schemas on tools/list, contrary to the MCP 2025-11-25 spec which mandates 2020-12. Strict validating clients reject those servers outright (#745 reported Claude Code returning 400s against Mapbox MCP for exactly this reason). For most simple schemas the break is silent (draft-07 is a structural subset of 2020-12), but tuple/composition-heavy schemas lose 2020-12-only keywords like prefixItems.
To Reproduce
- Install
@modelcontextprotocol/sdk@1.29.0andzod@^4. - Register a tool with any Zod
outputSchema. - Call
tools/list. - Observe
$schema: "http://json-schema.org/draft-07/schema#"on every tool'sinputSchemaandoutputSchema(or absent — falls back).
Expected behavior
Per SEP-1613, tools/list advertises $schema: "https://json-schema.org/draft/2020-12/schema" by default.
Code path evidence (1.29.0)
dist/esm/server/mcp.js:76-95 — both inputSchema and outputSchema emission paths call toJsonSchemaCompat with NO target option:
inputSchema: (() => {
const obj = normalizeObjectSchema(tool.inputSchema);
return obj
? toJsonSchemaCompat(obj, {
strictUnions: true,
pipeStrategy: 'input'
})
: EMPTY_OBJECT_JSON_SCHEMA;
})(),
...
if (tool.outputSchema) {
const obj = normalizeObjectSchema(tool.outputSchema);
if (obj) {
toolDefinition.outputSchema = toJsonSchemaCompat(obj, {
strictUnions: true,
pipeStrategy: 'output'
});
}
}
dist/esm/server/zod-json-schema-compat.js:9-18 — mapMiniTarget(undefined) returns 'draft-7':
function mapMiniTarget(t) {
if (!t)
return 'draft-7';
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
}
So mcp.js → toJsonSchemaCompat(obj, { strictUnions, pipeStrategy }) resolves to mapMiniTarget(undefined) === 'draft-7'.
Suggested fix
Pass target: 'draft-2020-12' in both mcp.js call sites (line 78 for inputSchema, line 91 for outputSchema):
inputSchema: (() => {
const obj = normalizeObjectSchema(tool.inputSchema);
return obj
? toJsonSchemaCompat(obj, {
+ target: 'draft-2020-12',
strictUnions: true,
pipeStrategy: 'input'
})
: EMPTY_OBJECT_JSON_SCHEMA;
})(),
if (tool.outputSchema) {
const obj = normalizeObjectSchema(tool.outputSchema);
if (obj) {
toolDefinition.outputSchema = toJsonSchemaCompat(obj, {
+ target: 'draft-2020-12',
strictUnions: true,
pipeStrategy: 'output'
});
}
}
Alternatively, change mapMiniTarget(undefined) to default to 'draft-2020-12' per SEP-1613's intent, but explicit passing at the call sites is safer for callers that might rely on the legacy draft-7 default.
Test to verify
Register a tool with a non-trivial Zod v4 schema (including a tuple, for prefixItems-evidence), call tools/list, and assert:
tools[].inputSchema.$schema === 'https://json-schema.org/draft/2020-12/schema'tools[].outputSchema.$schema === 'https://json-schema.org/draft/2020-12/schema'(when registered)- Tuple-array fields emit
prefixItems(notitems: [...])
Related
- #1057 (SEP-1613 tracking issue, closed 2025-11-21) — likely incomplete; this regression suggests the call-site update was missed
- #745 (original draft-07 → 2020-12 bug report, closed 2025-12-05 — possibly closed in anticipation of #1057's fix)
- Affected real-world consumer: lvlup-sw/exarchos#1366 — migration to Zod v4 to unlock native 2020-12 is blocked by this regression at the SDK boundary. We're patching locally via
patch-packagein the interim.
Environment
@modelcontextprotocol/sdk@1.29.0zod@^4.4.3(also reproduces on v3)- Node 20+
- Dominant language
- TypeScript
- Stars
- 13.4k
- Forks
- 2.2k
- Avg merge
- 3d 12h
- Merged PRs (30d)
- 3
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from modelcontextprotocol/typescript-sdk
-
Auth metadata discovery: fallback URL built on resource host instead of authorization-server host Open
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
modelcontextprotocol/typescript-sdk#2783 · 1 comment ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
-
Difficulty 1/5 Under an hour Newbie friendliness 92/100
modelcontextprotocol/typescript-sdk#2766 · 1 comment ·
-
Difficulty 2/5 1-2 days Newbie friendliness 72/100
All issues in modelcontextprotocol/typescript-sdk
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
copse-dev/agent-pane#2953 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
Eynzof/Hermes-CN-Desktop#610 ·
-
bug clawsweeper:linked-pr-open clawsweeper:needs-live-repro clawsweeper:no-new-fix-pr impact:message-loss issue-rating: 🐚 platinum hermit P2 regression
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
-
enhancement
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
-
calcite-components needs triage refactor
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
Esri/calcite-design-system#15203 ·