Skip to content

fix: publish tool schemas as JSON Schema 2020-12 - #268

Merged
mattpodwysocki merged 3 commits into
mainfrom
fix/emit-json-schema-2020-12
Oct 8, 2026
Merged

mattpodwysocki merged 3 commits into
mainfrom
fix/emit-json-schema-2020-12

Conversation

@mattpodwysocki

Copy link
Copy Markdown
Contributor

Problem

In Claude Desktop, calls to tools that declare an output schema fail with:

Tool 'search_and_geocode_tool' has an invalid outputSchema: JSON Schema declares an unsupported dialect ("$schema": "http://json-schema.org/draft-07/schema#"). The default validator supports JSON Schema 2020-12 only; pass a pre-configured Ajv instance to AjvJs

Of our 29 tools, 28 declare an output schema. The same tools work from Claude Code, so this depends on the client.

Cause

McpServer generates every tool's inputSchema and outputSchema as draft-07 and offers no option to change it. That's true in 1.30.0 (ours) and in 1.32.1, the latest on npm. MCP specifies 2020-12 as the default dialect, and Claude Desktop's newer client enforces that.

Changing only the $schema label would not be enough. 30 schema nodes are tuples (bbox, coordinate pairs), which draft-07 writes as items: [...], a form 2020-12 doesn't allow.

Fix

publishJsonSchema2020() (src/utils/jsonSchema2020.ts) wraps setRequestHandler on our own server instance before any tool is registered. The SDK installs its tools/list handler through that method, so the handler gets wrapped and its schemas are converted on the way out:

  • the $schema label becomes 2020-12;
  • tuple items: [...] becomes prefixItems, and additionalItems becomes items;
  • definitions becomes $defs, with refs rewritten to match.

The conversion walks only real subschema positions, so a property named items or definitions is left alone. It isn't a global patch, and the SDK's own validation of tool input and output still runs against the zod schemas as before.

Testing

  • New tests cover the converter, every registered tool's listed schemas, and a tool call whose structured output includes a tuple. Another test confirms the SDK alone still emits draft-07, so it will show when this wrapper can be removed.
  • Full suite passes (967 tests), plus the type check and lint.
  • Against the built server over stdio: all 29 tools list as 2020-12, and all 57 schemas compile under Ajv's 2020-12 validator. A live search_and_geocode_tool result validates against its converted output schema.
  • In Ajv's strict mode, which isn't the default, converted tuples still raise length warnings. zod's own 2020-12 output does the same. The converter doesn't add tuple lengths, because zod's output looks the same whether or not a tuple has optional elements.
  • Checked in Claude Desktop against a local build: the dialect error is gone and the tools run.

🤖 Generated with Claude Code

Claude Desktop rejects any tool whose outputSchema declares draft-07
("JSON Schema declares an unsupported dialect"). McpServer in SDK 1.x
always generates draft-07, so wrap setRequestHandler on our server
instance and convert the tools/list schemas to 2020-12: relabel
$schema, tuple items -> prefixItems, definitions -> $defs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@mattpodwysocki
mattpodwysocki requested a review from a team as a code owner October 7, 2026 17:35
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
jussi-sa
jussi-sa previously approved these changes Oct 7, 2026
@mattpodwysocki
mattpodwysocki merged commit 2d6f2d5 into main Oct 8, 2026
5 checks passed
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.

2 participants