Tool Misuse and Excessive Agency¶
Level: Advanced · Time: ~45 min · Exam relevance: CCAR-P D3 (3.1, 3.2), CCAR-P D5 (5.1, 5.3)
Scope: this page covers excessive agency as a system and governance risk. For the per-tool engineering controls, see tool security.
In plain English¶
Agency is what lets an LLM application do things: call APIs, run code, send messages, change records. Excessive agency is giving it more ability than the task needs, so that a mistake, a hallucination or an injected instruction can cause real damage. Tool misuse is the model using a legitimate tool in a harmful way, for example deleting instead of archiving, or querying every customer instead of one.
The fix is the oldest principle in security, least privilege, applied to three things: which tools the agent has (functionality), what each tool can reach (permissions) and how much it can do without a human (autonomy).
Why it matters¶
- OWASP lists Excessive Agency as LLM06:2025, and the 2026 agentic list starts with ASI01 Agent Goal Hijack and ASI02 Tool Misuse, then ASI03 Identity and Privilege Abuse.
- CCAR-P-3.1 asks you to evaluate tool and agent configuration for capability bloat. That is this page.
- Excessive agency multiplies every other risk on this module's list. Prompt injection without tools is an embarrassment; with a payments tool it is a loss.
How it works¶
The three dimensions of excess¶
| Dimension | Excess looks like | Least-agency alternative |
|---|---|---|
| Excessive functionality | A generic run_sql, http_request or shell tool when the task needs two specific queries |
Narrow, task-shaped tools (get_invoice(invoice_id)), separate read and write tools |
| Excessive permissions | Tools run as a service account with admin rights over all tenants | Tools run with the end user's scoped credential, or a dedicated identity per agent with only needed scopes |
| Excessive autonomy | Irreversible actions with no confirmation | Risk-tiered approval, dry-run previews, reversible actions by default |
Capability bloat¶
Agents accumulate tools over time: each team adds "just one more" MCP server. Large tool sets cause three problems:
more attack surface, more chances for the model to pick the wrong tool, and more context spent on tool definitions.
Review tool inventories the way you review IAM policies. For large catalogues that are genuinely needed, Claude's
tool search features let tools load on demand (defer_loading), which reduces context cost but does not reduce
privilege. Privilege review still applies to every tool that can be loaded.
Put a policy enforcement point outside the model¶
Every tool call should pass through a deterministic policy enforcement point (PEP) in your code before it executes:
sequenceDiagram
participant M as Claude
participant L as Agent loop
participant P as Policy enforcement point
participant H as Human approver
participant T as Tool / API
M->>L: tool_use {name, input}
L->>P: check(user, agent, tool, input)
alt denied
P-->>L: deny + reason
L-->>M: tool_result is_error=true
else needs approval
P->>H: show exact action and diff
H-->>P: approve / reject
P-->>L: decision
else allowed
P->>T: execute with scoped credential
T-->>L: result
L-->>M: tool_result
end
The PEP checks: Is this tool allowed for this agent? Is this input within bounds (schema, ranges, allow-listed identifiers)? Does the user on whose behalf the agent acts have the right to do this? What is the risk tier, and does it need approval? Has a rate or budget limit been reached?
Risk-tier your tools¶
| Tier | Examples | Default policy |
|---|---|---|
| 0 Read, own scope | Read the user's own tickets | Allow, log |
| 1 Read, broad or sensitive | Search all customer records | Allow with filters, log, alert on volume |
| 2 Reversible write | Draft an email, create a ticket, add a label | Allow with limits, log, reversible |
| 3 Irreversible or external | Send email, issue refund, merge code, delete data | Human approval showing exact parameters |
| 4 Prohibited | Change permissions, disable logging, transfer funds | Not exposed as a tool at all |
Strict schemas reduce misuse¶
Use JSON Schema with strict: true on tool definitions so the arguments Claude sends always validate against your
schema. Use enums and patterns to constrain identifiers, and set additionalProperties: false. Validation is still
your job at the PEP: a schema-valid customer_id may belong to a different tenant.
Agent-level guardrails¶
- Iteration and budget caps: maximum tool calls per task, token budgets, wall-clock timeouts (also limits OWASP LLM10 Unbounded Consumption).
- Sandboxing: run code-execution and shell tools in isolated containers or VMs with no ambient credentials and restricted network. Claude Code offers a sandboxed bash tool with filesystem and network isolation; see Claude Code permissions and settings.
- Kill switch: the ability to disable a tool or an agent across the fleet immediately.
Example¶
A minimal PEP for a support agent. It is pure Python and is called from your agent loop before any tool runs.
"""pep.py - deterministic policy enforcement for tool calls."""
from dataclasses import dataclass, field
@dataclass
class Context:
user_id: str
tenant_id: str
user_scopes: set[str]
calls_so_far: int = 0
refunds_today_cents: int = 0
approvals: set[str] = field(default_factory=set) # tool_use ids a human approved
POLICY = {
"get_order": {"tier": 0, "scope": "orders:read"},
"search_orders": {"tier": 1, "scope": "orders:read", "max_results": 20},
"create_ticket": {"tier": 2, "scope": "tickets:write"},
"issue_refund": {"tier": 3, "scope": "refunds:write", "max_cents": 20_000, "daily_cents": 50_000},
}
MAX_CALLS = 12
class Denied(Exception):
pass
class NeedsApproval(Exception):
pass
def check(ctx: Context, tool_use_id: str, name: str, args: dict, owner_of) -> None:
rule = POLICY.get(name)
if rule is None:
raise Denied(f"tool {name!r} is not enabled for this agent")
if ctx.calls_so_far >= MAX_CALLS:
raise Denied("tool-call budget exhausted for this task")
if rule["scope"] not in ctx.user_scopes:
raise Denied(f"user lacks scope {rule['scope']}")
if "order_id" in args and owner_of(args["order_id"]) != ctx.tenant_id:
raise Denied("order belongs to another tenant")
if name == "search_orders" and args.get("limit", 0) > rule["max_results"]:
raise Denied("search limit too large")
if name == "issue_refund":
amount = int(args["amount_cents"])
if amount > rule["max_cents"] or ctx.refunds_today_cents + amount > rule["daily_cents"]:
raise Denied("refund exceeds per-call or daily limit")
if rule["tier"] >= 3 and tool_use_id not in ctx.approvals:
raise NeedsApproval(f"{name} requires human approval: {args}")
ctx.calls_so_far += 1
In the agent loop, map Denied to a tool_result with is_error: true and a short reason (so Claude can adjust),
and map NeedsApproval to your approval workflow. Never let the model see or set ctx.approvals.
Enterprise use case¶
Northwind Lending reviews an internal operations agent before production. The inventory shows 41 tools from five
MCP servers, including execute_sql, send_email and update_customer. The agent's job is to answer questions
about delinquent accounts and draft outreach.
Review outcome:
- 33 tools removed as unused in two weeks of pilot logs.
execute_sqlreplaced with three parameterised read tools.send_emailreplaced withcreate_email_draft; a human sends from their own client.update_customerremoved; account changes stay in the core banking UI.- All remaining tools run with the operator's own delegated token through the PEP.
Tool-selection accuracy on the team's eval set improves because the model has fewer, clearer choices. Lab 20 walks through a similar review.
Common mistakes¶
- Generic "power tools" (SQL, HTTP, shell) exposed because they are quick to build.
- Enforcing limits in the tool description ("never refund more than 200") rather than in code.
- One service account shared by every agent and every user.
- Approval prompts that show the model's summary of the action instead of the exact parameters.
- No budget or iteration cap, so a confused agent loops for hours.
Security considerations¶
- Error messages returned to the model should be useful but not leak internals (stack traces, table names).
- A denied call is a security event. Alert on spikes, which may indicate an injection attempt.
- Approval fatigue is real: if humans approve everything, tier 3 is effectively tier 2. See human oversight.
- For multi-agent systems, each agent gets its own policy; an orchestrator must not be able to grant a sub-agent more than it has itself (see agent identity and permissions).
Troubleshooting¶
| Symptom | Likely cause | Fix |
|---|---|---|
| Agent retries a denied call repeatedly | Error message unclear | Return a concise reason and an allowed alternative |
| Model picks the wrong tool | Too many similar tools, vague descriptions | Consolidate tools, sharpen descriptions, test tool selection in evals |
| Approvals pile up | Too many actions in tier 3 | Make actions reversible so they can move to tier 2; batch approvals |
| Users bypass the agent for blocked tasks | Policy too strict for the real workflow | Revisit the task design with the business owner |
Check your understanding¶
- Name the three dimensions of excessive agency and one control for each.
- Why does
defer_loadingnot solve a privilege problem? - Where should the rule "refunds cannot exceed 200" be enforced, and why?
- What should the agent loop return to Claude when the PEP denies a call?
Answers
- Functionality (narrow task-shaped tools), permissions (user-scoped or per-agent least-privilege credentials), autonomy (risk-tiered human approval and reversible actions).
- It controls when a tool's definition enters context, not what the tool is allowed to do once called.
- In deterministic code at the policy enforcement point or in the downstream API, because the model can be manipulated or mistaken and a prompt rule is not enforcement.
- A
tool_resultwithis_error: trueand a short, non-sensitive reason, so the model can adjust or explain to the user.
Further reading¶
- OWASP: Top 10 for LLM Applications 2025 (LLM06 Excessive Agency)
- OWASP: Top 10 for Agentic Applications 2026
- Anthropic: Tool use overview
- Anthropic: Claude Code security
- In this repository: tool design best practices, tool security, agent reliability