<!-- https://zunderlabs.com/docs/integrations/mcp · Markdown version of the page -->

# MCP for Claude, ChatGPT and Cursor

Give an AI agent guarded trading tools through Guard's MCP server. The exact config for Claude Desktop, Claude Code, Cursor, the OpenAI Agents SDK and LangGraph, and why ChatGPT is different.

:::note[Planned]
The MCP server (`zunder-guard-mcp`) ships with Guard 1.0, which is not released yet. The tools and flags below are the ones built; the client configs follow each client's own docs, cited at the bottom, read 6 Oct 2026.
:::

A prompt is not a limit. A model can misread a number, repeat itself, or be talked out of an instruction. Guard's MCP server gives an agent tools that **cannot** break your limits, because every order still passes the same nine rules as any bot's, in Guard, on your machine.

## The tools

| Tool | What it does |
|---|---|
| `account_overview` | equity, positions with the stops that protect them, open orders, Guard's state |
| `limits` | your nine rules and the risk state (active, halted for the day, stopped, kill switch) |
| `preview_order` | "what would you allow for this trade?": the size from the stop, a resize and the rule that bound it, or the veto, without sending |
| `place_order` | an entry with its stop (or Guard's default stop), through the nine rules; returns Guard's verdict and the venue's reply |
| `move_stop` | move a stop; only tighter is accepted |
| `close_position` | reduce or close; never blocked |
| `cancel_order` | cancel one order; Guard keeps a position's last stop |
| `recent_decisions` | Guard's latest verdicts and why |
| `kill_switch` | pull the kill switch; needs `confirm: true` |

There is no tool to raise a limit, change the stop policy, change leverage, resume after a halt, release the kill switch, move funds or send a raw request. Those need you, at the machine running Guard.

Prices and sizes are decimal strings (`"58800"`, `"0.01"`); a size can be `"max"`, and a stop can be `"guard_policy"`. Every refusal comes back with a code and a plain reason, and says whether anything was sent.

**Example.** You ask the agent to "go long BTC, stop 2% below". It calls `preview_order` and gets back: "resize: 0.02553 BTC, bound by the loss at the stop (2% of equity)". It can report that to you; it cannot change it.

## Before you start

1. Guard is running (`zunder-guard run`).
2. The agent has its own **client key** from `zunder-guard init`, saved in a file only you can read:

   ```sh
   mkdir -p ~/.config/zunder-guard && umask 077
   cat > ~/.config/zunder-guard/mcp-client.key     # paste the key, Enter, Ctrl-D
   ```

   The key is never a command-line argument or an environment variable. Without one, the server is read-only.
3. Your Guard's kill file is its `state_dir` plus `/kill`.

The configs below start the program without a shell, so write absolute paths (no `~`).

| Flag | Default | |
|---|---|---|
| `--network` | `paper` | `paper`, `testnet` or `mainnet`: orders are refused unless Guard runs the same |
| `--key-file` | | the client key file (mode 600) |
| `--key-stdin` | | the client key on the first line of standard input |
| `--kill-file` | | Guard's `state_dir/kill`, for `kill_switch` |
| `--guard-url` | `http://127.0.0.1:8547` | Guard's address; this machine only |
| `--confirm-account` | | mainnet only, and required there: the account Guard trades |

## Claude Desktop

Edit `claude_desktop_config.json` (macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`; Windows: `%APPDATA%\Claude\claude_desktop_config.json`), then restart Claude Desktop.

```json
{
  "mcpServers": {
    "zunder-guard": {
      "command": "/usr/local/bin/zunder-guard-mcp",
      "args": [
        "--network", "testnet",
        "--key-file", "/Users/you/.config/zunder-guard/mcp-client.key",
        "--kill-file", "/Users/you/guard-state/kill"
      ]
    }
  }
}
```

## Claude Code

```sh
claude mcp add --scope user zunder-guard -- /usr/local/bin/zunder-guard-mcp \
  --network testnet \
  --key-file /Users/you/.config/zunder-guard/mcp-client.key \
  --kill-file /Users/you/guard-state/kill
```

The `--` separates Claude Code's options from the server's command. For one project, put the same in `.mcp.json`:

```json
{
  "mcpServers": {
    "zunder-guard": {
      "type": "stdio",
      "command": "/usr/local/bin/zunder-guard-mcp",
      "args": ["--network", "testnet", "--key-file", "/Users/you/.config/zunder-guard/mcp-client.key", "--kill-file", "/Users/you/guard-state/kill"]
    }
  }
}
```

## Cursor

`.cursor/mcp.json` in a project, or `~/.cursor/mcp.json` for all projects:

```json
{
  "mcpServers": {
    "zunder-guard": {
      "command": "/usr/local/bin/zunder-guard-mcp",
      "args": ["--network", "testnet", "--key-file", "/Users/you/.config/zunder-guard/mcp-client.key", "--kill-file", "/Users/you/guard-state/kill"]
    }
  }
}
```

## OpenAI Agents SDK

```python
from agents import Agent, Runner
from agents.mcp import MCPServerStdio

async with MCPServerStdio(
    name="zunder-guard",
    params={
        "command": "/usr/local/bin/zunder-guard-mcp",
        "args": ["--network", "testnet",
                 "--key-file", "/Users/you/.config/zunder-guard/mcp-client.key",
                 "--kill-file", "/Users/you/guard-state/kill"],
    },
    cache_tools_list=True,
) as guard:
    agent = Agent(name="Trader", instructions="Preview before placing.", mcp_servers=[guard])
    result = await Runner.run(agent, "What would Guard allow for a BTC long with a stop at 58800?")
```

## LangGraph

```python
from langchain_mcp_adapters.client import MultiServerMCPClient

client = MultiServerMCPClient({
    "zunder-guard": {
        "transport": "stdio",
        "command": "/usr/local/bin/zunder-guard-mcp",
        "args": ["--network", "testnet", "--key-file", "/Users/you/.config/zunder-guard/mcp-client.key"],
    }
})
tools = await client.get_tools()
```

## ChatGPT

ChatGPT's connectors reach MCP servers over the internet (SSE or streamable HTTP); a local server started from a command does not work there. Guard runs on your machine and listens on localhost only. So ChatGPT is **not** supported directly.

We do not recommend exposing Guard to the internet with a tunnel to make it work. A connection through the [relay](https://zunderlabs.com/docs/tools/relay) is being considered; it is not planned yet.

## What the MCP server needs, and what it cannot do

`zunder-guard-mcp` talks only to your running Guard on your machine. It holds no API wallet key: that stays in Guard. Its client key only identifies the agent to Guard; the venue would refuse it.

It can sign three kinds of request (an order, a cancel, a change to an order), and Guard judges each one again. It refuses to send anything when Guard runs another network than the one you named, when its key is not one of Guard's clients or is known to Hyperliquid, or when the address does not answer like a Guard. A client key must be a fresh key: never approve it as an API wallet. Text that comes back from Guard or the venue is passed to the model as quoted data, never as instructions, and tool calls are rate-limited (order requests: 4 at once, then 10 a minute). The kill switch is never rate-limited.

## Sources

- Claude Desktop: [modelcontextprotocol.io, "Connect to local MCP servers"](https://modelcontextprotocol.io/docs/develop/connect-local-servers).
- Claude Code: [code.claude.com/docs/en/mcp](https://code.claude.com/docs/en/mcp).
- Cursor: [cursor.com/docs/context/mcp](https://cursor.com/docs/context/mcp).
- OpenAI Agents SDK: [openai.github.io/openai-agents-python/mcp](https://openai.github.io/openai-agents-python/mcp/).
- LangGraph: [langchain-mcp-adapters](https://github.com/langchain-ai/langchain-mcp-adapters).
- ChatGPT: [OpenAI, "ChatGPT Developer mode"](https://developers.openai.com/api/docs/guides/developer-mode).
