MCP JSON Schema Validation Errors: Complete Fix Guide (2026)
MCP server tool schemas fail with invalid $ref, $dynamicRef, anyOf, and missing items errors. This guide explains every MCP JSON schema validation error and how to fix each one.
Have broken JSON right now? Fix it free in under 1 second — no signup.
Fix My JSON →If you're building or debugging an MCP (Model Context Protocol) server in 2026, you've likely hit this wall:
Tool schema validation error: $dynamicRef not supported
Or in Azure AI Foundry:
Invalid tool definition: anyOf is not a supported keyword
Or in VS Code with Claude extension:
MCP server tool "search_files" returned invalid JSON Schema
Your MCP server runs fine. The MCP Inspector accepts it. But the moment a real client — Claude, Azure AI Foundry, GitHub Copilot, VS Code — tries to use your tools, the schemas fail validation.
This guide explains why MCP JSON schema validation is stricter than the full JSON Schema specification, covers every common error, and gives you a fix for each.
Quick fix for broken JSON: If your MCP server is returning malformed JSON responses (not schema issues), paste them into the AI JSONMedic repair tool for instant repair.Why MCP Schema Validation is Stricter Than JSON Schema
MCP tool schemas look like standard JSON Schema Draft 2020-12, but they're not. Every MCP client enforces its own subset of the specification:
- Claude / Anthropic API: Supports a conservative subset. Rejects
$dynamicRef, complex$refchains, and most meta-schema features. - Azure AI Foundry: Enforces strict deterministic schemas. No
anyOf, no nullable unions, no$refto non-existent paths. - VS Code (MCP extension): Uses a validator that rejects Draft 2020-12 keywords not in Draft 07.
- GitHub Copilot: Similar restrictions to Claude — prefers flat, explicit schemas.
The result: a schema that passes ajv full validation can still fail every MCP client you care about.
The MCP Inspector tool compounds this problem — it silently accepts schemas that Claude and VS Code reject. If you only test with Inspector, you'll see failures in production.
The 5 Most Common MCP JSON Schema Validation Errors
1. $dynamicRef Not Supported
Error message:
MCP server tools have invalid JSON schemas in VS Code due to Draft 2020-12 $dynamicRef usage
What causes it:
Directus, some OpenAPI generators, and other tools auto-generate MCP schemas using Draft 2020-12 meta-schema features. $dynamicRef is a 2020-12 addition for creating extensible recursive schemas. No major MCP client supports it.
{
"type": "object",
"properties": {
"items": {
"$dynamicRef": "#items"
}
}
}
Fix:
Replace $dynamicRef with a concrete type. For recursive structures, flatten to a maximum known depth or use a plain array type:
{
"type": "object",
"properties": {
"items": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": true
}
}
}
}
If you're using Directus MCP server, the fix is tracked in directus/directus#25906. Watch that issue for an upstream patch.
2. Invalid $ref Path
Error message (Azure AI Foundry):
$ref '#/properties/body/properties/Message/properties/bccRecipients/items' does not exist
What causes it:
Some MCP server generators (especially email/calendar tool generators like ms-365-mcp-server) build schemas with internal $ref pointers that reference paths which don't exist at the schema root level sent to clients. The full schema is valid in isolation but the subset sent per-tool is not self-contained.
{
"type": "object",
"properties": {
"recipients": {
"$ref": "#/properties/body/properties/Message/properties/bccRecipients/items"
}
}
}
Fix:
Inline the referenced definition directly. MCP tool schemas should be self-contained — no external $ref paths, no reliance on the parent schema structure:
{
"type": "object",
"properties": {
"recipients": {
"type": "array",
"items": {
"type": "object",
"properties": {
"emailAddress": {
"type": "object",
"properties": {
"address": { "type": "string" },
"name": { "type": "string" }
},
"required": ["address"]
}
}
}
}
}
}
The rule: every MCP tool schema must be self-contained. Treat each tool's inputSchema as if it's the only schema in the universe.
3. anyOf / oneOf Not Supported (Azure AI Foundry)
Error message:
Validation keyword 'anyOf' is not supported. Foundry expects simple, deterministic schemas.
What causes it:
Azure AI Foundry enforces stricter schema validation than Anthropic's Claude API. It doesn't support nullable fields defined via anyOf, union types, or schema composition keywords. This trips up auto-generated schemas that express "string or null" as a union.
{
"type": "object",
"properties": {
"description": {
"anyOf": [
{ "type": "string" },
{ "type": "null" }
]
}
}
}
Fix for nullable fields:
Use a concrete type with an explicit description:
{
"type": "object",
"properties": {
"description": {
"type": "string",
"description": "Optional. Pass an empty string if not provided."
}
}
}
Or drop the nullable entirely if the field is truly optional — just omit it from required:
{
"type": "object",
"properties": {
"description": { "type": "string" }
}
}
The safe rule for Foundry: every property has exactly one type. No unions. No anyOf, oneOf, or allOf.
4. Array Without items Property
Error message (OpenAI function calling / Claude):
schema defines an array type without the required 'items' property
What causes it:
When a tool parameter accepts an array but the schema omits the items definition, some validators accept it (JSON Schema technically allows this for a "tuple of anything"), but Anthropic's Claude API and OpenAI function calling both require arrays to define items.
{
"type": "object",
"properties": {
"tags": {
"type": "array"
}
}
}
Fix:
Always define items:
{
"type": "object",
"properties": {
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "List of tag strings"
}
}
}
If the array can contain mixed types (rare but valid), use items: {} or items: { "type": "object", "additionalProperties": true } rather than omitting items entirely.
5. Parameters Sent as Strings Instead of Native Types
Error message:[BUG] MCP tools fail - parameters sent as strings instead of native types
What causes it:
Some MCP client implementations stringify all tool parameters before sending them — so a schema that expects { "count": 5 } (integer) receives { "count": "5" } (string). This is a client-side bug but the schema validation still fails because the type doesn't match.
{
"count": "5",
"enabled": "true"
}
Fix (schema side):
Add coercion to your tool handler, or use a more permissive type definition for parameters you know may be stringified:
{
"type": "object",
"properties": {
"count": {
"type": ["integer", "string"],
"description": "Number of results. Pass as integer when possible."
}
}
}
Fix (handler side — Python):
def handle_tool(params: dict):
count = int(params.get("count", 10)) # coerce string to int
enabled = str(params.get("enabled", "false")).lower() == "true"
Fix (handler side — TypeScript):
function handleTool(params: Record<string, unknown>) {
const count = Number(params.count ?? 10);
const enabled = String(params.enabled ?? "false") === "true";
}
This is tracked as an open bug in several MCP client implementations. Until it's fixed upstream, defensive coercion in your tool handler is the pragmatic solution.
Testing MCP Schemas Before Deployment
The MCP Inspector silently accepts invalid schemas. Don't rely on it alone. Use these checks:
1. Validate Against the Actual Client's Constraints
For Claude API:
import anthropic
client = anthropic.Anthropic()
Test your tool definition
tools = [
{
"name": "your_tool",
"description": "Does something",
"input_schema": {
"type": "object",
"properties": {
"query": {"type": "string"}
},
"required": ["query"]
}
}
]
A real call will validate the schema — errors surface immediately
response = client.messages.create(
model="claude-opus-4-7",
max_tokens=10,
tools=tools,
messages=[{"role": "user", "content": "test"}]
)
2. Use a Strict JSON Schema Validator Locally
npm install -g ajv-cli
Validate your schema file
ajv validate -s your-tool-schema.json -d test-input.json
3. The Safe MCP Schema Template
This schema pattern works reliably across Claude, Azure AI Foundry, VS Code, and GitHub Copilot:
{
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "The search query"
},
"limit": {
"type": "integer",
"description": "Maximum results to return",
"default": 10
},
"tags": {
"type": "array",
"items": { "type": "string" },
"description": "Optional filter tags"
}
},
"required": ["query"]
}
Rules for safe MCP schemas:
- Every property has exactly one
type - Arrays always define
items - No
$dynamicRef,$recursiveRef - No
anyOf,oneOf,allOf(unless you've confirmed client support) - No
$refto paths outside the current tool'sinputSchema - No Draft 2020-12 meta-schema keywords
- All required fields are listed in
required
When the JSON Response Itself Is Broken
MCP schema validation errors are about the tool definition — the schema you register. If your MCP tool is returning broken JSON responses (truncated, Python booleans, malformed structures), that's a different problem.
For broken JSON output from MCP tools or the AI agents calling them, the AI JSONMedic repair tool handles the most common patterns automatically — truncated output, Python True/False/None literals, trailing commas, and markdown-fenced JSON. The JSON Validator will identify exactly which line and character the error occurs at.
See also: LLM JSON Repair Guide — covers every failure pattern for AI-generated JSON output, including from MCP-connected models.
MCP JSON Schema Errors: Prevention Checklist
Before deploying any MCP server:
- [ ] All array properties define
items - [ ] No
$dynamicRefor$recursiveRef - [ ] No
anyOf/oneOf/allOfunless you've tested with your specific clients - [ ] All
$refpaths are self-contained within the tool'sinputSchema - [ ] Every property has exactly one concrete
type - [ ] Tested against the actual MCP client (not just Inspector)
- [ ] Tool handler defensively coerces string-typed parameters to native types
FAQ
Why does my MCP schema pass the Inspector but fail in VS Code?
The MCP Inspector has more permissive validation than production MCP clients. It's designed for development convenience, not strict spec compliance. VS Code's MCP validator and the Claude API enforce a stricter subset of JSON Schema. Always test against the client you're targeting in production.
Can I use $defs in MCP tool schemas?
It depends on the client. $defs with local $ref (e.g., "$ref": "#/$defs/MyType") works in Claude API as long as the $defs are within the same inputSchema object. External $ref paths and cross-schema references are not supported.
Does Anthropic's Claude API support Draft 2020-12 JSON Schema?
No. The Claude API tool input_schema supports a conservative subset closer to Draft 07. Features added in Draft 2019-09 and 2020-12 (including $dynamicRef, $recursiveRef, unevaluatedProperties) are not supported.
Why are my integer parameters arriving as strings?
This is a known bug in some MCP client implementations. Until it's fixed upstream, add type coercion in your tool handler: const value = Number(params.myParam) in TypeScript or int(params["my_param"]) in Python.
What JSON Schema version should I target for MCP tool schemas?
Target Draft 07 semantics for maximum compatibility. Avoid any keyword introduced after Draft 07. When in doubt, use the flat explicit schema pattern: type, properties, required, description — nothing else.
My MCP server generates schemas from TypeScript types. How do I fix them?
Auto-generated schemas from TypeScript (via zod-to-json-schema, ts-json-schema-generator, or similar) often use Draft 2019-09/2020-12 features. Use zod-to-json-schema with the target: "openApi3" option, which produces a more compatible subset. Or post-process the schema to inline all $ref paths and remove unsupported keywords.
import { zodToJsonSchema } from "zod-to-json-schema";
import { z } from "zod";
const schema = z.object({
query: z.string(),
limit: z.number().int().optional()
});
const jsonSchema = zodToJsonSchema(schema, {
target: "openApi3", // more MCP-compatible than default
$refStrategy: "none" // inline all refs — no $ref paths
});
JSON schema errors not in this list? Paste your broken JSON into AI JSONMedic — it handles repair automatically and shows exactly what changed.
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