JSON Schema 2020-12 Migration Guide: MCP, Tool Schemas, and Breaking Changes
JSON Schema 2020-12 is now the default dialect for MCP tool inputSchema and outputSchema (SEP-2106, merged Jul 2026). This guide covers what breaks, new features, and how to migrate existing tool definitions.
Have broken JSON right now? Fix it free in under 1 second — no signup.
Fix My JSON →inputSchema and outputSchema. SEP-2106 merged in the July 28, 2026 MCP specification, upgrading from the previous restricted subset to full 2020-12.
If you're running MCP servers in production, your existing tool definitions may be rejecting valid client requests or passing through invalid ones — depending on how your server validates schemas. This guide covers what changed, what breaks, and the migration path.
What Is JSON Schema 2020-12?
JSON Schema has several active drafts. The key versions you'll encounter:
| Draft | Key features | Status |
|---|---|---|
| Draft 4 | Core JSON Schema, $ref, definitions | Legacy — still widely used |
| Draft 7 | if/then/else, readOnly/writeOnly | Widely used — many tools target this |
| Draft 2019-09 | $vocabulary, $recursiveRef | Transitional |
| Draft 2020-12 | prefixItems, $dynamicRef, unevaluatedProperties | MCP default — Jul 28, 2026 |
Most schema validators default to Draft 7 or auto-detect. With MCP's move to 2020-12, tool schemas need to be valid against 2020-12 — not an earlier draft.
What Changed in MCP (SEP-2106)
The change is straightforward but has downstream effects:
Before (pre-Jul 28, 2026):inputSchemaaccepted a restricted subset of JSON Schema$ref,oneOf,anyOf,allOf,if/then/elsewere allowed but behavior was implementation-defined- No
outputSchema— servers had to document output structure out-of-band
inputSchemaandoutputSchemaboth conform to full JSON Schema 2020-12- MCP servers validate inputs against the schema at the protocol level
- Invalid inputs are rejected at the boundary with a 422-equivalent error
- New 2020-12 keywords (
prefixItems,$dynamicRef,unevaluatedProperties) are now valid in tool schemas
New 2020-12 Features You Can Use in Tool Schemas
prefixItems — Typed tuple arrays
In Draft 7, typed tuples used items as an array:
// Draft 7 (deprecated in 2020-12)
{
"type": "array",
"items": [
{"type": "string"},
{"type": "number"}
]
}
In 2020-12, use prefixItems:
// 2020-12
{
"type": "array",
"prefixItems": [
{"type": "string"},
{"type": "number"}
],
"items": false // Disallow additional items
}
Why it matters for MCP tools: If your tool accepts a fixed-length tuple (e.g., [x, y] coordinates, [key, value] pairs), 2020-12's prefixItems expresses this precisely.
unevaluatedProperties — Strict additional property control
additionalProperties doesn't catch properties allowed by anyOf/oneOf. unevaluatedProperties does:
{
"type": "object",
"oneOf": [
{"properties": {"type": {"const": "query"}, "sql": {"type": "string"}}},
{"properties": {"type": {"const": "insert"}, "data": {"type": "object"}}}
],
"unevaluatedProperties": false
}
With additionalProperties: false, properties from the matched oneOf branch would be rejected. unevaluatedProperties: false correctly allows those properties.
$dynamicRef — Recursive schemas
For schemas that reference themselves (tree structures, nested JSON documents), $dynamicRef replaces $recursiveRef:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$defs": {
"node": {
"type": "object",
"properties": {
"value": {"type": "string"},
"children": {
"type": "array",
"items": {"$dynamicRef": "#node"}
}
}
}
},
"$dynamicAnchor": "node",
"$ref": "#/$defs/node"
}
$defs instead of definitions
2020-12 uses $defs (with a $). Both work in most validators, but strict 2020-12 validation expects $defs:
// Draft 7
{"definitions": {"Address": {...}}}
// 2020-12
{"$defs": {"Address": {...}}}
What Breaks When Migrating
1. Array-style items for tuples
// This breaks under strict 2020-12 validation
{
"type": "array",
"items": [{"type": "string"}, {"type": "number"}]
}
Migrate to prefixItems. Most validators accept the old form for compatibility, but MCP servers running strict 2020-12 will reject it.
2. definitions vs $defs
Internal schema references using definitions:
{
"type": "object",
"properties": {
"address": {"$ref": "#/definitions/Address"}
},
"definitions": {
"Address": {"type": "object", "properties": {"city": {"type": "string"}}}
}
}
Update to:
{
"type": "object",
"properties": {
"address": {"$ref": "#/$defs/Address"}
},
"$defs": {
"Address": {"type": "object", "properties": {"city": {"type": "string"}}}
}
}
3. additionalProperties with oneOf
If you're using additionalProperties: false alongside oneOf to create discriminated unions, it likely breaks. Switch to unevaluatedProperties: false.
4. $schema declaration
Some MCP server validators now require an explicit $schema declaration:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {...}
}
Add this to all tool schemas to declare the dialect explicitly.
Migration Checklist
For each tool in your MCP server:
□ Add "$schema": "https://json-schema.org/draft/2020-12/schema" to inputSchema
□ Replace "definitions" with "$defs"
□ Replace "$ref": "#/definitions/X" with "$ref": "#/$defs/X"
□ Replace array-style "items" with "prefixItems" for typed tuples
□ Replace "additionalProperties: false" + "oneOf" with "unevaluatedProperties: false"
□ Add "outputSchema" to document tool return structure (new — optional but recommended)
□ Validate against 2020-12 with ajv or check.jsonschema
Validating Against 2020-12
The two most reliable 2020-12 validators:
ajv (Node.js)
npm install ajv ajv-formats
import Ajv2020 from "ajv/dist/2020.js";
import addFormats from "ajv-formats";
const ajv = new Ajv2020();
addFormats(ajv);
const validate = ajv.compile(yourSchema);
const valid = validate(data);
if (!valid) console.log(validate.errors);
check-jsonschema (Python)
pip install check-jsonschema
# Validate data against a 2020-12 schema
check-jsonschema --schemafile tool-schema.json test-input.json
Online validation
Use aijsonmedic.com/json-schema-generator to generate a 2020-12 schema from sample JSON, or aijsonmedic.com/validate to validate JSON against your schema.
Adding outputSchema to Existing Tools
SEP-2106 also introduces outputSchema — a schema describing what the tool returns. This was previously undocumented at the protocol level. Adding it is optional but improves client-side validation:
{
"name": "search_products",
"description": "Search product catalog",
"inputSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"query": {"type": "string"},
"limit": {"type": "integer", "default": 10}
},
"required": ["query"]
},
"outputSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {"type": "string"},
"name": {"type": "string"},
"price": {"type": "number"}
},
"required": ["id", "name", "price"]
}
},
"total": {"type": "integer"}
},
"required": ["results", "total"]
}
}
MCP clients can use outputSchema to validate tool responses before passing them to the LLM, catching tool errors early.
SDK-Specific Notes
TypeScript SDK (MCP)
import { Tool } from "@modelcontextprotocol/sdk/types.js";
const searchTool: Tool = {
name: "search",
description: "Search the catalog",
inputSchema: {
$schema: "https://json-schema.org/draft/2020-12/schema",
type: "object" as const,
properties: {
query: { type: "string" as const }
},
required: ["query"]
}
};
The TypeScript SDK's type definitions were updated in the 2026-07-28 release to match SEP-2106. If you're on an older SDK version, upgrade:
npm install @modelcontextprotocol/sdk@latest
Python SDK (FastMCP)
FastMCP auto-generates schemas from Python type hints and Pydantic models. With FastMCP v2+, the generated schemas target 2020-12 by default:
from fastmcp import FastMCP
from pydantic import BaseModel
mcp = FastMCP()
class SearchInput(BaseModel):
query: str
limit: int = 10
@mcp.tool()
def search(input: SearchInput) -> dict:
"""Search the catalog"""
return {"results": [], "total": 0}
FastMCP generates the 2020-12 inputSchema from SearchInput automatically. You don't need to write JSON Schema by hand.
Fixing Schema Errors with AI JSONMedic
If your tool schema has syntax errors or structural issues, AI JSONMedic's validator can identify problems. For generation, the JSON Schema Generator creates a 2020-12-compliant schema from any sample JSON payload.
FAQ
Do I need to migrate if my MCP server is working?If your server isn't using unevaluatedProperties, prefixItems, or $dynamicRef, your existing schemas likely still validate correctly under 2020-12. But clients may start sending 2020-12-specific schema features, and your server should handle them. Validate your schemas against 2020-12 to confirm.
Most Draft 7 schemas are forward-compatible with 2020-12 for common patterns. The main incompatibility is array-style items for tuples. Run your schemas through a 2020-12 validator to find issues.
outputSchema required?
No — outputSchema is optional in SEP-2106. It's a new addition that enables client-side response validation. Add it if you want clients to validate your tool's return structure.
The canonical spec is at modelcontextprotocol.io/seps/2106-json-schema-2020-12.
Which ajv version supports 2020-12?ajv v8+ with the ajv/dist/2020.js import supports JSON Schema 2020-12. ajv v6 (Draft 7 only) and v7 do not.
Still dealing with broken JSON?
Paste it in and get it fixed in under 1 second — free, no signup, no install. Works with ChatGPT, Claude, n8n, and any AI output.
Fix My JSON Free →Related Articles