{"slug": "mcp-guardrails-with-aarm-and-microsoft-agent-governance-toolkit", "title": "MCP Guardrails with AARM and Microsoft Agent Governance Toolkit", "summary": "Agentgateway has added support for MCP guardrails, integrating with Microsoft's Agent Governance Toolkit (AGT) based on the AARM paper to enforce policy decisions in agentic architectures. The system addresses the challenge that AI agents' emergent behavior requires context-aware policy decisions beyond traditional stateless allow/deny models, using richer context and a four-category decision vocabulary (forbidden, context-dependent deny, context-dependent allow, context-dependent defer/escalate).", "body_md": "Agentgateway can call out to policy engines for LLM guardrail or for enterprise policy decisions. We’ve recently added support for MCP guardrails. This blog goes a layer deeper and shows where MCP guardrails would fit into an agentic architecture following the principals from the [AARM paper](https://arxiv.org/html/2602.09433v1).\n\nTL;DR\n\nI have put together a demo that digs into the AGT framework with agentgateway.\n\nYou can find it here: [https://github.com/christian-posta/agent-governance-agw](https://github.com/christian-posta/agent-governance-agw)\n\nAI agents [are not like microservices](https://blog.christianposta.com/difference-between-microservices-and-ai-agents/). An agent’s intent is “interpreted” and explores its way to a solution for a goal. When agentgateway sits on the request path to other agents, MCP tools, or APIs as a policy enforcement point (PEP), we are one step closer to coralling this emergent behavior with policy and guardrails.\n\nTypical policy decision systems ([think things like OPA](https://www.openpolicyagent.org)) are good at evaluating a number of signals and making policy decisions. Think “attribute based access control”. They are stateless, take some “context” to evaluate (subject, roles, the resource being accessed, the action being taken) and return a deterministic allow/deny decision.\n\nFor traditional API calls, this works brilliantly. The context is predictable and discrete: user A with role B is trying to invoke operation C on resource D. Write the Rego rule, ship the policy, done.\n\nBut AI agents break this model. The same MCP tool call can be entirely legitimate in one context and a serious security or compliance violation in another, and the difference isn’t captured in any single call’s attributes. We have to dig into the pattern of calls and the intent behind them. An agent that reads a config file, then queries a database for credentials, then calls an external HTTP endpoint hasn’t violated any individual policy rule **in isolation**. Each step may be fully permitted. The threat is the sequence.\n\nThis is where agent-native governance toolkits like [Microsoft’s Agent Governance Toolkit](https://github.com/microsoft/agent-governance-toolkit) (AGT) come in. AGT is based on the excellent [“AARM” paper](https://arxiv.org/html/2602.09433v1) (Autonomous Action Runtime Management) from [Hermano Errico](https://www.linkedin.com/in/hermanerrico/).\n\nSo what is it that AARM / AGT add on top of something like OPA? Actually these systems do use OPA/cedar/whatever under the covers. So specifically from what I can see it adds is:\n\nAll of this can be fed into the stateless policy engine to get richer decisioning. I recommend reading the [AARM paper](https://arxiv.org/html/2602.09433v1#S2) for more.\n\nBeyond richer context, AARM also introduces a richer **decision vocabulary** than a plain allow/deny. The paper defines four action categories that the policy engine must be able to return:\n\n| Decision | Meaning |\n|---|---|\nForbidden |\nHard block regardless of context. No session history needed — these are absolute organizational limits (e.g., `rm -rf /` , known-malicious endpoints). Static policy suffices. |\nContext-dependent deny |\nThe action is policy-permitted in isolation but blocked because accumulated session context reveals inconsistency with the user’s original intent. Classic example: reading customer PII then immediately emailing an external address — neither action alone triggers a violation, but the composition does. |\nContext-dependent allow |\nDenied by default but permitted when context demonstrates clear alignment with legitimate intent. Deleting database records looks dangerous in isolation; if the session confirms the user said “clean up my test data,” blocking it is wrong. Context transforms a default-deny into an informed allow. |\nContext-dependent defer / escalate |\nRisk cannot be conclusively determined from available context. Rather than committing to an unsafe allow or deny, execution is suspended and escalated for human approval — for example, a credential rotation outside a maintenance window where the context is ambiguous. |\n\nThe AGT implementation maps these to verdict decisions of `allow`\n\n, `deny`\n\n, `warn`\n\n, `escalate`\n\n, and `transform`\n\n. The `transform`\n\nverdict is particularly interesting: instead of rejecting a response outright, AGT can *mutate* it by redacting PII from a tool result before it reaches the agent’s context window, for instance.\n\nOne of the big things that stood out to me in the AARM approach is the need for a trusted component to do things like session accumulation and runtime enforcement of policy. It can be done within the agent (SDK), but that’s technically within an untrusted domain. That’s where agentgateway fits into the picture.\n\n[Microsoft’s Agent Governance Toolkit](https://github.com/microsoft/agent-governance-toolkit) is an implementation of the AARM paper. AARM specifies two main ways to implement its ideas: SDK or proxy/gateway. AGT provides for both. We are going to look at the proxy approach.\n\nBefore looking at the configuration, it helps to understand where in the agent lifecycle AGT can intervene. The ACS (Agent Control Specification) — the policy layer inside AGT — defines eight **intervention points** that span the full agent loop:\n\n| Intervention point | When it fires |\n|---|---|\n`agent_startup` |\nBefore the agent run begins — evaluate session metadata and identity |\n`input` |\nAt request ingress, before the agent loop starts |\n`pre_model_call` |\nBefore the LLM is called — inspect messages, context, and tool definitions |\n`post_model_call` |\nAfter the model responds, before the host acts on it |\n`pre_tool_call` |\nBefore each tool/MCP call executes |\n`post_tool_call` |\nAfter each tool result, before it returns to the agent |\n`output` |\nOn the assembled final response to the user |\n`agent_shutdown` |\nOn session termination — evaluate summaries and audit metadata |\n\nThis matters for the agentgateway integration because agentgateway covers several of these intervention points directly at the network layer: the `mcpGuardrails`\n\nfeature maps onto `pre_tool_call`\n\nand `post_tool_call`\n\n, while agentgateway’s existing [LLM guardrails](https://agentgateway.dev/docs/standalone/latest/llm/prompt-guards/) cover `pre_model_call`\n\nand `post_model_call`\n\n. The remaining points (startup, input, output, shutdown) can be covered by the AGT SDK inside the agent itself, giving you defense in depth across both the application layer and the network layer.\n\n[Agentgateway 1.3](https://github.com/agentgateway/agentgateway/releases/tag/v1.3.0) recently [added a new “mcpGuardrails” functionality](https://github.com/agentgateway/agentgateway/issues/175) (delivered in [PR #1842](https://github.com/agentgateway/agentgateway/pull/1842)) to complement its existing LLM [guardrails capabilities](https://agentgateway.dev/docs/standalone/latest/llm/prompt-guards/) and [External Authz callouts](https://agentgateway.dev/docs/kubernetes/latest/migrate/examples/external-auth/).\n\nThe wire protocol is modeled directly on Envoy’s `ext_authz`\n\n, but operates at the **JSON-RPC method layer** of MCP — gating and mutating individual methods like `tools/call`\n\n, `tools/list`\n\n, `prompts/get`\n\n, and `resources/read`\n\n— rather than at the raw HTTP layer. A remote gRPC policy server implements a two-method service:\n\n```\nservice ExtMcp {\n rpc CheckRequest(McpRequest) returns (McpRequestResult);\n rpc CheckResponse(McpResponse) returns (McpResponseResult);\n}\n```\n\nThat split matters: `ext_authz`\n\nonly sees the inbound request, but `mcpGuardrails`\n\nalso gives the policy server a clean shot at the **response** after agentgateway has merged any fanned-out results into the client-facing view. This is what makes `transform`\n\n-style verdicts (redacting PII from a tool result, for example) feasible without each policy server having to re-implement MCP framing and multiplexing.\n\nFor example, we can configure this mcpGuardrails like this:\n\n```\n mcp:\n guardrails:\n processors: # ordered chain, first reject short-circuits \n - methods: # allowlist with phase per method\n \"tools/call\": Full  # Request | Response | Full | Off\n \"*/list\": Response  # exact, `prefix/*`, `*/suffix`, or `*`\n remote:\n backendRef: # Service or Backend \n name: my-policy-server\n failureMode: FailClosed  # or FailOpen \n metadata: # CEL → google.protobuf.Struct\n tenant: \"request.headers['x-tenant']\"\n allowedRequestHeaders: [x-tenant] \n disallowedRequestHeaders: [\":authority\"]\n```\n\nWith the mcpGuardrails config we can:\n\n`tools/call`\n\n), prefix wildcards (`tools/*`\n\n), suffix wildcards (`*/list`\n\n), or `*`\n\nfor everything.`Request`\n\n, `Response`\n\n, `Full`\n\n, or `Off`\n\n. ``tools/list`\n\nfan out to every backend. Even so, agentgateway fires `CheckRequest`\n\nfor the whole client call (with `service_names`\n\nlisting all targets) and `CheckResponse`\n\non the `Mutated`\n\nrequest replaces the JSON-RPC `params`\n\nbytes before they reach the upstream; a `Mutated`\n\nresponse replaces the JSON-RPC `result`\n\nbefore the client sees it. This is what enables AGT’s `transform`\n\nverdict — redact a field from a `tools/call`\n\nargument, drop a tool from a `tools/list`\n\nresult, scrub a PII column from a database query response.`failureMode: FailClosed`\n\n(default) rejects the call if the policy server is unreachable or returns garbage; `FailOpen`\n\nlets it through. The same setting governs gRPC errors and protocol violations (e.g. a `Mutated`\n\nresponse that doesn’t parse back into a valid `ServerResult`\n\n).`processors`\n\nlist is an ordered chain; the first to `Reject`\n\nshort-circuits, mutations from earlier processors are visible to later ones, and metadata maps merge across the chain.I have put together a demo that digs into the AGT framework with agentgateway.\n\nYou can find it here: [https://github.com/christian-posta/agent-governance-agw](https://github.com/christian-posta/agent-governance-agw)\n\nReady to put MCP guardrails in front of your own agents and tools? Start with the [standalone](https://agentgateway.dev/docs/standalone/latest/quickstart/) or [Kubernetes](https://agentgateway.dev/docs/kubernetes/latest/quickstart/) quickstart, then follow the [MCP guardrails docs](https://agentgateway.dev/docs/standalone/latest/mcp/guardrails/) to wire up an external policy server. Clone the [AGT + agentgateway demo](https://github.com/christian-posta/agent-governance-agw) to walk through the full AARM flow end to end.", "url": "https://wpnews.pro/news/mcp-guardrails-with-aarm-and-microsoft-agent-governance-toolkit", "canonical_source": "https://agentgateway.dev/blog/2026-06-29-mcp-guardrails-microsoft-agent-governance-toolkit/", "published_at": "2026-06-29 00:00:00+00:00", "updated_at": "2026-07-13 13:25:23.247082+00:00", "lang": "en", "topics": ["ai-agents", "ai-safety", "ai-policy", "ai-infrastructure", "ai-research"], "entities": ["Agentgateway", "Microsoft", "Agent Governance Toolkit", "AARM", "Hermano Errico", "OPA"], "alternates": {"html": "https://wpnews.pro/news/mcp-guardrails-with-aarm-and-microsoft-agent-governance-toolkit", "markdown": "https://wpnews.pro/news/mcp-guardrails-with-aarm-and-microsoft-agent-governance-toolkit.md", "text": "https://wpnews.pro/news/mcp-guardrails-with-aarm-and-microsoft-agent-governance-toolkit.txt", "jsonld": "https://wpnews.pro/news/mcp-guardrails-with-aarm-and-microsoft-agent-governance-toolkit.jsonld"}}