ToolApprovalGuard inspects the tool calls an agent proposes while pausing for human approval, before those calls are ever surfaced for review.
It can block, log, or fully delegate handling to a custom callback.
Why this middleware exists
Every other Intercept middleware inspects what goes out: the prompt, and the text a human supplies when resolving a paused run. None of them see what comes back in. Tool results, retrieved documents, and conversation history never pass through the middleware pipeline, which is exactly where indirect prompt injection lives. When an agent is manipulated by content Intercept never saw, the damage almost always surfaces as a tool call:ToolApprovalGuard inspects those proposed calls. It is the first point downstream of that blind spot where the middleware pipeline can still act.
By default it is the card number that flags this call, not the address — a mail tool is expected to carry an email address. See secret-like data for why the defaults are drawn that way.
Installation
Basic usage
How it differs from the other middleware
This is the first Intercept middleware that acts on the response rather than the prompt, because the tool calls it guards are proposed by the model.What it checks
Three checks run over every proposed tool call.Tool policy
An emptyallowed_tools list permits every tool. A non-empty list permits only the tools named in it. Anything in denied_tools is always refused.
Secret-like data
The middleware runs the same detectors as PII Redactor, including the Luhn check on card numbers. By default it scans for only three entities:credit_card, api_key and bearer_token.
Entities listed in block_entities stop the run regardless of the configured action, matching PII Redactor’s behaviour.
Prompt injection patterns
A proposed argument that matches an injection pattern suggests the model was manipulated by content the middleware never saw. Uses the same patterns as Injection Guard. This scan is off by default. Prose a model writes for a human reader routinely contains phrases the patterns match — “You are now subscribed to weekly updates.”, “From now on we will email you every Monday.”, “System: scheduled maintenance at 3pm.” None of those indicate manipulation in that context. Enable it when a proposed argument feeds another model or agent rather than a person:Supported actions
There is deliberately no mutating action. A proposed tool call is part of the paused turn the provider recorded, so rewriting it would desynchronise the run when it is resumed — the same constraint that applies to resumed prompts.
Streaming
On a streamed run, the middleware cannot block.$next() returns before the model has proposed anything, and by the time the completion hook fires the caller has already received the streamed text.
The action therefore degrades to logging, recorded as degraded_from, matching the pattern used for tool approval resumes.
The security outcome still holds: the tool has not executed. Approval is a separate later call, so a logged-but-unblocked proposal still requires a human to act on it before anything happens.
Blocked runs
The exception names the offending tool call and field, but never the matched value:Reading the logs
Findings are logged with asource of pending_approvals:
log_preview to true to add a short cleartext preview, which is off by default because proposed arguments can carry sensitive data.
The type is one of denied_tool, pii, or injection. The detail carries the entity type or the matched pattern.
Custom callback handling
A callback receives the prompt, the response, and the findings, and fully replaces the configured action:ApprovalFinding exposing toolCallId, tool, type, field, detail, and value. Use reference() for a string that names the location without leaking the matched value.
Configuration
All options may be set globally inconfig/intercept.php under tool_approval_guard, or per agent through the constructor. Constructor values always win.
Limitations
This middleware inspects the tool calls the model proposed. It does not inspect the tool results, retrieved documents, or conversation history that may have influenced them — those never reach the middleware pipeline. It also does not validate arguments against the tool’s schema, or re-scope owner keys such asuser_id server-side. Do that in your own tool implementations.
Read the security notes for the full picture of what Intercept can and cannot see.