Claude Tool Use JSON Schema Validation Guide
When you give Claude a tool definition, it uses the JSON schema in that definition to decide what arguments to generate and how to structure them. "Claude tool use JSON schema validation" usually means one of two things: either you want to know how Claude validates the arguments it produces against your schema, or you're hitting errors because Claude returned arguments that don't match your schema and you need to fix it.
The short answer: Claude does not run a formal JSON Schema validator against its own output before sending it back to you. It uses the schema as a strong structural guide during generation, but the burden of validating the final tool call arguments against your schema is on your application code. This matters because malformed or incomplete arguments can slip through, especially with complex nested schemas, enums, or required fields that depend on other fields.
How tool use schemas actually work
When you call the Messages API with a tools array, each tool has a name, description, and an input_schema field written in standard JSON Schema (draft-07 style, using type, properties, required, enum, etc.). Claude reads this schema as part of the prompt context, not as a hard runtime constraint enforced by a separate validation engine. The model is trained to produce JSON that matches the schema shape, but it's still a generative process — not a guarantee.
A minimal tool definition looks like this:
{
"name": "create_invoice",
"description": "Create a new invoice for a customer",
"input_schema": {
"type": "object",
"properties": {
"customer_id": { "type": "string" },
"amount_cents": { "type": "integer", "minimum": 1 },
"currency": { "type": "string", "enum": ["USD", "EUR", "GBP"] },
"due_date": { "type": "string", "format": "date" }
},
"required": ["customer_id", "amount_cents", "currency"]
}
}
Claude will generally respect required fields and enum constraints well, because those are explicit, low-ambiguity instructions. Where things get fragile is with:
- Conditional requirements ("if type is X, then field Y is required") — JSON Schema's
if/then/elseoroneOfconstructs are harder for the model to apply consistently than flat required/optional fields. - Deeply nested objects with multiple levels of
properties— the deeper the nesting, the more likely a field gets dropped or misplaced. - Numeric constraints like
minimum,maximum,multipleOf— Claude usually respects these but won't always reject invalid values if the surrounding context strongly implies one. - Format strings like
date,email,uri— these are hints, not enforced patterns.
Why you still need a validator in your code
Because the model generates rather than validates, you should always run the tool call arguments through a real JSON Schema validator before executing any side effects (writing to a database, calling a payment API, sending an email). This is standard practice regardless of which LLM provider you use, and it protects you from:
- Hallucinated fields that don't exist in your schema
- Missing required fields when the model's reasoning took a shortcut
- Type mismatches (string instead of integer, for example)
- Enum values that are close but not exact matches
In Node.js, a common pattern uses ajv:
import Ajv from "ajv";
const ajv = new Ajv();
const schema = {
type: "object",
properties: {
customer_id: { type: "string" },
amount_cents: { type: "integer", minimum: 1 },
currency: { type: "string", enum: ["USD", "EUR", "GBP"] }
},
required: ["customer_id", "amount_cents", "currency"]
};
const validate = ajv.compile(schema);
function handleToolCall(toolInput) {
if (!validate(toolInput)) {
// Return a tool_result with an error so Claude can retry
return { error: true, details: validate.errors };
}
// proceed with the validated input
}
If validation fails, the recommended pattern in the Claude API is to return a tool_result content block with an error message describing exactly what was wrong. Claude will typically self-correct on the next turn and resend corrected arguments, because it now has explicit feedback instead of guessing.
Practical tips for more reliable schema adherence
- Keep schemas flat where possible. Flatten nested objects into top-level fields with clear names when you can. Fewer levels of nesting means fewer places for the model to drop a field.
- Use
enumaggressively. Enums are the most reliably respected constraint type. If a field has a small, known set of valid values, enumerate them instead of relying on a free-text description. - Write descriptions, not just types. A
descriptionfield on each property ("ISO 4217 currency code, uppercase") improves adherence more than the schema structure alone. - Mark everything that's truly required. Don't leave fields optional "just in case" — ambiguous requiredness increases the chance of missing data.
- Add a repair loop. Always validate and return structured errors on failure rather than silently discarding bad tool calls. Claude handles corrective feedback well within the same conversation.
- Test with edge cases. Run your tool through prompts that are likely to produce boundary values (zero, negative numbers, empty strings) and confirm your validator catches what the model gets wrong.
Where SubToAPI fits in
If you're already calling tool use through SubToAPI, the schema validation responsibility is the same — SubToAPI proxies your Messages API calls with your sub_live_... key and returns Claude's raw tool call output, including usage metadata, so you can run your own validation layer exactly as you would against the direct API. See the tool use docs for request and response shapes, and the messages docs for how tool results are returned in streaming and non-streaming modes. SubToAPI doesn't add a schema validator on top of Claude's output — that stays your responsibility — but it does give you clean, consistent JSON you can plug straight into ajv or your validator of choice without reformatting.
questions
Does Claude guarantee tool call arguments match my JSON schema exactly? No. Claude uses the schema as a strong guide during generation but doesn't run a separate validation pass on its own output. You should validate arguments yourself before executing any side effects.
What's the best way to handle a tool call that fails validation? Return a tool_result content block describing the validation error. Claude will typically read the error and resend corrected arguments on the next turn.
Which JSON Schema features does Claude respect most reliably? Flat required fields and enum constraints are the most consistently respected. Conditional logic (if/then), deep nesting, and format strings like date or email are weaker and should be double-checked in your own validator.