AI agent security

Which runtime security tools integrate with LangChain or AutoGPT?

An integration is a hook that can deny before the side effect. LangChain Python, LangChain JS, LangGraph JS, CrewAI, and the Vercel AI SDK have published wrappers. AutoGPT does not.

9 min read
In short: An integration is a hook that can deny before the side effect. LangChain Python, LangChain JS, LangGraph JS, CrewAI, and the Vercel AI SDK have published wrappers. AutoGPT does not.

Which runtime security tools integrate with LangChain or AutoGPT?

The ones that sit on a hook the host actually exposes, and that can still deny before the side effect. A vendor page that says "LangChain integration" isn't enough. Ask which function runs between generated arguments and execute, and what happens on deny.

LangChain, AutoGPT, and CrewAI don't share a security API. LangChain Python wraps a tool or sits on create_agent. LangChain JS createAgent uses @arcjet/guard/langchain/v1. LangGraph JS (StateGraph plus ToolNode) uses @arcjet/guard/langgraph/v1. CrewAI uses register_arcjet_hooks on PRE_TOOL_CALL. AutoGPT gives you the command or block run method, or the HTTP client those commands share. There's no published Arcjet adapter for AutoGPT. Use only the hooks that exist.

For more information about inbound and retriever screens in those SDKs, see prompt injection protection for LangChain, LlamaIndex, and the Vercel AI SDK. Don't treat that page as the integration guide.

Hook points that can still deny

The following table lists the hook that can still deny on each host:

HostHook that can denyObserves onlyPublished Arcjet helper
LangChain Python

Wrap the @tool function, arcjet.guard.langchain.guard_tool, or LangChain wrap_tool_call middleware

Callbacks such as on_tool_start

Yes: guard_tool after pip install "arcjet[langchain]"

LangChain JS

guardTool on authored tool(), and guardMiddleware on wrapToolCall

Callbacks

Yes: @arcjet/guard/langchain/v1. See the

LangChain agent guard

LangGraph JS

guardTool on authored tool(), and guardToolNode in place on ToolNode

Graph hooks and interrupt()

Yes: @arcjet/guard/langgraph/v1. See the

LangGraph agent guard

Vercel AI SDK

guardTool around tool() execute

A tool with no execute, or a wrapped tool called without toolsContext

Yes: @arcjet/guard/vercel-ai/v7

AutoGPT

Command or block run, or a shared HTTP client

After-execute pluginsNone
CrewAI

register_arcjet_hooks on PRE_TOOL_CALL, or guard_tool on a standalone BaseTool.run

Hooks that log and return, and hooks that raise anything other than HookAborted

Yes: arcjet.guard.crewai. There is no arcjet[crewai] extra. Install official crewai yourself. See the CrewAI agent guard

Other products sit on the same kinds of hooks: they wrap execute, they sit on the host hook, or they only see HTTP that happens to route through them. Rein is an in-code peer. Datadog AI Guard is an evaluator you call with prompts or tool payloads. Guardrails AI and NVIDIA NeMo Guardrails judge text. None of those replace object-level authorization in the handler. For more information about the layer map, see AI agent runtime security and runtime security for LLM applications.

LangChain

LangChain Python has a published Guard wrapper. Install the extra, wrap the tool you already declared, and pass that wrapped tool to the agent. Actor and inputs come from server-controlled RunnableConfig and from validated arguments, not from a string the model invented for identity.

from arcjet.guard import launch_arcjet, local_input, server_input
from arcjet.guard.langchain import guard_tool
from langchain_core.tools import tool
arcjet = launch_arcjet(key=ARCJET_KEY)
@tool
async def create_github_issue(title: str, body: str) -> str:
"""Open a GitHub issue in the support repo."""
await github.create_issue(title=title, body=body)
return "opened"
guarded_create_issue = guard_tool(
guard=arcjet,
tool=create_github_issue,
action="issue.opened",
actor=lambda config: config["configurable"]["user_id"],
inputs=lambda arguments, _config: {
"title": server_input.string(arguments["title"]),
"body": local_input.string(arguments["body"]),
},
)

Use the async Guard client with ainvoke() and the sync client with invoke(). Resolvers receive RunnableConfig and the validated arguments, which is why actor reads from config["configurable"]: a model that can write its own actor can pick a policy scope.

The wrapper raises two different errors, and treating them as one is the mistake worth avoiding:

  • ArcjetToolDeniedError is a real DENY. It carries the decision. Tell the caller and don't retry.
  • ArcjetToolUnavailableError is an evaluation that couldn't be completed: a deadline, a resolver that raised, or an ALLOW whose has_failed_open() is true. That's an operational failure worth alerting on, not a policy result.

guard_tool blocks on both by default. Set on_guard_error="allow" only where availability beats enforcement, such as a read-only lookup. A denial follows the wrapped tool's handle_tool_error behavior when you configured that on the tool.

If you don't want a vendor wrapper, wrap the function yourself and keep the same two moments: before the HTTP client, and on the result before you return it to the model. LangChain's own wrap_tool_call middleware is an around-hook for every tool. It isn't a policy engine. You still write the allow or deny. The Python how-to is How do I secure a LangChain Python agent?. The docs page is the LangChain agent guard.

LangChain JS createAgent has a published Guard adapter. Import @arcjet/guard/langchain/v1. Wrap authored tool() with guardTool. Put agent-wide policy on guardMiddleware. The middleware skips branded (guardTool) tools when it can look them up. humanInTheLoopMiddleware is a hold, not a policy.

import { createAgent } from "langchain";
import { tool } from "@langchain/core/tools";
import { z } from "zod";
import { tokenBucket } from "@arcjet/guard";
import { guardMiddleware, guardTool } from "@arcjet/guard/langchain/v1";
import { arcjet } from "./arcjet.js";
const issueLimit = tokenBucket({
bucket: "github-create-issue",
refillRate: 5,
intervalSeconds: 60,
maxTokens: 10,
});
const createGithubIssue = guardTool(
arcjet,
tool(
async ({ title, body }) => {
await github.createIssue({ title, body });
return "opened";
},
{
name: "create_github_issue",
description: "Open a GitHub issue in the support repo",
schema: z.object({
title: z.string(),
body: z.string(),
}),
},
),
{
action: "issue.opened",
rules: () => [issueLimit({ key: "github", requested: 1 })],
},
);
const agent = createAgent({
model: "openai:gpt-4o",
tools: [createGithubIssue],
middleware: [guardMiddleware(arcjet, { sessionId: conversationId })],
});

On DENY, guardTool returns a plain ArcjetDenialResult. Don't throw. Don't fabricate a ToolMessage. The combined guide is LangChain security guide. The product map is the LangChain agent guard.

LangGraph JS is a different host. The Graph API adapter (StateGraph plus ToolNode) is documented at LangGraph agent guard. Import @arcjet/guard/langgraph/v1. That is not LangChain JS createAgent, and it is not Python create_agent. The how-to is How do I secure a LangGraph JS agent?.

LangGraph JS

LangGraph JS has a published Guard adapter. Screen inbound text with a direct guard() call before graph.invoke. Wrap authored tool() / StructuredTool with guardTool. Wrap ToolNode in place with guardToolNode so unwrapped and MCP tools hit Guard. interrupt() is a human hold, not a policy.

This is the Graph API only. It is not deprecated createReactAgent, and it is not Python LangChain create_agent. MCP tools execute inside ToolNode; graph hooks cannot stop tool.invoke. See How do I secure MCP tools in LangGraph?.

Vercel AI SDK

The Vercel AI SDK is the other published JavaScript wrapper. guardTool from @arcjet/guard/vercel-ai/v7 sits between generated arguments and execute:

import { launchArcjet, tokenBucket } from "@arcjet/guard";
import { guardTool } from "@arcjet/guard/vercel-ai/v7";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const issueLimit = tokenBucket({
bucket: "github-create-issue",
refillRate: 5,
intervalSeconds: 60,
maxTokens: 10,
});
const createIssue = guardTool(arcjet, createIssueTool, {
// Slug, past tense. Underscores and uppercase are rejected server-side.
action: "issue.opened",
// A callback over the parsed tool input, so the limit keys on the repo
// the model actually chose.
rules: ({ repo }) => [issueLimit({ key: `repo:${repo}`, requested: 1 })],
actor: (_input, context) => context?.userId ?? "anonymous",
onGuardError: "deny",
});

On DENY the tool's execute never runs and the model receives a structured result carrying reason, retryable, and, for a rate limit, retryAfterSeconds. Only rate-limit denials are retryable. Reshape that result with onDeny if the model needs different wording, and put a line in the system prompt telling it not to retry a denial, or it burns steps trying.

Two constraints are easy to trip. guardTool throws if the tool already declares its own contextSchema, because that's how the wrapper injects the run context. And the correlation context is optional at the type level, so forgetting toolsContext on the generateText call still compiles and leaves every check uncorrelated. Run once with ARCJET_LOG_LEVEL=warn and confirm the correlation before you ship.

onGuardError defaults to "deny", the opposite of a direct guard() call. For more information, see framework integrations. The action-gate how-to is How do I secure a Vercel AI SDK agent?. Inbound prompt-injection screens for this SDK live on the dedicated page.

AutoGPT

There's no @arcjet/guard/autogpt package.

AutoGPT Forge commands and AutoGPT Platform blocks both end at a run (or @command) body. That body is the hook. After-execute plugins run too late.

import httpx
async def run_http_command(url: str, payload: dict, actor: str) -> str:
if not allow_outbound(actor=actor, url=url, payload=payload):
raise PermissionError("Denied by policy")
async with httpx.AsyncClient() as client:
response = await client.post(url, json=payload, timeout=15)
text = response.text
if not allow_output(text):
raise PermissionError("Denied by policy")
return text

Put run_http_command behind every command or block that calls an external API. A tool that opens its own socket bypasses it, so the audit is a grep for httpx, requests, and aiohttp in the command directory. The allow functions are yours: rate limit, host allowlist, argument screen, object check. For more information about those checks, see runtime controls on external APIs.

CrewAI

Official CrewAI on PyPI has published Guard helpers in arcjet.guard.crewai. There is no arcjet[crewai] extra and no @arcjet/guard/crewai JS package. Install crewai>=1.15.3,<2 yourself. Arcjet does not depend on CrewAI, because CrewAI pulls chromadb, which has included a critical remote code execution vulnerability (CVE-2026-45829).

register_arcjet_hooks registers a process-wide PRE_TOOL_CALL hook. On deny it raises HookAborted(reason=..., source="arcjet"). CrewAI swallows any other exception and the tool still runs. Don't raise ArcjetDeniedError from a raw hook. POST_TOOL_CALL is not registered and is not a deny point.

guard_tool wraps a standalone BaseTool you invoke with run() or arun(). Those calls never dispatch PRE_TOOL_CALL.

from arcjet.guard import TokenBucket, launch_arcjet_sync
from arcjet.guard.crewai import register_arcjet_hooks
arcjet = launch_arcjet_sync(key=ARCJET_KEY)
issue_limit = TokenBucket(
refill_rate=5,
interval_seconds=60,
max_tokens=10,
bucket="github-create-issue",
)
handle = register_arcjet_hooks(
guard=arcjet,
tools=["github_create_issue"],
action="issue.opened",
rules=[issue_limit(key="github", requested=1)],
)

Registration is once per process. Call handle.unregister() before you register again in tests. If you write the hook yourself, raise HookAborted. A ValueError or a timeout inside the SDK you call lets the tool run. Don't use legacy @before_tool_call with return False as the Arcjet gate.

Don't install an npm package named crewai. The helpers are official Python only. The guide is CrewAI security guide. The product map is the CrewAI agent guard.

CrewAI task guardrail validates task output. It isn't an action gate on stripe_charge. Human approval through human_input or request_human_input is a hold, not a policy. For more information about that split, see human approval is not a security policy.

What counts as an integration

A runtime security tool integrates with a host when your code can call it on that host's hook and branch on allow or deny before the vendor request, the file write, or the MCP call. Logs after send() don't count. A web application firewall (WAF) on the chat route doesn't see the Stripe call. An MCP gateway doesn't see a local httpx post.

Use the preceding table. Then put the controls from runtime controls on external APIs on that hook: authorize the action, rate-limit the identity, screen arguments, screen output. For Model Context Protocol (MCP) tools with no local execute function, see secure MCP server and agent tool calls.

The OWASP Top 10 for LLM Applications still applies: prompt injection, excessive agency, and sensitive-information disclosure. Detection on the user bubble doesn't close a live GitHub or Stripe tool. A correlationId reconstructs the run. It doesn't deny a later step because of earlier ones.

If the framework doesn't expose a hook, the check still belongs in execute.

Frequently asked questions

Which runtime security tools integrate with LangChain or AutoGPT?

Tools that sit on a hook the host exposes and that can deny before the execute function. LangChain Python has guard_tool and ArcjetMiddleware. LangChain JS has @arcjet/guard/langchain/v1. LangGraph JS has @arcjet/guard/langgraph/v1. CrewAI has arcjet.guard.crewai. The Vercel AI SDK has @arcjet/guard/vercel-ai/v7. AutoGPT has no published adapter; wrap the command or block run method or the shared HTTP client.

How to secure AI agents built with LangChain or the Vercel AI SDK

Wrap authored tools so that the check runs between generated arguments and the execute function. LangChain Python uses guard_tool or ArcjetMiddleware. LangChain JS uses guardTool or guardMiddleware from @arcjet/guard/langchain/v1. The Vercel AI SDK uses guardTool from @arcjet/guard/vercel-ai/v7. Prompt-injection screens for those SDKs live on the dedicated prompt-injection page. See the LangChain security guide and How do I secure a Vercel AI SDK agent.

Is there an Arcjet adapter for AutoGPT or CrewAI?

AutoGPT has no published adapter. Put the check in the command or block run method or in a shared HTTP client. CrewAI has arcjet.guard.crewai (register_arcjet_hooks and guard_tool). There is no arcjet[crewai] extra and no @arcjet/guard/crewai JS package. Install official crewai yourself.

Is there a published LangGraph adapter?

Yes. LangGraph JS Graph API (StateGraph plus ToolNode) uses @arcjet/guard/langgraph/v1. LangChain JS createAgent uses @arcjet/guard/langchain/v1. See the LangGraph security guide and the LangChain security guide.

AI runtime security in your code

Protect your AI agent workflows with Arcjet

Arcjet guards run inside the tool, so the allow or deny arrives before the side effect rather than after it.