Editorial

Research guide · MCP tool schemas · updated 13 September 2026

MCP outputSchema unsupported dialect: fix draft-07 vs 2020-12 errors

If tools/list succeeds but a tool call immediately fails with “invalid outputSchema” or “unsupported dialect”, check the JSON Schema dialect before rewriting the tool. A second failure class looks similar but is different: the call succeeds on the wire and structuredContent disappears later in the client pipeline. This guide separates those boundaries.

Published 13 September 2026 · refreshed 13 September 2026

2020-12

MCP default JSON Schema dialect when $schema is absent

draft-07

Permitted when explicitly declared, but not universally implemented

tools/list

First place to inspect the schema actually advertised on the wire

structuredContent

Machine-readable result channel after a call is dispatched

Fast diagnosis

First decide whether the tool was rejected before dispatch

Observed signalLikely boundaryFirst action
tools/list succeeds; every tool call fails with unsupported dialectClient-side JSON Schema dialect validationInspect outputSchema.$schema before changing tool business logic.
$schema is draft-07; client supports only 2020-12Server/client dialect mismatchPrefer a real 2020-12 emission path; do not blindly relabel a non-equivalent schema.
Raw response has no structuredContentServer or server-SDK serialization boundaryInspect the exact tools/call result before changing the client.
Raw response has structuredContent; model cannot see itClient, bridge or model-transcription boundaryCompare transport evidence with the model-visible function result.
Array/scalar result fails but object worksOlder object-only assumptionVerify the negotiated revision and the tool's declared schema.
Returned structured value fails outputSchema validationResult does not match its published contractValidate the exact returned JSON against the advertised schema and dialect.

Protocol boundary

MCP requires 2020-12 support, not 2020-12-only schemas

MCP's JSON Schema rules make 2020-12 the default when $schema is absent. A schema may explicitly declare another dialect. Implementations must support at least 2020-12, and when they do not support an explicitly declared alternative they should fail with an appropriate unsupported-dialect error.

That distinction matters. A draft-07 outputSchema is not automatically invalid MCP. But a server that advertises draft-07 can still be unusable in a client whose validator only implements 2020-12. The practical fix is interoperability: emit a schema the target client can correctly validate, or use a client that honors the declared dialect.

The 2026-07-28 specification also expands tool schemas to the full JSON Schema 2020-12 vocabulary. That makes honest dialect conversion more important: new constructs such as $defs, conditionals and composition should not be silently relabeled as an older or newer dialect without checking semantics.

Fresh implementation evidence

A current TypeScript SDK issue reproduces draft-07 emission against strict clients

TypeScript SDK issue #2795, opened 12 September 2026, reproduces registerTool advertising http://json-schema.org/draft-07/schema# and tracks older open issues and fixes around the same emission path. The reporter notes that SDK-side tolerance for draft-07 does not solve rejection by a different strict client.

Independent client reports in August 2026 show the exact high-intent symptom: tools are discovered successfully, then invocation fails before the request reaches the MCP server because the client rejects a draft-07 outputSchema. Reports span multiple MCP servers, which is a strong signal to inspect the schema boundary rather than one server's business logic.

Treat those reports as implementation evidence, not a protocol rewrite. MCP explicitly permits a declared alternative dialect; the compatibility failure occurs when the consuming implementation does not support that dialect.

Safe repair sequence

Fix the dialect without lying about the schema

  1. 1. Capture tools/list. Record the exact inputSchema and outputSchema that leave the server. Do not infer them from your Zod or framework source.
  2. 2. Read $schema. If it is absent, MCP treats the schema as 2020-12. If it explicitly says draft-07, a 2020-12-only client can reject it before dispatch.
  3. 3. Confirm where the failure occurs. If no tools/call reaches the server, changing handler logic or structuredContent cannot fix a client-side schema-registration failure.
  4. 4. Prefer native 2020-12 generation. Use the SDK/schema-library path that actually generates 2020-12 rather than post-processing the label when one is available.
  5. 5. If you must rewrite on the wire, prove equivalence. Test every advertised schema for dialect-sensitive constructs. A changed $schema string is safe only when the schema means the same thing under the target dialect.
  6. 6. Add an interoperability test. Assert the advertised dialect and validate the emitted schema with the same class of validator your target client uses. A test that checks only tool names will miss this defect.
  7. 7. Re-test structuredContent after dispatch works. Once the tool is callable, separately verify that the returned machine-readable result survives the client/model boundary.

Do not apply these false fixes

  • Do not delete outputSchema just to suppress validation. That removes useful contract information instead of fixing interoperability.
  • Do not globally replace “draft-07” with “2020-12”. Dialects differ. Relabeling without semantic checks can advertise a contract the server never authored.
  • Do not assume the server is broken because the error names its tool. A client can reject the schema before any request reaches the server.
  • Do not confuse schema acceptance with result visibility. A callable tool can still lose structuredContent later in a client bridge or model transcription layer.

Result-channel check

If the call does reach the server, test structuredContent separately

If tools/call executes successfully but the model sees an empty result, capture the raw response. When structuredContent is present on the wire but absent from the UI or model, the failure is downstream of the server. For broad compatibility, keep the structured value authoritative and mirror equivalent serialized JSON in a TextContent block as recommended by the tools specification.

const payload = { orderId: "ord_123", status: "ready" };

return {
  content: [{ type: "text", text: JSON.stringify(payload) }],
  structuredContent: payload,
};

Next checks

Keep debugging the actual MCP boundary

Sources