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

# LangChain

> Wrap LangChain tools so every tool call is authorized by AtlaSent before it executes.

The AtlaSent LangChain integration wraps your tool functions with
authorize-first semantics: each tool call is evaluated and its permit verified
through AtlaSent before the underlying function runs. If authorization does not
succeed, the tool does not execute. It works with any LangChain tool factory and
has no dependency on `langchain` itself.

## Install

<Warning>
  **Not yet published.** `atlasent-langchain` is not yet on PyPI and `@atlasent/langchain` is not yet on
  npm, so the install commands below do not work yet. Until a release is
  published, install from source: [`python/atlasent_langchain`](https://github.com/Atlasent/atlasent-sdk/tree/main/python/atlasent_langchain)
  and [`typescript/packages/langchain`](https://github.com/Atlasent/atlasent-sdk/tree/main/typescript/packages/langchain)
  in the `atlasent-sdk` repository.
</Warning>

<CodeGroup>
  ```bash Python theme={null}
  pip install atlasent-langchain
  ```

  ```bash TypeScript theme={null}
  npm install @atlasent/langchain @atlasent/sdk
  ```
</CodeGroup>

## Python

Wrap the callable, then hand the wrapped function to LangChain's `@tool`,
`StructuredTool.from_function`, or any other tool factory:

```python theme={null}
import os
from atlasent import AtlaSentClient
from atlasent_langchain import with_langchain_guard
from langchain_core.tools import tool

client = AtlaSentClient(api_key=os.environ["ATLASENT_API_KEY"])

def write_ehr(patient_id: str, note: str) -> dict:
    ...

guarded_write_ehr = with_langchain_guard(
    write_ehr,
    client,
    agent="service:clinical-agent",
    extra_context={"environment": "production"},
    # Action classes require a state snapshot by default (SNAPSHOT_REQUIRED otherwise)
    state_snapshot={"source": "clinical-agent", "complete": True},
)

write_ehr_tool = tool(guarded_write_ehr)
```

Use `async_with_langchain_guard` with an `AsyncAtlaSentClient` for async tools.

## TypeScript

```typescript theme={null}
import { AtlaSentClient } from "@atlasent/sdk";
import { withLangChainGuard } from "@atlasent/langchain";
import { DynamicStructuredTool } from "@langchain/core/tools";
import { z } from "zod";

const client = new AtlaSentClient({ apiKey: process.env.ATLASENT_API_KEY! });

const [queryDb] = withLangChainGuard(
  [
    {
      name: "query_database",
      description: "Run a read-only SQL query and return results as JSON",
      execute: async ({ sql }) => JSON.stringify(await db.query(sql)),
    },
  ],
  client,
  {
    agent: "service:analytics-bot",
    extraContext: { environment: "production" },
    // Action classes require a state snapshot by default (SNAPSHOT_REQUIRED otherwise)
    stateSnapshot: { source: "analytics-bot", complete: true },
  },
);

const tool = new DynamicStructuredTool({
  name: queryDb.name,
  description: queryDb.description,
  schema: z.object({ sql: z.string() }),
  func: queryDb.execute,
});
```

## What the guard does

* Before each tool call, evaluates the call through AtlaSent and verifies the
  resulting permit.
* Sends the tool's name as `context.tool` and the call's input as part of the
  evaluated context.
* Runs the tool only if authorization succeeds. Otherwise it raises
  `AtlaSentDeniedError` (the default), or — with `on_deny="tool-result"`
  (Python) / `onDeny: "tool-result"` (TypeScript) — returns a structured denial
  instead, so the model can see the reason and adapt its plan. The tool still
  does not run.
* When the tool returns a JSON object, annotates it with
  `_atlasent_permit_id` and `_atlasent_audit_hash`, linking the result to the
  audit chain.

## Action type and required context

By default every call is evaluated as the canonical `agent.tool.invoke`
action. That action class requires the `tool` and `environment` context inputs:
the guard supplies `tool`, and you supply `environment` (via `extra_context` /
`extraContext`) — the guard never invents one. Action classes also require a
state snapshot by default; pass `state_snapshot` / `stateSnapshot` (for example
`{"source": "my-agent", "complete": true}`) or calls are denied with
`SNAPSHOT_REQUIRED`.

To gate a tool under a different action type, pass `action`. Map tools to
**stable action types** that match your AtlaSent policy — don't use the Python
function name, which couples regulatory meaning to code refactors.

## Related

* [LlamaIndex integration](/integrations/llamaindex)
* Policy authoring


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.