An agent should not treat every available tool as permission to execute it.
A production-friendly tool layer needs at least three outcomes:
-
allow: safe to execute automatically -
review: pause and ask a person -
deny: reject the call
Here is a small, dependency-free gate you can run with Node.js.
Prerequisites
- Node.js 18 or later
- No external packages
Save the following as approval-gate.mjs:
const policy = {
searchDocs: { risk: "low", required: ["query"] },
updateCustomer: { risk: "medium", required: ["customerId", "patch"] },
issueRefund: { risk: "high", required: ["paymentId", "amount"] },
};
function decideToolCall(call) {
const rule = policy[call.name];
if (!rule) return { decision: "deny", reason: "Unknown tool" };
const missing = rule.required.filter(
(key) => call.arguments[key] == null,
);
if (missing.length) {
return {
decision: "deny",
reason: `Missing: ${missing.join(", ")}`,
};
}
if (rule.risk === "low") return { decision: "allow" };
return {
decision: "review",
reason: `${rule.risk}-risk action requires human approval`,
approvalKey: call.id,
};
}
const calls = [
{
id: "call_1",
name: "searchDocs",
arguments: { query: "refund policy" },
},
{
id: "call_2",
name: "issueRefund",
arguments: { paymentId: "p_123", amount: 49 },
},
{
id: "call_3",
name: "deleteWorkspace",
arguments: { workspaceId: "w_1" },
},
];
console.table(
calls.map((call) => ({ tool: call.name, ...decideToolCall(call) })),
);
Run it:
node approval-gate.mjs
Expected decisions:
```plain text
searchDocs allow
issueRefund review
deleteWorkspace deny
## Why this pattern helps
### 1. Unknown tools fail closed
If a model invents a tool name or reaches a tool that was not registered for this workflow, the gate returns `deny`. It never assumes that an unknown action is harmless.
### 2. Arguments are checked before execution
The example only checks required fields. In production, validate types, ranges, identifiers, and object shapes with a schema library before any side effect.
### 3. Risk changes the execution path
Low-risk reads can continue. Medium- and high-risk actions return `review` with a stable `approvalKey`. A UI or queue can use that identifier to collect a human decision.
## What to add before production
This example is intentionally small. A real approval service should also include:
- An immutable audit log
- An approval expiry time
- The exact tool arguments shown to the reviewer
- Idempotency protection
- The reviewer identity and decision reason
- A fresh authorization check when execution resumes
- Tests for policy changes and previously observed failures
Do not execute the original call just because an approval record exists. Verify that the approval matches the same tool, arguments, user, and current policy version.
OpenAIโs Presence announcement describes the same broader production problem: companies need to define allowed actions, approval points, evaluations, escalation rules, and controlled changes after launch.
If your agent also needs one OpenAI-compatible API for models across providers, RouterBase is available at [https://routerbase.com](https://routerbase.com).
Top comments (0)