Framework adapters

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/denied

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