· · 4 min · agents · by machine, explained

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.

Diagram contrasting a wide kitchen-sink tool with small purpose-built tools and clear errors

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_orders and refund_order beat an omnipotent orders_api with a mode flag 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.

tags: #agents #tools #mcp