On this page
- Why this matters now
- Where the contract actually lives
- A production schema gateway
- Implementation pattern
- Failure modes and tradeoffs
- Rollout guidance
- Practical checklist
- FAQ
- Is strict schema support enough to make tool use safe?
- Should every tool output be structured?
- How should teams version agent tool schemas?
- Where should validation happen?
- References
Agent reliability does not start with a better prompt. It starts at the tool boundary. If an agent can take actions, retrieve data, or hand work to another system, its schemas need the same design pressure as APIs: validation, compatibility, observability, ownership, and rollout control.
Why this matters now
The current agent stack is converging on the same shape: a model sees a list of tools, chooses one, emits structured arguments, waits for application code or server infrastructure to execute the call, and then reasons over the result. OpenAI documents function calling and structured outputs around JSON schemas and constrained formats, Anthropic exposes client tools through tool_use blocks and input_schema, Google Gemini describes function calling as a four-step loop where the application executes the function, and MCP standardizes tools/list, tools/call, inputSchema, and optional outputSchema for external systems (OpenAI structured outputs, Anthropic tool use, Gemini function calling, MCP tools).
That convergence is useful, but it also creates a trap. Many teams still treat schemas as lightweight prompt accessories. They write a loose description, accept a permissive object, parse whatever arrives, and rely on retries when the model drifts. That may work in a demo. It fails under production load because the schema is the place where ambiguity becomes an API call.
A tool schema is not only a model hint. It is an operational contract between the model runtime, the application, downstream services, audit logs, and humans approving risky actions. The contract needs to say what can be called, what each parameter means, what output shape is guaranteed, how errors are represented, and how the contract changes without breaking older agents.
Where the contract actually lives
There are three related contracts, and they often get collapsed into one file.
| Layer | Purpose | What breaks when it is weak |
|---|---|---|
| Input schema | Constrains arguments the model may send | Bad side effects, missing fields, silent defaults |
| Output schema | Constrains data returned to the model and app | Brittle parsing, hallucinated post-processing, lost provenance |
| Execution policy | Controls whether, where, and by whom a call may run | Unsafe autonomy, privilege leaks, weak auditability |
JSON Schema gives teams a shared vocabulary for structure, validation, documentation, and interaction control (JSON Schema core). The model providers each expose only subsets or product-specific variants, so production systems should maintain a canonical internal schema and compile provider-specific forms from it instead of hand-editing each integration (Gemini function calling, OpenAI structured outputs).
The distinction matters because provider conformance is not the same thing as business correctness. A model can produce valid JSON that requests the wrong customer account. A tool can return schema-valid data that lacks source freshness. A schema can pass constrained decoding while still leaking policy decisions into free-form descriptions. Treat constrained output as the first gate, not the only gate.
Practical rule: if a human reviewer would ask "what exactly is this allowed to do?", that answer belongs in schema, policy, or typed metadata, not only in a prompt.
A production schema gateway
A useful pattern is to put a schema gateway between the agent runtime and every action-capable system. The model provider may already validate some structure, but the gateway owns the contract your company can test, monitor, and evolve.
flowchart LR
U["User request"] --> A["Agent runtime"]
A --> C["Provider schema layer"]
C --> G["Schema gateway"]
G -->|"validate input"| P["Policy engine"]
P -->|"approve or deny"| E["Tool executor"]
E --> S["Downstream service"]
S --> E
E -->|"typed result"| G
G -->|"validated output"| A
G --> O["Logs, traces, evals"]
The gateway does five jobs.
First, it validates arguments before execution. MCP says servers must validate tool inputs and implement access controls, while clients should ask for confirmation on sensitive operations and show tool inputs before calling a server (MCP tools). Do not outsource this to the model. Validate again at the boundary you control.
Second, it normalizes outputs. MCP allows tool results to include unstructured content, resource links, embedded resources, and structuredContent; when an outputSchema is provided, servers must return structured results that conform to it and clients should validate them (MCP tools). For agent applications, that means downstream tools should return typed objects for state transitions and reserve prose for human-readable explanation.
Third, it attaches policy. Execution policy should not hide inside a model-facing description. Mark tools as read-only, write, destructive, externally visible, privileged, or human-approval-required in machine-readable metadata. MCP tool annotations are explicitly untrusted unless they come from trusted servers, so the gateway should combine server metadata with local trust configuration instead of blindly accepting remote claims (MCP tools).
Fourth, it emits observability. Every tool call should log schema version, request ID, model, tool name, validated arguments with sensitive fields redacted, policy decision, executor latency, downstream status, output validation result, and final agent step. Anthropic notes that tool definitions, tool use blocks, and tool result blocks add tokens, which means tool design also affects cost and context pressure (Anthropic tool use).
Fifth, it owns compatibility. Agents become distributed systems once tool schemas are shared across clients, MCP servers, sub-agents, and eval suites. Schema changes need semantic versioning, deprecation windows, and replay tests, not Friday edits to a JSON blob.
Implementation pattern
Start by writing a canonical contract that is stricter than the prose you show the model. Keep descriptions direct, but make policy and validation explicit.
{
"name": "create_refund_case",
"version": "1.2.0",
"risk": "write_external",
"approval": "required_over_100_usd",
"input_schema": {
"type": "object",
"additionalProperties": false,
"properties": {
"customer_id": { "type": "string", "pattern": "^cus_[A-Za-z0-9]+$" },
"order_id": { "type": "string", "pattern": "^ord_[A-Za-z0-9]+$" },
"amount_usd": { "type": "number", "minimum": 0.01, "maximum": 500.0 },
"reason_code": {
"type": "string",
"enum": ["duplicate_charge", "failed_delivery", "policy_exception"]
},
"evidence_uri": { "type": "string", "format": "uri" }
},
"required": ["customer_id", "order_id", "amount_usd", "reason_code"]
},
"output_schema": {
"type": "object",
"additionalProperties": false,
"properties": {
"case_id": { "type": "string" },
"status": { "type": "string", "enum": ["opened", "queued_for_review"] },
"audit_url": { "type": "string", "format": "uri" }
},
"required": ["case_id", "status", "audit_url"]
}
}
Then generate provider-specific tool definitions from that contract. Anthropic emphasizes detailed tool descriptions, optional schema-validated input examples, and strict: true for schema validation on tool inputs (Anthropic define tools). OpenAI structured outputs support strict schemas, but applications still need to handle incomplete responses, refusals, content-filter interruptions, and supported-schema limits (OpenAI structured outputs). Google recommends clear descriptions, strong typing, validation, robust error handling, and security for Gemini function calling (Gemini function calling).
In application code, make validation a hard dependency before execution and after execution.
from jsonschema import Draft202012Validator
input_validator = Draft202012Validator(contract["input_schema"])
output_validator = Draft202012Validator(contract["output_schema"])
def execute_tool_call(call, user, policy):
input_validator.validate(call["arguments"])
decision = policy.authorize(user=user, tool=contract, args=call["arguments"])
if not decision.allowed:
return {"is_error": True, "code": "policy_denied", "message": decision.reason}
raw = refund_service.create_case(**call["arguments"])
result = {
"case_id": raw.id,
"status": raw.status,
"audit_url": raw.audit_url
}
output_validator.validate(result)
return {"is_error": False, "structured_content": result}
This is intentionally ordinary engineering. The point is not to add ceremony. The point is to make the model's action surface behave like the rest of your production interfaces.
Failure modes and tradeoffs
The first failure mode is over-broad schemas. If a tool accepts a generic query string or an arbitrary object, the model has too much room to infer business logic. Use enums for known modes, typed IDs for entities, bounded numbers for amounts, and explicit booleans only when each value has a tested meaning.
The second failure mode is description drift. Anthropic says detailed descriptions are the most important factor in tool performance, but descriptions are not type systems (Anthropic define tools). Put invariant requirements in schema and policy. Put examples and usage guidance in descriptions.
The third failure mode is output prose. If the tool returns a paragraph, the next model step must parse it. MCP's structuredContent and optional outputSchema exist because downstream clients need typed data, not just readable text (MCP tools). Return both when compatibility requires it, but treat the structured object as authoritative.
The fourth failure mode is cached or sensitive schemas. Anthropic documents that structured output schemas may be cached for up to 24 hours for optimization and warns not to include PHI in schema definitions (Anthropic structured outputs). Avoid tenant names, patient categories, secrets, customer-specific enum values, and internal incident identifiers in schema text.
The fifth failure mode is mixing protocol errors with tool execution errors. MCP distinguishes JSON-RPC protocol errors, such as unknown tools or invalid arguments, from tool execution failures reported in the result with isError: true (MCP tools). Keep that separation in your gateway so retries, alerts, and user-facing explanations do not treat a denied refund, a schema bug, and a downstream outage as the same class of failure.
The sixth failure mode is assuming strict decoding equals safe execution. Strict schemas reduce parse errors and missing fields, but they do not prove the request should run. The policy engine still needs authorization, rate limits, human approval, idempotency keys, and data-loss prevention checks.
There are tradeoffs. Strict schemas can slow iteration. Narrow enums can reject legitimate new cases. Complex schemas consume tokens and may hit provider limits. Splitting a large toolset across sub-agents can improve selection quality but adds orchestration overhead. The right answer is not maximum strictness everywhere. It is strictness proportional to risk.
Rollout guidance
Inventory tools first. Classify every tool by side effect: read-only, write-internal, write-external, destructive, privileged, or human-facing. If a tool can send money, delete data, message users, deploy code, or alter permissions, it should not share the same approval path as a search tool.
Add versioned contracts next. Every schema should have an owner, a version, a changelog, examples, and eval fixtures. Breaking changes include renamed fields, narrowed enums, changed defaults, different output status names, and new required fields. Non-breaking changes still need replay tests because model behavior can change when descriptions or examples change.
Build evals around tool calls, not only final answers. For each tool, keep golden prompts that should call it, prompts that should not call it, boundary values, malicious arguments, missing identifiers, and stale-context scenarios. Evaluate argument correctness, policy decision, executor behavior, and final answer.
Roll out in shadow mode. Let the new gateway validate and log calls while the old path executes. Measure rejection reasons, parse errors, policy denials, schema-version skew, tool latency, and user correction rate. Then move low-risk tools to enforcement before write or destructive tools.
Finally, make schema review part of production review. A tool schema change deserves the same scrutiny as an API endpoint change. Security, platform, product, and support teams all feel the blast radius when an agent calls the wrong thing. The publish-readiness gate is simple: if the schema cannot be tested, versioned, observed, and rolled back, it is not ready for an autonomous agent.
Practical checklist
Before exposing a tool to an agent, answer these questions:
- Does the input schema reject unknown fields and bound risky values?
- Are descriptions specific enough to guide tool selection without embedding policy secrets?
- Is there a typed output schema for any value the agent or application will reuse?
- Are protocol errors separated from tool execution errors?
- Is every side effect protected by authorization and, when needed, human approval?
- Are calls logged with schema version, policy result, latency, and validation status?
- Can old and new schema versions run during a migration window?
- Do evals cover both correct calls and calls that must be refused?
- Are external images or screenshots absent from the article or clearly credited if present?
FAQ
Is strict schema support enough to make tool use safe?
No. Strict schemas reduce malformed inputs and outputs, but safety also needs authorization, rate limits, confirmation UX, sandboxing, audit logs, and business validation. MCP explicitly separates tool schemas from client and server security responsibilities (MCP tools).
Should every tool output be structured?
Every value that drives another action should be structured. Human-readable prose is fine as a secondary explanation, but the agent and application should consume typed fields for IDs, statuses, timestamps, provenance, and next-step decisions. MCP supports structuredContent for this reason (MCP tools).
How should teams version agent tool schemas?
Use semantic versions for the canonical contract, include the schema version in each call log, keep old versions during migration, and replay eval fixtures against both versions. Treat new required fields, renamed fields, narrowed enums, and changed output statuses as breaking changes.
Where should validation happen?
Validate at the provider layer when available, then validate again in your own gateway before execution and after execution. Provider validation catches malformed model output. Gateway validation protects business invariants, authorization, compatibility, and downstream service assumptions.