Policy-as-Code Enforcement for AI Agent Tool Calls
Enforce policies at tool-call time before agents act on malicious instructions.

AI agents now make decisions and call tools faster than any human can review them, and the industry has been trying to govern that behavior with prose. Policy-as-code is the only approach built to enforce, rather than suggest, what an agent is allowed to do at the exact moment it tries to do it. This piece lays out why natural language fails at that job, what happens if nobody fills the gap, and what a working policy system has to cover.
Catching a malicious chain of individually benign tool calls requires a policy engine that evaluates each call against the full session history, not just the arguments of the call in front of it. The Vercel AI SDK's @ai-sdk/policy-opa package demonstrates this concretely: the input it passes to Open Policy Agent's Rego rules carries the complete message history and tool-call history for the entire run, so a rule can detect accumulating patterns across prior calls, running totals, and sequences that only become harmful in combination. A policy schema also needs a temporal dimension that tracks behavior across sessions, because the systematic survey of agentic-attack research identifies cross-session cumulative threats as simultaneously the least-studied and most severe category, visible only once behavior is tracked across multiple sessions or a composed stack of actions. A single-call check cannot catch this; the policy input has to carry the chain, and the schema has to contain rules written to read it.
Why tool-call governance cannot be expressed in natural language
An agent built on the Model Context Protocol can decide to read a file, query a database, or call another agent dozens of times in a single task, each decision made in milliseconds, with no human in the loop to catch a bad one before it runs. Human review works on a schedule measured in hours or days. Agent action runs on a schedule measured in model inference time. That mismatch is the whole reason natural-language governance cannot hold: tool selection and invocation are mediated entirely by free-form natural-language descriptions interpreted at inference time by the LLM, the same mechanism that makes MCP powerful makes it ungovernable through prose.
MCP, released by Anthropic in November 2024, gave agents a standard way to reach file systems, databases, web services, and other agents through a shared JSON-RPC 2.0 message format. That standardization is what made adoption so fast, and what makes governance hard, because the same mechanism that lets an agent pick the right tool for a job is a free-form natural-language description that the model reads and interprets on the fly. A prompt is a suggestion to the model. A policy is an enforceable rule. Nothing in a system prompt stops an agent from acting on a cleverly worded tool description, because the model has no way to tell an instruction from its developer apart from an instruction embedded in whatever text it happens to be reading.
MCP is so easy to build on that the exposure gets worse, not better. A developer can stand up an MCP server against a production database in an afternoon, with none of the scrutiny a traditional integration would get from a security review, and across a landscape of more than 10,000 active public MCP servers as of 2026, the basic questions, which ones are trusted, what permissions they carry, whether their definitions have changed since anyone looked at them, who is accountable for them, stop having clear answers. Gartner named agentic AI oversight its top cybersecurity trend for 2026 for this reason: unmanaged agent proliferation is already running ahead of the governance structures meant to contain it. None of this is solved by writing better prompts.
The attack surface that emerges when tool calls go ungoverned
The risks in an ungoverned tool-call environment are not hypothetical. You can find them in disclosed incidents, in benchmark studies, and in live CVEs across the MCP ecosystem.
Tool poisoning is the clearest example of how natural-language trust breaks down. An agent trusts a tool's description as much as its own system prompt, so if an instruction is hidden inside that description, the agent carries it out with no separate check. A finance team's agent was connected to a third-party invoice enrichment tool that had been approved once but never checked again, Microsoft's Defender research team disclosed, and when an attacker later edited the tool's description to exfiltrate unpaid invoices, MCP picked up the change on its own, with no re-approval step to catch it. The MCPTox benchmark put this risk in numbers: it tested adversarial versions of real tools pulled from 45 live MCP servers against twenty language models. The average attack success rate across all models came out above a third, and even Claude-3.7-Sonnet, the most resistant model tested, still refused poisoned instructions only rarely.
Credential theft is already producing large-scale breaches, not small test cases. In August 2025, the threat actor UNC6395 used stolen OAuth tokens issued to the Salesloft Drift chatbot's integration with Salesforce to reach customer Salesforce environments directly through the API, pulling data out of hundreds of organizations in the process. A separate architectural flaw disclosed by OX Security sits deeper in the supply chain: it affects an estimated 200,000 vulnerable instances across a chain of software tied to hundreds of millions of package downloads. Exposure at the server level compounds the problem. Trend Micro found 492 MCP servers sitting open on the internet with zero authentication, and separate research published in early 2026 confirmed that large numbers of active public MCP servers still run with no authentication at all; the spec allows this, but in practice it is not secure.
The EchoLeak attack, tracked as CVE-2025-32711, showed what this looks like with zero user involvement at all. A specially crafted email was enough to trigger Microsoft 365 Copilot into quietly sending sensitive information, email summaries, calendar details, document contents, to a server controlled by the attacker, without the user clicking anything or approving anything. A systematic survey of research papers published between 2021 and April 2026 found a pattern across these incidents: agentic attacks are emergent, with harm coming from the combination of individually authorized actions rather than any one action on its own; compositional, since a defense placed at one point in the system cannot catch an attack routed through a different component; and temporally extended, since a payload can sit dormant for weeks before it executes. As of May 2026, at least seven confirmed high- or critical-severity CVEs span major MCP-integrated platforms, including MCP Inspector, LiteLLM, Cursor IDE, LibreChat, and Windsurf. None of these incidents trace back to a single broken rule. They trace back to the absence of any rule enforced at the moment the tool call happened.
The identity and visibility gap that makes every other control unreliable
Every policy depends on knowing who, or what, is asking for access. Without that, the policy has nothing to check against, and that is the condition most enterprises are actually in today. Only a small minority of organizations have a formal, enterprise-wide strategy for agent identity, and an even smaller share say they are highly confident their identity and access management systems can handle agent identities at all. Among the same group of security leaders surveyed, 86% do not enforce access policies for AI identities, even though those AI systems already reach into core business platforms like ERP, CRM, and financial systems, the 2026 CISO AI Risk Report found.
MCP's real-world authentication practices lag the protocol's own spec by a wide margin. Only a small fraction of MCP server deployments implement any form of access scoping for tool permissions, and most expose credentials as hard-coded values sitting in plain configuration files. The protocol still has no reliable way to prove which agent or runtime holds a given token, so verifiable workload identity remains unsolved, and without it no policy enforcement can mean much.
Enterprise identity tooling is starting to close part of this gap. Platforms like Microsoft Entra, Keycloak, and Auth0 can act as centralized authorization servers for MCP connections, Auth0's "Auth for MCP" became generally available in May 2026, and Microsoft Azure Foundry now supports OAuth identity passthrough, carrying a user's identity and permissions through to every agent-to-MCP-server call they trigger. These are meaningful steps, but they are infrastructure, and the deeper point stands regardless of which vendor closes the gap first: agent identity built on just-in-time credentials issued per session is the precondition for policy-as-code, not an optional extra sitting alongside it. If the system cannot identify an actor, it cannot evaluate a policy against it.
How do you enforce policy-as-code at the tool-call boundary so that a chain of individually benign calls is still caught?
Policy-as-code splits the decision of whether an action is allowed from the agent's own act of taking it, so a machine-evaluable rule sits between what the agent intends and what the tool executes. This pattern has a long track record in infrastructure security, and the architecture follows the same policy decision point and policy enforcement point split used in access-control standards like XACML: a central control plane defines and evaluates the policy, while enforcement points sit inline with the agent's actions, distributed wherever those actions happen. Open Policy Agent already runs on this same logic for infrastructure and Kubernetes authorization, and now it extends to cover every tool call an agent makes. The enforcement point sits not at deploy time but at call time: policy gets evaluated on every single action, during a live session, in the exact window between the agent deciding to act and the action actually running.
That enforcement can take three forms, and most working systems combine all three. Preventive enforcement blocks or modifies an action before it ever executes, the strongest form of control, applied directly at tool calls, API access points, and data reads. Detective monitoring catches policy violations or behavioral drift after the fact, and it feeds the audit records and alerts that later investigation depends on. Corrective enforcement kicks in automatically once a violation is confirmed: it can revoke access, end a session outright, or roll back changes that already happened. The same design challenges that operating systems have handled for decades, isolating resources, separating privileges, mediating communication between processes, map almost directly onto the problem of controlling what an agent's tool calls can touch, and many of the OS techniques built to solve them translate with little modification.
Production systems already reflect this pattern. Amazon Web Services built policy enforcement directly into Amazon Bedrock AgentCore, where policy lives entirely outside the agent and gets enforced before the tool invocation through AgentCore Gateway, regardless of what the agent does, how it was prompted, or what bugs exist in the agent's own code. The Vercel AI SDK takes a related approach through its @ai-sdk/policy-opa package, which applies Open Policy Agent's Rego language to agent tool calls; the policy input it evaluates carries not just the arguments of the current call but the full message history and tool-call history for the entire run, so a rule can account for what already happened earlier in the session, prior calls, sequences of actions, running totals across the conversation. This matters for the attacks described earlier, because a single-call check cannot catch harm that appears only once several individually fine actions are strung together.
Even well-built enforcement has an architectural blind spot: policy enforcement only fires when the model calls a tool directly, so a coarse dispatcher tool, a bash tool that can run a command like git push, a generic HTTP tool, or an MCP proxy, gives the model a way to route around a per-action rule entirely by hiding the real action inside a broader call the policy was never written to inspect. A policy design has to account for this gap on purpose; it cannot assume that covering the obvious tool calls covers everything that matters.
What a complete tool-call policy schema must express
A policy schema that only handles allow or deny decisions on tool names is not doing the job a tool-call policy needs to do. Real coverage means the schema has to express rules across several dimensions at once, and missing any one of them leaves a gap an attacker, or an ordinary mistake, can walk through.
The schema needs an identity dimension that ties every rule to a specific agent acting on behalf of a specific human identity, under a specific enterprise role, since a policy bound only to a tool name, with no identity attached, cannot distinguish a legitimate call from an impersonated one. It needs a scope dimension that checks what permissions a tool actually carries, whether those permissions still match least-privilege for the task at hand, and whether scope has crept wider since the tool was last approved; the OWASP MCP Top 10, currently in beta (Phase 3) as of 2026, with risks numbered MCP01:2025–MCP10:2025 under v0.1 and actively updated through 2026, names privilege escalation via scope creep as a primary risk category, and a schema has to be able to express scope boundaries and detect drift. It needs a context dimension that tracks what the agent has already done earlier in the session, because a policy that checks only the current call's arguments will miss attacks that only show up once you consider a sequence of actions together.
It needs a tool integrity dimension, because tool poisoning succeeds precisely by changing a tool's description after approval, and MCP picks up that change automatically; so a policy schema has to include a mechanism that detects and blocks mutations nobody signed off on. It needs a data sensitivity dimension that tracks what data a given tool can read or write, whether that data crosses a classification boundary it shouldn't, and whether a path for exfiltration exists. It needs a temporal dimension too, and this is the one most schemas miss first. The systematic survey of agentic-attack research finds that cross-session cumulative threats and sub-session-stack threats are both the least-studied categories in the literature and the most severe, because you only see the harm once you track behavior across multiple sessions or across a composed stack of actions, never within any single call. A schema built only to judge one call at a time will never catch this category of harm, no matter how carefully each individual rule is written.
Finally, the schema needs to distinguish a hard gate from a soft gate for every governance intervention it makes. The EU AI Act's Article 12 requires high-risk AI systems to automatically log events, including the period of each use, which reference databases were checked, the input data that produced a match, and the natural persons who verified the results, but the Act itself does not require systems to record whether a given intervention was a hard block or a soft warning. That distinction still has to be built into the schema directly, because without it, the audit record cannot show whether a violation was actually stopped or merely flagged after the fact.
Audit trails and the compliance obligations that require them
Enforcement at the tool-call level only has value if it leaves behind a record that can be checked later, and that record is no longer optional in several jurisdictions. The EU AI Act's full enforcement window opened August 2, 2026, and organizations deploying AI agents that influence decisions affecting people in high-risk categories now face fines reaching €15 million or a percentage of global annual turnover, whichever is higher, with even steeper caps reserved separately for prohibited practices under Article 5. A policy engine that blocks a bad tool call but keeps no record of the block, the identity behind it, or the data it touched, satisfies the enforcement half of the requirement and fails the accountability half. Article 12's logging requirements, covering the period of use, the reference databases checked, the input data behind a match, and the people who verified the outcome, describe exactly the kind of record a tool-call policy engine is positioned to produce automatically, as long as it was built from the start to capture identity, scope, context, tool integrity, data sensitivity, and the hard-gate or soft-gate distinction together, in one trail, rather than scattered across logs nobody designed to be read as evidence.


