Control which tools an AI agent may call
Grants re-checked at execution rather than at the list, and a changed descriptor is quarantined. The head of IT whose support agent holds a refund tool nobody meant it to reach A whole server was granted rather than an action, so the endpoint that issues refunds sits next to the one that reads orders. Nobody outside the team that wrote the configuration can say what the agent may do.
A filtered tool list reads as access control and is not.
Tool access starts as a list of servers in a client configuration file, granting whole servers rather than actions, so an agent that needs one read against the order database gets the refund endpoint beside it. A gateway that filters the list is the next attempt, and a client can call a tool name it was never shown. Almost nobody governs the descriptor, which is text the model reads and obeys.
4 steps, in this order.
Register each server behind one endpoint
A server row carries a name, a destination and the name of an environment variable holding its Authorization header, never the credential itself.
Grant actions, not systems
Write the specific tools each agent's declared purpose requires. Everything else is denied by default.
Point the client at one URL
Replace every upstream server with the gateway endpoint and the agent's own bearer token. Tools arrive namespaced and filtered to its grants.
Pin the descriptors you reviewed
Approving a tool hashes its name, description and input schema. A later change quarantines it and raises a high-severity finding.
See how the gateway checks tool grants and returns a refusal.
See how a tool call is authorisedBuilt for
- Agents whose tool calls route through one endpoint, so the call is authorised rather than the list.
- A team willing to write grants as an allowlist of actions, and to find grants nobody can justify.
- Anyone who treats an upstream tool's description as part of the prompt.
Wrong for
- Anyone whose agents hold and call tools from their own process. Evaluating proposed calls is defence in depth rather than a guarantee.
- Anyone who needs time-bound or just-in-time grants. Credentials are long-lived bearer tokens rotated by hand.
- Anyone who expects a quarantine screen to show a diff. Only the hash of the approved descriptor is stored.
One thing on the record
The tool gateway's refusal paths used to record raw arguments, so a call blocked for containing a secret wrote that secret into the trace store and its full-text index. It is in the published defect list beside its fix.
Arguments on a refused call are now masked irreversibly before anything is written. Read the defect list, then look at a blocked trace on your own install.
Where this is publishedPermissions decide the tool; policy decides the argument.
An action is allowed only when a permission on one of the agent's roles explicitly allows it and none denies it, and the deny path returns first, so a guardrail role wins wherever it is listed. Delegation intersects rather than unions: the failure mode of an intersection is under-privilege, which surfaces as a ticket, while the failure mode of a union is an escalation that looks legitimate.
- Wildcards do not leak
- tool:orderdb/* matches every tool on that server and nothing on any other. Every other metacharacter is escaped.
- Roles are flat
- A name, a description and a list of permissions, with no inheritance. More typing, and less to reason about.
- Permissions do not read arguments
- Refunds over £200 need approval is a policy. A permission has to be readable by a reviewer in one line.
- Roles cannot be deleted while referenced
- The check runs in the same transaction as the deletion. Deleting a role under a running agent is a silent permission change.
The attack this prevents is the one every naive permission model allows: an agent that has been refused something asks a more privileged agent to do it instead.
- Read: Customer Account
- Update: Shipping Address
- Delete: Account
- Issue: Refund
Deny-by-default: an action the policy does not list is denied, which is why the list of denials is short and the list of grants is the whole policy.
- Read: Customer Account
- Issue: Refund
- Delete: Account
Only what both hold. A refund is not reachable through the chain, because A never had it, and an explicit deny anywhere in the chain wins outright.
The tool descriptor is part of the prompt, so it is pinned.
Every catalogue refresh recomputes the hash and compares. Unpinned is deliberately not a block (a gateway that refused every unreviewed tool would not be adopted), so an unpinned tool stays usable and is described to the model as unverified, while drift quarantines immediately. Only the hash is kept, so a quarantine screen cannot show a diff and says so rather than letting an operator approve on a false premise.
4 things this still does not solve.
- A tool call that never reaches Token Observe is not refused by anything here. Evaluating proposed calls is defence in depth rather than a guarantee.
- There are no time-bound or just-in-time grants. A permission stands until somebody edits the role, and credentials are long-lived bearer tokens rotated by hand.
- An unpinned tool is not blocked, and a quarantine screen cannot show you a diff, because only the hash of the approved descriptor is stored.
- The bearer token is the agent. Any process holding that string is that agent, with no binding to a workload unless an operator wired one up.
Check the product limits alongside the job you want to do.
What we do not claimThe parts that do the work.
MCP gateway
The half of an agent that moves money is a tool call, not a sentence. This is the checkpoint that call goes through.
A tool call routed around it is not governed here
Agent permissions
Deny by default, explicit deny wins, and delegation intersects, so an agent cannot borrow authority it was never granted.
Off by default; only enforce refuses anything
Policy engine
One deterministic verdict on every governed request: allow, block, redact, or park it for a human.
Injection scoring is nine fixed patterns, not a model
Questions
Why does the gateway check permissions twice?
Because filtering a tool list is a usability feature, not access control: a client can call a tool name it was never shown, so the call path re-evaluates from scratch. A tool the agent cannot see returns the protocol error for an unknown tool, because confirming existence is an inventory disclosure.
What happens when an upstream tool's description changes?
It is quarantined on the next catalogue refresh and refused until a human approves it again. A mismatch against the pinned hash hides the tool, records the drift with both hashes, publishes an event and raises a high-severity discovery finding.
Can a low-privileged agent get access by asking a higher-privileged agent?
No. The effective permission set is the intersection of every agent in the chain, so the request is refused at the first link that does not hold the grant. The chain is asserted rather than proven, but because it can only add links and every link must allow, forging it buys strictly less than sending none.
Describe the version of this you have.
Say what your estate contains and which part of it worries you, and the reply will say where to start.