The agent needed to read order status, so the quickest integration won: the team wrapped the help desk's entire API client in one tool named tickets and described it as managing support tickets. Every demo went fine. Then a customer typed "clean up my duplicate requests," and the agent did exactly that, closing four similar tickets, including the one holding the only record of an open billing dispute. The postmortem found no bug: the tool worked as built and the transcript reads like competent work. What failed was a decision nobody remembers making. The day a lookup feature shipped with a tool that manages tickets, the product acquired the authority to close them, and every turn since has carried it whether the job needed it or not.
When your product starts doing things had you inventory the actions behind your feature. This chapter moves that inventory into code.
The action surface is the complete set of tools your agent can call, and it is where the real design happens, because a prompt only asks for behavior while the tool list decides which behavior is possible at all.
The tool list is the risk profile
Anything not on the tool list cannot happen: however badly a run goes and however hostile the input, a capability no tool provides is one the product does not have. Anything on the tool list can happen on a bad day, because runs vary and a tool that exists will eventually get called in a way you did not picture. Put those two facts together and the tool list is the product's risk profile, written in code and reviewable like code.
This is also why you design at the level of the action, not the prompt. A risk review that only reads the system prompt is checking a polite request that the run is free to ignore; the document that actually binds the product is the tool list, and the question to ask of each entry is what the worst plausible sequence of calls could do with it. Every tool you register hands the agent real authority, a key you cut for it.
Define every tool three ways
A tool is fully specified when you can state three things, and the third belongs in the definition itself, not in anyone's memory.
- Capability: what it does. One verb acting on one object, like searching tickets or fetching availability. If the honest sentence needs an "and," you are looking at two tools wearing one name.
- Constraints: on what, how much, how often. Which mailbox, which table, how many rows per call, how many calls per run, what ceiling on any amount. A constraint enforced inside the tool holds under every input; one asked for in the prompt holds only until something in the conversation pushes the other way. Blast radius: bound what one turn can touch turns per-tool limits into a budget for the whole turn.
- Side-effect class: read, write, or irreversible write. A read leaves the world unchanged. A write changes something you can change back. An irreversible write lands in someone's inbox, moves money, or deletes the only copy. The class lives in the tool itself, in its name, metadata, or registry entry, so the harness can route approvals and logging by class instead of from memory.
A scheduling assistant makes the classes concrete. Searching events and fetching availability are reads, drafting an agenda and placing a hold are reversible writes, and sending the invites is a write nobody can unsend. The API marks none of that; a send returns a success code just as a search does, so the class has to live where the system can act on it.
The description is what the agent goes by
The model selects a tool by matching the request against the descriptions and parameter schemas in context, and that text is all it gets about what a tool does. So the agent acts on what the description says, not on what the code actually does, and when the two disagree the resulting bad behavior is one you wrote into the description yourself.
- Vague descriptions invite everything. "Manages support tickets" matches every ticket-like request, destructive ones included, which is how the cold open happened.
- Wrong descriptions hide the real tool. The words claim a search while a parameter quietly purges the archive, and any parameter you expose will eventually get filled in.
- Stale descriptions outlive the code. The implementation moved on and the words did not, so the agent works from a contract that no longer exists.
If your tools arrive over MCP, the Model Context Protocol that appeared in late 2024 and has since become the common wiring between tools and models, none of this is a metaphor. The description ships inside the tool definition and travels with it to every client, so the words are part of the behavioral contract. Treat them like code: description diffs get reviewed, tool selection gets its own cases (inputs where the right call is read_ticket, not close_ticket), and a description edit goes through The regression gate: no change ships blind like any other behavior change.
Least capability by default
The default that keeps the tool list honest is older than AI products: grant the smallest capability that still completes the job, and treat anything broader as a deliberate exception.
- Split broad tools into narrow ones. The
ticketstool from the cold open becomessearch_tickets,read_ticket, andclose_ticket, each with its own class and constraints, each ready for its own rung of the autonomy ladder. A search tool that can also delete is a delete tool, and it has to be reviewed and gated like one. Splitting feels redundant precisely because the model composes narrow tools without trouble; a few extra definitions buy a worst case that matches the job. - Separate read tools from write tools. Most turns in a run gather, check, and decide; only a few commit. Wire the phases so gathering turns hold read tools only and the write key appears for the step that commits. The payoff is more than tidiness, because untrusted content meets your agent on read turns, and a hostile instruction arriving on a turn with no write key has nothing to use. Injection: the input is the attack surface builds on this separation.
The last filter is whether the tool is justified at all: a tool earns its place because the job requires it, not because the client library happened to include it.
Try it now
Set aside about twenty minutes and work on the agent feature you ship or the one you plan, with your action inventory beside you.
Pull the real list. Work from the code, not from memory: list every registered tool and every connected server, with descriptions and parameters verbatim. Point Claude Code at the repository or the MCP configuration and ask for every tool the agent can currently call.
Write the tool table. Give every tool a row that records what it reads, what it writes, whether its writes can be undone, and why it needs to exist. Everything but the last column comes from the implementation; the why is a product decision, so write it yourself, one sentence per row.
Reconcile the descriptions. Any tool whose description claims less than its row shows is a behavior bug waiting for the right request, so fix the words or narrow the tool. While you are in the definitions, push each row's side-effect class into the tool itself.
Delete one tool. Find the tool the job can live without, usually the row whose why column reads like "it came with the integration," and remove it. Rerun your cases. The job should pass unchanged, and your worst case just got smaller.
Chapter Summary
- The action surface is the full set of tools your agent can call, and that set, not the prompt, is where the real design happens.
- The tool list is the product's risk profile: anything not on it cannot happen, and anything on it can happen on a bad day, so review the list like code.
- Specify every tool three ways: what it does, what limits it runs under, and whether it reads, writes, or makes a change you cannot take back.
- Put a constraint inside the tool, not the prompt, because a limit in the code holds under every input while a limit asked for in the prompt can slip.
- Mark each tool's read or write status in the tool itself so the system can route approvals and logging by that status instead of relying on memory.
- The agent acts on the tool's description, so a vague, wrong, or stale description is a bug you wrote, and a description edit ships through the regression gate like any other behavior change.
- Grant the smallest capability that still does the job: split broad tools into narrow ones, keep gathering turns read-only, and make every tool prove the job needs it.
- Next is The autonomy ladder: place every action deliberately, which decides how much of that tool list runs without you, one action at a time.
Sources
- Anthropic, announcement and documentation of the Model Context Protocol (late 2024).
- Vendor announcements and industry reporting on Model Context Protocol adoption across major AI platforms (2025).
- Classic computer-security literature on the principle of least privilege (1970s onward).