Design tools for the model you have, not the intern you imagine
Agents do not read your mind, they read your tool descriptions. Most agent failures I debug are tool design failures with extra steps.
cat contents.txt
My favorite agent bug of the year: a tool named query with one
parameter named input. The description said, in full, "runs a query".
The agent used it to send SQL, then natural language, then, in one
majestic trace, a JSON object containing an apology. Every one of those
was a reasonable read of the docs, because there were no docs.

The model only knows what the schema and description tell it. That sentence is boring and it decides everything.
rules I stopped breaking
- One job per tool.
search_ordersandrefund_orderbeat an omnipotentorders_apiwith amodeflag every time. Wide tools make the model do API design at runtime, which is how you get apologies as payloads. - Describe when to use it, not just what it does. "Look up a customer by email. Use before any account action" outperforms three paragraphs of parameter prose.
- Error messages are prompts. "invalid input" teaches nothing. "date must be YYYY-MM-DD, got 6/12" gets self-corrected on the next turn without a human touching anything.
- Return less. Ten fields the next step needs, not the 200-field row. Everything you return occupies window and attention (see the context budget rant).
how i test a tool before the model gets it
The exercise that catches most bad tools: hand the schema and description, and nothing else, to a colleague who has never seen your system. Ask them what the tool does, when they would call it, and what they expect back. If a person with general engineering context and zero project context gets it wrong, the model will get it wrong in the same direction, just more confidently and at 2 a.m.
Then run the mean version of the test. Feed the agent tasks that
should NOT use the tool and count how often it reaches for it anyway.
A tool named search gets called for everything shaped like a
question. Rename it search_product_catalog and the false-positive
rate collapses. Names are load-bearing. The model reads them the way
juniors read function names, which is to say as the entire truth.
Numbers from my own harness, because receipts: renaming two tools and rewriting three descriptions took an afternoon and cut wrong-tool calls from 19 percent of traces to 4. No prompt changes, no model upgrade. The cheapest capability gain I have ever shipped.
pagination, the boss fight nobody trains for
Every real API paginates, and agents are comically bad at it unless
the tool holds their hand. Return a cursor in an obvious field with an
obvious name, state in the description that more results exist and
how to get them, and cap the page size server-side because the model
WILL ask for 10,000 rows the moment a task feels urgent. My rule:
the tool returns the first page plus a count, and a separate
get_next_page tool exists so the trace shows the decision to
continue. Making pagination a visible choice instead of a parameter
turned three different infinite loops into two-call sequences.
destructive tools get seatbelts, not warnings
Any tool that deletes, sends, pays or publishes gets three built-in behaviors in my codebase, regardless of what the description says. It requires an explicit confirmation parameter that the model must set, so the trace shows intent instead of accident. It supports a dry-run mode that returns exactly what WOULD happen, because agents plan noticeably better when they can rehearse. And it returns a receipt with an undo handle when undo is possible, because the next turn's model is a different execution that has never seen this one's regrets. None of this is exotic. It is the same design you would want in a CLI handed to a brilliant contractor on their first day, which is the correct mental model for every tool you ship.
The confirmation parameter sounds like theater until you read traces.
Mine show the model filling confirm: true with a one-line
justification about 90 percent of the time, and in the other 10
percent, stopping to re-check something first. That pause is the
product. You cannot prompt your way into it; the schema has to ask.
the spec is catching up to the folklore
The July MCP release formalizes a bunch of this: tasks for long-running work instead of tools that block, and a stateless core that punishes tools hoarding session state. The protocol is quietly encoding the same lesson every agent team learned the hard way: the model is a competent operator with zero context. Write the runbook.
And keep the runbook versioned next to the code, because tool descriptions rot faster than any other prose in the repo. The API grows a field, the description does not mention it, and three weeks later you are debugging why the agent never uses the thing you added. My CI now fails if a tool's schema changes without its description changing in the same commit. Crude, annoying, and it has caught every single instance since.