← Blog

AI Tool Call Policy Enforcement: Why the Tool Surface Is the Real Attack Surface

A chatbot that only generates text has a small attack surface. The same chatbot wired to ten tool functions that read files, query databases, and call external APIs has the attack surface of those ten functions plus the model that decides when to call them. AI tool call policy enforcement evaluates each function invocation against the identity that triggered it, the data classification of its arguments, and the policy version in force. This walkthrough covers the boundary where the gateway sees tool calls, the rules that scale across hundreds of functions, and the audit record per invocation.

ByParminder Singh· Founder & CEO, DeepInspect Inc.
AI Security Solutionstool-callsai-gatewayfunction-callingpolicy-enforcementagentic-ai
AI Tool Call Policy Enforcement: Why the Tool Surface Is the Real Attack Surface

OpenAI's tools parameter, Anthropic's tools parameter, and the equivalent on Bedrock and Gemini turned every chat completion into a potential function invocation. The model reads the request, decides a tool is appropriate, returns a structured tool_call block, and the application invokes the function. The function reaches into the application's data plane and the application's downstream services. The decision to invoke is the model's; the consequence is the enterprise's.

I want to walk through the boundary where the gateway sees tool calls, the rules that scale across hundreds of functions, and the audit record per invocation that the regulator expects.

The tool-call lifecycle the gateway has to see

A tool-call lifecycle has three points of interest.

The first point is the request from the application to the LLM. The request includes the prompt and the tool catalog. The catalog describes every function the model is allowed to suggest. The gateway records the catalog as part of the request envelope so the audit row can show what tools were on the menu.

The second point is the LLM response that contains a tool_call block. The block names a tool and its arguments. The application is about to invoke the function with the arguments the model picked. The gateway inspects the tool_call before the application receives it.

The third point is the function's invocation against the downstream system. Most function calls hit an internal API or an external service over HTTP. The same gateway inspects this call as a separate request and produces a separate audit row.

[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

The gateway sees each arrow. The audit row that links them carries the same request_id so the auditor can reconstruct the whole sequence from a single key.

The classification of tool arguments

Tool arguments often carry richer classification than free-form text. A function signature that takes customer_id, amount, and account_number is self-classifying through the argument names. The gateway maps the schema to classifications at registration time.

[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

The gateway evaluates the rule before the application invokes the function. An agent that does not match the allowed_identities set receives a denied tool_call result; the application surfaces the denial back to the model, which retries with an alternative path or returns a refusal.

Why per-function rules scale better than per-prompt heuristics

A chatbot with 200 tool functions has 200 surfaces a regulator can ask about. A per-function rule set assigns each function an identity allowlist, an argument-classification allowlist, a rate budget, and a residency constraint. The rules live in the policy plane as code, with the policy versioning that the audit record carries.

The alternative path, where the application tries to detect dangerous calls heuristically inside its own code, breaks under several pressures. The application has to understand the data classification of every argument it builds. The application has to know which identity is calling. The application has to log enough to satisfy the audit. The gateway absorbs all three because the gateway sees the call as data, not as code.

The audit record per invocation

The audit row for a tool call carries the model's decision and the application's invocation in linkable form.

[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

downstream_call_id points at the audit row for the HTTP call the function actually made. The pair of rows captures the model's intent and the downstream execution. The auditor's question about whether the function did what the model asked resolves against the pair.

How tool-call enforcement interacts with prompt injection

Prompt injection that survives the model layer typically tries to get the model to invoke a tool with arguments the attacker controls. The first defense is the model's own refusal; the second defense is per-function enforcement at the gateway. The second defense holds when the first fails.

A jailbroken model that produces a tool_call for payments.transfer with attacker-controlled arguments runs into the per-function rule. The agent identity does not match the allowlist; the call is denied; the audit row records the attempt with the classification of the arguments. The denial is visible in the SOC, the SIEM, and the regulator's record.

How this maps to OWASP Top 10 for Agentic Applications

OWASP's 2026 Top 10 for Agentic Applications calls out tool-use risks across several entries. The "agentic skills" layer that OWASP describes is the layer per-function rules operate at. A control-point mapping the gateway implements covers identity binding, argument classification, rate limiting, residency, and per-invocation audit. The CISO's spend-justification mapping points at this layer.

DeepInspect

DeepInspect intercepts tool calls at three points: the request to the LLM that carries the tool catalog, the model's tool_call block before the application invokes, and the application's HTTP call to the downstream service. Each point produces an audit row; the rows link through a shared request_id. Rules are per function and versioned with the policy plane.

The gateway runs in-line with sub-50ms p95 enforcement overhead from internal DeepInspect testing. Denied tool calls return a structured result the application can surface back to the model. Book a technical deep dive at deepinspect.ai to walk through tool-call enforcement against your current agent posture.

Frequently asked questions

Do we need per-function rules for every tool?

The starting position is fail-closed: a tool with no rule is denied. Teams populate rules for the production tool catalog and ship them with the application. The catalog grows with the application; the rules grow alongside.

How does this interact with streaming tool calls?

Anthropic and OpenAI both stream tool_call blocks as part of the response stream. The gateway accumulates the block, evaluates the rule, and forwards the full block to the application or returns a denial. Streaming preserves the model's incremental output for the user; the tool decision is atomic.

What is the rate-budget granularity?

Rate budgets are per-agent-identity, per-tool, per-window. A support agent that calls tickets.escalate 60 times per minute is well outside normal operation; the budget catches the anomaly without affecting the rate of normal chat completions.

How does the gateway record the model's reasoning?

The model's chain-of-thought is not always available; the model's final tool_call block is. The audit row records the block, the policy decision, and the model identifier so the auditor can ask the model team about the decision when needed.

Can the gateway override the model's tool selection?

The gateway denies tool calls it cannot allow under policy; it does not synthesize new tool calls. The application receives the denial and decides how to surface it to the model. The model can pick a different path within its own reasoning.