Enforce Structured Output Using Tool Use and JSON Schemas
This module is about making Claude return machine-readable structured data reliably.
The exam’s core idea:
If you need guaranteed structured output:
- Do not just ask Claude to “return JSON”.
- Use tool use with a JSON schema.
- Configure tool_choice so Claude must call the tool.
Current Anthropic docs describe structured outputs as schema-constrained responses that avoid malformed JSON, missing required fields, inconsistent data types, and schema violations.
They also distinguish JSON outputs for final response formatting from strict tool use for validated tool inputs. Focus on the tool-use path: define an extraction tool with an input_schema, then read the structured data from Claude’s tool_use.input. (Claude)
1. Core Mental Model
There are three levels of reliability:
| Approach | Reliability | Problem |
|---|---|---|
| “Return JSON” in prose | Low to medium | Claude may add text, malformed JSON, wrong fields |
| JSON schema in prompt only | Medium | Better, but still not guaranteed |
| Tool use with JSON schema + forced/required tool call | Highest | Output is emitted as structured tool input |
Exam shortcut:
Need parseable structured output? Use tool_use.
Need a tool call no matter what? Use tool_choice.
Need exact schema compliance? Use strict schema/tool configuration.
Need semantic correctness? Add validation and prompt rules.
Anthropic’s tool docs say that with user-defined tools, you provide a tool with an input_schema, Claude returns a tool_use block, and your application reads the structured input object from that block. (Claude Platform Docs)
2. Why Tool Use Beats “Return JSON”
Weak prompt:
Possible problems:
Claude adds a preamble.
JSON has trailing commas.
Field names vary.
Dates are inconsistently formatted.
Required fields are missing.
Numbers are strings.
2.1 Stronger Approach
Define a tool:
With a JSON schema for the fields you need. Then force Claude to call that tool.
Result:
{
"type": "tool_use",
"name": "extract_invoice",
"input": {
"invoice_number": "INV-1042",
"invoice_date": "2026-06-01",
"currency": "USD",
"total_amount": 1299.5
}
}
You parse JSON input, not free-form assistant text.
Anthropic’s docs explicitly warn that without structured outputs, even careful prompting can produce invalid JSON syntax, missing required fields, inconsistent data types, and schema violations. Structured outputs address these through constrained decoding. (Claude)
3. Extracting Data from the tool_use Response
With tool use, the structured result is inside the assistant response content block.
for block in response.content:
if block.type == "tool_use" and block.name == "extract_invoice":
invoice_data = block.input
break
if invoice_data is None:
raise RuntimeError("Claude did not call extract_invoice")
print(invoice_data)
Heuristic:
Do not parse assistant prose. Parse the
tool_use.input.
Anthropic’s tool docs show that model responses with tools include tool_use blocks containing a name and an input object. (Claude Platform Docs)
4. tool_choice: auto Vs any Vs tool
This is one of the highest-yield parts.
tool_choice |
Meaning | Use |
|---|---|---|
{"type": "auto"} |
Claude may call a tool or may answer in text | Use when tool use is optional |
{"type": "any"} |
Claude must call one of the provided tools | Use when structured output is required but document type is unknown |
{"type": "tool", "name": "extract_metadata"} |
Claude must call that specific tool | Use when a specific extraction must happen |
{"type": "none"} |
Claude cannot call tools | Not central to this exam task |
Anthropic’s docs define these exact tool-choice modes and note that auto is the default when tools are provided, while any requires one of the tools and tool forces a specific named tool. (Claude Platform Docs)
4.1 auto: Model May Return Text
Use when:
Tool use is optional.
A normal text answer is acceptable.
Claude should decide whether extraction is needed.
Risk:
For that, do not use auto.
4.2 any: Must Call One of Several Tools
Use any when you have multiple extraction schemas and the document type is unknown.
Example:
tools = [extract_invoice_tool, extract_receipt_tool, extract_contract_tool]
tool_choice={"type": "any"},
Why this is correct:
The application requires structured output.
The document type is unknown.
Claude must choose one extraction schema.
Heuristic:
So the available tools must be scoped carefully and have strong descriptions.
4.3 Forced Tool Selection
Use forced tool selection when a specific step must run.
Example workflow:
1. Force extract_metadata.
2. Read metadata from tool_use.input.
3. Then run enrichment or classification using the extracted metadata.
Use forced selection when:
A required first step must happen.
A pipeline needs a specific schema.
Downstream logic depends on that exact extraction.
You do not want Claude to choose another tool or answer in prose.
5. Strict Schema Compliance Does Not Guarantee Semantic Correctness
This is the most important conceptual trap. Strict schemas eliminate syntax and schema errors such as:
- Malformed JSON
- Missing required fields
- Wrong type
- Extra fields when additionalProperties is false
- Invalid enum value
But they do not guarantee semantic correctness. Meaning, schema-valid but semantically wrong output:
This is valid JSON and matches the schema, but:
Other semantic errors:
Line items do not sum to total.
Invoice date is actually due date.
Customer name appears in vendor field.
Currency inferred incorrectly.
Ambiguous category forced into the wrong enum.
Missing field fabricated to satisfy a required field.
Current docs state that strict/structured outputs guarantee schema compliance, type safety, and fewer parsing errors; they do not claim that the extracted values are semantically correct. Semantic validation remains your application’s responsibility. (Claude)
5.1 Complexity Limits of Strict Mode
Strict schemas also have practical complexity limits. Very deep nesting or very large schemas may not be fully enforceable in strict mode. The practical workaround is to split or flatten the schema rather than relying on one giant nested schema. This reinforces the point above: strict mode reliably eliminates syntax/schema errors, but it is neither a guarantee of semantic correctness nor a license to model arbitrarily complex schemas.
If the scenario says:
Best answer is not:
Better answer:
6. Required Vs Optional Vs Nullable Fields
The exam specifically calls out optional/nullable fields.
6.1 Bad Schema
{
"type": "object",
"properties": {
"termination_date": {
"type": "string",
"description": "Termination date in format dd-mm-yyyy"
}
},
"required": ["termination_date"]
}
Problem:
Many contracts do not contain a termination date.
If the field is required and non-nullable, Claude may fabricate a value.
6.2 Better Schema: Required but Nullable
{
"type": "object",
"properties": {
"termination_date": {
"type": ["string", "null"],
"description": "Termination date or null if not present."
}
},
"required": ["termination_date"]
}
This keeps a stable output shape while allowing absence.
6.3 Optional Field
{
"type": "object",
"properties": {
"termination_date": {
"type": ["string", "null"],
"description": "Termination date in format dd-mm-yyyy."
}
},
"required": []
}
Use optional fields when:
The downstream consumer can handle missing keys.
The field applies only to some document types.
The schema should stay sparse.
Current Anthropic docs note schema complexity limits for optional parameters and union types like ["string", "null"], so production schemas should balance robustness with complexity.
The conceptual point is still: nullable fields prevent forced fabrication when data may be absent. (Claude)
7. Enum Design: unclear, other
Enums are good for stable categories. But too-narrow Enums create bad behavior when the source does not fit.
7.1 Bad Enum
If the document type is a "quotation", Claude will be forced to choose a wrong category.
7.2 Better Enum
"document_type": {
"type": "string",
"enum": ["invoice", "receipt", "contract", "other", "unclear"]
}
Use:
unclear = source is ambiguous or insufficient.
other = source clearly indicates a category outside the enum.
Heuristic:
Do not force ambiguous or out-of-taxonomy inputs into a closed Enum.
8. Format Normalization Belongs in the Prompt, Not Just the Schema
A schema can say:
But that does not by itself tell Claude how to normalize:
Add prompt rules:
- Normalize dates to YYYY-MM-DD.
- If the date is ambiguous between MM/DD/YYYY and DD/MM/YYYY
- return null and set date_ambiguity_reason.
Preserve original values in evidence fields when useful.
Schema:
"invoice_date": {
"type": ["string", "null"],
"description": "Date normalized to YYYY-MM-DD, or null if absent."
},
"invoice_date_ambiguity_reason": {
"type": ["string", "null"],
"description": "When invoice_date is null due to ambiguous format."
}
Heuristic:
Schema defines the shape.
Prompt rules define normalization semantics.
Post-validation checks semantic consistency.
9. Common Traps
9.1 Trap 1: Asking for JSON Instead of Enforcing Schema
Bad:
Better:
9.2 Trap 2: Using auto When Structured Output Is Mandatory
Bad:
When the pipeline requires structured output.
Better:
tool_choice = {"type": "any"}
# Or use a specific tool:
tool_choice = {"type": "tool", "name": "extract_metadata"}
9.3 Trap 3: Assuming any Means Correct Tool
Wrong:
Correct:
any guarantees a tool call. Tool descriptions and scoped tool sets still determine correct selection.
9.4 Trap 4: Assuming Schema Validity Means Factual Validity
Wrong:
Correct:
9.5 Trap 5: Required Non-Null Fields for Missing Source Data
Bad:
When many documents do not contain a termination date, Claude might fabricate data.
Better:
9.6 Trap 6: Closed Enum with no Escape Hatch
Bad:
Better: