# MCP server: let Claude, Cursor, VS Code and other AI agents trade MT5

URL: https://docs.fxapis.com/guides/mcp

> Connect an AI assistant or agent to fxapis over the Model Context Protocol. Setup for Claude, Claude Code, Claude Desktop, Cursor, VS Code and the OpenAI and…



fxapis runs a hosted [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server.
Point an MCP client at it with an fxapis API key and the assistant can list your
MetaTrader 5 accounts, read positions, orders and deals, bring accounts online and
— if the key allows it — place, modify and close trades.

```
https://api.fxapis.com/mcp
```

There is nothing to install or run: the server is part of the API, and the MT5
terminals run in our EU cloud, as they do for every other fxapis call.

<Callout type="warn" title="An agent with a trading key trades real money">
  Every fxapis key, `fx_test_` included, reaches a real broker. Start with a **broker demo
  account** and a **read-only key**. Give an agent `trading:reduce` (close, move stops, cancel)
  before you ever give it `trading:execute` (open positions). Read [Safety model](#safety-model)
  before connecting a live account.
</Callout>

## How it works [#how-it-works]

* **Transport:** Streamable HTTP, stateless. It speaks both the 2026-07-28 revision of the
  protocol and the 2025 revisions (clients that start with an `initialize` handshake), and
  answers in plain JSON. There are no sessions, so any of our servers can answer any request.
* **Authentication:** your fxapis API key, in `Authorization: Bearer fx_live_…`. `X-API-Key: fx_live_…`
  is accepted too, for clients that only offer that header. A missing or invalid key gets
  `401` with a `WWW-Authenticate: Bearer` challenge. OAuth sign-in is not available yet.
* **The same rules as the REST API.** Each tool calls the ordinary `/v1` endpoints with your
  key. Scopes, workspace isolation, rate limits, plan limits, idempotency and our trading kill
  switch apply exactly as they do to your own code — the MCP server cannot do anything the key
  could not do over REST. Each tool call counts as one or two API calls on your plan.
* **Deliberately missing:** connecting an MT5 account and managing API keys. Connecting takes
  the broker password, and a broker credential must never pass through a language model's
  context. Do both in the [fxapis console](https://fxapis.com/dashboard).

## Create a key for the agent [#create-a-key-for-the-agent]

In the console, [create a key](https://fxapis.com/dashboard/keys) just for the assistant, with
the fewest scopes that do the job:

| The agent should…                                        | Scopes                                          |
| -------------------------------------------------------- | ----------------------------------------------- |
| Report on accounts, positions and history                | `accounts:read`, `trading:read`, `history:read` |
| …and bring accounts online or take them offline          | add `accounts:write`                            |
| …and manage risk: close, move stops, cancel — never open | add `trading:reduce`                            |
| …and open new positions                                  | add `trading:execute`                           |

A key without the scope for a tool gets `MISSING_SCOPE` back from that tool, and the agent is
told to ask you rather than work around it. Revoke the key in the console and the agent is
cut off on its very next call.

## Connect your client [#connect-your-client]

Replace `fx_live_…` with your key in each example. Keep the key out of anything you commit.

### Claude (claude.ai and Claude Desktop) [#claude-claudeai-and-claude-desktop]

Custom connectors are added in **Customize → Connectors → Add custom connector** (on Team and
Enterprise plans an Owner adds it under **Organization settings → Connectors**).

1. **MCP server URL:** `https://api.fxapis.com/mcp`
2. **Authentication:** choose **No sign-in**.
3. **Request headers:** add `authorization` with the value `Bearer fx_live_…` — include the
   word `Bearer` and the space; Claude sends the value exactly as typed.

Connectors you add on claude.ai are also available in the Claude desktop app.

<Callout title="Request headers are in beta at Claude">
  Anthropic is rolling out the **Request headers** section to a limited set of organizations
  ([Anthropic's connector docs](https://claude.com/docs/connectors/custom/remote-mcp), checked 30
  September 2026).
  If your dialog does not show it, claude.ai cannot send an API key yet — use Claude Code, or
  Claude Desktop through `mcp-remote` as below, until it does. Header settings cannot be edited
  later: to change the key, remove the connector and add it again.
</Callout>

**Claude Desktop without request headers.** Claude Desktop can run the open-source
[`mcp-remote`](https://www.npmjs.com/package/mcp-remote) bridge, which adds the header for it.
Add this to `claude_desktop_config.json` (Settings → Developer → Edit Config), then restart
Claude Desktop. Node.js 18 or newer must be installed.

```json title="claude_desktop_config.json"
{
  "mcpServers": {
    "fxapis": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://api.fxapis.com/mcp", "--header", "Authorization:${FXAPIS_AUTH}"],
      "env": { "FXAPIS_AUTH": "Bearer fx_live_…" }
    }
  }
}
```

The header is passed through an environment variable because some systems split arguments on
spaces.

### Claude Code [#claude-code]

```bash
claude mcp add --transport http fxapis https://api.fxapis.com/mcp \
  --header "Authorization: Bearer $FXAPIS_KEY"
```

Or share it with your team in the project's `.mcp.json`, reading the key from each person's
environment:

```json title=".mcp.json"
{
  "mcpServers": {
    "fxapis": {
      "type": "http",
      "url": "https://api.fxapis.com/mcp",
      "headers": { "Authorization": "Bearer ${FXAPIS_KEY}" }
    }
  }
}
```

Run `/mcp` inside Claude Code to check that `fxapis` is connected.

### ChatGPT [#chatgpt]

ChatGPT's developer-mode apps currently connect with OAuth or with no authentication; they do
not send an API key ([OpenAI's developer mode guide](https://developers.openai.com/api/docs/guides/developer-mode),
checked 30 September 2026). Because fxapis requires a key and does not offer OAuth sign-in yet, the
fxapis MCP server cannot be added to ChatGPT today. Agents built on the OpenAI API can use it —
see [OpenAI API](#openai-api-responses).

### Cursor [#cursor]

Add to `~/.cursor/mcp.json` (all projects) or `.cursor/mcp.json` (one project):

```json title="~/.cursor/mcp.json"
{
  "mcpServers": {
    "fxapis": {
      "url": "https://api.fxapis.com/mcp",
      "headers": { "Authorization": "Bearer ${env:FXAPIS_KEY}" }
    }
  }
}
```

`${env:FXAPIS_KEY}` reads the key from your environment; you can also paste the key in its
place in a file that is never committed. The server appears under **Settings → MCP**.

### VS Code (GitHub Copilot agent mode) [#vs-code-github-copilot-agent-mode]

Add `.vscode/mcp.json`. VS Code asks for the key the first time and stores it securely:

```json title=".vscode/mcp.json"
{
  "inputs": [
    { "type": "promptString", "id": "fxapis-key", "description": "fxapis API key", "password": true }
  ],
  "servers": {
    "fxapis": {
      "type": "http",
      "url": "https://api.fxapis.com/mcp",
      "headers": { "Authorization": "Bearer ${input:fxapis-key}" }
    }
  }
}
```

### OpenAI API (Responses) [#openai-api-responses]

```json
{
  "type": "mcp",
  "server_label": "fxapis",
  "server_url": "https://api.fxapis.com/mcp",
  "authorization": "fx_live_…",
  "require_approval": {
    "never": { "tool_names": ["list_accounts", "get_account_status", "list_positions", "get_order", "list_orders"] }
  }
}
```

Put this in the request's `tools` array. `require_approval` as written lets the model read
freely and asks your code to approve every other call, including every trade. The value in
`authorization` is not stored by OpenAI; send it with each request
([OpenAI's MCP guide](https://developers.openai.com/api/docs/guides/tools-connectors-mcp), checked 30
September 2026).

### Anthropic API (Messages) [#anthropic-api-messages]

```json
{
  "model": "claude-opus-5",
  "max_tokens": 16000,
  "mcp_servers": [
    { "type": "url", "url": "https://api.fxapis.com/mcp", "name": "fxapis", "authorization_token": "fx_live_…" }
  ],
  "tools": [{ "type": "mcp_toolset", "mcp_server_name": "fxapis" }],
  "messages": [{ "role": "user", "content": "Which of my MT5 accounts are online?" }]
}
```

Send it with the `anthropic-beta: mcp-client-2025-11-20` header. Both halves are needed: the
server under `mcp_servers` and the `mcp_toolset` entry in `tools`.

### Any other client [#any-other-client]

Any client that supports Streamable HTTP and custom headers works: URL
`https://api.fxapis.com/mcp`, header `Authorization: Bearer fx_live_…`. To test from a terminal
with the [MCP Inspector](https://github.com/modelcontextprotocol/inspector):

```bash
npx @modelcontextprotocol/inspector --cli https://api.fxapis.com/mcp --transport http \
  --header "Authorization: Bearer $FXAPIS_KEY" --method tools/list
```

## Tools [#tools]

Arguments are `snake_case`. Account ids are the fxapis UUIDs from `list_accounts`, not MT5 login
numbers. Volumes and prices are best sent as strings (`"0.10"`, `"1.08450"`); numbers are
accepted and converted. Every result carries the API's own data as structured content, plus a
one-line summary for the model.

### Read-only [#read-only]

These never change anything. Clients that honour `readOnlyHint` can run them without asking.

| Tool                        | What it does                                                                              | Scope                                       |
| --------------------------- | ----------------------------------------------------------------------------------------- | ------------------------------------------- |
| `get_workspace`             | Plan, its limits and features, and whether trading is switched off                        | `accounts:read`                             |
| `get_usage`                 | This month's orders, multi-account orders and API calls against the plan                  | `accounts:read`                             |
| `list_accounts`             | Every connected MT5 account with its id, login, server and state                          | `accounts:read`                             |
| `get_account`               | One account: broker, currency, leverage, state                                            | `accounts:read`                             |
| `get_account_status`        | Just the state — poll it after `bring_accounts_online` until `ready`                      | `accounts:read`                             |
| `list_positions`            | Positions on an account as last observed, with `observedAt`                               | `trading:read`                              |
| `list_orders`               | Orders, newest first, filtered by account, state, symbol and time; paginated              | `trading:read`                              |
| `get_order`                 | One order, the broker's return code and fill; optionally its deals                        | `trading:read` (+ `history:read` for deals) |
| `list_deals`                | The broker's deal history for an account, including deals no order of yours caused        | `history:read`                              |
| `list_multi_account_orders` | Recent multi-account orders, with a result per account                                    | `trading:read`                              |
| `get_multi_account_order`   | One multi-account order, to follow it until it settles                                    | `trading:read`                              |
| `calculate`                 | Margin an order would need, or profit between two prices — asks the broker, opens nothing | `trading:read`                              |

### Account lifecycle [#account-lifecycle]

These start or stop the account's terminal in our cloud. They never trade and are safe to repeat.

| Tool                    | What it does                                                                        | Scope            |
| ----------------------- | ----------------------------------------------------------------------------------- | ---------------- |
| `bring_accounts_online` | Starts and logs in up to 200 accounts; returns at once with a result per account    | `accounts:write` |
| `take_account_offline`  | Stops the terminal. Open positions, stop losses and take profits stay at the broker | `accounts:write` |

### Trading [#trading]

These act on a real broker account. They are marked `destructiveHint: true`, so a client that
asks before acting will ask. Each one **requires an `idempotency_key`**.

| Tool                         | What it does                                                        | Scope             |
| ---------------------------- | ------------------------------------------------------------------- | ----------------- |
| `place_market_order`         | Opens a position at market, with optional stop loss and take profit | `trading:execute` |
| `place_pending_order`        | Places a limit, stop or stop-limit order that waits at the broker   | `trading:execute` |
| `close_position`             | Closes a position in whole or in part                               | `trading:reduce`  |
| `modify_position`            | Moves or removes a position's stop loss and take profit             | `trading:reduce`  |
| `cancel_order`               | Cancels a pending order that has not triggered                      | `trading:reduce`  |
| `place_multi_account_order`  | Places the same trade on many accounts at once                      | `trading:execute` |
| `cancel_multi_account_order` | Calls off a multi-account order before anything is sent             | `trading:reduce`  |

**The idempotency key.** The agent generates a fresh key — a UUID is ideal — for each new
action, and sends the same key again if it retries that action after an error or a timeout.
It is the [`Idempotency-Key` header](/trading) of the REST API, passed through unchanged.
Market orders, pending orders, closes and multi-account orders take an Idempotency-Key: with
the same key, fxapis returns the first answer instead of acting twice, so a retried order is
never a second order and a retried close is never an opposite position. Stop changes and
cancels are safe to repeat.

## Errors [#errors]

A failure comes back as an MCP tool error (`isError: true`) whose text starts with the API's
own [error code](/errors), followed by the message and what to do next:

```
ORDER_UNRESOLVED (HTTP 502): Something went wrong and we do not know yet whether the order was sent. We are confirming it with the broker. Do not resend it.
Next step: Do NOT resend this order. Its outcome is unknown and it may be live at the broker. Poll get_order with the orderId in details until it leaves `unknown`, and tell the user.
```

The same code, status, message, request id and next step are in the result's `_meta` under
`com.fxapis/error`, for programs. The codes that matter most to an agent:

| Code                               | Meaning for the agent                                                                |
| ---------------------------------- | ------------------------------------------------------------------------------------ |
| `ORDER_UNRESOLVED`                 | The outcome is unknown and the order may be live. **Never resend**; poll `get_order` |
| `SEND_FAILED`                      | Nothing reached the broker. Retry with the **same** `idempotency_key`                |
| `ORDER_REJECTED`                   | The broker refused it; nothing was opened. Tell the user the reason                  |
| `MISSING_SCOPE`                    | The key does not allow this. Ask the user; do not look for another way               |
| `ACCOUNT_NOT_READY` / `NO_RUNTIME` | Bring the account online, wait for `ready`, then retry                               |
| `RATE_LIMITED`                     | Wait `retryAfterSeconds`, then retry once                                            |
| `TRADING_DISABLED`                 | Trading is switched off for the account or workspace. Do not retry                   |

## Safety model [#safety-model]

An agent is a new kind of caller: it can misread a request, act on text planted in a web page
or a message it was asked to summarize, or loop. The protections, from strongest to weakest:

1. **The key's scopes.** These are enforced by the API on every call, whatever the model
   decides. A read-only key cannot trade, and a `trading:reduce` key can only ever close,
   cancel and move stops — it cannot open exposure. Start there.
2. **Demo accounts.** Connect a broker demo account while you learn how an agent behaves with
   it. There is no simulated market at fxapis; demo accounts are how you test.
3. **Your plan's limits.** Opening orders are refused once the plan's limits are reached
   (closing is never refused for billing), and rate limits apply per class of request.
4. **Your client's approvals.** Trading tools are marked destructive and read-only tools are
   marked read-only, so clients such as Claude Code and Cursor ask before a trade. Do not choose
   "always allow" for trading tools on a live account.
5. **The tool descriptions.** Every trading tool tells the model it trades real money and to
   confirm symbol, side, volume and account with you first. That shapes behaviour; it does not
   enforce anything. Scopes do.

Every order an agent places is recorded like any other — in `list_orders` and in the console —
with the key that placed it. Tool calls are logged with the tool name, the
workspace and the outcome — never the arguments.

## Example prompts [#example-prompts]

* "Which of my MT5 accounts are online, and what positions are open on each?"
* "What was my realised profit on EURUSD this week on the Vantage demo account?"
* "How much margin would 0.5 lots of XAUUSD need on account 2?"
* "Bring all my accounts online, then tell me when they are ready."
* "Move the stop loss on my gold position to break-even." *(needs `trading:reduce`)*
* "Close half of every open EURUSD position on the demo account." *(needs `trading:reduce`)*
* "Buy 0.01 lots of EURUSD on the demo account with a 20-pip stop." *(needs `trading:execute`)*
* "Did anything go wrong with today's orders? Show me any that are still `unknown`."

## Limits of the MCP server today [#limits-of-the-mcp-server-today]

* **API keys only.** OAuth sign-in, which would let a claude.ai or ChatGPT user connect with
  their fxapis login instead of a key, is planned.
* **No streaming or notifications.** Tools answer once. Following an account coming online or a
  multi-account order settling is done by polling, as with the REST API; fxapis has no event
  webhooks yet.
* **MetaTrader 5 only.** MT4 is not supported.
