Tool Use Is a Contract Problem, Not a Prompting Problem

Pull-quote: “If your agent misuses a tool, read the tool’s schema before you rewrite the prompt. Nine times out of ten, the schema is the bug.”
Why this matters
When an agent misuses a tool, the reflex is to prompt harder: add examples, add warnings, add capital letters. This treats a systems problem as a persuasion problem. A tool call is an API call made by a client that happens to be a model, and it fails for the same reasons API integrations have always failed: ambiguous contracts, unsafe retries, and errors the caller cannot act on. The durable fix is the same one distributed systems settled on decades ago: design the contract.
The schema is the contract
A tool schema is not documentation for the model; it is the interface the model programs against. Every field is a decision the model no longer has to guess.
| Contract property | Weak tool | Strong tool |
|---|---|---|
| Parameters | query: string |
Typed fields, enums for closed sets, defaults stated |
| Ambiguity | “Search for things” | One tool, one job; overlapping tools removed |
| Output | Unbounded prose or raw dumps | Structured, bounded, paginated |
| Errors | Stack trace or generic 500 | Typed error + what to change before retrying |
| Side effects | Undocumented writes | Explicitly declared read-only vs mutating |
The single highest-leverage change on most tool sets is splitting vague multipurpose tools into narrow ones. A model choosing among five precise tools makes fewer mistakes than a model guessing the mode flags of one omnibus tool, for the same reason human developers prefer small, honest interfaces.
Idempotency and the retry problem
Agents retry. They retry on timeouts, on ambiguous results, and sometimes on nothing at all. If a mutating tool is not idempotent, a retry is a double-write: two tickets filed, two orders placed, two emails sent. The contract fix is standard engineering: accept an idempotency key so a repeated call with the same key is a no-op, make operations upserts where the domain allows, and separate the propose step from the commit step for anything irreversible. The loop can then retry proposals freely, and the commit happens exactly once behind a gate.
Error surfaces the model can recover from
Tool result taxonomy What the agent can do
──────────────────── ─────────────────────
OK + data ────► Continue the plan
OK + empty ────► Broaden or re-scope the query
INVALID_ARGUMENT (field, ────► Fix that field, retry once
reason, example)
NOT_FOUND (which id) ────► Re-resolve the reference
RATE_LIMITED (retry_after)────► Wait, then retry
INTERNAL (opaque) ────► Stop. Escalate. Never guess.
A recoverable error names the field, states the constraint, and ideally shows a valid example. An unrecoverable error should say so explicitly, because the worst outcome is not a failed call; it is a model that fabricates a workaround for an error it cannot parse. Design the error surface as carefully as the happy path, since the agent will spend real time in it.
The same principle explains why mature systems compute every number in their outputs with code rather than letting the model write figures from context: the tool contract guarantees the arithmetic, and the model’s job narrows to orchestration, where it is strong.
Why contracts beat prompts
Prompts and contracts fail differently. A prompt instruction competes with everything else in context and degrades as the context grows; a schema constraint is enforced at the boundary on every single call. Prompt fixes are also unfalsifiable: you cannot test “be careful with dates” the way you can test that a tool rejects a malformed date with a typed error. Contracts turn tool reliability into ordinary software engineering, with unit tests, versioning, and reviews.
Closing
Treat every tool as a public API whose consumer is a fast, literal-minded client that will retry, mis-nest, and probe the edges of the schema. Type the parameters, declare the side effects, make mutations idempotent, and return errors the caller can act on. Do that, and the prompt shrinks to a sentence, because the contract already carries the intent.
