Excessive agency is what you get when a tool can do more than the task in front of it ever needed — not because anyone misused it, but because it was built with a wider blast radius than its name suggests. A tool called update_profile that also deletes the account. A “read” tool that happens to accept a write parameter nobody mentioned in the description. The model doesn’t need to be tricked into using that extra reach — it’s just sitting there, available the moment the tool is called for something completely ordinary.
What it looks like in an actual MCP tool
This isn’t hypothetical; it’s a pattern you can spot by reading a tool’s schema, not just its description:
- A
send_messagetool whose input schema includes anactionfield that also accepts"delete_channel"— because the server reuses one generic endpoint internally, and the tool wraps it one-to-one instead of exposing the one capability the task actually needs. - A
search_customerstool that, to keep the implementation simple, is handed a database credential scoped for full read-write access rather than a read-only replica — so the tool is narrow, but what it’s running on top of isn’t. - A single
manage_calendartool covering create, edit, delete, and share, where three out of those four actions are never the one a given prompt is actually asking for.
In every case, nobody had to hide anything. The extra capability is right there in the schema, visible to anyone who reads it — it’s just wider than the task called for, and width is exactly what turns a single bad instruction, a model mistake, or one successful prompt injection into a bigger incident than it needed to be.
Why this isn’t the same as scope creep
It’s easy to file this under OAuth scope creep, but they’re different layers. Scope creep is about the grant: an MCP consent screen that hands a whole server access when the task only needed one corner of it — what OWASP’s MCP Top 10 calls MCP02, privilege escalation via scope creep. Excessive agency is about the tool itself, downstream of whatever scope you already granted. You can have a perfectly scoped OAuth grant — read access to exactly one calendar — and still hand the model a tool that, within that one calendar, can create, edit, delete, and share, when the feature you’re building only ever needed to read. Fixing the grant doesn’t fix the tool.
It’s also not what destructiveHint and the other MCP tool annotations are for. Annotations describe a tool’s risk after the fact — a flag a client can read to decide whether to ask before calling it. They don’t make the tool do less. A tool can carry an honest destructiveHint: true and still be exactly as over-broad as one with no annotation at all; the hint tells you to be careful, it doesn’t narrow what you’re being careful about.
How to spot it before you connect a server
You don’t need the vendor’s source code for this — the input schema and description a server publishes are usually enough:
- Read the actual parameters, not just the tool name. A tool named for a narrow action can still accept an
actionormodefield that opens up other ones. gate’s free MCP security scanner lists every tool a server exposes along with an over-broad-permissions check, specifically so you can see this before you connect, not after. - Count how many of a tool’s stated capabilities your actual use case touches. If a calendar tool does four things and your integration only ever needs one, that gap is the excess agency you’re carrying for no reason.
- Ask what credential the tool runs on, not just what the tool says it does. A read-looking tool backed by a write-capable credential is excessive agency hiding one layer down, and no amount of reading the tool description will surface it — that requires asking the vendor or checking their docs directly.
- Treat it the same way you’d treat any other finding in a pre-connection vetting pass: a reason to look closer, not an automatic disqualifier — plenty of legitimate tools are deliberately broad because splitting them would be worse for the vendor to maintain.
What to do about it if you’re building the server
The fix lives in tool design more than in access control:
- Split by capability, not by object.
delete_calendar_eventandread_calendar_eventas two tools let a client gate one without touching the other. Onemanage_calendar_eventtool with an action field forces an all-or-nothing decision on every client that connects it. - Scope the credential to the tool, not the server. If a tool only ever reads, run it on a credential that can only ever read, independent of what else the server could technically do with a broader one.
- Narrow the schema to what the task needs, not what the underlying API supports. Wrapping an internal endpoint one-to-one is the fastest way to build a tool, and the fastest way to ship excessive agency by accident — see the same trap covered in turning an API into an MCP server.
- Set annotations honestly once the tool itself is narrow — they’re a useful signal on top of a well-scoped tool, not a substitute for scoping it.
The bottom line
Excessive agency doesn’t require an attacker, a bug, or a bad actor — just a tool that was built wider than any one task needs, sitting there the moment a model reaches for it. Scope your OAuth grant and read the annotations, but check the tool’s own schema too: that’s where the extra capability actually lives, and it’s the one layer neither a tighter grant nor an honest hint will narrow for you.