Skip to main content
The LangChain adapter takes a list of BaseTools and returns a parallel list with the same names and schemas, but each func/_run is wrapped to run through governance first.

Install

Pulls langchain-core as a peer dep. Compatible with langchain-core 0.3.x.

Basic usage

govern_tools returns a new list — the originals are not mutated. Drop governed into wherever you would have passed [list_files, delete_file].

What you pass to evaluate()

For each governed call:
  • tool_name = tool.name (the LangChain tool’s name).
  • args.<index> = positional arguments.
  • kwargs.<key> = keyword arguments.
  • framework = "langchain" on the audit event.
A typical policy targets tool_name and one of the kwargs fields:

Denied calls

Unlike MCP and Claude Agent, LangChain has no native deny channel — tools either return a value or raise. The adapter raises:
LangChain’s agent loop sees PermissionError, surfaces it as a tool failure to the model, and the model adapts. If you’d rather catch the deny and handle it yourself, wrap your invocation:

StructuredTool support

StructuredTools (with args_schema) are preserved — the wrapper rebuilds the tool with the same schema so the agent’s prompt and validation are unchanged. Plain Tools and @tool-decorated functions also work. If a tool has neither a func nor a _run (rare; usually a custom subclass with overridden invoke), the adapter passes it through unwrapped and logs a warning.

With traces

The adapter doesn’t have hooks for user/assistant messages — that’s your harness’s job. The adapter only appends ToolCallMessages when calls happen.

Full example

A runnable end-to-end script ships with the SDK at examples/langchain_quickstart.py. It shows a ReAct agent with two governed tools and a published deny policy.