LangChain / LangGraph integration
Prismor Warden adapter for LangChain and LangGraph. Ships from
adapters/langchain/ as prismor-warden-langchain.
Registry entry: id: langchain in
warden/integrations/registry.yaml.
Install
pip install "prismor[langchain]"
Needs
prismor >= 1.14.2. Until that version is on PyPI, the same one-liner works from source:pip install "prismor[langchain] @ git+https://github.com/PrismorSec/prismor.git@main".
Guard tools (easy path)
from langchain_core.tools import tool
from prismor.warden.langchain import guard_tools
@tool
def run_shell(command: str) -> str:
"""Execute a shell command."""
...
@tool
def read_file(path: str) -> str:
"""Read a file."""
...
tools = guard_tools([run_shell, read_file]) # every tool now policy-checked
agent = create_react_agent(llm, tools)
guard_tools wraps tool.func (sync) and tool.coroutine (async) in-place and
returns the same list. Your tool schemas are untouched — the LLM sees the same
tool definitions.
Guard a single tool
from prismor.warden.langchain import warden_guard_tool
guarded = warden_guard_tool(run_shell, mode="enforce", subject="user:alice")
Callback handler (alternative)
If you already use LangChain callbacks or need to guard an agent executor without
modifying its tool list, use WardenCallbackHandler:
from prismor.warden.langchain import WardenCallbackHandler
handler = WardenCallbackHandler(mode="enforce")
agent_executor.invoke({"input": prompt}, config={"callbacks": [handler]})
The handler fires on on_tool_start — before the tool executes. It raises
WardenBlocked in enforce mode, which aborts the tool call and surfaces as an
error in the agent's output.
Note:
guard_tools(direct wrapping) blocks before the tool body runs and the model recovers gracefully.WardenCallbackHandlerraises an exception that interrupts execution — use it when you can't modify the tool list.
Per-user control (multi-tenant)
from prismor.warden.langchain import guard_tools, use_subject
tools = guard_tools([run_shell, read_file]) # once, at startup — no bound subject
agent = create_react_agent(llm, tools)
# per-request handler
with use_subject("user:alice"):
agent.invoke({"messages": [("user", prompt)]})
The use_subject context manager sets the subject for every tool call that runs
inside the block. Thread-safe and async-safe (contextvar-backed) — concurrent
requests each see their own subject.
Per-user IAM example (.prismor/iam.yaml):
agents:
user:bob:
deny_tools: [Bash]
deny_network: true
allowed_paths: ["**"]
user:bob will have run_shell blocked by IAM even if the call would otherwise
be allowed by the org-wide policy. user:alice (no profile) gets org defaults.
LangGraph
Works identically — guard the tools before passing them to the graph:
from langgraph.prebuilt import create_react_agent
from prismor.warden.langchain import guard_tools, use_subject
tools = guard_tools([run_shell, search])
graph = create_react_agent(llm, tools)
with use_subject("user:alice"):
result = graph.invoke({"messages": [("user", prompt)]})
Event mapping
| Tool call type | Event type | Field | Rules that apply |
|---|---|---|---|
| Default (shell commands, etc.) | shell | command | destructive-command, secret-exfiltration, … |
Override with event_type="network" | network | url | suspicious-network, secret-in-url-params |
Override with event_type="file_write" | file_write | path | path-based rules |
Pass event_type and command_builder to warden_guard_tool for tools whose
risk is a URL or path rather than a shell command.
Reference
| Symbol | Purpose |
|---|---|
guard_tools(tools, **kwargs) | Guard a list of LangChain tools in one call |
warden_guard_tool(tool, **kwargs) | Guard a single tool |
WardenCallbackHandler(**kwargs) | Callback-based guard (for agent executors) |
use_subject(value) | Per-request subject contextmanager |
WardenBlocked | Raised on enforce-mode block (when raise_on_block=True) |
All functions accept: subject, workspace, agent, mode, session_id,
event_type, raise_on_block. See adapters/langchain/
for full signatures.