Tool Design
Tools are the model's user interface to the world, and the model sees only the surface: name, description, parameters, and return values — never the implementation. So tool design is interface design for an alien reader. The craft: write descriptions like onboarding docs for someone with zero shared context, prefer a few powerful tools over many overlapping ones, return results that are concise and errors that teach the next move, and gate capability by risk — reads are free, writes are guarded, irreversible actions need approval. MCP standardizes how tools are packaged and shared, but a confusing tool is confusing over any protocol. Tool definitions are prompts; test them like prompts.
Prerequisites: The Agent Loop. Feeds problems: The tool interface gap, Prompt injection and untrusted content.
Practitioner
A tool, to the model, is four strings: a name, a description, a parameter schema, and whatever comes back when it’s called. Your implementation — however elegant — is invisible. Every design decision below follows from taking that seriously.
Start with the contrast that teaches most of the craft. A tool as an engineer first writes it:
{ "name": "search", "description": "Searches the index.",
"parameters": { "q": "string", "n": "int", "mode": "string" } }
The same tool designed for its actual reader:
{
"name": "search_customer_orders",
"description": "Full-text search over customer order history. Returns the 10 best-matching orders as one-line summaries with order IDs. Use get_order_details with an ID for the full record. Matches product names, notes, and status — not payment data.",
"parameters": {
"query": "Search terms, e.g. 'refunded blender November'. Broader terms return more results."
}
}
Everything the second version adds — scope, return shape, the follow-up tool, what’s not covered, an example argument — is information the model cannot get anywhere else. Write every description as onboarding for a smart colleague with zero shared context: what it does, when to reach for it, what comes back, and what to do next. If you find yourself explaining a tool’s quirks in the system prompt, that text belongs in the tool description instead — it’s cheaper to maintain and travels with the tool.
Consolidate. Ten narrow tools (search_by_name, search_by_date, search_by_status…)
force the model to make a choice it can only guess at, and every wrong guess is a wasted turn.
One search with a clear query parameter removes the guess. The heuristic: if two tools’
descriptions need a sentence explaining when to use one versus the other, they probably want to
be one tool. Aim for the smallest set of orthogonal, powerful tools that covers the job — for a
coding agent, that’s roughly read, search, edit, run; not forty.
Design the return, not just the call. Results land in the context window and pay rent there every turn afterward, so return the distillate: IDs and one-line summaries with a way to fetch detail, not the full dump. Paginate anything unbounded. And treat error messages as the highest- leverage strings you own — the model can only recover from what it can see. “Error 400” is a dead end; “date must be YYYY-MM-DD, got ‘11/3/2025’” gets self-corrected on the next turn. Write every error to answer: what happened, and what should the caller try instead?
Gate by risk. Sort every tool into three buckets before shipping: safe to call freely (reads, searches), guarded (writes — reversible, logged, sandboxed where possible), and gated (irreversible or outward-facing — sending email, deleting data, spending money — which require human approval or don’t exist). This is your least-privilege story and most of your prompt- injection story in one move: an injected instruction can only do what the tool surface allows. Scope aggressively — the file tool gets the project directory, not the disk; the email tool gets drafts, not send. Capability you didn’t grant is capability you don’t have to defend.
Package with MCP when sharing. Model Context Protocol is the standard way to ship tools so any agent can connect to them — worth adopting the moment a tool has a second consumer. Just keep the layers straight: MCP solves distribution and discovery; the design problems above are yours either way.
Then test like it’s a prompt — because it is. Tool descriptions change model behavior the way prompts do, so changes go through evals: run the task set, compare tool-selection accuracy and wasted-turn counts, keep what wins. Practitioners routinely find that rewording one description moves task success more than swapping models.
Expert pointers
Watch the “code as universal tool” line of work: instead of many bespoke tools, give a strong model a sandboxed code interpreter and let it write what it needs — it collapses tool-selection errors into a domain the model is best at, at the cost of a bigger sandbox to secure. Also live: programmatic tool calling (models orchestrating tools in generated code rather than one round-trip per call), tool-use fine-tuning, and the MCP ecosystem’s growing pains around security review of third-party servers — connecting a stranger’s tool server is running a stranger’s code.
Misconceptions
- “More tools, more capability.” Each added tool grows the decision surface and the overlap ambiguity. Capability comes from a few powerful tools with crisp boundaries.
- “The model will figure out my API.” It sees four strings. Anything not in the name, description, schema, or return value does not exist for it.
- “Hide errors from the model; retry in the harness.” Semantic errors (bad path, malformed query) are exactly what the model can fix — if it sees them. Reserve silent retries for mechanical transients.
Check yourself
- An agent keeps calling
get_userwhen it needsget_user_profile. Diagnose this as an interface problem — give the two most likely fixes without touching the model or system prompt. - Your database tool returns full rows — 4,000 tokens each. What breaks as trajectories get long, and what does the redesigned return look like?
- Sort these into free / guarded / gated and justify: search the wiki, update a wiki page, email a customer, run generated code in a sandbox, run generated code on the host.
- Why does least-privilege tooling mitigate prompt injection even though it does nothing to stop the injection itself?
Apply it
Take the two tools from your Agent Loop agent and make them hostile-reader-proof: rewrite both descriptions to cover scope, return shape, and next steps; make every error message name the problem and the fix. Then run the same task five times with the old definitions and five with the new, and count wasted turns (calls that a better description would have prevented). Keep the before/after — it’s a ready-made fragment for a capstone review or prototype.