> For the complete documentation index, see [llms.txt](https://svpchain.gitbook.io/svpchain-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://svpchain.gitbook.io/svpchain-docs/agents/langchain-agent.md).

# Trade with a LangChain agent

The [Client setup](/svpchain-docs/agents/client-setup.md) guide connects GUI clients (Claude Code, Cursor, …) to svpchain. This guide does the same thing **in code**: a small Python script that wires both svpchain MCP servers into a [LangChain](https://python.langchain.com/) agent, so an LLM can read chain state and place orders programmatically — in a backtest harness, a bot, a notebook, or a larger app.

The agent talks to the same two servers as every other client:

* **`svpchain-remote`** — builds, reads, and broadcasts chain state. Reached over HTTP.
* **`svpchain-signer`** — holds your key and signs locally. The adapter launches it over stdio; the key never leaves your machine.

The agent composes them: ask the remote to build a transaction, ask the signer to sign it, ask the remote to broadcast. Authentication is self-service — the agent proves it controls a key by signing a one-time challenge.

## Prerequisites

* **CLI setup from** [**Client setup → CLI setup**](/svpchain-docs/agents/client-setup.md#cli-setup) — a 32-byte hex key imported into your OS credential store (via `import` or the macOS app), the `svpchain-signer-mcp` binary at `/usr/local/bin/svpchain-signer-mcp`, and a reachable `svpchain-remote` URL. macOS GUI users: complete [macOS (GUI app)](/svpchain-docs/agents/client-setup.md#macos-gui-app) first, then use the `command` path the app configured. This LangChain guide requires the CLI binary path in code.
* **Python 3.10+**.
* **An Anthropic API key.** The example drives the agent with Claude. Export it before running:

  ```
  export ANTHROPIC_API_KEY=sk-ant-...
  ```

  > This is billed to your Anthropic account, separate from any Claude subscription — add credits at [console.anthropic.com](https://console.anthropic.com).

## Install

Save these dependencies as `requirements.txt`:

```
langchain-mcp-adapters   # turns MCP tools into LangChain tools (MultiServerMCPClient, load_mcp_tools)
langgraph                # the agent runtime under create_agent
langchain                # provides create_agent and resolves the "anthropic:…" model string
langchain-anthropic      # the Anthropic provider for that model
```

Then create a virtual environment and install them:

```
python3 -m venv venv
source venv/bin/activate          # Windows: venv\Scripts\activate
pip install -r requirements.txt
```

## The code

Save this as `svpchain_agent.py`. The `command` below uses the recommended signer path from [Client setup](/svpchain-docs/agents/client-setup.md) (`/usr/local/bin/svpchain-signer-mcp` — change it if you installed elsewhere). Set `--chain-id` to your remote's chain id (`svp-2517-1` here — confirm with whoever runs your remote).

```python
import asyncio
from langchain.agents import create_agent
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain_mcp_adapters.tools import load_mcp_tools

client = MultiServerMCPClient({
    # Remote DEX service — HTTP. svpchain hosts one; or point at your team's.
    "svpchain-remote": {
        "transport": "http",
        "url": "https://indexer.svpchain.com/mcp",
    },
    # Local signer — stdio. The adapter launches it; the key never leaves
    # this process. No key in the config — the signer reads it from your OS
    # credential store (see the client-setup guide's "Import your key" step).
    "svpchain-signer": {
        "transport": "stdio",
        "command": "/usr/local/bin/svpchain-signer-mcp",
        "args": ["--chain-id", "svp-2517-1"],
    },
})

async def main():
    # Hold one persistent session per server open for the whole agent run.
    # The remote binds the auth tenant to a single Mcp-Session-Id, so the
    # session must outlive the agent's auth calls AND its later tool calls —
    # get_tools() opens a fresh session per call and would lose the tenant.
    async with client.session("svpchain-remote") as remote, \
               client.session("svpchain-signer") as signer:
        tools = await load_mcp_tools(remote) + await load_mcp_tools(signer)

        # Return tool errors to the model as a ToolMessage instead of raising
        # (which aborts the graph). This is what lets the agent authenticate
        # itself: its first call fails with "missing tenant context", it sees
        # that error, reads the auth_* tool descriptions, and runs the
        # handshake on its own — no system prompt needed.
        for tool in tools:
            tool.handle_tool_error = True

        agent = create_agent("anthropic:claude-opus-4-8", tools)

        res = await agent.ainvoke({"messages":
            "Get the BTC-USD oracle price. Do not place any orders."})
        print(res["messages"][-1].content)


asyncio.run(main())
```

Run it:

```
python svpchain_agent.py
```

The agent authenticates itself, then answers — something like:

```
The BTC-USD oracle price is $63,524.59. No orders were placed.
```

Under the hood, with no auth code on your side, the agent tries a read, gets `missing tenant context` back, authenticates itself, then retries:

```
get_market (fails) -> whoami -> auth_challenge -> sign_challenge -> auth_verify -> get_market
```

## How it works

Four details make this robust. Three are about the svpchain auth model; the last is generic agent hygiene.

**Two servers, one agent.** `load_mcp_tools` pulls each server's tools and merges them into one tool list. The agent sees `build_place_limit_order` (remote) and `sign_transaction` (signer) side by side and chains them itself — it doesn't know or care that they live on different servers.

**Persistent sessions, not `get_tools()`.** `MultiServerMCPClient.get_tools()` is convenient but opens a *fresh* MCP session per tool call. svpchain binds your authenticated tenant to a single session (`Mcp-Session-Id`), so a new session per call throws away the auth and every call fails with `missing tenant context`. Holding `client.session(...)` open for the whole run keeps one session — and one tenant — alive across the auth handshake *and* the business calls.

**The agent authenticates itself — no system prompt.** There's no token to configure. The agent's first call fails with `missing tenant context`; it sees that error, reads the `auth_*` tools' own descriptions, and runs `auth_challenge` → `sign_challenge` → `auth_verify` on its own, minting a 24-hour bearer bound to the session. The tool descriptions cross-reference each other, so no prompt engineering is needed — this is exactly how a GUI client behaves. (If you'd rather it skip that one failed first call, a one-line system prompt telling it to authenticate up front does the trick, but it's purely an optimization.)

**Surface tool errors to the model.** By default LangGraph re-raises a tool error that isn't an argument-validation error, which aborts the run before the agent can react. Setting `tool.handle_tool_error = True` returns the error to the model as a tool result instead, so the agent can *see* `missing tenant context` and recover by authenticating — exactly what a human-driven client relies on.

## Placing an order

Swap the prompt for an action and the same agent will build → sign → broadcast:

```python
        res = await agent.ainvoke({"messages":
            "What's the oracle price of the BTC-USD market, and place a limit "
            "buy for 0.001 BTC 5% below it on subaccount 0."})
```

> **This places a real on-chain order.** Use a dedicated trading wallet (see the client-setup guide's warning), and test with read-only prompts first. The remote's safety caps still apply — an order or transfer beyond the configured limits is rejected *before* anything is signed.

After it runs, confirm with a follow-up prompt like *"Show my open orders on subaccount 0"* (the agent calls `get_orders`), or check the tx on a block explorer.

## Troubleshooting

* **`Could not resolve authentication method`** — `ANTHROPIC_API_KEY` isn't set in the environment the script runs in. `export` it, then re-run.
* **`Your credit balance is too low`** — add credits to your Anthropic account; this is the LLM bill, unrelated to svpchain.
* **`missing tenant context`** — the agent didn't complete the auth handshake. Make sure you kept `tool.handle_tool_error = True` (without it the first error aborts the run before the agent can react) and that you're using a persistent `client.session(...)` rather than `get_tools()`. See the full entry in [Client setup → Troubleshooting](/svpchain-docs/agents/client-setup.md#troubleshooting).
* **`recovered address does not match the owner` / `payload.chain_id …` / `account sequence mismatch`** — these are svpchain-side, not LangChain. They're covered in [Client setup → Troubleshooting](/svpchain-docs/agents/client-setup.md#troubleshooting).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://svpchain.gitbook.io/svpchain-docs/agents/langchain-agent.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
