Framework adapters
Eda is all-in-one: the same package that gives you eda.check() also ships
first-class wrappers for every major agent framework. The philosophy is simple —
if a tool call runs in JavaScript, Eda can gate it, usually in one line. Every
adapter is exported from @eda-holding-inc/sdk.
All adapters funnel into the same eda.check() and the same audit log. Pick the
one that matches your stack; mix them freely in one app.
The universal wrapper: gateTool
Wrap any async function into a gated version. This is the lowest common denominator — every other adapter is sugar over this.
import { Eda, gateTool } from "@eda-holding-inc/sdk";
const eda = new Eda({ apiKey: process.env.EDA_API_KEY! });
const sendEmail = gateTool(
async ({ to, subject, body }) => mailer.send({ to, subject, body }),
{ eda, agent: "support-agent", action: "send_email" },
);
await sendEmail({ to: "user@acme.com", subject: "Hi", body: "…" });
// checks first; throws EdaDeniedError if blocked/deniedYou can derive the action name and params dynamically:
gateTool(fn, {
eda,
agent: "ops",
action: (args) => `deploy_${args.service}`,
params: (args) => ({ service: args.service, env: args.env }),
});Vercel AI SDK
Gate every tool in a tools object with one call — each execute is checked
before it runs.
import { generateText } from "ai";
import { openai } from "@ai-sdk/openai";
import { Eda, gateAiSdkTools } from "@eda-holding-inc/sdk";
const eda = new Eda({ apiKey: process.env.EDA_API_KEY! });
const tools = gateAiSdkTools(
{
refundCustomer: {
description: "Refund a customer",
parameters: z.object({ customerId: z.string(), amount: z.number() }),
execute: async ({ customerId, amount }) =>
stripe.refunds.create({ customer: customerId, amount }),
},
},
{ eda, agent: "support-agent" },
);
await generateText({ model: openai("gpt-4o"), tools, prompt });When Eda blocks a tool call, the adapter returns a structured “blocked” result to the model (with the reason) so the agent can explain itself instead of crashing.
OpenAI function calling
Guard a tool call the model emitted before you execute it.
import { Eda, guardOpenAiToolCall } from "@eda-holding-inc/sdk";
const eda = new Eda({ apiKey: process.env.EDA_API_KEY! });
for (const toolCall of message.tool_calls ?? []) {
const decision = await guardOpenAiToolCall(toolCall, { eda, agent: "support-agent" });
if (decision.status !== "approved") {
// hand the reason back to the model as the tool result
submitToolResult(toolCall.id, { blocked: true, reason: decision.reason });
continue;
}
await runTool(toolCall);
}Anthropic tool use
Same idea for Anthropic’s tool_use blocks.
import { Eda, guardAnthropicToolUse } from "@eda-holding-inc/sdk";
const eda = new Eda({ apiKey: process.env.EDA_API_KEY! });
for (const block of response.content) {
if (block.type !== "tool_use") continue;
const decision = await guardAnthropicToolUse(block, { eda, agent: "ops-bot" });
if (decision.status !== "approved") {
toolResults.push({ type: "tool_result", tool_use_id: block.id, content: `Blocked: ${decision.reason}`, is_error: true });
continue;
}
toolResults.push(await runTool(block));
}LangChain / LangGraph
Wrap a LangChain tool so its _call/invoke is gated.
import { Eda, gateLangChainTool } from "@eda-holding-inc/sdk";
const eda = new Eda({ apiKey: process.env.EDA_API_KEY! });
const gatedRefund = gateLangChainTool(refundTool, { eda, agent: "support-agent" });
// use `gatedRefund` anywhere you'd use `refundTool` — in an AgentExecutor, a graph node, etc.MCP
Gate a Model Context Protocol server so every tools/call is approved first — with
zero changes to the tools themselves.
import { Eda, gateMcpHandler } from "@eda-holding-inc/sdk";
const eda = new Eda({ apiKey: process.env.EDA_API_KEY! });
// wrap your MCP request handler
server.setRequestHandler(CallToolRequestSchema, gateMcpHandler(myHandler, { eda }));There’s a lot more to MCP — a standalone Eda MCP server, a proxy for any existing server, and dashboard-managed connections. See MCP.
Bring your own framework
Nothing stops you from calling eda.check() directly wherever your agent decides to
act — the adapters are conveniences, not requirements. If you’re on a framework we
don’t list, use gateTool or a raw check(); it’s the same three lines.