MUTEX
Developers · MCP server

MCP server

Trade Mutex with an AI agent. This is a local MCP server: point your agent at it and it can preview, place, modify and cancel orders, and read positions, fills and market data.

It has two modes, chosen by the key you give it. A mtx_bot_… key drives one isolated bot sub-account and can never move money in or out; the bot's balance is the whole downside. A mtx_agent_… key acts as you on your main account and can withdraw, but only to wallets you linked. Start with bot mode. Reach for user mode only for an agent you already trust with your account.

Agent access which key to give an agent, and why
Overview

Two modes

KeyModeThe agent canThe agent cannot
mtx_bot_…Bot modeTrade the ONE account the key trades, an API bot's sub-account or your vault: preview, place, modify, cancel; read positions, fills and market data.Deposit, withdraw, swap, transfer, or touch anything outside that account.
mtx_agent_…User modeAct as you: trade your main perp account, read balances, positions, orders and history, show your deposit address, and withdraw to your own linked wallets, signed by a key that lives only on your machine.Withdraw anywhere else (the api and the signer both refuse), or add, remove or replace a linked wallet (no tool exists and the api refuses agent sessions).

The server logs which mode it started in on stderr. Public market data (get_markets, get_candles) works in both.

Bot mode · Step 1

Create a bot and fund it small

In the web app, create a bot with the AI Agent + API template (no strategy params - the agent drives it), fund it and press Execute.

Start small. Mutex has no testnet, so every fill is real. An isolated bot with a small balance is the sanctioned safe way to try an agent: real slippage, bounded real downside. Fund only what you would accept losing to a bad prompt.

Bot mode · Step 2

Generate its trading key

Open Settings > API, where each API bot that is ready, running or paused has its own panel, and generate the trading key. It is shown once - copy it (mtx_bot_…). Full walk-through on the Trading API page.

Bot mode · Step 3

Add the server to your agent

The server runs via npx, so there is no install step. Add it to your agent's MCP config with the key in the environment.

Claude Desktop / Claude Code (claude_desktop_config.json or .mcp.json):

{
  "mcpServers": {
    "mutex": {
      "command": "npx",
      "args": ["-y", "@trade-mutex/mcp"],
      "env": { "MUTEX_API_KEY": "mtx_bot_your_key_here" }
    }
  }
}
Env varPurpose
MUTEX_API_KEYYour mtx_bot_… (bot mode) or mtx_agent_… (user mode) key. Required for everything except public market data.
MUTEX_API_BASEOverride the base URL. Bot mode defaults to https://trade.mutex.trade, user mode to https://api.mutex.trade. The app prints the right line next to a new agent key.
MUTEX_SIGNER_KEY_FILEUser mode only: where the local signing key lives. Default ~/.config/mutex/agent-signer.json, created with mode 0600 on first run.
Bot mode · Step 4

Give the agent the know-how

Paste this into your agent's system prompt (or a MUTEX.md it reads). It teaches the agent to preview before ordering, size in coin units, respect the balance, and poll on a cadence. That is the difference between an AI-friendly tool and a plain REST wrapper. The same prompt covers user mode: the agent tells the modes apart by the tools it has.

You work on Mutex through the `mutex` MCP tools. The server runs in one of two
modes and you can tell which from the tools you have:

- **Bot mode** (you have `place_order`, `get_account`): you are driving **one
  isolated account**, the one the key trades: an API bot's sub-account or a
  vault. You cannot deposit, withdraw, swap, or transfer. The only money at
  stake is this account's balance, and that balance is your hard ceiling.
  Everything below applies. On a vault, the balance is the lead trader's stake
  and every depositor's money together, and each order is for all of them. A
  vault's orders also include the ones its lead trader placed in the app, and
  a resting order can change size or `order_id` by itself, because a
  withdrawal from the vault makes every resting order that is not reduce-only
  smaller: read
  `get_orders` before you act on an order id you kept.
- **User mode** (you have `mutex_withdraw`, `mutex_signer_address`,
  `get_balances`): you act **as the user** on their own account. You trade
  their **main perp account** with the same `preview_order` / `place_order` /
  `modify_order` / `cancel_order` / `set_leverage` tools as bot mode, plus
  `place_oco` and `cancel_all`; you can read balances, positions, orders and
  trade history, show their deposit address, and withdraw **only to a wallet
  they have already linked**. You can never add, remove or replace a linked
  wallet. Everything below applies, with these differences: the bankroll is
  the perp collateral `get_positions` reports (`available`), not a bot
  balance; there is no `client_order_id`, so if an order call times out read
  `get_orders` before retrying (it lists every status; `resting_only: true`
  keeps the live ones); `mutex_withdraw` needs a `client_ref` that is unique per
  withdrawal; the api treats a repeated one as a replay of the same withdrawal
  (never a second payout), so if that call times out, retry with the SAME
  `client_ref`, or read `get_withdrawals` and look for it; brackets are `{ trigger_price, price? }`
  objects (a bare trigger price also works); most errors come back as the
  api's refusal copy under an `http_<status>` code rather than a stable code,
  so read the message. Trading and withdrawing move the user's real money: state
  what you are about to do and get the user's explicit yes before
  `place_order`, `place_oco` or `mutex_withdraw`, unless they have told you
  to act on your own. If the api or the signer refuses, repeat its message to
  the user word for word; do not retry with another destination. If
  `mutex_withdraw` says the signer is not authorized, give the user the
  address from `mutex_signer_address` to paste into Settings > Agents.
- **Vaults, in user mode**: `mutex_vault_list`, `mutex_vault_get` and
  `mutex_vault_mine` read vaults and the user's share in each.
  `mutex_vault_deposit` moves money into a vault from the balance its `kind`
  takes: a `perp` vault the user's Perp account (or Funding, with
  `source: funding`), a `spot` vault the Funding balance (or the Spot balance,
  with `source: spot`). The lead trader trades it there, and
  `mutex_vault_withdraw` moves it back, to the Perp account or the Funding
  balance; call `mutex_vault_withdraw_preview` first. Both money tools move real money:
  confirm the intent with the user first. Each needs a fresh `client_ref`; if
  the call times out, retry with the SAME one, or read `mutex_vault_mine`. A
  withdrawal stays pending until it is paid and cannot be cancelled. A vault
  refusal has a stable `code` (for example `no_room`, `under_minimum`,
  `nothing_free`) and a sentence with the number: tell the user the sentence.
  No user mode tool makes, changes, closes or trades a vault. To trade a vault
  of kind `perp`, the server runs with the vault's trading key: bot mode. A
  vault of kind `spot` has no trading key.

**Before you trade**

- **Preview first.** Call `preview_order` before `place_order` unless you have a
  specific reason not to. It returns the estimated fill, fee, and margin. Trading
  without previewing is trading blind.
- **Sizes are in COIN units, never dollars.** `size: "0.01"` on BTC means 0.01
  BTC. To spend a dollar budget, divide by price yourself (get it from
  `get_markets` or a candle).
- **Respect the balance.** Call `get_account` (user mode: `get_positions`)
  and size positions against `available`. Do not assume funds that are not there. The worst case is losing
  this account's balance, so treat it as the whole bankroll.
- **Respect the market's limits.** `get_markets` gives `min_size`,
  `min_notional`, `size_decimals`, and `max_leverage`. Round to the decimals and
  clear the minimums, or the order is rejected.

**When you place**

- `place_order` already echoes the **resulting position and open-order
  state**, and in bot mode a fill/fee/cost **estimate**. Read that response. Do **not** call
  `get_positions` or `get_orders` again just to confirm what you just did.
- Set a fresh `client_order_id` on each order (unique per account). It is your
  idempotency handle: if you are unsure whether a call landed, reuse the same id
  and a duplicate is safely rejected rather than doubled.
- Attach `take_profit` and `stop_loss` trigger prices to an **entry** order to
  fan out reduce-only exits automatically. On a not-yet-filled limit entry, place
  the exits standalone after the fill instead.

**Cadence: do not burst**

- Poll market data on a cadence tied to the bar (once per candle `resolution`),
  not between every thought. Reads are capped at 300/min and orders at 60/min per
  key; sustained flooding auto-suspends the key.
- You almost never need to re-read right after acting, because the mutators hand
  you the new state. Re-poll only when you are waiting on the market to move, not
  to re-check your own action.

**When something is wrong**

- Errors carry a stable `code` and a plain-language recovery hint. Match on the
  code, not the prose.
- `bot_not_trading` means the bot is not in a trading state, and NOT always
  "paused": the message names the state and the control that clears it (a
  `ready` bot has never been armed and needs Execute; only a `paused` one
  resumes). Placement is blocked but cancels still work, so you can always
  unwind. To fully stop the agent, the human pauses the bot in the Mutex web
  app.
- On a vault, `trading_stopped` means new orders and leverage changes are
  refused; reduce-only orders and cancels still work, so you can close
  positions. `vault_closed` means the vault is closed and nothing more trades.
- On `rate_limited`, wait the `Retry-After` seconds. Do not tight-loop.
- On `order_rejected`, the message names the reason (size, notional, leverage).
  Fix it and re-preview before retrying.
User mode

Give an agent your whole account

User mode lets the agent act as you: it trades your own main perp account and has real access to your funding balance. Approval is "paste a key"; there is no device flow. What can and cannot happen is on the Agent access page.

  1. In the app, open https://app.mutex.trade/settings > Agents, turn Agent access on and create an agent key (mtx_agent_…, shown once). One key is live per account; to rotate, revoke it first, then create a new one.
  2. Put the two lines the app shows into the MCP config and start the server once. On first run it generates a secp256k1 signing key at MUTEX_SIGNER_KEY_FILE. The private key never leaves that file and is never printed.
  3. Ask the agent for its signing address (the mutex_signer_address tool) and paste it under Agent signing key in the same Settings tab, pick 30 or 90 days and Authorize with your linked wallet. Until you do, mutex_withdraw is refused; trading works without it.
  4. Paste the system prompt above into the agent, as in bot mode.
{
  "mcpServers": {
    "mutex": {
      "command": "npx",
      "args": ["-y", "@trade-mutex/mcp"],
      "env": {
        "MUTEX_API_KEY": "mtx_agent_your_key_here",
        "MUTEX_API_BASE": "https://api.mutex.trade"
      }
    }
  }
}

What holds it in check, server-side, not by prompt:

  • Withdrawals go only to your own linked wallets. The api refuses a challenge for any other destination and the signer refuses the delegate signature for anything but a linked wallet.
  • The agent can never add or remove a wallet. Every linked-wallet and delegate route answers 403 to an agent session, and the server has no tool for them.
  • Two kill switches. Revoke the agent key, or the signing address, in Settings > Agents. Either one stops withdrawals; the key alone stops everything. Turning Agent access off refuses every agent key at once.

The signing key is a file on the machine that runs the server. Treat it like an SSH key: back it up to keep the authorization across machines, delete it (and revoke the address) if the machine is lost.

Worked example

What a run looks like

With the server wired up and the system prompt in place, prompt your agent:

Check the BTC market and my account balance. If I have room, preview a small long of 0.001 BTC market, then place it with a stop-loss 3% below and a take-profit 5% above.

The agent calls get_markets and get_account (user mode: get_positions), then preview_order to quote the fill and fee, then place_order with inline stop_loss and take_profit. The place response echoes the estimate plus the resulting position and open orders, so it reports back without a single extra poll.

To stop it at any time, pause the bot in the web app. A paused bot is reads-and-cancels-only, so the agent can still unwind but cannot open anything new. In user mode there is no bot to pause: revoke the agent key in Settings > Agents, and cancel_all keeps working while trading is halted platform-wide.

Reference

Tools

Bot mode (mtx_bot_…):

ToolDoes
get_marketsList markets (or one) with fees and limits.
get_candlesOHLCV history.
get_accountThe account's balance: equity, available, margin, uPnL.
get_positionsOpen positions.
get_ordersList orders (or fetch one by id).
get_fillsFill history.
preview_orderPre-trade fill/fee/margin quote, no side effects.
place_orderPlace an order; echoes estimate and resulting state.
modify_orderReprice or resize a resting order; echoes state.
cancel_orderCancel one or all; echoes state.
set_leverageSet per-symbol leverage.

User mode (mtx_agent_…). The trading tools take the same inputs as their bot-mode twins: symbol, coin-unit size, brackets as trigger prices. There is no client_order_id; if an order call times out, read get_orders before retrying. mutex_withdraw needs a client_ref unique per withdrawal; the api treats a repeated one as a replay, never a second payout, so on a timeout retry with the same one.

ToolDoes
mutex_signer_addressThe local signing key's address, to paste into Settings > Agents. Never the private key.
get_balancesYour ledger accounts; the perp row is a live venue read.
get_positionsMain perp account: collateral, available, positions.
get_ordersOrders, newest first, every status; resting_only keeps the live ones.
get_fillsTrade history, one row per closed position.
mutex_deposit_addressYour deposit address on Ethereum or Solana.
get_withdrawalsWithdrawal history with state and client_ref; the agent reads it before retrying a timed-out withdraw.
mutex_withdrawWithdraw USDC (or swap to an asset) to one of your linked wallets, signed locally.
preview_orderFees, margin and worst-case fill. Pure read.
place_orderMarket, limit, stop or take-profit order on your main account, with inline brackets.
place_ocoLinked take-profit + stop-loss exit pair on a position.
modify_orderReprice or resize a resting order.
cancel_orderCancel one order.
cancel_allCancel every resting order, or one market's.
set_leveragePer-market leverage, capped by the venue maximum.
mutex_vault_listThe listed vaults with their public numbers and your share in each.
mutex_vault_getOne vault, its state, and your share in it.
mutex_vault_mineEvery vault you lead (closed ones too) and every vault you hold a share in.
mutex_vault_depositDeposit USDC into a vault: a Perps vault from your Perp account, or from Funding with source: funding; a Spot vault from Funding, or from Spot with source: spot. client_ref makes a retry safe.
mutex_vault_withdraw_previewAn estimate of a vault withdrawal: the payout and the profit share. Nothing is stored.
mutex_vault_withdrawWithdraw from a vault: a Perps vault to your Perp account, a Spot vault to Funding; pending until paid, no cancel.
Safety

Safe start

  • The balance is the cap. Fund with $X and the worst case is $X. There are no per-order size caps because the bot balance already is one.
  • The pause is the kill switch. Pause the bot to stop the agent; cancels stay allowed so positions can be unwound.
  • Prompt-injection risk is bounded. A hijacked agent can lose the bot's balance but cannot reach beyond the sub-account: no withdrawals, no main account, no other bots. With a vault's key the same holds for the vault: it can lose the vault's money, the depositors' share included, and it reaches nothing else. To stop it, delete the vault's key; Mutex can stop the vault's trading too.

In user mode the bounds are different, and you should know them before you start: a hijacked agent trades your main perp account and can withdraw your funding balance, but only to a wallet you linked, and it can never change which wallets those are. The destination is bounded, the value is not: the agent picks the amount and the asset it arrives as, and a swap into a thin token can lose most of the value to slippage. Nothing on your machine asks you before the signing key signs. Revoke the agent key in Settings > Agents to stop it.

The vault tools add one path: a hijacked agent can deposit your money into a vault, where another person trades it. That reaches no further than trading your main account already does, and a vault withdrawal comes back only to your Perp account or your Funding balance. The server has no tool that makes, changes, closes or trades a vault, but the agent key itself can make, change, trade and close your own vault. Two acts cannot be undone and the key can do both: a close, and a lower profit share.

Isolated sub-account