A tool name tells an agent what it can ask for. A capability contract tells the application which requests it is actually authorized to execute.
A tool catalogue is not a permission system
An assistant may know that a tool called update_record exists without being entitled to update every record. The function signature describes how to express a request, while authorization decides whether that request is allowed for this principal, task and resource. I would keep those concerns separate from the beginning. Hiding a tool from the prompt can reduce accidental use, but it is not sufficient enforcement if the backend accepts an unauthorized call anyway. Every execution path needs a trusted decision point, including retries, background jobs and calls produced by components other than the conversational model.
Capability-oriented thinking is useful here because it asks what concrete authority a caller possesses. Birgisson and colleagues’ Macaroons paper explores credentials that can be restricted with contextual caveats. RFC 8693 specifies OAuth token exchange concepts relevant to representing delegation and the identities involved. Neither source defines a complete agent permission system, and I would not invent one by renaming a bearer token. Their useful contribution is a vocabulary for limited, delegated authority. An agent tool should receive only the authority needed for its operation, with the application able to explain who granted it and under which conditions.
References: [1] Macaroons — Birgisson and colleagues[2] RFC 8693: OAuth 2.0 Token Exchange
Delegation should only narrow the grant
Suppose a parent task may read documents A and B and update document B. It delegates a subtask that only needs to read A. The delegated authority should be the intersection of the parent grant and the subtask request, not a newly created broad credential. If the subtask asks for deletion, that operation remains unavailable because the parent never possessed it. This is an illustrative set model; real capabilities may include predicates, cryptographic verification and revocation semantics. The key invariant is monotonic restriction: a delegation chain must not gain authority merely because another component restated the request in different words.
Macaroons’ caveats are one concrete way to reason about attenuating authority, but an implementation must use a reviewed library and the protocol’s actual verification rules. The tiny example below is only a policy algebra demonstration, not a secure credential format. It leaves out authentication, expiry, replay protection and atomic execution. I would keep that distinction explicit in engineering documentation because a short set intersection is easy to copy into a system that needs stronger guarantees. The example proves one local property: requesting an operation that was not granted does not cause that operation to appear in the delegated set.
parent = frozenset({('read', 'A'), ('read', 'B'), ('update', 'B')})
requested = frozenset({('read', 'A'), ('delete', 'B')})
delegated = parent & requested
assert delegated == frozenset({('read', 'A')})
assert delegated <= parent
assert ('delete', 'B') not in delegatedReferences: [1] Macaroons — Birgisson and colleagues
Budgets need atomic enforcement
An authority limit can be quantitative: read at most a certain number of records, spend at most a task budget or commit at most one approved change. Those limits are stateful. If two concurrent workers each observe that one action remains and both proceed, a check performed outside the execution boundary can exceed the grant. I would enforce shared budgets with an atomic reservation or another coordination mechanism appropriate to the storage system. The model’s internal count is not a reliable ledger, and a client-side counter cannot protect against retries or multiple processes using the same delegated authority.
Expiry has a related ambiguity: does the operation need to start before the deadline, finish before it or remain authorized at the moment of commitment? Different tools may need different answers. A long-running read can often finish after admission, while a consequential write may need to recheck authority just before commit. The policy should name that boundary. Cancellation also needs explicit semantics for reserved budget and already completed effects. Releasing a reservation is reasonable when no effect occurred; refunding a completed operation simply because its response was lost can allow a retry to exceed the intended limit.
Revocation and audit are part of the design
A self-contained credential can be convenient to verify, but revoking it before expiry may require additional state or a short validity period. That is a real architectural tradeoff. I would decide how quickly authority must be withdrawable and choose the credential and verification strategy accordingly. Rotating a model prompt does not revoke a tool credential already issued to a worker. The enforcement service needs a path that reflects account changes, task cancellation and compromised delegation. Long-lived background work is especially important because it may outlast the interactive session that originally justified its access.
The audit record should identify the initiating principal, any delegated actor, the evaluated scope, the concrete operation and its result. RFC 8693’s distinctions around subject and actor are useful context for avoiding a log that attributes every delegated action only to a generic service account. The log should not expose credentials or unnecessary private data. It should provide enough information to reconstruct why an operation was authorized and whether it actually committed. A model-generated narrative of what it attempted is helpful context, but the authoritative execution record must come from the component that performed the operation.
References: [2] RFC 8693: OAuth 2.0 Token Exchange
Test the negative space of the grant
Permission tests should include operations that are almost allowed: the right action on the wrong resource, the right resource after expiry, an allowed read followed by an unapproved write, and concurrent calls near a shared limit. Test delegation chains and retry paths as well as the happy path. A useful property is that removing authority cannot make a previously forbidden operation become allowed. Another is that changing a resource revision invalidates any approval that depended on the old revision when the policy requires it. These are application invariants, not questions to be graded by a language model.
I want agents to be useful precisely because the authority boundary is clear. The model can explore possibilities, prepare proposals and recover from ordinary errors without being entrusted with the ability to redefine its own permissions. Narrow capabilities make that separation concrete: a tool receives a verifiable grant for a specific class of work, and the execution service enforces it at the relevant boundary. The result is not merely a safer prompt. It is a system in which delegation, concurrency and retries remain understandable even when the component choosing the next action is probabilistic.
Sources and further reading
- Macaroons — Birgisson and colleagues
Primary capability attenuation research; the set example illustrates policy only and does not implement credentials.
- RFC 8693: OAuth 2.0 Token Exchange
Primary protocol reference for token exchange, delegation and actor/subject distinctions.