Design tools for agents, not for people

The tools you give an agent shape its behavior as much as the prompt does. Seven habits for tools a model can use reliably.

evanbyte3 min read

When an agent misbehaves, the prompt gets blamed first. Often the tools are the problem. A tool is an interface whose user is a model: it reads the name and description, guesses the arguments, and acts on whatever comes back. Interfaces built for human developers don't always survive that.

Seven habits

1. Fewer, sharper tools

Ten overlapping tools force the model to choose between near-identical options. Prefer a small set where each tool has one clear job, and merge the ones that always run together.

2. Names and descriptions are prompts

The model decides what to call from the name and description alone. Say what the tool does, when to use it, and when not to. 'Look up one order by ID. For searching by customer, use orders.search' beats 'orders'.

3. Strict, boring schemas

Use typed arguments, enums instead of free text, and explicit units. Reject bad input with a clear message instead of guessing what the model meant.

4. Errors that say what to do next

'Error 400' teaches nothing. 'No order A-229. IDs look like A-2291. Try orders.search with the customer's email' lets the agent recover on its own.

A tool definition, sketched in TypeScript
const getOrder = {
  name: "orders.get",
  description:
    "Look up one order by its ID (format A-1234). " +
    "To find an order by customer email, use orders.search instead.",
  input: {
    order_id: { type: "string", pattern: "^A-\\d{4}$" },
  },
  // Errors are written for the model, not for a log file.
  onNotFound: (id: string) =>
    `No order ${id}. IDs look like A-2291. Try orders.search with the customer's email.`,
};

5. Keep reads and writes apart

Make read tools freely available and put write tools behind policy. Name them so the difference is obvious, and give every write a dry-run option so the agent, and an approver, can see the effect before it happens.

6. Make writes safe to repeat

Agents retry, and networks fail. A write that charges a card twice when called twice is a bug waiting for a bad day. Accept an idempotency key and return the original result on repeats.

7. Return less

A tool that returns forty pages of JSON burns the context window and distracts the model. Return what the next step needs, paginate the rest, and include identifiers so the agent can ask for more.

A redesign, step by step

A common starting point is one tool that does everything: a query tool that accepts raw SQL. It's flexible, and it's a trap. The model has to write correct queries against a schema it can't see, and one bad call can read or change almost anything.

Before: one broad tool
crm.query({ sql: string })

Three narrow tools usually serve better. Each one is easier to describe, to test, and to permission.

After: three narrow tools
crm.find_account({ email?: string, name?: string })
  // read · returns up to 5 matches

crm.get_account({ account_id: string })
  // read · returns the fields the agent needs

crm.log_note({ account_id: string, note: string, dry_run?: boolean })
  // write · idempotent · needs approval

The agent loses nothing it needs, and the guardrails come for free: reads are always allowed, and the one write is easy to gate.

What to return

Think of a tool result as the agent's working memory. Return identifiers and the fields the next step needs, not the whole record. When there's more, say so, and tell the agent how to ask for it.

A result that guides the next step
{
  "matches": [
    { "account_id": "acc_3141", "name": "Acme Supply", "owner": "Priya" }
  ],
  "total": 12,
  "shown": 1,
  "hint": "12 matches. Narrow the search with an email address, or call crm.get_account for details."
}

Anti-patterns to avoid

  • Tools that accept free-form query languages or shell commands.
  • Descriptions that explain how a tool works instead of when to use it.
  • Silent failures that return an empty list when something actually went wrong.
  • Write tools with no way to preview or undo the change.
  • Results whose shape changes from one call to the next.

Test the tools on their own

Before an agent uses a tool, test it like any other API: valid input, invalid input, timeouts, and duplicates. Then include tool-use cases in your evals, so you notice when a change to a description sends the model down a different path. Descriptions are code that the model reads, and they deserve the same review as any other change.

Keep reading

  • Agents

    Write the evals first

    An agent you can't measure is an agent you can't improve. How to build a small, honest test set before you write the first prompt.

    4 min readRead

Tell us what you're building

A few lines are enough. We'll reply by email to set up a call.

What do you need?

Your message goes directly to our team. Privacy Policy.