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

# LangGraph integration

> Drop Aethis into a LangGraph flow as a deterministic decision node. ~10 lines via langchain-mcp-adapters.

**You're building a LangGraph agent and you want a deterministic decision node** — same inputs, same answer, every time, traceable to source. Aethis is already an MCP server (`aethis-mcp`), and LangGraph has first-class MCP support via the official [`langchain-mcp-adapters`](https://pypi.org/project/langchain-mcp-adapters/) package. No custom adapter needed — register `aethis-mcp` as a stdio MCP server and its tools become LangChain Tools your graph nodes can call.

The runtime decision path stays in formal logic. The LLM still drives the conversation; the eligibility step does not.

## Install

```bash theme={null}
uv add langgraph langchain langchain-mcp-adapters langchain-anthropic
```

You also need [`aethis-mcp`](/mcp-server/overview) reachable on your PATH. Either install it once:

```bash theme={null}
npm install -g aethis-mcp
```

…or let `npx` resolve it on first call (slower cold start, no global install).

## The recipe

```python theme={null}
import asyncio
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain_anthropic import ChatAnthropic
from langchain.agents import create_agent

async def main():
    # 1. Register aethis-mcp as a stdio MCP server.
    client = MultiServerMCPClient({
        "aethis": {
            "transport": "stdio",
            "command": "npx",
            "args": ["-y", "aethis-mcp"],
        }
    })

    # 2. Pull Aethis tools as LangChain Tools.
    tools = await client.get_tools()

    # 3. Build a LangGraph agent that can call them.
    agent = create_agent(
        ChatAnthropic(model="claude-opus-4-7"),
        tools,
    )

    # 4. Ask the agent a question that requires a deterministic check.
    result = await agent.ainvoke({
        "messages": [{
            "role": "user",
            "content": (
                "I'm 10, year 6, at a state-funded school, my household "
                "receives Universal Credit with £5,000 annual net earnings. "
                "Am I eligible for UK Free School Meals? Use the Aethis "
                "ruleset aethis/uk-fsm/child-eligibility."
            ),
        }]
    })
    print(result["messages"][-1].content)

asyncio.run(main())
```

<Note>
  `create_agent` lives in the `langchain` package as of LangChain 1.x. The
  older `langgraph.prebuilt.create_react_agent` still imports but is
  deprecated in favour of it, so it emits a deprecation warning on every run.
  Same prebuilt tool-calling agent; the argument for a system prompt is
  `system_prompt=` rather than `prompt=`.
</Note>

The agent will call `aethis_schema` (to discover fields), then `aethis_decide` (to evaluate), and report the result. The decision itself never touches the LLM — it's evaluated by the constraint solver on `api.aethis.ai` and returns a stable `decision_id` + `inputs_hash` you can store for audit.

## What just happened

```mermaid theme={null}
%%{init: {"theme": "dark"}}%%
sequenceDiagram
    autonumber
    actor U as User
    participant A as LangGraph agent (LLM)
    participant S as aethis_schema
    participant D as aethis_decide

    U->>A: question / facts
    A->>S: discover fields (anonymous)
    S-->>A: field schema
    Note over A: parse user message,<br/>collect field values
    A->>D: decide (no LLM in path)
    D-->>A: decision + trace
    A-->>U: response with audit fields<br/>(decision_id, inputs_hash)
```

The LLM picks the ruleset, parses the user's free-text, and presents the result. The **decision** is made by formal logic on the server and is byte-stable across runs.

## Anonymous vs keyed paths

* **`aethis_decide` / `aethis_schema` / `aethis_explain` on a public ruleset** — anonymous, no API key. Works in the recipe above as-is.
* **`aethis_decide` on a composed [Rulebook](/concepts/nomenclature) (`rulebook_id=...`)** — always scope-gated. Pass an API key via the MCP server env:
  ```python theme={null}
  client = MultiServerMCPClient({
      "aethis": {
          "transport": "stdio",
          "command": "npx",
          "args": ["-y", "aethis-mcp"],
          "env": {"AETHIS_API_KEY": "ak_live_..."},
      }
  })
  ```
* **Authoring tools** (`aethis_create_ruleset`, `aethis_generate_and_test`, `aethis_publish`) — require a key and are in private beta. Request access at [aethis.ai/developer-access](https://aethis.ai/developer-access).

## When to use this vs an LLM-only node

Use Aethis when:

* The decision must be **stable** across runs (audit, compliance, regulated decisions).
* You need a **trace** back to the source clause for every result.
* The rule is encodable (legislation, policy, contract, internal SOP) — anything where "the answer is in this document" rather than "the answer requires judgment."

Stay LLM-only when:

* The decision genuinely requires open-ended reasoning.
* The cost of a wrong answer is low.
* The rule changes every conversation.

The right pattern is usually **both**: LLM nodes for routing, summarisation, and user interaction; Aethis nodes for the regulated check at the centre of the flow.

## See also

* [Evaluate a case](/recipes/evaluate-a-case) — the four-call pattern (`schema → fields → decide → explain`) without LangGraph.
* [MCP server overview](/mcp-server/overview) — full tool list and stdio config.
* [Decision envelope](/concepts/decision-envelope) — what's in the response, and how to use `inputs_hash` + `decision_id` for audit.
