Claude Agents: Enforcing Strict Structured Output
Learn how to lock claude agents into valid JSON or XML across multi-step MCP workflows, prevent filler text, and handle nested schema ambiguity reliably.
By Solomon Udoh · AI Architect & Certification Lead

Structured output is one of the sharpest pain points when building claude agents in production. The model is fluent, which is exactly the problem: left without strong constraints, it adds preamble, appends explanatory prose, or invents keys that fit the spirit of a schema but break the parser. This post is a practical engineering guide to preventing all three failure modes across single-turn and multi-step agentic workflows.
We focus on Domain 4 (Prompt Engineering and Structured Output, 20% of the CCA-F exam) and Domain 2 (Tool Design and MCP Integration, 18%), because the two domains are inseparable once agents start calling tools.
Why do Claude agents produce unstructured text alongside structured payloads?
The root cause is almost always ambiguity about the response contract. Claude is trained to be helpful and conversational. When the system prompt does not explicitly forbid prose, the model hedges: it outputs the JSON block and then adds a sentence like "Let me know if you need any changes." That sentence silently breaks a downstream JSON parser.
Three conditions reliably trigger filler text:
- No explicit output-mode declaration. The system prompt describes what to do but not what the response must look like.
- Mixed-mode instructions. The prompt asks the model to "explain your reasoning and then return the JSON," which teaches it that prose is welcome.
- Schema described in natural language only. When the model has no machine-readable reference, it infers the shape and occasionally invents plausible-looking keys.
The fix is layered: declare the output contract, embed the schema, and use tool-call mechanics to enforce the boundary programmatically.
What is the most reliable way to declare a strict output contract?
Answer the question in the system prompt, not the user turn. The system prompt sets the persistent behavioural frame; the user turn is treated as variable input. A contract placed only in the user turn can be overridden by conversational momentum.
A minimal but effective system prompt structure looks like this:
You are a data-extraction agent. Your responses MUST be valid JSON thatconforms exactly to the schema below. Output NOTHING outside the JSON object.Do not add commentary, apologies, or explanations. If a field cannot bedetermined, use null for nullable fields or omit optional fields entirely.Do not invent keys that are not in the schema.SCHEMA:{"invoice_id": "string","vendor": "string","line_items": [{ "description": "string", "amount": "number" }],"total": "number","currency": "string (ISO 4217)"}
Notice three things: the prohibition is stated positively ("Output NOTHING outside"), the schema is embedded verbatim rather than described, and the null/omit policy for missing data is explicit. Without that last clause, the model sometimes fabricates plausible values rather than admitting uncertainty.
For the CCA-F exam, this pattern maps directly to the Prompt Engineering and Structured Output domain. The exam consistently rewards solutions that eliminate ambiguity at the contract level rather than patching it downstream.
How does tool-call mechanics enforce the JSON boundary programmatically?
The cleanest enforcement mechanism is not a prompt at all: it is routing the structured output through a tool call. When you define a tool whose input schema matches your desired output, Claude is constrained by the API to produce a valid JSON object matching that schema. Filler text is structurally impossible because the tool-call response channel is separate from the text channel.
import anthropicclient = anthropic.Anthropic()extraction_tool = {"name": "record_invoice","description": "Record a parsed invoice. Call this once per invoice.","input_schema": {"type": "object","properties": {"invoice_id": {"type": "string"},"vendor": {"type": "string"},"line_items": {"type": "array","items": {"type": "object","properties": {"description": {"type": "string"},"amount": {"type": "number"}},"required": ["description", "amount"]}},"total": {"type": "number"},"currency": {"type": "string"}},"required": ["invoice_id", "vendor", "line_items", "total", "currency"]}}response = client.messages.create(model="claude-opus-4-5",max_tokens=1024,tools=[extraction_tool],tool_choice={"type": "tool", "name": "record_invoice"},system="Extract invoice data from the document provided.",messages=[{"role": "user", "content": "<document>...</document>"}])# The tool call input is guaranteed to be a valid JSON objecttool_input = response.content[0].input
Setting tool_choice to {"type": "tool", "name": "record_invoice"} forces the model to call that specific tool. It cannot respond with plain text. This is the High-Stakes Enforcement Decision Rule in practice: when the cost of a malformed response is high, use programmatic enforcement rather than relying on prompt instructions alone.
Tool use is the most reliable way to get structured data out of Claude when you need machine-readable output. The model's tool call inputs are validated against the JSON schema you provide.
How should nested schema ambiguity be handled in multi-step MCP workflows?
Nested schemas are where hallucinated keys most commonly appear. The model sees a parent object, infers that child objects should have certain properties, and adds them. In a multi-step MCP workflow, this is compounded because each agent step may receive a partially-filled context object and attempt to "complete" it.
The practical mitigations are:
| Problem | Mitigation | Where to apply |
|---|---|---|
| Invented keys in nested objects | Use additionalProperties: false in JSON Schema | Tool input schema |
| Optional vs required confusion | Enumerate required fields explicitly | Tool input schema + system prompt |
| Null vs missing field ambiguity | State the null policy in the system prompt | System prompt |
| Type coercion (string "42" vs number 42) | Add type examples in field descriptions | Tool input schema descriptions |
| Dynamic MCP resource definitions | Pre-fetch the schema and inject it at runtime | Orchestrator logic |
The last row deserves elaboration. When an MCP server exposes resources whose schema varies at runtime (for example, a CRM that returns different field sets per record type), the orchestrator should fetch the schema definition before constructing the agent prompt, then inject it as a concrete JSON Schema block. Relying on the model to infer a dynamic schema from examples alone produces inconsistent results.
# Pseudocode: runtime schema injection for dynamic MCP resourcesresource_schema = mcp_client.get_resource_schema(resource_type="deal")system_prompt = f"""You are a CRM data agent. Extract deal data and call record_deal witha JSON object that conforms EXACTLY to this schema:{json.dumps(resource_schema, indent=2)}additionalProperties is false. Do not add fields not listed above."""
For deeper coverage of how MCP scoping interacts with tool definitions, see Tool Design and MCP Integration.
Which prompt patterns prevent conversational filler most effectively?
We have tested four patterns in order of increasing reliability:
Pattern 1: Negative instruction only
Do not add any text outside the JSON.
Reliability: moderate. The model respects this most of the time but occasionally adds a closing remark after the JSON block.
Pattern 2: Output-mode declaration
RESPONSE FORMAT: JSON only. Your entire response is the JSON object. Nothing before it, nothing after it.
Reliability: high for single-turn. Degrades slightly in long multi-turn conversations where the instruction drifts out of the attention window.
Pattern 3: Role-framing as a serialiser
You are a JSON serialiser. You receive input and emit a single JSON object. You have no conversational mode.
Reliability: high. Role-framing suppresses the conversational register more durably than a rule statement alone.
Pattern 4: Tool-call enforcement (programmatic)
As shown in the code block above. Reliability: highest. The API enforces the boundary; the model cannot produce filler text in the tool-call channel.
For exam purposes, the Prompt-Based vs Programmatic Enforcement concept covers exactly this trade-off. The exam rewards choosing programmatic enforcement when the downstream consumer is a machine parser and the cost of a malformed response is non-trivial.
How do you balance zero-shot efficiency with schema fidelity for complex decision trees?
Zero-shot prompting is cheaper and faster; few-shot prompting is more reliable for complex or ambiguous schemas. The decision rule is not binary.
Use zero-shot when:
- The schema has fewer than roughly 10 fields.
- All fields are primitive types (string, number, boolean).
- The field names are self-explanatory.
- The agent will call this endpoint thousands of times per hour (cost matters).
Use few-shot when:
- The schema contains nested arrays of objects.
- Field semantics are ambiguous (for example,
statuscould mean many things). - The model must make classification decisions within the schema (for example, assigning a category from a controlled vocabulary).
- You have observed schema violations in zero-shot runs.
A practical hybrid: use zero-shot for the first 50 to 100 calls in a new workflow, log every schema violation, then add targeted examples that address the specific failure modes observed. This avoids over-engineering the prompt before you know where it actually breaks.
{"role": "user","content": [{"type": "text","text": "Here are two examples of correct output:\n\nExample 1:\n{\"status\": \"open\", \"priority\": 2, \"tags\": [\"billing\"]}\n\nExample 2:\n{\"status\": \"closed\", \"priority\": 1, \"tags\": [\"technical\", \"escalated\"]}\n\nNow extract from the following ticket:\n<ticket>...</ticket>"}]}
The Agentic Architecture and Orchestration domain of the CCA-F exam (27% weighting, the largest single domain) tests whether candidates understand when to use model-driven flexibility versus pre-configured rigidity. Structured output enforcement is a direct application of that trade-off.
How should multi-step agents handle schema validation failures mid-pipeline?
A validation failure mid-pipeline is not a reason to abort the entire workflow. The correct response depends on the failure type.
The retry prompt should be surgical, not a full re-statement of the original instructions. Inject only the specific violation:
Your previous response contained an unexpected key "invoice_notes" which isnot in the schema. Remove it and resubmit the JSON object. All other fieldswere correct.
This approach is consistent with the Multi-Agent Error Handling and Routing pattern: route the error back to the originating agent with specific context rather than propagating a failure signal upward.
When Claude makes a mistake in a multi-step task, the most effective correction is targeted and specific rather than a repetition of the original instruction.
After three retries without resolution, the correct action is escalation or graceful degradation, not an infinite loop. The CCA-F exam penalises solutions that retry indefinitely without a termination condition.
What does the CCA-F exam test about structured output in agentic contexts?
Domain 4 (Prompt Engineering and Structured Output, 20%) and Domain 2 (Tool Design and MCP Integration, 18%) together account for 38% of the exam. The scenario questions in these domains typically present a broken agent and ask you to identify the root cause and the proportionate fix.
| Domain | Weight | Structured output relevance |
|---|---|---|
| Domain 1: Agentic Architecture and Orchestration | 27% | Pipeline design, error routing, retry logic |
| Domain 2: Tool Design and MCP Integration | 18% | Tool schema design, MCP resource handling |
| Domain 3: Claude Code Configuration and Workflows | 20% | CLAUDE.md constraints, CI output formatting |
| Domain 4: Prompt Engineering and Structured Output | 20% | Schema embedding, few-shot, output contracts |
| Domain 5: Context Management and Reliability | 15% | Context drift, stale schema injection |
Common exam traps in this area:
- A scenario where the fix is "add
additionalProperties: falseto the tool schema" but a distractor offers "add a longer system prompt." The exam rewards the programmatic fix. - A scenario where the agent is retrying indefinitely. The correct answer identifies the missing termination condition, not a prompt tweak.
- A scenario where few-shot examples are proposed for a simple 3-field schema. The correct answer is zero-shot; few-shot adds unnecessary token cost without reliability benefit.
Our concept library covers all 174 atomic concepts mapped to these domains, including the specific tool-call and schema-design patterns tested in the exam.
How do you test structured output reliability before deploying a Claude agent?
Reliability testing for structured output should be systematic, not anecdotal. A minimal test harness:
- Generate a diverse input set. Include edge cases: empty documents, documents with ambiguous fields, documents with fields that look like schema keys but are not.
- Run 50 to 100 calls per prompt variant. Single-call testing is insufficient; the model is probabilistic and failures may appear at low rates.
- Validate every response against the schema programmatically. Use
jsonschemain Python or equivalent. Log the full response for every failure, not just the error type. - Measure violation rate by failure type. Invented keys, missing required fields, and type mismatches have different root causes and different fixes.
- Compare prompt variants statistically. A variant that reduces the invented-key rate from 4% to 0.5% is a meaningful improvement even if it adds 50 tokens to the prompt.
import jsonschemaimport jsondef validate_agent_output(raw_response: str, schema: dict) -> dict:try:parsed = json.loads(raw_response)jsonschema.validate(instance=parsed, schema=schema)return {"valid": True, "parsed": parsed}except json.JSONDecodeError as e:return {"valid": False, "error_type": "json_parse", "detail": str(e)}except jsonschema.ValidationError as e:return {"valid": False, "error_type": "schema_violation", "detail": e.message}
This test harness is lightweight enough to run in CI. For agents that will process high volumes, even a 1% schema violation rate translates to thousands of failed records per day.
Building reliable claude agents is fundamentally a contract-design problem. The model is capable; the question is whether the system around it is precise enough to channel that capability into machine-parseable output every time. Programmatic enforcement via tool calls is the highest-reliability option. Layered prompt contracts are the fallback. Systematic testing is non-negotiable before production deployment.
For exam preparation, AI Skill Certs is an independent adaptive prep platform (not affiliated with or endorsed by Anthropic) with 174 concepts mapped to the five CCA-F domains. Our practice exams are scored on the same 100 to 1000 scale as the real exam, with 720 as the passing bar.
Frequently asked questions
Can Claude agents output both a text explanation and a JSON object in the same response?
Does setting additionalProperties: false in a JSON Schema actually prevent Claude from inventing keys?
How many retry attempts should a Claude agent make before escalating a schema validation failure?
Is few-shot prompting always better than zero-shot for structured output in Claude agents?
What is the passing score for the CCA-F exam and how does structured output feature in it?
How do I prevent a Claude agent from leaking schema field names into its conversational output?
People also ask
How do Claude agents handle structured output?
What is the best way to get Claude to output only JSON?
How do Claude agents work with MCP?
What causes Claude to add extra text when I ask for JSON output?
How do I stop Claude from hallucinating JSON keys?
About the author
AI Architect & Certification Lead
Solomon Udoh is an AI Architect who designs and ships production agent systems on the Claude API and Claude Code. He built AI Skill Certs' adaptive engine and authored its 174-concept knowledge graph, mapping every Claude Certified Architect - Foundations objective to hands-on, exam-aligned practice.
- Designs production multi-agent systems on the Claude API and Agent SDK
- Author of the AI Skill Certs knowledge graph (174 mapped exam concepts)
- Builds with MCP, Claude Code, structured outputs, and agentic loops daily
- Reviews every concept page against the official Anthropic exam guide
You might also like
Ready to put it into practice?
Study every exam concept with an adaptive tutor.