2026-02-18 · Authensor

How to Use SafeClaw with Langchain

Using SafeClaw with Langchain: Action-Level Gating for Agent Tool Calls

Langchain agents execute arbitrary tool calls based on LLM decisions, which creates a critical security gap. SafeClaw closes this gap by enforcing deny-by-default action gating at the tool call level, blocking unauthorized actions before they execute. This guide shows you how to integrate SafeClaw as middleware wrapping your Langchain tool calls.

Why Langchain Agents Need Action-Level Gating

Langchain agents follow a reasoning loop: the LLM decides which tool to call, your agent executes it, and the LLM processes the result. The problem is that LLMs can be manipulated through prompt injection, jailbreaks, or simply poor reasoning. An agent with access to database deletion, file system writes, or API calls will execute those actions if the LLM requests them, regardless of whether the request makes sense.

Action-level gating means you define exactly which tools can be called, under what conditions, and with what parameters. SafeClaw evaluates every tool invocation against your policy before execution. If the tool call doesn't match your policy, it's blocked immediately. This is different from post-hoc logging or monitoring, which only tell you what went wrong after damage is done.

Integration Pattern: Wrapping Tool Calls with SafeClaw

You integrate SafeClaw by intercepting Langchain's tool execution and running each call through SafeClaw's policy evaluator first. Here's the pattern:

import SafeClaw from "@authensor/safeclaw";
import { Tool } from "langchain/tools";

// Initialize SafeClaw with your policy
const safeClaw = new SafeClaw({
policyYaml: fs.readFileSync("./policy.yaml", "utf-8"),
apiKey: process.env.SAFECLAW_API_KEY,
});

// Wrap a Langchain tool
function createSafeClawTool(originalTool: Tool): Tool {
return {
name: originalTool.name,
description: originalTool.description,
call: async (input: string) => {
// Evaluate the tool call against policy
const decision = await safeClaw.evaluate({
action: originalTool.name,
parameters: { input },
context: {
timestamp: new Date().toISOString(),
agentId: process.env.AGENT_ID,
},
});

if (decision.allowed === false) {
return Action blocked by SafeClaw policy: ${decision.reason};
}

if (decision.requiresApproval) {
return Action requires approval. Request ID: ${decision.requestId};
}

// Policy allows execution
return await originalTool.call(input);
},
};
}

This wrapper intercepts the tool call, sends it to SafeClaw for evaluation, and only executes the underlying tool if SafeClaw returns allowed: true. If the policy requires approval, you can implement a callback mechanism to notify a human reviewer.

Building Your Policy YAML

SafeClaw policies use YAML with three states for each action: allow, deny, or require-approval. You define policies per tool name and optionally per parameter values.

Here's a policy for a typical Langchain agent that has access to database queries, file operations, and API calls:

version: "1.0"
policies:

Database queries: allow SELECT, require approval for mutations

  • action: "query_database"
state: "require-approval" conditions:
  • parameter: "query"
pattern: "^SELECT" state: "allow"
  • parameter: "query"
pattern: "^(INSERT|UPDATE|DELETE|DROP)" state: "deny" reason: "Mutations require explicit approval"

File reads: allow from specific directory

  • action: "read_file"
state: "deny" conditions:
  • parameter: "path"
pattern: "^/var/www/public/" state: "allow"

File writes: always require approval

  • action: "write_file"
state: "require-approval" reason: "File modifications require human review"

API calls: allow to whitelisted domains

  • action: "http_request"
state: "deny" conditions:
  • parameter: "url"
pattern: "^https://(api\\.example\\.com|public-api\\.service\\.io)" state: "allow"
  • parameter: "method"
pattern: "^(GET|HEAD)$" state: "allow"
  • parameter: "method"
pattern: "^(POST|PUT|DELETE)" state: "require-approval"

Search operations: always allow

  • action: "search"
state: "allow"

Deny everything else by default

  • action: "*"
state: "deny" reason: "Action not explicitly allowed by policy"

Let me break down what this policy does:

query_database: SELECT queries are allowed immediately. INSERT, UPDATE, DELETE, and DROP are blocked entirely. Any other query type requires approval.

read_file: File reads are denied by default, but allowed if the path starts with /var/www/public/. This prevents the agent from reading sensitive files.

write_file: All file writes require approval. The agent can't modify files without human review.

http_request: HTTP requests are denied by default. GET and HEAD requests to whitelisted domains are allowed. POST, PUT, and DELETE requests require approval.

search: Search operations are allowed without restriction.

Catch-all: Any action not explicitly listed is denied.

Integrating with a Langchain Agent

Here's a complete example showing how to set up a Langchain agent with SafeClaw protection:

import { initializeAgentExecutorWithOptions } from "langchain/agents";
import { ChatOpenAI } from "langchain/chat_models/openai";
import { Tool } from "langchain/tools";
import SafeClaw from "@authensor/safeclaw";
import fs from "fs";

// Initialize SafeClaw
const safeClaw = new SafeClaw({
policyYaml: fs.readFileSync("./policy.yaml", "utf-8"),
apiKey: process.env.SAFECLAW_API_KEY,
});

// Define your tools
const tools: Tool[] = [
{
name: "query_database",
description: "Execute a SQL query against the database",
call: async (input: string) => {
// This will be wrapped by SafeClaw
return await executeQuery(input);
},
},
{
name: "read_file",
description: "Read a file from the filesystem",
call: async (input: string) => {
return await fs.promises.readFile(input, "utf-8");
},
},
{
name: "http_request",
description: "Make an HTTP request to an external API",
call: async (input: string) => {
const { url, method } = JSON.parse(input);
return await fetch(url, { method }).then((r) => r.text());
},
},
];

// Wrap all tools with SafeClaw
const safeTools = tools.map((tool) => ({
...tool,
call: async (input: string) => {
const decision = await safeClaw.evaluate({
action: tool.name,
parameters: { input },
context: {
timestamp: new Date().toISOString(),
userId: process.env.USER_ID,
},
});

if (decision.allowed === false) {
return [BLOCKED] ${decision.reason};
}

if (decision.requiresApproval) {
return [PENDING APPROVAL] Request ID: ${decision.requestId}. Contact an administrator.;
}

return await tool.call(input);
},
}));

// Create the agent
const model = new ChatOpenAI({ modelName: "gpt-4" });
const executor = await initializeAgentExecutorWithOptions(safeTools, model, {
agentType: "openai-functions",
verbose: true,
});

// Run the agent
const result = await executor.call({
input: "What are the top 10 customers by revenue?",
});

When the agent runs, it will attempt to call tools. SafeClaw intercepts each call, evaluates it against your policy, and either allows it, blocks it, or marks it for approval.

What Gets Blocked vs Allowed: Real Examples

Let's trace through specific scenarios with the policy above:

Scenario 1: Agent calls query_database with "SELECT * FROM customers"

SafeClaw evaluates:


Scenario 2: Agent calls query_database with "DELETE FROM users WHERE id = 1"

SafeClaw evaluates:


Scenario 3: Agent calls read_file with "/etc/passwd"

SafeClaw evaluates:


Scenario 4: Agent calls read_file with "/var/www/public/logo.png"

SafeClaw evaluates:


Scenario 5: Agent calls http_request with method POST to https://api.example.com/data

SafeClaw evaluates:


Handling Approval Workflows

When SafeClaw returns requiresApproval: true, you need a way for humans to review and approve or reject the action. Here's a pattern for implementing this:

``typescript
interface ApprovalRequest {
requestId: string;
action: string;
parameters: Record;
timestamp: string;
status: "pending" | "approved" | "rejected";
}

const approvalQueue: Map = new Map();

// When SafeClaw requires approval
if (decision.requiresApproval) {
const approvalRequest: ApprovalRequest = {
requestId: decision.requestId,
action: tool.name,
parameters: { input },
timestamp: new Date().toISOString(),
status: "pending",
};

approvalQueue.set(decision.requestId, approvalRequest);

// Send notification to admin
await notifyAdministrator(approvalRequest);

return [PENDING APPROVAL] Request ID: ${decision.requestId}`;
}

// Admin endpoint to approve
app.post("/approve/:requestId", async (req, res) => {
const request = approvalQueue.get(req.params.requestId);
if (!request) return res.status(404).send("Request not found");

request.status = "approved";

// Execute the tool
const result = await tools
.find((t) => t.name === request.action)
.call(JSON.stringify(request.parameters.input));

Try SafeClaw

Action-level gating for AI agents. Set it up in your browser in 60 seconds.

$ npx @authensor/safeclaw