MCP Governance

Enforcing Policy Before Tool Execution

OAuth gets the agent into the MCP server. Nothing in the protocol decides whether this particular tool call should run.

What is MCP governance?

MCP governance is the enforcement of machine-readable policy on each Model Context Protocol tool call, evaluated after the call is composed and before the tool executes. It decides whether this client, acting for this principal, may invoke this tool with these arguments, and it records the decision as signed evidence.

MCP solved a real problem. It gave agents a uniform way to discover and invoke tools, which is why tool surfaces expanded from a handful of hand-wired functions to catalogs spanning databases, payment systems, ticketing, cloud control planes, and internal APIs. The protocol made capability portable.

It did not make authority portable. There is a decision point the protocol leaves empty, and it sits exactly where the consequences are: between the moment a model emits a tool call and the moment that tool changes something.

Does the Model Context Protocol authorization spec cover this?

Not at this layer. The MCP authorization specification is an OAuth 2.1 profile: it establishes that a client holds a valid token for a server and that the token was issued for that server as its audience. It governs admission to the server. It does not evaluate an individual tool invocation or its arguments, and it is not designed to.

That is the correct scope for an authorization spec, and calling it a gap in the protocol would be unfair. Token audience binding closed a genuine confused-deputy problem. Resource indicators stop a token minted for one server being replayed against another. This is good work and it should be adopted.

But admission and authorization are different questions, and the protocol answers the first. Once the client is admitted, every tool the server exposes is reachable, and the arguments carried by any given call are the client's business. From the server's perspective a well-formed call from an authenticated client is a valid call. The server has no model of your operational limits because it was never given one.

Why are MCP scopes not enough?

A scope is granted once and consumed many times. It describes a class of capability, such as access to a database server or a payments server, while the damage is done by particular calls inside that class. Every destructive tool call in a real incident is inside the scope that was deliberately granted, which is why the scope check returns success.

Work the arithmetic of a single connected server. A database MCP server exposes query, execute, and schema tools. Granting an agent that server is granting the full cross product of those tools against every table, every predicate, and every value the agent can compose. Nobody enumerated that set, nobody reviewed it, and it is the actual permission that was issued.

Then multiply by the number of servers a working agent connects to, and add the interactions between them: a value read from one server becoming an argument to a call on another, which is a path no single server can see.

What the token provesA valid client, admitted to a specific server.
What the scope coversA class of capability, granted once, consumed indefinitely.
What is left undecidedWhether this call, with these arguments, against this resource, may run.

Where should the policy check sit in an MCP deployment?

On the path between the client composing the call and the tool producing an effect, as a component neither side can skip. Placing it inside the agent makes it advisory, because the same reasoning that composed the call also decides whether to consult it. Placing it after the tool returns makes it a log.

Three placements are commonly proposed, and they are not equivalent.

Inside the model or the agent loop. A system prompt instructing the agent to check policy first. This is the weakest position available, because it asks a probabilistic component to be the enforcement mechanism against itself. It also fails the reproducibility test: the same request can be handled two different ways on two different days.

Inside each MCP server. Better, and worth doing for server-specific invariants. But it distributes policy across every server you connect, including ones you do not operate, and gives you no single place where "what is this agent allowed to do" has an answer. It also means a new server arrives ungoverned by default.

At a boundary in front of the tool call. The client proposes, the boundary decides, the tool executes only on an allow. One policy surface covers every connected server, a newly added server inherits the existing posture instead of starting from nothing, and the decision is rendered by a component that is not the thing being governed.

What a governed tool call looks like

Take a concrete call: an agent connected to a database server emits a tool invocation to execute a statement. Under an admission-only model the sequence is short. Token valid, client admitted, statement runs.

Under a governance boundary the same call is evaluated first, gate by gate, and every gate records the facts it saw:

Gate Question Failure means
Structure Is the call well formed and canonicalized into a stable intent The record would not be comparable across calls
Catalog Is this operation one the subject may perform, and do its arguments validate against its declared schema The operation is outside the catalog, or the arguments are malformed
Grant Does an active, unmodified grant cover this operation for this subject No authority exists for this action
Bounds Are the argument values inside the effect bounds the grant attached The operation is permitted, the magnitude is not
Target Is the specific resource in the grant's target set Right operation, wrong table, account, or device
Temporal Is the grant still in force and the authority state current The authority expired, or is being replayed from an earlier epoch
Invariant Does the resulting state violate a condition that must hold regardless of the grant A standing constraint would be broken

The verdict is allow, refuse, or escalate to a human, and it is signed either way. A refusal names the gates that failed and the facts behind them, so the operator can tell whether the grant is too narrow or the agent is wrong.

How does MCP governance handle prompt injection through tool results?

It does not attempt to detect the injection. It constrains what a successfully injected agent is able to do. Injected instructions still have to leave the model as a tool call, and that call still has to pass the same gates as any other, so an instruction to exfiltrate a table fails on the operation catalog, the target set, or the effect bounds regardless of how persuasive the text was.

This matters specifically for MCP because tool results are the main injection surface in agent systems. A document fetched by one tool, a ticket description, a web page, a row in a table: any of it can carry text that reads as instruction when it returns into the model's context. Detection approaches have to win every round against an adversary who can rewrite the payload. An authority boundary does not have to win the detection contest, because it never asks whether the text was trustworthy.

The honest limit: this bounds effects, it does not bound reasoning. An injected agent can still waste a session, produce a wrong answer, or attempt actions that are all refused. What it cannot do is convert persuasion into an effect outside granted authority.

What governance produces that logging does not

Every governed call leaves a signed authority receipt: the canonicalized intent and its hash, the subject, the grant identifier and grant content hash, the policy version, the full rule trace, the verdict with reason codes, and an Ed25519 signature over the whole record. Receipts chain, so each commits to the one before it and a removed record leaves a detectable gap.

Three properties follow, and none of them is available from a tool-call log.

Refusals are evidence. A log records what ran. A receipt stream records what was attempted and refused, which is the only way to demonstrate that a control was enforcing during a period when nothing went wrong.

Verification does not require the vendor. A receipt verifies offline against a published key. If checking a record means calling an API owned by the party whose behavior is under review, the record is an assertion rather than evidence.

The policy version is bound in. Six months later the question is never what the policy says, it is what the policy said at the moment of the decision. The grant content hash answers that directly.

Adopting this without rewriting your agent

The agent does not change. It keeps reasoning, planning, chaining tools, and proposing calls. What changes is that proposing and executing become separate steps, with a decision between them.

Practical sequencing that works: start by governing the servers that can cause irreversible effects, which is usually a much shorter list than the full connected set. Write grants for the operations those servers expose, with real parameter bounds rather than a permit-all default. Run the boundary in a mode that records decisions before it enforces them, so the refusal rate is visible against real traffic. Then enforce, and use the refusal reason codes to widen grants that were drawn too tightly.

The grants you end up with are the first honest description of what your agents are actually permitted to do. Most teams find that document does not exist anywhere else.

Evidence and further reading

The enforcement model on this page is published and citable. ExecLayer holds seven provisional patent filings covering deterministic governance enforcement, authority receipts, runtime policy bundles, and governed agent supply-chain control.

Related reading