BaseTools and returns a parallel list with the same names and schemas, but each func/_run is wrapped to run through governance first.
Install
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.
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: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
ToolCallMessages when calls happen.
Full example
A runnable end-to-end script ships with the SDK atexamples/langchain_quickstart.py. It shows a ReAct agent with two governed tools and a published deny policy.