LetterMCP

MCP Tool Schema Design for Reliable Agent Calls

Proper schemas make agents recover from errors without guessing.

· 8 min read
Cover illustration for “MCP Tool Schema Design for Reliable Agent Calls”
MCP Developer Workflows · October 17, 2026 · 8 min read · 1,912 words

MCP tool schema design enables an agent to select a tool, supply valid arguments, and recover when a call is rejected. Reliable calls need executable contracts. The contract must make the rules machine-checkable, validate both sides of the call, and give the agent enough information to repair a mistake without guessing.

Why tool schemas fail in production even when the server code is correct

The agent tried the tool, the call failed, and the natural instinct was to fix the prompt. That can hide the problem when one client talks to one server. A larger model, another instruction, or a manual retry can get the next call through. None of those changes closes a gap in the schema. Once multiple replicas and concurrent agent sessions sit behind a gateway, that gap becomes a repeated failure.

The first failure is a broken contract. The model emits JSON unchecked against the tool’s domain rules, potentially omitting a field required under certain conditions, inventing a key, or mixing arguments from different operations. If the schema allows those combinations, the object passes validation and fails later in application code. An unstructured error then leaves the agent without a clear basis for repair.

The second failure is in the topology. A session-bound server behind a generic reverse proxy cannot be scaled and governed like the rest of an HTTP fleet. Validation, authorization, and tracing end up being rebuilt for each agent or tool. Both failures show the same weakness in the design: the contract depends on the application around it to enforce its rules. The consumer is a model choosing among dozens or hundreds of tools.

What the MCP 2026-07-28 spec revision changes about the contract surface

The MCP 2026-07-28 specification loosens inputSchema and outputSchema to allow any JSON Schema 2020-12 keywords. The MCP 2025-11-25 specification had already established JSON Schema 2020-12 as the default. The later revision broadens what tool authors can express within that dialect, bringing conditional requirements and composition rules into the enforceable contract.

The input schema still has an object-root constraint, but the output schema no longer does. Earlier protocol versions restricted output schemas to JSON objects. The 2026-07-28 revision permits schemas that validate primitives and arrays too. A backend response therefore does not have to be an object to receive shape validation. Unexpected output can be caught before it reaches model context.

The revision also changes how the contract travels. Under stateless Streamable HTTP, protocol version and client capabilities travel per request in _meta. The Mcp-Method and Mcp-Name headers expose routing information without requiring intermediaries to parse the JSON-RPC body. These changes make the contract more than a description held by the client. Its rules and request identity can support a structured -32602 invalid-params rejection before the tool runs.

The seven schema design mistakes that appear most often in production audits

A god-tool with an omnibus parameter blob and no audit gate is not a set of isolated defects. It combines ambiguous intent, weak validation, and poor accountability in one execution path. The seven recurring mistakes identify where that path breaks.

The first is schema over-fit. A tool works for the exact request used during development but fails when “today” becomes “this morning.” The corrective direction is to test nearby wording. The second is prose-only conditional logic. Its signal is an illegal payload that passes schema validation because a required condition exists only in the description, so that rule belongs in schema structure.

The third is the god-tool, which bundles fetch, parse, filter, and format behind one broad argument surface. When the call fails, the failed operation is unclear, so the corrective direction is focused tools with narrow contracts. The fourth is an ambiguous discriminator. Internal account fields can mix with IBAN/SWIFT fields and still pass, so each operation shape needs a branch that rules out the others.

The fifth is schema drift from shared types. Fragments for account, amount, and identifier are copied inline across tools, so after a change the definitions no longer agree. Shared definitions are the corrective direction. The sixth is unstructured output, where whatever the backend returns goes straight into model context without validation against a declared response contract.

The seventh is authentication deferred until after launch. Its signal is an integrated server whose trust boundary has not been settled. Retrofitting authentication can invalidate hosts that already use it. The boundary must instead be chosen at the start, whether it is stdio-local, scoped-remote, or multi-tenant.

Using JSON Schema 2020-12 features to make conditional logic machine-checkable

Conditional requirements belong in if, then, and else. For a transfer, if can use exclusiveMinimum to identify an amount above a threshold, while then requires compliance_approval_code. For a simpler dependency in which the presence of one key requires another, dependentRequired avoids a full conditional block.

Operation ambiguity needs a different tool. The oneOf keyword requires exactly one schema branch to validate. Separate branches can describe internal and wire transfers so mixed argument shapes fail before application code has to work out what they mean. The branches must spell out which combinations are valid for each operation. Merely placing two permissive shapes under oneOf does not replace careful contract design.

Shared definitions close another gap. Account, amount, IBAN, and SWIFT fragments can live in $defs, with $ref pointing to them wherever they are used. A change to the shared definition then reaches each reference. That reuse has a security boundary. Implementations must not automatically dereference external $ref URIs, and schema depth and validation time should be bounded.

Closed object shapes address invented keys. Setting unevaluatedProperties to false rejects properties the schema has not accounted for, while outputSchema checks the shape returned by the backend. The caldeirav reliability demo isolates the conditional-rule difference by running description-only and strict contracts against the same agent, model, and prompt. The strict contract makes a missing compliance code a named validation failure rather than a rule the model was merely expected to remember.

How tool name and description quality determines whether a call is attempted at all

Names and descriptions tell the agent which tool to choose before it looks at the argument structure. A vague name can therefore cause a selection failure by leading the agent to pass over a tool whose structure is sound.

Tool names should state the verb and the object. transfer_funds gives the agent a clearer choice than process or execute. The description should then explain what the tool does, when to use it, and what separates it from adjacent tools. Implementation detail does not answer those questions. Parameter descriptions should add semantic meaning and valid ranges rather than repeat a type already declared in the schema.

The same clarity should govern tool boundaries. Bundling independent operations into a composite tool makes selection less precise as well as making errors harder to attribute. Business context, including team defaults, project scopes, and output constraints, belongs at the tool level. On-demand discovery and minimal upfront context keep that information from becoming a verbose schema dump. Even with those decisions right, the contract still needs a place where its rules are enforced.

Where schema enforcement belongs in the execution architecture

That enforcement point belongs at the gateway, before business logic runs. Server-side validation remains a check, but it is not enough to govern the path by which calls reach execution. The architectural principle is to separate orchestration, governance, and execution into different processes. In that arrangement, the agent does not call the execution server directly.

The caldeirav demo illustrates the split with LangGraph and agentgateway. LangGraph handles tool selection and the bounded repair loop. The gateway routes the call, checks it, and enforces policy before it reaches the tool server. The product names are incidental to the boundary. The important distinction is that deciding what to call, deciding what is allowed, and carrying out the operation are separate responsibilities.

Stateless Streamable HTTP makes this separation practical across replicas. A session-bound server otherwise needs sticky routing or a distributed session store, so ordinary round-robin L7 load balancing will not work. With Mcp-Method and Mcp-Name, intermediaries can classify requests without parsing the JSON-RPC body. Routing and policy can then compose with TLS termination and load balancing.

This placement also makes governance specific to each tool call. One agent identity can have different permissions for different tools. Audit traces can be tied to the particular call. Rate limiting and traffic splitting can use Mcp-Name without unmarshalling JSON-RPC bodies. Schema validation becomes a check at that shared enforcement boundary, saving each agent from having to build its own.

Building the agent-side repair loop that closes the contract

The agent receives a -32602 error naming a missing compliance_approval_code. In the caldeirav demo’s strict path, the agent reads the violation, extracts the required code from the user prompt, and retries with corrected arguments. It must not invent the code. The transfer is recorded only when the call is valid.

That recovery needs firm limits. A retry must have a changed argument fingerprint, because sending the same invalid payload again is not repair. This loop should run only when a validation error is marked -32602. Opaque server errors and business rejects belong to different error classes and do not trigger argument repair. The retry budget must also be bounded so that an unsatisfied schema does not become an availability failure.

The error payload must explain the violated constraint. “amount exceeds threshold; compliance_approval_code required” tells the agent what changed the requirement and what is missing. “invalid params” does neither. The schema must also ask only for information the agent can get from its context.

The legacy path shows the consequence of leaving that loop open. The same class of missing-code payload receives an opaque error, so repair does not run. The strict path tells the agent how to fix a rejected call and lets it run only after it passes validation.

A practical schema design checklist for tool authors

Begin with the trust boundary, because authentication added later can break existing integrations. Next, define focused tools and give each a verb-and-object name that distinguishes it from its neighbors. Test whether nearby wording still selects the intended tool. Then write descriptions around purpose, use, and meaning, leaving implementation detail out. This settles who can call the tool and whether the agent can recognize when it should.

Next, make each business rule a check that a call must pass. Use conditional keywords for state-dependent requirements and dependentRequired for simpler key dependencies. Give distinct operation shapes separate oneOf branches. Put shared types in $defs, refer to the same definitions wherever they are used, and control how external references are resolved. Close object shapes so undeclared keys cannot slip through.

Then define the return contract, including primitives and arrays where those are the intended response shapes. Place validation at the gateway before execution, with identity and policy attached to the individual tool call. Make each validation error say which constraint failed clearly enough for the agent to fix the call. That error design needs changed-argument retries, validation-only recovery, and a fixed repair budget.

Before shipping, check the whole path and how its schema defects affect one another. Over-fit selection, prose-only rules, broad tools, mixed operation shapes, copied types, unchecked outputs, and deferred authentication can reinforce one another. The final test is whether an invalid call is rejected clearly and can be corrected using information the agent actually has. If success still depends on a stronger prompt explaining rules the contract cannot enforce, the schema work is not finished.

Sources

  1. GitHub - caldeirav/mcp-2026-07-28-schema-reliability-demo: Eliminate tool-calling failures in small local models. Demonstrates self-validating MCP tools with JSON Schema 2020-12 conditional logic, agentgateway edge validation, and LangGraph error recovery. · GitHub

More in MCP Developer Workflows