Answers
How do I add approval to tool calls in the OpenAI Agents SDK, LangGraph or the Vercel AI SDK?
Wrap the framework’s tools with Immiscible’s guard. Each tool keeps its name and schema, asks before it runs, waits for a person when the rule says so, and returns a refusal to the model as the tool’s result.
Wrap the framework’s tools with Immiscible’s guard: each tool keeps its name, description and schema, asks Immiscible before it runs, waits for a person when the rule says so, and settles afterwards. A refusal comes back to the model as the tool’s result with plain-English reasons, so the agent tells its person instead of trying another way.
#OpenAI Agents SDK
npm install @immiscible/sdk @openai/agentsimport { Agent, run } from '@openai/agents';
import { Immiscible } from '@immiscible/sdk';
import { guardOpenAITools } from '@immiscible/sdk/openai-agents';
const immiscible = new Immiscible().run();
const agent = new Agent({
name: 'Buyer',
tools: guardOpenAITools([buy], {
client: immiscible,
mapToAction: ({ args }) => Immiscible.paymentAction({ amount: args.pence, currency: 'GBP', merchant: args.domain, provenance: [{ source: 'user' }] }),
}),
});
await run(agent, 'Renew the team licence at vendor.example.');The tool call id is the idempotency key, so a retried call is the same action. Python uses guard_tools from immiscible.integrations. Every option: the OpenAI Agents SDK guide.
#LangChain and LangGraph
from immiscible import Immiscible
from immiscible.integrations import guard_langchain_tools
immiscible = Immiscible().run(client="langchain")
tools = guard_langchain_tools(
[buy, search],
client=immiscible,
map_to_action=lambda call: None if call.name == "search"
else Immiscible.payment_action(amount=call.args["pence"], currency="GBP", merchant=call.args["domain"]),
)The guarded tools drop into ToolNode, bind_tools and the prebuilt agents unchanged; None from the mapper means “no decision needed”. TypeScript uses guardLangChainTools from @immiscible/sdk/langchain. See LangChain and LangGraph.
#Vercel AI SDK
import { Immiscible, toolAction } from '@immiscible/sdk';
import { guardAiTools } from '@immiscible/sdk/ai';
const immiscible = new Immiscible().run({ client: 'vercel-ai' });
const tools = guardAiTools({ deploy }, {
client: immiscible,
mapToAction: ({ name, args }) => toolAction(name, args, { domain: 'mycompany.com' }),
});A refusal is returned as the tool’s result, and the generation’s abort signal also aborts a wait for approval. See the Vercel AI SDK.
#Why not use the framework’s own approval feature?
Use it where it is enough. The OpenAI Agents SDK’s tool approvals and LangGraph’s interrupt() pause a run inside one framework for whoever is watching it. Immiscible adds a rule a person wrote outside the code, a named approver reached in the console, Slack or Teams, the same rules and kill switch for every agent whatever its framework, and a signed record of who decided what. The two combine: a framework interrupt can wait on an Immiscible decision.
#What does it not do?
- It guards the tools you wrap. A tool the agent can reach unwrapped, or a credential it holds directly, is not governed; for those use the MCP proxy.
mapToActiondecides what the action is (a payment, a data release, a tool call); a mapper that calls a payment a lookup gets a lookup’s rule.