SDK Guide

Installation note

The SDKs are currently distributed as part of the TameFlare monorepo. Install them locally:

# Node.js — from the monorepo root
npm install ./packages/sdk-node
 
# Python — from the monorepo root
pip install ./packages/sdk-python

When published to npm/PyPI (coming soon), you'll be able to install with npm install @agentfirewall/sdk and pip install agentfirewall.


Node.js

# Install from source (npm package coming soon)
npm install ./packages/sdk-node

Basic usage

import { AgentFirewall } from "@agentfirewall/sdk";
 
const TameFlare = new AgentFirewall({
  apiKey: process.env.AAF_API_KEY,
  baseUrl: process.env.AAF_BASE_URL ?? "http://localhost:3000/api",
});
 
const result = await TameFlare.requestAction({
  type: "github.pr.merge",
  resource: {
    provider: "github",
    account: "my-org",
    target: "my-org/my-repo",
    environment: "production",
  },
  parameters: { pull_number: 42 },
});
 
if (result.status === "allowed") {
  await TameFlare.execute(result.action_request_id, result.decision_token);
} else if (result.status === "pending_approval") {
  const approved = await TameFlare.waitForApproval(result.action_request_id);
  if (approved) await TameFlare.execute(result.action_request_id, approved.decision_token);
}

Handling all outcomes

const result = await TameFlare.requestAction({
  type: "payment.transfer.initiate",
  resource: {
    provider: "stripe",
    account: "acct_123",
    target: "transfers",
    environment: "production",
  },
  parameters: { amount: 25000, currency: "USD" },
  risk_hints: {
    financial_impact: true,
    irreversible: true,
    production_target: true,
  },
});
 
switch (result.status) {
  case "allowed":
    // Action approved immediately — execute it
    const execResult = await TameFlare.execute(
      result.action_request_id,
      result.decision_token
    );
    console.log("Executed:", execResult);
    break;
 
  case "pending_approval":
    // Action needs human approval — wait or use webhook
    console.log("Waiting for approval from:", result.approval.required_approver_groups);
    const approved = await TameFlare.waitForApproval(result.action_request_id, {
      timeoutMs: 300_000, // 5 minutes
      pollIntervalMs: 5_000, // check every 5s
    });
    if (approved) {
      await TameFlare.execute(result.action_request_id, approved.decision_token);
    } else {
      console.log("Approval timed out or was rejected");
    }
    break;
 
  case "denied":
    // Action blocked by policy
    console.log("Denied:", result.decision.reason);
    // Do NOT retry — the policy will deny it again
    break;
}

Error handling and rate limits

try {
  const result = await TameFlare.requestAction({ /* ... */ });
} catch (error) {
  if (error.status === 429) {
    // Rate limited — the SDK auto-retries by default
    // but you can handle it manually if needed
    const retryAfter = error.retryAfter; // seconds
    console.log(`Rate limited. Retry after ${retryAfter}s`);
  } else if (error.status === 401) {
    // Invalid API key
    console.error("Check your AAF_API_KEY");
  } else {
    console.error("Unexpected error:", error.message);
  }
}
Tip
The Node.js SDK automatically retries on 429 (rate limit) and 5xx (server error) responses with exponential backoff. You can disable this with autoRetry: false in the client config.

Failover mode (circuit breaker)

By default, the SDK throws an error when TameFlare is unreachable (failoverMode: "closed"). You can switch to "open" mode to get a synthetic deny result instead of an exception:

const TameFlare = new AgentFirewall({
  apiKey: process.env.AAF_API_KEY,
  failoverMode: "open",
  onFailover: (error) => {
    // Alert your monitoring system
    console.error("TameFlare unreachable, failover triggered:", error.message);
  },
});
 
// If TameFlare is down, this returns a deny result instead of throwing
const result = await TameFlare.requestAction({ /* ... */ });
if (result.status === "denied" && result.decision.reason.includes("failover")) {
  // Handle gracefully — TameFlare was unreachable
}

| Mode | Behavior when TameFlare is down | Use case | |---|---|---| | closed (default) | Throws error — agent must handle | Safety-critical: no action without TameFlare | | open | Returns synthetic deny | Graceful degradation: agent gets a clear signal |

Using webhooks instead of polling

Instead of polling for approval status, you can provide a webhook_url to be notified when a decision is made:

const result = await TameFlare.requestAction({
  type: "github.pr.merge",
  resource: { /* ... */ },
  parameters: { pull_number: 42 },
}, {
  webhookUrl: "https://your-server.com/TameFlare-webhook",
});
 
// Your webhook endpoint receives:
// POST /TameFlare-webhook
// {
//   "action_request_id": "act_abc123",
//   "status": "approved",
//   "decision_token": "eyJhbGci..."
// }

Python

# Install from source (PyPI package coming soon)
pip install ./packages/sdk-python

Basic usage

from agentfirewall import AgentFirewall
import os
 
TameFlare = AgentFirewall(
    api_key=os.environ["AAF_API_KEY"],
    base_url=os.environ.get("AAF_BASE_URL", "http://localhost:3000/api"),
)
 
result = TameFlare.request_action(
    action_type="github.issue.create",
    provider="github",
    account="my-org",
    target="my-org/my-repo",
    environment="development",
    parameters={"title": "Bug fix", "body": "Details..."},
)
 
if result.status == "allowed":
    TameFlare.execute(result.action_request_id, result.decision_token)

Handling all outcomes

result = TameFlare.request_action(
    action_type="payment.transfer.initiate",
    provider="stripe",
    account="acct_123",
    target="transfers",
    environment="production",
    parameters={"amount": 25000, "currency": "USD"},
    risk_hints={
        "financial_impact": True,
        "irreversible": True,
        "production_target": True,
    },
)
 
if result.status == "allowed":
    exec_result = TameFlare.execute(result.action_request_id, result.decision_token)
    print(f"Executed: {exec_result}")
 
elif result.status == "pending_approval":
    print(f"Waiting for approval from: {result.approval.required_approver_groups}")
    approved = TameFlare.wait_for_approval(
        result.action_request_id,
        timeout_seconds=300,
        poll_interval_seconds=5,
    )
    if approved:
        TameFlare.execute(result.action_request_id, approved.decision_token)
    else:
        print("Approval timed out or was rejected")
 
elif result.status == "denied":
    print(f"Denied: {result.decision.reason}")

Async client

from agentfirewall import AsyncAgentFirewall
import asyncio
 
async def main():
    TameFlare = AsyncAgentFirewall(
        api_key=os.environ["AAF_API_KEY"],
    )
 
    result = await TameFlare.request_action(
        action_type="infra.server.provision",
        provider="aws",
        account="123456789",
        target="ec2",
        environment="staging",
        parameters={"instance_type": "t3.large", "count": 3},
    )
 
    if result.status == "allowed":
        await TameFlare.execute(result.action_request_id, result.decision_token)
 
asyncio.run(main())

Error handling

from agentfirewall.exceptions import RateLimitError, AuthError, AAFError
 
try:
    result = TameFlare.request_action(...)
except RateLimitError as e:
    print(f"Rate limited. Retry after {e.retry_after}s")
except AuthError:
    print("Invalid API key")
except AAFError as e:
    print(f"TameFlare error: {e.message}")

CLI

The CLI is useful for managing agents and policies from your terminal or CI/CD pipelines.

npx tf init          # Set up TameFlare config in current directory
npx tf status        # Check control plane connectivity
npx tf agents list   # List all registered agents
npx tf policies list # List all policies

Agent management

# Create a new agent
npx tf agents create --name "deploy-bot" --env production
 
# Revoke an agent's API key
npx tf agents revoke --id agent_abc123
 
# View agent activity
npx tf agents activity --id agent_abc123 --limit 20

Policy management

# Install a policy from a YAML file
npx tf policies install ./policies/payment-controls.yaml
 
# List all policies with status
npx tf policies list
 
# Dry-run a policy against a test action
npx tf policies dry-run \
  --type payment.transfer.initiate \
  --provider stripe \
  --env production \
  --param amount=25000
Tip
The CLI reads configuration from .TameFlare.yaml in your project root. Run npx tf init to create one with your API key and base URL.

Framework integration

LangChain (Python)

Wrap TameFlare checks around LangChain tool calls:

from langchain.tools import tool
from agentfirewall import AgentFirewall
 
TameFlare = AgentFirewall(api_key=os.environ["AAF_API_KEY"])
 
@tool
def create_github_issue(title: str, body: str, repo: str) -> str:
    """Create a GitHub issue with TameFlare policy enforcement."""
    result = TameFlare.request_action(
        action_type="github.issue.create",
        provider="github",
        account="my-org",
        target=repo,
        environment="production",
        parameters={"title": title, "body": body},
    )
 
    if result.status == "denied":
        return f"Blocked by policy: {result.decision.reason}"
 
    if result.status == "allowed":
        exec_result = TameFlare.execute(result.action_request_id, result.decision_token)
        return f"Issue created: {exec_result.result}"
 
    return f"Pending approval from: {result.approval.required_approver_groups}"

OpenAI function calling (Node.js)

Gate tool execution behind TameFlare before calling external APIs:

import { AgentFirewall } from "@agentfirewall/sdk";
import OpenAI from "openai";
 
const TameFlare = new AgentFirewall({ apiKey: process.env.AAF_API_KEY! });
const openai = new OpenAI();
 
async function handleToolCall(toolCall: OpenAI.ChatCompletionMessageToolCall) {
  const { name, arguments: args } = toolCall.function;
  const parsed = JSON.parse(args);
 
  // Check with TameFlare before executing
  const result = await TameFlare.requestAction({
    type: `tool.${name}`,
    resource: {
      provider: "openai-tools",
      account: "my-org",
      target: name,
      environment: "production",
    },
    parameters: parsed,
    context: { reason: "OpenAI function call" },
    risk_hints: {},
  });
 
  if (result.status !== "allowed") {
    return { error: `Blocked: ${result.decision.reason}` };
  }
 
  // Execute the actual tool call
  return executeToolFunction(name, parsed);
}

CrewAI (Python)

Add TameFlare as a pre-execution hook for CrewAI agents:

from crewai import Agent, Task, Crew
from agentfirewall import AgentFirewall
 
TameFlare = AgentFirewall(api_key=os.environ["AAF_API_KEY"])
 
def aaf_guarded_tool(action_type: str, params: dict):
    """Wrapper that checks TameFlare before executing any tool."""
    result = TameFlare.request_action(
        action_type=action_type,
        provider="crewai",
        account="my-org",
        target=action_type,
        environment="production",
        parameters=params,
    )
    if result.status != "allowed":
        raise PermissionError(f"TameFlare denied: {result.decision.reason}")
    return result
 
# Use in your CrewAI tools
researcher = Agent(
    role="Researcher",
    goal="Find information",
    tools=[your_aaf_wrapped_tools],
)
Tip
The pattern is the same for any framework: call requestAction() before executing the tool, check the result, and only proceed if allowed. TameFlare is framework-agnostic — it works with any agent that makes API calls.