Allow one tool, deny the next
Permit an agent one tool on an MCP server and refuse the adjacent one, so access is a property of policy rather than of the agent behaving as expected.
An MCP server usually exposes many tools, and access to it is usually all or nothing. If an agent needs one of those tools, it gets the write ones too, and nothing but the agent’s own restraint stands between them.
The problem
Section titled “The problem”Access to an MCP server tends to be a single decision. The agent can reach the server, and the server exposes whatever it exposes.
That collapses a set of very different operations into one grant. A server that reads issues and also closes them offers both to anyone who can reach it. The agent that needed the first now holds the second, and the difference between a summary and a deletion is a matter of which tool the model decides to call.
Restraint is where the control usually lives, and it is the wrong place for it. A prompt that says only read, a system message, a carefully scoped instruction: all of those are requests rather than rules, and none of them survives a model that misreads the situation or a prompt that was crafted to redirect it.
Splitting the server into two servers is the workaround, and it multiplies the thing you have to run. Every new distinction becomes another deployment, another registration, and another URL for someone to configure, which is why teams stop drawing distinctions.
flowchart LR
A[Your Agent] -->|"one grant to the server"| B[MCP Server]
B --> C[read_issue]
B --> D[close_issue]
B --> E[delete_project]
When this applies
Section titled “When this applies”- One MCP server exposes tools you would answer differently if asked about individually
- The distinction you need is per tool rather than per server
- A reviewer wants the boundary enforced somewhere they can inspect, rather than described in a prompt
- You would rather refuse a call than rely on an agent not making it
Suppose an internal MCP server wraps your ticketing system. An assistant should be able to read tickets and add comments. It should not be able to close or delete them. Same server, same agent, and the difference should hold whether or not the model behaves as intended.
When it does not
Section titled “When it does not”Where the tool is fine but the caller is the question, the pattern you want is per-user access. See Act as the user who asked and Same agent, different user.
Where the agent runs tools locally rather than through a server, there is no gateway in the path and the enforcement point is the agent’s own tool-call loop. See Govern a coding agent.
Where the distinction you need is inside a single tool, such as permitting a query but constraining which rows it returns, the Resource enforces that itself.
The solution
Section titled “The solution”Register the MCP server behind a Unified Access Gateway and the gateway becomes the point where each call is decided.
Rather than one grant to the server, policy names the individual tool. The action Cedar evaluates is the method plus the tool name, so permit on one tool and silence on the next produces exactly the boundary you wanted, and default deny means silence is a refusal.
The decision happens before the call reaches the upstream. A refused tool call does not arrive at your ticketing system and get rejected there; it stops at the gateway, and the refusal is a policy record rather than an application error.
How it works
Section titled “How it works”flowchart LR
A[Your Agent] -->|"tools/call"| B[Unified Access Gateway]
B -->|"evaluate action + tool name"| C{Policy}
C -->|permit| D[MCP Server]
C -->|no match, default deny| E[Refused and recorded]
- Your MCP server is registered as an upstream of a Unified Access Gateway, so agents reach it through one gateway URL.
- You write fine-grained policies naming individual tools, where the Cedar action carries the MCP method and the tool name together.
- On each call the gateway evaluates policy against the invoking identity, the upstream, and that specific tool.
- A permitted call is forwarded. One that matches no
permitis refused under default deny, without reaching the upstream. - The Activity feed and the Audit Log record the policy decision, so an allow and a refusal are equally legible.
What you get
Section titled “What you get”The boundary is a rule rather than a hope. Whether the agent calls the adjacent tool stops being a question about the model and becomes a question about policy, which is a thing you can read, review, and change without redeploying an agent.
One server can serve several different levels of access. The distinction you were going to draw by running two deployments is drawn in policy instead, against the same upstream and the same URL.
Refusals are evidence. A denied tool call is a policy record naming the identity, the upstream, and the decision, so “prove this agent could not do that” is answerable from the log rather than from the prompt.
What you take on
Section titled “What you take on”You write policy per tool, which means knowing the tool names. The names come from the server, so a server that renames a tool changes what your policy matches. Treat tool names as an interface and review policy when an upstream version changes.
Tools are the surface the gateway covers. Requests for other MCP surfaces, such as prompts or resources, are answered with a method-not-found error rather than forwarded, so a server whose value is in those surfaces is not the case to start with. The prompts and resources section covers the intended shape.
The decision is recorded; the execution is not. What lands in the audit trail is the authorization decision carrying the tool it was made against. The downstream effect of a permitted call belongs to the upstream system’s own logs, so an end-to-end story usually joins the two.
Off-the-shelf and custom upstreams differ in what policy can express. A server built with the SDKs can carry distinctions of your own design. One you did not write exposes whatever tools it exposes, and policy works with those names rather than with finer scopes inside them.
Common questions
Section titled “Common questions”Is this enforcement, or filtering?
Section titled “Is this enforcement, or filtering?”Enforcement, and at the gateway rather than in the agent. A refused call does not reach the upstream. That matters for a reviewer, because the boundary does not depend on the agent’s cooperation or on the upstream implementing anything.
What happens if the gateway cannot tell which tool is being called?
Section titled “What happens if the gateway cannot tell which tool is being called?”It refuses. The gateway pins the operation it authorized against the full request before forwarding, and a request whose contents do not match what was authorized is rejected rather than passed through. The failure direction is shut, which is the answer a security reviewer is looking for when they ask about parser edge cases.
Can we scope inside a tool, for example read-only against a provider’s API?
Section titled “Can we scope inside a tool, for example read-only against a provider’s API?”Not through this mechanism. Policy decides which tool may be called, and constraining what a single tool may do inside the upstream is the upstream’s own concern. Where a provider offers finer scopes of its own, those are configured on the Provider rather than expressed as a tool policy.
How is this different from what our MCP client already does?
Section titled “How is this different from what our MCP client already does?”A client’s allow list lives with the client and is only as good as the client’s configuration on each machine. This decision is made centrally, applies to every agent reaching the gateway, and produces a record. Where you need both, the two compose: the client narrows what the agent is offered, and policy decides what it may actually call.
Where else this applies
Section titled “Where else this applies”- A deployment server where read and plan are permitted and apply is not
- A finance MCP server where lookups are broadly available and anything that moves money is not
- A customer-data server where an assistant may search but not export
- A shared internal server used by several agents, each permitted a different subset of its tools
The shape is the same each time. One server exposing operations of genuinely different consequence, and a boundary you want enforced somewhere other than the model’s judgment.
Related
Section titled “Related”- Fine-grained authorization is the how-to for writing these policies
- Unified Access Gateway covers registering upstreams behind one URL
- Access Policies covers Cedar policy generally, and what policy can see
- Read an Activity Feed is the fastest way to find a specific refusal
- Activity events lists the events a decision produces
- Govern a coding agent is the same question where there is no server in the path