Governing AI agents at the gateway with Cerbos and agentgateway

AAlex OlivierJune 10, 20269 min read

Most teams rolling out AI agents end up in the same place. An employee kicks off an agent to handle a task, or an event like an inbound support ticket spins one up autonomously, and that agent needs to reach a model and a handful of tools to get its job done. The agent connects to an LLM. It discovers MCP servers for the CRM, the ticketing system, payments, payroll. And the moment it has those connections, you have a problem that the agent runtime was never built to answer. Which model is this agent allowed to call. Which MCP servers can it even open. And when it produces a tool call, are the arguments inside the policy you would have enforced if a human had asked.

agentgateway closes the network side of that gap. It is a CNCF data plane that sits in front of agents and becomes the single front door for everything they reach, routing model traffic to the right provider and proxying every MCP server behind one address. What it does not decide on its own is who is allowed to do what. That is the authorization question, and it belongs in a policy engine, the same one your platform already uses for the rest of the stack.

Cerbos Synapse plugs into agentgateway as the authorization guardrail. agentgateway calls Synapse on every model request and every MCP tool call over standard Envoy ext_authz, Synapse asks Cerbos, and the decision comes back before the upstream is ever touched. No custom plugin to write, no SDK to keep in step with gateway releases. The gateway routes. The decisions move to where access decisions already live.

Three questions on every agent hop

Authorization for an agent at the gateway is not one decision. It is three, and they fire at different points as the agent works.

Which model can this identity call. A basic triage classification does not need a frontier model, and a junior support tier does not need the same model menu as a lead. agentgateway already knows which providers are wired up. The policy decides who gets which one. In the demo the LLM route carries a completion action and the resource id is the model name the agent asked for, so the resource policy is a plain allow-list. Tiered support agents can reach gpt-4o and gpt-4o-mini, while the autonomous triage role is pinned to the cheaper gpt-4o-mini and nothing else. Everything not named is default-deny, so a request for a model the caller has no business using is refused before any token is spent upstream.

Which MCP servers and tools the agent can see. Before an agent can call a tool it has to open the MCP server and list what is there. agentgateway proxies each server on its own route, and every route is guarded. In the demo the autonomous triage agent opens tickets and CRM cleanly, but its attempt to even initialize the payments and payroll servers is denied at the gateway, so those tool catalogues never reach it. Payroll is the sharp edge of this. It is wired up exactly like every other server, but its policy only allows a payroll-admin role, so the MCP initialize call denies for everyone else before a single tool name is exposed. The model never gets the chance to choose a tool it should not have, because it never learns the tool exists.

What the tool call is actually asking for. This is the decision a gateway cannot make on its own, and it is the one that matters most for agents. When the model produces a refund call, agentgateway forwards the request body to Synapse, so the policy reads the structured arguments directly and joins them with the calling identity. A refund against a ticket the agent's task is not bound to is one rule. A refund above the caller's tier cap is another. Both live in the policy, not in gateway config, so changing them is a policy change rather than a redeploy of the data plane.

How the integration is shaped

agentgateway is a good fit for this pattern because the extension point is already a standard. Every route in the gateway config carries an extAuthz policy that points at Synapse over gRPC, with failureMode: deny so a gateway that cannot reach the policy engine fails closed rather than open. The model route and each MCP route carry the same block. There is no adapter binary and no agentgateway-specific authorization primitive in the path, which is the whole point. Because ext_authz is an Envoy standard, the same Cerbos policies and the same Synapse mapper run unchanged behind Envoy, Istio, or any other gateway that speaks it. Swapping the gateway is a routing-config change, not a rewrite of your access rules.

The credential that the gateway hands to the policy is a per-resource token. The agent runs inside a sandbox with outbound access blocked, so agentgateway is its only way out, and at boot it exchanges for one runtime token per server it is allowed to reach, each scoped to exactly that resource. A token captured from the payments wire is useless at the LLM route or at CRM. The identity inside that token is whatever your environment attests. It can be the user the agent is acting on behalf of, or for a truly autonomous agent it can be a workload identity, a SPIFFE-attested non-human principal that the agent earned at startup rather than a human it is impersonating. Either way it rides through to Cerbos on every call, and the decision comes back in milliseconds.

One more property falls out of the ordering. agentgateway applies the upstream provider key after ext_authz, so a Cerbos deny stops the request before the model or the MCP server is ever contacted. The denied model call costs nothing. The denied refund never reaches payments. The guardrail is in front of the spend, not behind it.

What a rule looks like

A Cerbos rule reads like the access control it actually is. Here is the pair that governs the refund tool, the join between identity and tool arguments that the demo turns on.

# Tiered support agents may issue refunds...
- name: allow-listed-actions
  actions: [tools/call/refund_issue]
  effect: EFFECT_ALLOW
  roles: [tier-1-support, tier-2-lead]

# ...but a tier-1 agent cannot go over their cap.
- name: tier-1-refund-cap
  actions: [tools/call/refund_issue]
  effect: EFFECT_DENY
  roles: [tier-1-support]
  condition:
    match:
      expr: R.attr.arguments.amount_cents > 5000
  output:
    when:
      ruleActivated: |-
        {
          "decision": "deny",
          "reason": "tier-1-support refund cap exceeded",
          "remediation": { "type": "step_up", "approvers_role": "tier-2-lead" }
        }

R.attr.arguments are the arguments the model produced for the tool call, forwarded by the gateway. P is the calling identity from the token. A separate cross-ticket-guard rule denies when R.attr.arguments.ticket_id does not match the ticket the agent's task is bound to, so an agent can only act on the case it was spun up for. The cap rule does something a proxy-native check cannot. It returns structured context with the denial, and that context flows back to the agent. In the demo the refund handler asks for $9,000, the cap rule fires, and the agent reads the reason and updates the ticket to tell the customer it needs to escalate rather than silently failing. The denial is not just a wall, it is a signal the agent can act on.

Why this matters for AI-agent governance

Area Details
Per-model access at the gateway Different roles and autonomous agents get different model tiers without touching agent code. A triage agent is pinned to a cheap model, and a request for anything off its allow-list denies before any spend reaches the provider.
Per-server and per-tool control before tools reach the model An agent only opens the MCP servers its identity allows. Servers it should not touch, like payments and payroll for an autonomous triage agent, deny at MCP initialize, so their tool catalogues never enter the model's context.
Argument-aware ABAC The gateway forwards the request body, so the policy reads the real tool arguments and joins them with the caller. A refund is allowed only on the agent's bound ticket and only within the caller's cap. Denials carry structured reasons the agent can read and act on.
One PDP for the whole stack Model access, MCP access, and your application's own authorization run on the same engine and the same policy language, with one audit trail across every plane.
Standard extension point agentgateway calls Cerbos over Envoy ext_authz with no adapter. The same policies port to any ext_authz-capable gateway, so the data plane stays swappable.

The kill switch and the audit trail

The reason a gateway is the right place for all of this is that it is also the right place for the things you reach for when an agent goes wrong. Because every model call and every tool call passes one decision point, you get a single record of what every agent tried and what was allowed, enriched with which agent, which harness version, which model, and which case it was bound to. Cerbos Hub is the control plane for that, both the place the policies are versioned and the unified log of every decision across the LLM and MCP planes.

It is also the kill switch. An agent's authority is scoped to a task, and that task is separately revocable. Pull it and the agent's next call denies, no matter how far into its run it is, because the gateway checks on every hop rather than once at the start. There is no standing access to claw back and no agent process to chase down. The authority simply stops being granted.

The broader pattern

agentgateway is one shape of this. The pattern applies to anything sitting on the request path between an agent and the surfaces it reaches. A gateway that speaks ext_authz calls Cerbos with no adapter at all. A proxy with hookable middleware, like LiteLLM, calls it from a guardrail instead. The data layer that enriches the principal is shared. The policy language is the same. The audit trail lives in one place.

If your agents reach OpenAI and Anthropic through one gateway and also proxy a catalogue of MCP servers for your tools, you do not want two policy stores, two audit pipelines, and two sets of role definitions, one for the model plane and one for the tools. You want one place where access policy is written down, tested, and enforced, and you want it to plug into the agent sandboxes, gateways, and MCP servers you already run rather than asking you to rebuild any of them. That is what Cerbos does for agentic systems end to end.

Get a walkthrough on your stack

The fastest way to see whether this fits your setup is to walk through it with the Cerbos team. We can show how the ext_authz route plugs into agentgateway, what the policy bundle looks like for your agent roles and workload identities, and how the decision trail flows back into the system you already use for compliance. Most calls take thirty minutes and end with you knowing what the integration would cost in time and what it would change in your identity model.

Try Cerbos to author and ship a policy bundle for your agents, or book a call to talk through your gateway architecture with the team.

Go deeper:

FAQ

How do you govern what an AI agent is allowed to do at the gateway?

What are the three authorization questions for an AI agent?

What is argument-aware authorization for AI agents?

How does Cerbos integrate with agentgateway without a custom adapter?

How does identity work for autonomous AI agents?

Can one policy engine cover model access, MCP access, and application authorization?

Free policy workshop

Get your first Cerbos policy written by our team.

Book a session to talk through your requirements and walk away with a working policy.

Book a session