> ## Documentation Index
> Fetch the complete documentation index at: https://docs.certior.io/llms.txt
> Use this file to discover all available pages before exploring further.

# LangChain

> Attach CertiorCallbackHandler to any LangChain chain or agent. Every tool call is verified before execution.

<a href="https://colab.research.google.com/github/certior/certior/blob/main/notebooks/langchain_integration.ipynb" target="_blank" rel="noreferrer"><img noZoom src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab" /></a> — run a live agent blocked mid-task (needs your own OpenAI key).

LangChain calls registered tools through its callback system. `CertiorCallbackHandler` taps that system and runs `Guard.verify(...)` before each tool execution. Allowed calls proceed; blocked calls raise `CertiorBlocked` and the chain stops.

## Wiring it in

```python theme={null}
from langchain.agents import AgentExecutor, create_openai_tools_agent
from langchain_openai import ChatOpenAI
from certior import Guard
from certior.adapters.langchain import CertiorCallbackHandler

guard = Guard(
    policy="hipaa",
    permissions=["network:http:read", "filesystem:read"],
    budget_cents=5000,
)

llm = ChatOpenAI(model="gpt-4o-mini")
agent = create_openai_tools_agent(llm, tools=[...], prompt=...)
executor = AgentExecutor(agent=agent, tools=[...])

handler = CertiorCallbackHandler(
    guard,
    capabilities={                              # turns on the capability gate
        "lookup_records": ["filesystem:read"],
        "send_external":  ["network:http:write"],  # not granted -> blocked
    },
    costs={"lookup_records": 2},                # optional per-tool budget debit
)

executor.invoke(
    {"input": "Summarize today's intake records."},
    config={"callbacks": [handler]},
)
```

## What the handler does

`CertiorCallbackHandler(guard, capabilities=...)` subscribes to LangChain's `on_tool_start` event. For each invocation:

1. Resolves the tool's name and inputs.
2. Calls `guard.verify(tool=name, params=inputs, content=..., required_capabilities=capabilities[name], cost_cents=costs[name])`.
3. On allow: lets LangChain proceed with the original inputs (or the redacted version when the policy redacts).
4. On block: raises `CertiorBlocked` carrying the `VerifyResult`. The chain halts.

## Declaring capabilities per tool

LangChain's callback does not carry capability metadata, so the handler can't infer what a tool needs — you declare it once in the `capabilities` map above. A tool whose needs are not a subset of the guard's grant is blocked before it runs; without a map, only the content gate applies.

If you'd rather keep the declaration next to the function, wrap it with `@guard.wrap` instead — the capability check then runs inside the tool body:

```python theme={null}
from langchain.tools import tool

@tool("web_fetch")
@guard.wrap(required_capabilities=["network:http:read"], cost_cents=2)
def web_fetch(url: str) -> str:
    ...
```

Use both together for defence in depth: the wrap raises `CertiorBlocked` synchronously inside the tool body; the callback enforces the same check at the framework boundary.

## See also

* [Custom loop](/guides/custom-loop) - the same `Guard.verify()` directly.
* [How it works](/concepts/how-it-works) - the three gates this handler enforces.
