← Back to Learn
sdktutorialopen-source

Creating framework adapters for Authensor

Authensor

Authensor provides official adapters for LangChain, OpenAI Agents SDK, and CrewAI. If you use a different framework, you can build your own adapter. This guide explains the adapter architecture and walks through building one.

What an adapter does

An adapter has one job: intercept tool calls in a specific framework and route them through Authensor's guard function before execution. The adapter translates between the framework's tool interface and Authensor's evaluation API.

Framework Tool Call → Adapter → guard(toolName, args) → Framework Tool Execution

Adapter interface

Every adapter implements the same pattern:

  1. Intercept: Hook into the framework's tool execution mechanism
  2. Extract: Pull the tool name and arguments from the framework's format
  3. Evaluate: Call guard(toolName, args)
  4. Handle: Based on the decision (allow, block, escalate), either proceed, return an error, or wait for approval
  5. Log: Ensure the receipt is recorded

Building an adapter: step by step

Here is how to build an adapter for a hypothetical framework called "AgentKit":

Step 1: Identify the interception point

Find where AgentKit executes tool calls:

// AgentKit's tool execution
class AgentKitRunner {
  async executeTool(tool: Tool, args: unknown): Promise<unknown> {
    return await tool.execute(args);
  }
}

Step 2: Create the wrapper

import { createGuard, type Guard } from '@authensor/sdk';

export function withAuthensor(
  runner: AgentKitRunner,
  options: AuthensorOptions
): AgentKitRunner {
  const guard = createGuard(options);

  const originalExecute = runner.executeTool.bind(runner);

  runner.executeTool = async (tool: Tool, args: unknown) => {
    // Step 3: Evaluate
    const decision = guard(tool.name, args);

    // Step 4: Handle
    if (decision.action === 'block') {
      return { error: `Blocked: ${decision.reason}` };
    }

    if (decision.action === 'escalate') {
      if (options.onEscalate) {
        const approved = await options.onEscalate(decision);
        if (!approved) {
          return { error: `Denied: ${decision.reason}` };
        }
      } else {
        return { error: `Escalated: ${decision.reason}` };
      }
    }

    // Allowed: execute the tool
    return await originalExecute(tool, args);
  };

  return runner;
}

Step 3: Add metadata forwarding

Pass framework-specific metadata to the guard for richer receipts:

runner.executeTool = async (tool: Tool, args: unknown) => {
  const decision = guard(tool.name, args, {
    framework: 'agentkit',
    toolDescription: tool.description,
    runId: runner.currentRunId,
  });
  // ...
};

Testing your adapter

Test that the adapter correctly intercepts, evaluates, and handles each decision type:

test('blocks unauthorized tool calls', async () => {
  const runner = withAuthensor(new AgentKitRunner(), {
    policy: { rules: [{ tool: '*', action: 'block' }] },
  });

  const result = await runner.executeTool(searchTool, { query: 'test' });
  expect(result.error).toContain('Blocked');
});

test('allows authorized tool calls', async () => {
  const runner = withAuthensor(new AgentKitRunner(), {
    policy: { rules: [{ tool: 'search', action: 'allow' }] },
  });

  const result = await runner.executeTool(searchTool, { query: 'test' });
  expect(result.error).toBeUndefined();
});

Publishing your adapter

If you build an adapter for a framework that does not have one, consider contributing it to the Authensor repository or publishing it as a community package. Adapters are small (typically under 200 lines) and benefit the entire community.

Keep learning

Explore more guides on AI agent safety, prompt injection, and building secure systems.

View All Guides