Least Privilege at the Tool Layer is the practice of writing each tool so that the worst thing it can do is acceptable. The tool definition is a permission grant expressed in code, it is enforced by your own function rather than by the model's cooperation, and it is the cheapest control in the book because you own both sides of the interface.
Key takeaways
- A tool signature is a permission grant. Whatever the function can do, a compromised agent can do, and no wording in the description changes that.
- OWASP names excessive functionality, excessive permissions and excessive autonomy as the three root causes of excessive agency. This chapter fixes the first, and it is the one under your direct control.
- Build tools that fit the task rather than tools that wrap the resource. A tool that takes free-form SQL or a shell string has granted everything the credential can reach.
- Constrain parameters at the boundary, since an identifier the model composes is attacker-influenceable and an identifier derived from the authenticated session is not.
- Classify every tool as read, reversible write or irreversible action before granting it, and keep an inventory that a person reviews when it changes.
Read this after Chapter 3, which established that the agent may be following somebody else's instructions, and beside Chapter 6, which enforces the same reduction again at the destination system. Chapter 2's map is the input, since the tool list on it is the agenda for this chapter.
A team ships an agent with one tool. It is called run_query, it accepts a SQL string, and it exists because it was the fastest way to answer the widest range of questions.
The demo is spectacular. Any question about the business gets an answer, and the agent composes joins nobody expected it to find.
Then somebody asks what happens if the agent decides to write instead of read. The credential permits it. The tool permits it. The only thing that ever prevented it was a sentence in the prompt asking politely.
The tool signature is the permission grant
Read a tool definition as a security document and it becomes much easier to evaluate.
Whatever the function body is capable of, the agent is capable of, and the description text is irrelevant to that. A tool documented as retrieve customer email for support replies, whose implementation selects any column from any row given an identifier, has granted read access to the whole table. The description is a hint to a model. The implementation is the grant.
This reframing is useful because it moves the review to familiar ground. Nobody would approve an internal HTTP endpoint that accepted arbitrary SQL from an unauthenticated caller. That is what a broad tool is, once Chapter 3's assumption is taken seriously, because the caller is a component that a stranger may be instructing.
So the question for each tool stops being whether the agent needs it and becomes what an adversary would do with it. Those have different answers surprisingly often, and the gap between them is the excess to remove.
Excessive functionality, and why it is the cheapest fix
OWASP's 2025 excessive agency entry names three root causes, and separating them is what makes the problem tractable rather than overwhelming.
Excessive functionality means the agent has access to capabilities beyond what the task requires. Excessive permissions means its credentials reach further than needed on downstream systems. Excessive autonomy means high-impact operations proceed without independent verification.
The first is the one you fix by editing your own code this afternoon. The second needs identity work and cooperation from whoever owns the downstream system, which is Chapters 5 and 6. The third needs a product decision about approvals, which usually needs somebody more senior than the engineer who noticed the problem.
Start with functionality for that reason alone. The entry's own mitigation list opens the same way, recommending that extensions be minimised to the minimum necessary and that open-ended ones be avoided in favour of granular ones.
There is a second reason to start here. Reducing the tool surface shrinks the argument for every later control, since a tool that does not exist needs no scope, no approval and no audit trail.
Build for the task, not for the resource
The single most common design error is wrapping the resource, and it is committed with the best intentions.
Wrapping the resource means one tool per system: a database tool, a filesystem tool, an HTTP tool, an email tool. It feels clean and general, it minimises the number of definitions, and it hands the agent the full capability of each system.
Building for the task means one tool per operation the product actually performs. Instead of a database tool there is get_order_status, list_open_tickets_for_user and search_knowledge_base. Each has a fixed query behind it, a fixed set of columns, and a scope that comes from the session rather than from the model.
The objection is that this produces more tools, and it does. The count is not the cost that matters. Twenty narrow tools whose worst case is individually acceptable are safer and easier to review than one general tool whose worst case is the whole system, and they document the agent's intended job in a way that a general tool never can.
There is a pleasant side effect. Narrow tools make the agent more reliable rather than less, because the model no longer has to invent correct SQL under pressure, which is a theme the reliability argument in Agents That Finish develops at length.
Refuse the open-ended three
Three tool shapes account for most of the serious blast radius, and each is worth refusing by default rather than reviewing case by case.
A tool that executes arbitrary shell commands grants everything the process can do, which includes reading its own credentials from the environment. A tool that executes arbitrary SQL grants everything the connection can do, and connection strings are rarely scoped to one table. A tool that fetches an arbitrary URL grants outbound network access, which is an exfiltration channel wearing a research capability as a disguise, and Chapter 8 is about that specifically.
Each has a legitimate version. Shell access can be a fixed set of commands with validated arguments. Database access can be a set of named queries with typed parameters. URL fetching can be an allowlist of domains resolved before the request leaves.
The pattern in all three is the same. Move the open-ended part from runtime, where the model chooses, to build time, where a person chose and a reviewer approved. If the agent genuinely needs unrestricted execution to do its job, that is not a tool problem, it is the sandboxing problem in Chapter 7.
Parameters are part of the surface
Narrowing the tool is half of the work. The other half is deciding where its arguments come from, and that is where otherwise careful designs leak.
Consider list_invoices, which takes a customer identifier. It is narrow, it reads one kind of record, and it looks disciplined. If the model supplies the identifier, the tool will happily list any customer's invoices, because the model is the component under attack and the identifier is a free parameter.
The fix is to derive whatever can be derived from the authenticated context rather than accepting it from the model. The customer identifier comes from the session. The tenant comes from the request. What the model supplies is the part that is genuinely a choice, such as a date range or a search term, and even those get types, bounds and length limits.
OWASP's mitigation list makes the same point in a different vocabulary, recommending that extensions execute in the user's context with minimum privileges. Deriving the scope from the session rather than the prompt is what that looks like in a tool signature.
The rule generalises. Every parameter the model controls is attacker-influenceable, so the design question for each one is what an adversary would put there.
Classify by reversibility before you grant
Not all tools deserve the same scrutiny, and treating them uniformly wastes review effort in the wrong places.
Three classes are enough. Reads, which disclose information and cannot damage state. Reversible writes, which change something that can be undone, such as a draft, a non-final status or a record with soft deletion. Irreversible actions, which move money, send messages to people outside the organisation, delete data or change permissions.
The classes get different treatment. Reads need scoping and rate limits, because disclosure at volume is its own harm. Reversible writes need an audit trail and a way to undo them in bulk, since one confused agent will make a hundred of them before anybody notices. Irreversible actions need the approval step OWASP recommends for high-impact operations, and they need it enforced in the tool rather than in the prompt.
Write the classification down beside each tool in the inventory. Arguments about whether a tool is really irreversible are productive, and they surface assumptions that were never stated, such as whether the email integration has a recall function that works.
The inventory drifts, and drift is the failure mode
Tool surfaces are not designed once. They accumulate, and the accumulation is where a carefully bounded agent becomes an unbounded one.
The pattern is consistent. A tool is added to debug an incident and never removed. A tool gains a parameter that widens its scope. An MCP server is connected for one capability and brings eleven others with it. Nobody made a bad decision, and the surface is now unrecognisable against the map from Chapter 2.
Three habits contain it. Keep a written inventory listing each tool, its class, the credential it uses and its owner, and treat adding one as a change that needs review rather than a commit. Generate the inventory from the running configuration rather than maintaining it by hand, so it cannot silently diverge. Diff it on every deploy and fail loudly when the diff is unexpected.
External tool servers deserve a specific line in that inventory. The Model Context Protocol specification, at version 2025-11-25, requires hosts to obtain explicit user consent before invoking a tool and treats tool annotations from an untrusted server as untrusted input. A connected server can change what it offers between sessions, which makes the inventory a runtime concern rather than a documentation exercise.
The objection: narrow tools cripple the agent
This is the real cost, it is raised by good engineers, and it should be answered honestly rather than waved away.
Something is genuinely lost. A general tool lets the agent answer questions nobody anticipated, which is the capability that makes the demo memorable, and narrowing the surface does remove some of it.
What is gained is worth more in production. A bounded agent can be deployed with real credentials, which an unbounded one cannot be, so the comparison is not narrow against broad. It is narrow and shipped against broad and permanently behind a human approving every action.
There is a middle position that works when exploration has real value. Give the agent broad capability against a copy, a read replica or a synthetic environment where the worst case is a wasted afternoon, and keep the production surface narrow. Exploration and authority are separable, and separating them costs an environment rather than a principle.
When the pressure to widen a tool persists, treat it as evidence about the product rather than about security. A task that genuinely requires unbounded database access is a task that needs a person, or a feature that needs building properly.
Chapter summary
A tool definition is a permission grant expressed in code, so whatever the function can do a compromised agent can do, and the description text is a hint to a model rather than a constraint on it. OWASP's excessive agency entry names three root causes, being excessive functionality, excessive permissions and excessive autonomy, and functionality is the one fixed in your own repository without anyone else's cooperation, which is why its mitigation list opens by recommending that extensions be minimised and open-ended ones avoided. The design error to unlearn is wrapping the resource, since one tool per system hands over the full capability of that system, where one tool per task carries a fixed query, fixed columns and a scope taken from the session. Three shapes deserve refusal by default, being arbitrary shell, arbitrary SQL and arbitrary URL fetching, each of which has a legitimate constrained version that moves the open-ended choice from runtime to build time. Parameters are half the surface, so anything derivable from the authenticated context should be derived rather than accepted from the model, which is what executing in the user's context means in practice. Classify every tool as a read, a reversible write or an irreversible action, since the three need different scrutiny and only the third needs an approval enforced in code. And keep the inventory generated from the running configuration rather than written by hand, because tool surfaces drift through debugging tools nobody removed and external servers that change what they offer.
Narrowing the tool bounds what the agent may ask for. It does nothing about what the credential behind the tool can reach, which is the second root cause and a harder problem. Chapter 5 is One Identity per Agent, which is where the agent stops borrowing the application's service account.
Sources
- LLM06:2025 Excessive AgencyOWASP Gen AI Security Project · 2025 · Standard · verified
- LLM01:2025 Prompt InjectionOWASP Gen AI Security Project · 2025 · Standard · verified
- SpecificationModel Context Protocol, version 2025-11-25 · 2025-11-25 · Standard · verified