# 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.

Source: https://immiscible.fly.dev/docs/answers/framework-tool-approvals

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

```bash
npm install @immiscible/sdk @openai/agents
```

```ts
import { 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](https://immiscible.fly.dev/docs/sdks/integrations/openai-agents.md).

## LangChain and LangGraph

```python
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](https://immiscible.fly.dev/docs/sdks/integrations/langchain.md).

## Vercel AI SDK

```ts
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](https://immiscible.fly.dev/docs/sdks/integrations/vercel-ai.md).

## 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](https://immiscible.fly.dev/docs/guides/mcp-proxy.md).
- `mapToAction` decides 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.
