Skip to main content
AI Agents

Tool Calling Design Patterns for Reliable Agents

Tool calling is where agents touch the real world, and it is where production systems earn or lose trust. The patterns that work are unglamorous: narrow single-purpose tools instead of broad Swiss-army schemas, validation that treats model arguments as hostile input, idempotency keys on every mutation, error responses written for the model to recover from rather than for humans to read, permission tiers enforced in code, and mocked tools behind every eval. Real tool-call failure rates in production are high enough — wrong arguments, wrong tool choice, hallucinated parameters — that designing for recovery matters more than designing for the happy path. This post is the pattern catalogue, with the failure statistics framed honestly.

By Raman Makkar, CEO & Founder··14 min read

🧨Why tool calling is the fragile layer

Everything else in an agent system fails softly — a weak summary, a drifted plan — and a human or a later step can catch it. Tool calls fail hard: a payment is issued, a row is deleted, an email is sent. The blast radius of a model mistake is exactly the blast radius of the tools you handed it, which is why tool design is security design and not merely API ergonomics.

The compounding point deserves emphasis because it drives the whole design philosophy. At 95 percent per-call reliability, a ten-call task succeeds about 60 percent of the time if failures are independent and fatal. The engineering response is not to demand a better model; it is to make failures recoverable, retries safe, and dangerous actions gated — so a failed call is an event the system handles, not a corpse in the transcript.

That framing — assume failure, design for recovery — is the through-line of every pattern below.

Tool calling, defined in the glossary

📐Tool schema design: narrow beats broad

The dominant design error is building tools that mirror your internal APIs. Internal APIs are broad because they serve many callers with client-side logic; a model has no client-side logic — it is the caller, and every degree of freedom in the schema is a way for it to be wrong. A generic query_database(sql: string) tool can express anything and will eventually express something catastrophic. A set of narrow tools — get_customer_by_email, list_open_orders(customer_id) — constrains the model to the operations you have actually made safe.

Narrow tools also give the model a better decision surface. Tool selection accuracy degrades as the number of similar-looking tools grows, but it degrades faster when tools are parameterized clones of each other than when each tool has a distinct, nameable purpose. The sweet spot for most agents is a small catalogue of single-purpose tools with names that read like the user intent: refund_order, not post_to_billing_api with an action field.

Parameter design is its own craft. Prefer enums over free strings wherever the value space is closed — an enum cannot be hallucinated into an invalid value. Prefer IDs the agent obtained from earlier tool results over IDs it must construct or recall. Give every parameter a description that states format and units, because "date" will be interpreted three ways by the same model in the same week. And keep required parameters few: every required field the model must source is a failure opportunity.

Design choiceBroad styleNarrow styleWinner for agents
Endpoint shapeOne generic endpoint, action parameterOne tool per operationNarrow — selection and validation both improve
ParametersFree-form strings, flexible payloadsEnums, typed scalars, constrained formatsNarrow — closes hallucination surface
Catalogue sizeFew tools, many modesMany single-purpose toolsNarrow up to ~dozens, then add tool routing
ErrorsHTTP status + generic bodyStructured, model-recoverable messagesNarrow — see error contracts below
SafetyCaller-side checks (absent)Safety baked into each toolNarrow — the tool is the guardrail

Every degree of freedom in a tool schema is a way for the model to be wrong. Narrow tools are not less powerful; they are power with the sharp edges pre-removed.

🛡️Argument validation: treat model output as hostile input

The correct security posture for tool arguments is the one you already use for user-submitted form data: validate everything, trust nothing. The model will, with enough calls, produce arguments that are malformed, out of range, semantically absurd ("refund -400 dollars"), or — in adversarial settings — deliberately injected through content the agent read. Schema-level validation (types, enums, ranges, formats) is the floor, not the ceiling.

Semantic validation is the layer most systems skip. Schema says amount is a number; business logic says a refund cannot exceed the order total, the order must be in a refundable state, and the order must belong to the customer in the conversation. These checks live in the tool implementation or a policy layer in front of it — never in the prompt, because prompt-level rules are instructions the model follows when it remembers to, not constraints.

Validation failures should feed back, not throw. When arguments fail validation, return the failure as a structured, correctable error to the model (see the error-contract section) and let it retry — a large share of argument errors are one-shot slips the model fixes immediately when told precisely what was wrong. Reserve hard failures for validation problems that are unrecoverable or repeated, and log every rejected call: your validation log is the highest-signal dataset you have for how the model actually uses your tools.

🔁Idempotency: because retries are not optional

Agents retry. Timeouts fire, the orchestrator re-attempts a step, the model itself re-issues a call it is unsure completed. If your mutation tools are not idempotent, retries are how customers get charged twice and tickets get created in triplicate. Idempotency is not a nice-to-have in an agent system; it is the property that makes the entire reliability architecture — timeouts, retries, at-least-once execution — safe to use.

The mechanism is standard and borrowed straight from payments engineering: the orchestrator generates an idempotency key per logical operation (not per call attempt), the tool stores the result keyed by it, and repeat executions return the stored result instead of re-executing. The subtlety is key ownership: the model should not generate the key — it will generate a fresh one on retry, defeating the purpose. Keys come from the deterministic layer that wraps the model.

Design tools to be checkable as well as idempotent where you can. A dry_run or confirm parameter, or a paired check_* read tool, lets the agent verify state before and after a mutation — which converts the model own uncertainty ("did that refund go through?") into a tool call instead of a guess. Agents that can check their work are dramatically easier to make reliable than agents that must act on faith.

Tool classRetry safetyPattern
Reads (get, list, search)Naturally safeNothing needed — but cache and rate-limit
Idempotent writes (set, update-by-key)Safe with careUpsert semantics; last-write policy documented
Mutations (charge, refund, create)Unsafe by defaultOrchestrator-issued idempotency keys + stored results
External side effects (email, third-party API)Unsafe and uncontrollableTransactional outbox + executor with dedupe
Destructive (delete, cancel)Unsafe and irreversibleSoft-delete + confirmation gate + audit trail

📨Error contracts the model can recover from

A tool error is a message to the model, and most tool errors are written for humans or machines that parse status codes — neither of which is your consumer. "500 Internal Server Error" teaches the model nothing; it will retry the identical call or hallucinate a workaround. A recoverable error contract answers three questions in the response body: what went wrong, whether retrying helps, and what to change. invalid_parameter: date must be ISO-8601, got "next Friday" is an instruction the model can act on, and it will.

Classify errors by recoverability and say which class they are. Retryable-as-is (rate limited, transient downstream failure — include retry timing). Retryable-with-changes (validation failure — include exactly what to change). Not retryable with this tool (permission denied, resource does not exist — tell the model to escalate or choose another path rather than hammering). And never-retry red lines (policy violation) that should also trip your monitoring, because repeated policy-violating attempts are a signal about the task or an attack, not bad luck.

The payoff shows up in run-level reliability more than anywhere else. Most multi-step task failures are not unrecoverable errors — they are recoverable errors presented in a form the model could not recover from. Fixing the error contract is routinely the highest-ROI reliability change in an existing agent system, ahead of model upgrades, ahead of prompt work.

The model is the parser of your error messages. Write errors as instructions — what failed, whether retry helps, what to change — and watch run-level reliability move further than any model upgrade would move it.

🔐Permission tiers: least privilege for models

Not every tool deserves the same trust level, and the permission structure should reflect blast radius, not convenience. A workable tiering: read-only tools available by default; reversible mutations behind a policy check; irreversible or financial mutations behind a confirmer or human approval; and administrative actions outside the agent reach entirely, reachable only through a human-operated path. The tier of a tool is a property of the action, and it should be enforced in code the model cannot talk its way around.

Permissions must be scoped to the task context, not just the agent. A support agent handling customer X conversation should hold credentials that can touch customer X records — so a prompt injection that hijacks the agent hits a wall at the authorization layer. Per-task or per-session scoped credentials are more operational work than one god-mode service account, and they are the difference between an injection that is embarrassing and one that is a breach. This connects directly to memory and retrieval: anything the agent reads can attempt to steer it, so the write surface must assume the read surface is compromised.

Human-in-the-loop approval is a tier, not a philosophy. It belongs on the narrow set of actions that are irreversible and high-value — and it must be engineered to be usable (clear summary of the proposed action, diff or amount, one-click approve) or operators will rubber-stamp, at which point the control is theatre. If everything requires approval, nothing does.

Audit everything, structurally. Every tool call with its arguments, its tier, its policy decision, and its outcome, keyed to the run and the principal. When something goes wrong — it will — the audit trail is how you distinguish model error from injection from design flaw, and it is also the evidence your security review and your customers will ask for.

Securing agents end to end: injection to sandboxing

TierExamplesGateApproval latency cost
Tier 0 — read-onlySearch, fetch records, report dataNone beyond authNone
Tier 1 — reversible mutationsDraft a message, stage a config changePolicy checks, scoped credentialsNone
Tier 2 — bounded irreversibleRefund under a limit, send to a known contactConfirmer step + hard capsOne round-trip
Tier 3 — high-blast-radiusPayments above limits, deletions, external announcementsHuman approval with a usable UIHuman-time — use sparingly
Tier 4 — administrativeCredential rotation, permission changesNot available to the agent at allOut of band

⏱️Rate limiting and the loop problem

Agents discover rate limits the hard way: a tool call fails, the model retries immediately, the failure feeds the loop, and a confused agent can generate a self-inflicted denial-of-service against your own infrastructure or a third-party quota in seconds. Tool-level rate limiting is therefore not just about protecting downstream systems; it is about making the failure mode survivable. Limits should return retryable errors with explicit timing — information the model can plan around rather than a wall it walks into repeatedly.

Budget the loop, not just the tool. Every agent run should carry hard caps: maximum steps, maximum wall-clock, maximum tokens, and maximum spend — enforced by the orchestrator, with a defined terminal behaviour (return partial results with an explicit incomplete status) when a cap fires. An agent without step caps is an infinite loop with a billing meter attached, and every production team has the incident story to prove it.

Third-party quota strategy deserves design thought. When the agent uses external APIs with their own limits, the wrapper should implement queuing and backoff in the tool layer, so the model sees latency rather than failures. Where quotas are scarce, surface them in tool results ("3 of 50 daily lookups remaining") so the model can ration — models respond surprisingly well to explicit scarcity signals.

🧪Mocking tools for evals

You cannot evaluate an agent against live tools: the world changes under the eval, side effects are unacceptable, and third-party flakiness gets scored as agent failure. Every eval harness needs a tool virtualization layer — recorded or simulated tool implementations behind the same schemas the agent sees in production. If your tools were designed narrow and structured (the earlier sections), this layer is straightforward to build; if your tools are broad and side-effecting, the eval problem is telling you something about the design.

Three mock flavours cover the need. Replays serve recorded responses from real interactions — highest fidelity, best for regression suites, with the maintenance cost that recordings age as APIs evolve. Scripted mocks implement per-scenario behaviour (this customer exists, this order is already refunded) — the workhorse for scenario evals. Fault injectors return calibrated failures — timeouts, validation errors, rate limits — because the behaviours you most need to evaluate are the recovery behaviours, and happy-path mocks cannot surface them.

Score the tool use, not just the outcome. An eval that only checks the final answer credits an agent that got lucky with wrong calls. Grade call sequences against scenario expectations: right tools, valid arguments, no policy-violating attempts, recovery behaviour after injected faults. Tool-call-level scoring is also what makes evals diagnostic — "fails 40 percent of refund scenarios by skipping the eligibility check" is actionable; "score dropped" is not.

The full agent eval methodology

Replay mocks

Recorded real responses for regression suites; refresh them on API change or they become tests of a world that no longer exists.

Scripted scenario mocks

Per-scenario state and behaviour; the backbone of task-level evals.

Fault injectors

Timeouts, validation errors, rate limits on demand — because recovery behaviour is the thing you most need to measure.

Realistic mess

Duplicates, stale fields, truncation, noise. A tidy mock environment evaluates an agent production will never meet.

📊Honest failure rates and what to do with them

You should assume your tool-calling layer fails regularly and design the system so that regular failure is survivable. Wrong tool selection, malformed arguments, hallucinated parameter values, and premature give-ups after recoverable errors are all routine events in production traces, for every model, as of writing. Anyone selling you an agent architecture that does not discuss its failure rate per call and its recovery rate per failure is selling you the demo.

Measure four numbers on your own traces: per-call argument-validity rate, tool-selection accuracy on multi-tool tasks, recovery rate after first failure (does call two fix it), and end-to-end task success. The first three are leading indicators you can improve with the patterns above; the fourth is the number your users feel. Improve error contracts and watch recovery rate move. Narrow the schemas and watch argument validity move. The measurements tell you which pattern to apply next — which is why the instrumentation comes before the cleverness.

Know also when the answer is less agency. If a workflow tool-calling reliability cannot reach the bar the business needs, the correct engineering decision is often to demote the flow from agent-chosen tools to a deterministic chain with model steps inside it. Reliability you cannot achieve with autonomy you do not strictly need is a bad trade, and making it deliberately is the mark of a team that will still be running the system next year.

How to build an MCP server with well-designed toolsTalk to us about production agent reliability

FAQ

Frequently Asked
Questions.

Common questions on ai agents, answered by the Codazz engineering team.

Ask Us Anything

Narrow, single-purpose tools with names that read like user intents; closed value spaces as enums rather than free strings; few required parameters; typed formats and units stated in every parameter description; and safety checks baked into the implementation rather than the prompt. Broad generic tools (one endpoint with an action field) maximize the surface on which the model can be wrong. Keep the catalogue small enough for reliable selection — a couple of dozen tools is where you add a routing step rather than more tools.

Issue an idempotency key per logical operation from the deterministic orchestrator layer — never from the model, which will generate a fresh key on retry and defeat the mechanism. The tool stores results keyed by it and replays the stored result on repeat execution. For side effects you do not control (email, third-party APIs), use a transactional outbox: record intent, execute asynchronously with dedupe, let the agent read status. Pair mutations with check tools so the agent can verify state instead of guessing.

As structured instructions, not status codes. Every error states what failed, which recoverability class it is (retryable as-is with timing, retryable with specific changes, not retryable with this tool, or a policy red line), and exactly what to change. Keep payloads small — code, class, message, correction hint, call ID for your logs. Run-level reliability usually improves more from fixing error contracts than from upgrading the model, because most task failures are recoverable errors presented in an unrecoverable form.

Tier by blast radius and enforce in code: read-only tools by default; reversible mutations behind policy checks; bounded irreversible actions behind a confirmer step and hard caps; high-blast-radius actions behind a genuinely usable human approval; administrative actions out of agent reach entirely. Scope credentials per task or session so a hijacked agent hits an authorization wall, and audit every call with arguments, tier, policy decision, and outcome.

Virtualize the tools behind the same production schemas: replays of recorded responses for regression suites, scripted scenario mocks for task-level evals, and fault injectors for timeouts, validation errors, and rate limits — because recovery behaviour is what most needs measuring. Seed mocks with realistic mess (duplicates, stale fields, truncation) or you evaluate an agent production will never meet. Score the call sequence, not just the final answer: right tools, valid arguments, no policy violations, correct recovery after injected faults.

Treat single-call reliability in the high ninety percents on simple, well-designed tools as a good case — and verify current model behaviour on your own traces rather than trusting any published number, because figures move with every release. The governing math is compounding: at 95 percent per-call reliability, a ten-call task succeeds roughly 60 percent of the time when failures are fatal, which is why the design target is a high recovery rate per failure, not a perfect first-call rate.

When the flow is knowable in advance and the reliability bar exceeds what agent-chosen tools can hit. A deterministic chain with model steps inside it is strictly more reliable than an agent loop re-deciding each step. Autonomy buys flexibility for genuinely variable inputs; paying its reliability and cost price for a flow you could have written as a flowchart is the most common over-engineering mistake in current agent systems.

Need agents whose tool calls you can trust?

We build production agent systems with the tool layer done properly — schemas, idempotency, permission tiers, evals with fault injection. 500+ projects since 2018. Call +1 (403) 604-8692 or send us the workflow you need automated.

Get a Free Quote

Tell us about your project

Or talk to an engineer