Skip to main content
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 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

uv add langgraph langchain-mcp-adapters langchain-anthropic
You also need aethis-mcp reachable on your PATH. Either install it once:
npm install -g aethis-mcp
…or let npx resolve it on first call (slower cold start, no global install).

The recipe

import asyncio
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain_anthropic import ChatAnthropic
from langgraph.prebuilt import create_react_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_react_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())
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

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 (rulebook_id=...) — always scope-gated. Pass an API key via the MCP server env:
    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.

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 — the four-call pattern (schema → fields → decide → explain) without LangGraph.
  • MCP server overview — full tool list and stdio config.
  • Decision envelope — what’s in the response, and how to use inputs_hash + decision_id for audit.