IAM-05 · Identity & Access

Capability-Based Security for Agent Tools: Enforcing Least Privilege with Scoped, Per-Tool Tokens and Object Capabilities

How capability-based security, object capabilities, and scoped per-tool tokens let you enforce least privilege for AI agents, so a compromised tool cannot reach what it was never handed.

Agent identity flow: an agent authenticates to an issuer, receives a short-lived scoped token, and presents it to a resource that verifies scope before actingagentnon-human idtoken issuerOAuth / mTLSresourceverifies scopeauthenticatescoped tokenleast privilege: the token grants only this task, expires fast, and is bound to the agent's identity

An AI agent is only as safe as the authority you hand its tools. Give a single tool a broad API key and you have given the whole agent that key, including any prompt-injected instruction that hijacks it. Capability-based security flips that default: each tool holds an unforgeable, narrowly scoped token that grants exactly one ability and nothing more. This article explains what that means in practice, how object capabilities and scoped per-tool tokens enforce least privilege, and how to build it into a real agent stack.

What is capability-based security for agent tools, in one paragraph?

Capability-based security for agent tools means every tool an agent can call receives its own unforgeable token that carries a specific, narrow set of permissions, and that token is the only way the tool can reach a resource. There is no ambient authority, no shared “agent account,” and no way for a tool to ask for more than it was handed. If a tool needs to read one calendar, it holds a capability to read that one calendar; it cannot list your inbox, delete files, or call a payments API, because it was never given the references to do so. This is the principle of least authority turned into a runtime guarantee rather than a policy you hope everyone respects.

The contrast is with the way most agents ship today. A typical setup loads one OpenAI-or-vendor API key, one database connection, and a few service tokens into the agent’s environment, then exposes a dozen tools that all share that ambient authority. Whoever or whatever controls the agent’s reasoning — including an attacker who slips instructions into a web page the agent reads — inherits the full set of permissions. Capability-based security removes that blast radius by design.

Why does ambient authority break least privilege for agents?

Least privilege says a component should hold the minimum authority needed to do its job. Ambient authority — permissions that float in the environment and apply to any code that runs — makes that almost impossible to enforce. The agent’s planner, its tool router, and every individual tool all run inside the same trust boundary, so they all wield the same permissions. You cannot grant the “read weather” tool less than you grant the “send email” tool, because there is nothing separating them.

This matters more for agents than for ordinary software because agents take untrusted input and turn it into actions. A web page, an email, a PDF, or a returned tool result can all contain instructions the model treats as goals. That is prompt injection, and it is not a bug you patch once — it is a structural property of systems that mix instructions and data in the same channel. If injected text can make the agent call any tool, and every tool shares broad authority, then injection equals full account takeover. The defensible answer is not “stop injection” (you cannot fully) but “make injection worthless” by ensuring the only tools the agent can reach are already scoped to harmless or tightly bounded actions.

How do object capabilities make least privilege a runtime guarantee?

The object-capability (ocap) model, formalized by researchers including Mark S. Miller, expresses authority as references rather than identities. A capability is a communicable, unforgeable token of authority that designates an object together with a set of access rights (Wikipedia: Capability-based security). The defining rule is simple and strict: a component can only act on resources it holds references to, and it can only obtain new references by being explicitly handed them. No reference, no reach.

That single rule collapses three things programmers usually treat separately — designation, authorization, and the actual call — into one act. When you pass a tool a capability to a specific S3 bucket, you have simultaneously named the resource, authorized the access, and provided the mechanism, with no separate access-control list to consult and no chance for the tool to “confuse” which resource it is allowed to touch. This is why the object-capability community describes least privilege as built in by design (F# for Fun and Profit: A functional approach to authorization).

The practical upshot for agents: instead of giving a tool an identity (“you are the agent, here is the master key”) you give it a thing it can do (“here is a function that reads invoice #5512, nothing else”). Authority flows along references, so you can reason about exactly what each tool can affect by looking at what you passed it. The Capability Myths Demolished paper by Miller and colleagues is the canonical reference for why this model resists the confused-deputy class of attacks that plagues identity-based access control.

Object capabilities vs. scoped tokens vs. roles: which model gives agents the least authority?

In a distributed agent stack you rarely get pure object capabilities everywhere. You combine an in-process ocap discipline with scoped tokens on the network boundary. The table below compares the three approaches you will actually mix.

Approach Unit of authority Granularity Failure mode when compromised Best fit in an agent stack
Role-based access control (RBAC) Identity + role Coarse (per role) Attacker gets everything the role can do Human operators, admin consoles
Scoped tokens (OAuth 2.1 style) Bearer token with scopes/audience Medium (per scope, per resource) Anyone holding the token gets its scopes until expiry Network calls between agent and external APIs/MCP servers
Object capabilities Unforgeable reference to a specific object Fine (per object + rights) Holder can only touch the one object referenced In-process tool wiring, sandboxed plugins

The point is not to pick one. Use object capabilities to wire tools inside the agent process or sandbox, so a tool literally cannot name a resource it was not handed. Use scoped, short-lived, audience-bound tokens at every network hop, so even a leaked token is narrow and expires fast. Keep RBAC for the humans. The combination is what gives an agent defensible least privilege end to end.

How do scoped, per-tool tokens enforce per-tool permissions in practice?

A scoped per-tool token is an access token minted for one tool, carrying only the scopes that tool needs, bound to the specific resource it should call, and expiring quickly. Three properties make it work:

  • Per-tool issuance. The email tool and the calendar tool get different tokens. A compromised email tool holds nothing that talks to the calendar.
  • Audience binding. The token names its intended recipient so it cannot be replayed elsewhere. In OAuth terms, resource indicators let the client state the intended recipient, and the authorization server issues a token valid only for that specific server (Auth0: MCP specs update — all about auth).
  • Short lifetime plus just-in-time grant. A token minted for the duration of one task, then discarded, limits the value of any leak. Persistent broad tokens are the opposite of this.

The Model Context Protocol (MCP) is where most teams meet these ideas concretely today. As of the 2025-11-25 specification, MCP servers are classified as OAuth 2.1 resource servers, and servers are advised to advertise required scopes in the WWW-Authenticate header so clients request only what they need, following least privilege (Model Context Protocol: Authorization). The same release standardizes baseline scope names (SEP-835) so consent is predictable across servers, and adds step-up authorization: a server can answer an under-scoped request with a 403 and a header listing the additional scopes required, instead of granting broad access up front (WorkOS: MCP 2025-11-25 spec update).

A documented gap is worth naming honestly: the MCP community itself notes that if a server is compromised, a single static token grants widely scoped persistent access, and there is no fully native standard yet for just-in-time, task-duration privileges (Auth0). That is precisely the gap object capabilities and short-lived per-task tokens are meant to close — so do not treat OAuth scopes alone as the finish line.

How do you architect a least-privilege agent step by step?

You can build a least-privilege agent incrementally. The sequence below moves from the in-process trust boundary outward to the network.

  1. Inventory authority per tool. For each tool, write the single smallest sentence describing what it must do: “read events from calendar X between two dates.” If you cannot write it narrowly, the tool is doing too much — split it.
  2. Replace ambient secrets with passed capabilities. Tools should receive a capability object (a function or handle scoped to one resource) as an argument, not read a shared key from the environment. The agent process holds the broker; tools hold only what the broker hands them.
  3. Mint per-tool, audience-bound tokens at the network edge. When a tool must call an external API or MCP server, issue a token scoped to that resource and audience, with the shortest viable lifetime. Never share one token across tools.
  4. Default-deny the tool router. The component that decides which tool to call should reach only tools that already carry safe-by-construction capabilities. Injected instructions can at most invoke a bounded action.
  5. Gate irreversible actions behind explicit elevation. Use step-up authorization (or a human-in-the-loop approval) for high-impact operations — payments, deletions, outbound messages — rather than pre-granting them.
  6. Log capability use, not just identity. Record which capability performed which action. Identity logs tell you “the agent did it”; capability logs tell you “the invoice-reader capability read invoice 5512,” which is what you need for forensics.

This ordering matters: steps 1 and 2 shrink the blast radius inside your own code, where you have full control, before you depend on protocol features that are still maturing.

What does capability-based security NOT solve for agents?

Capabilities are a containment model, not a magic shield, and overselling them is a real E-E-A-T risk. They limit what a hijacked tool can reach; they do not stop the agent from being hijacked, and they do not validate the content of an allowed action. If you grant a tool the capability to send one specific email, prompt injection can still make it send that email with attacker-chosen text. Capabilities bound the set of reachable actions; they do not reason about whether a permitted action is a good idea.

Three other limits are worth stating plainly. First, key and capability management adds operational complexity — minting, rotating, and revoking many narrow tokens is more work than one broad key, and getting revocation wrong reintroduces standing access. Second, the model only holds if tools cannot escape their sandbox to grab ambient authority directly; a tool that can shell out or read the process environment defeats the whole scheme, so isolation (separate processes, WASM components, or microVMs) is a prerequisite, not an add-on. Third, much of the agent ecosystem still ships broad static tokens by default, so you are often building least privilege against the grain of your tools’ defaults. None of this argues against capabilities — it argues for treating them as one disciplined layer in defense in depth.

Where does identity fit into a capability-first agent?

Identity and capabilities are complementary, not competing. You still need to know who the agent is acting for — the human user, the service account, the tenant — so verifiable cryptographic identity lets you audit, bill, and revoke at the right grain. The shift is that identity authenticates the principal at the boundary, while capabilities authorize each specific action inside it. A clean pattern: the user authenticates once, the agent exchanges that session for a set of narrow, audience-bound capabilities and tokens per tool, and from then on every action is governed by the capability it carries rather than by re-checking the agent’s broad identity. This keeps the confused-deputy problem at bay, because authority travels with the reference instead of being re-derived from “who is asking.”


Further reading: the awesome-ocap list collects primary sources on object-capability security, and the MCP authorization specification documents the OAuth 2.1 model agent tooling is converging on.

Frequently asked

What is the difference between capability-based security and role-based access control for agents?

RBAC grants authority to an identity through a role, so any code running as that identity inherits everything the role can do. Capability-based security grants authority through unforgeable references to specific objects, so a tool can only touch resources it was explicitly handed. For agents, capabilities give far finer granularity and a smaller blast radius, because a hijacked tool holds only its one capability rather than the agent's whole role.

Are OAuth scopes the same thing as object capabilities?

No, though they overlap. OAuth scopes describe permission categories attached to a bearer token used across network boundaries, and anyone holding the token gets those scopes until it expires. Object capabilities are unforgeable references that bind designation and authorization together in-process, so the holder can only act on the exact object referenced. In a real agent stack you use scoped, audience-bound tokens at network hops and object capabilities for in-process tool wiring.

Does capability-based security stop prompt injection?

Not directly. Prompt injection can still hijack the agent's reasoning and trigger any tool the agent can reach. What capabilities do is make that hijack far less valuable: if every tool carries only a narrow capability, the worst an injected instruction can do is invoke a tightly bounded action. The goal is to make injection low-impact rather than to prevent it entirely, since instruction-data mixing cannot be fully eliminated.

How do scoped per-tool tokens limit the damage from a compromised tool?

Each tool gets its own token carrying only the scopes it needs, bound to the specific resource and audience it should call, with a short lifetime. A compromised email tool therefore holds nothing that can reach the calendar, the database, or a payments API, and even a leaked token expires quickly and cannot be replayed against a different server because of audience binding.

Does the Model Context Protocol support capability-based or least-privilege security?

MCP moves in that direction. As of the 2025-11-25 specification, MCP servers are OAuth 2.1 resource servers that advertise required scopes so clients request only what they need, baseline scope names are standardized, and step-up authorization lets a server request more scope only when an operation needs it. A known gap is that a compromised server with a static broad token still grants persistent wide access, so MCP scopes should be combined with short-lived per-task tokens and in-process capability discipline.

What are the downsides of capability-based security for agents?

The main costs are operational: minting, rotating, and revoking many narrow tokens is more complex than managing one broad key, and weak revocation can reintroduce standing access. Capabilities also only bound the set of reachable actions, not the content of an allowed action, and they assume tools cannot escape their sandbox to grab ambient authority. Effective use requires isolation such as separate processes, WASM components, or microVMs as a prerequisite.