ibkr-mcp-server
# ibkr-mcp-server
MCP server that exposes [Interactive Brokers](https://www.interactivebrokers.com/) via the TWS API
as 14 tools: account reads, market data, contract resolution, and order placement.
**Paper-by-default.** Live trading requires explicit opt-in via `IBKR_TRADING_MODE=live`.
## Prerequisites
1. **TWS** or **IB Gateway** running locally. Download from IBKR.
2. **API enabled** in TWS/Gateway: Configure → API → Settings
- Check "Enable ActiveX and Socket Clients"
- Uncheck "Read-Only API"
- Socket port: `7497` (paper) or `7496` (live) — defaults match this server
- Add `127.0.0.1` to "Trusted IPs"
3. **uv** installed: `curl -LsSf https://astral.sh/uv/install.sh | sh`
## Install
```bash
git clone <this repo>
cd ibkr-mcp-server
uv sync
```
## Configure
Copy `.env.example` and adjust as needed (or export the vars in your shell / MCP client config).
## Register with Claude Code
Add to `~/.claude.json` (or a per-project `.mcp.json`):
```json
{
"mcpServers": {
"ibkr": {
"command": "uv",
"args": [
"--directory", "/absolute/path/to/ibkr-mcp-server",
"run", "ibkr-mcp-server"
],
"env": {
"IBKR_TRADING_MODE": "paper",
"IBKR_CLIENT_ID": "17"
}
}
}
}
```
## Tools
| Tool | Purpose |
|---|---|
| `status` | Connection + mode + gateway info |
| `get_account_summary` | Cash, net liq, buying power, margin |
| `get_positions` | Open positions with market price enrichment |
| `get_pnl` | Account-level or per-position P&L |
| `get_executions` | Today's fills (or since ISO timestamp) |
| `search_symbol` | IBKR symbol search |
| `qualify_contract` | Resolve symbol → canonical contract with conid |
| `get_quote` | Snapshot bid/ask/last/volume |
| `get_historical_bars` | OHLCV bars |
| `place_order` | Place MKT/LMT/STP/STP_LMT order |
| `place_bracket_order` | Entry + take-profit + stop-loss |
| `modify_order` | Modify an existing open order |
| `cancel_order` | Cancel an open order |
| `list_open_orders` | List currently open orders |
## Safety
- **Mode gate:** server reads `managedAccounts` on connect and verifies prefixes
(`DU*` = paper, `U*` = live). On mismatch, all order tools are blocked.
- **Audit log:** every order placement / modification / cancellation is appended
as a JSON line to `$IBKR_AUDIT_LOG` (default `~/.ibkr-mcp-server/audit.log`).
- **Read-only mode:** set `IBKR_READONLY=1` to disable all order tools.
## Risk rails (v2)
Three additional rails apply to `place_order`, `place_bracket_order`, and `modify_order`. `cancel_order` and `list_open_orders` are intentionally not gated.
- **Notional caps.** Per-order and daily-cumulative ceilings in USD. Mode-aware: paper and live have independent envs. Set to `unlimited` to disable an individual cap.
- **Rate limit.** Each gated tool has its own token bucket (capacity 10, refill 10/min). Hardcoded in v2.
- **Kill switch.** If `IBKR_HALT_FILE` exists, every gated tool returns `{status:"rejected", error_code:"HALT", ...}` immediately. Remove the file to re-enable.
Blocks are returned as data (same shape as IB-side rejections) and appended to the audit log. They do not raise.
`get_quote` now returns a `data_type` field: `"live" | "delayed" | "delayed_frozen" | "frozen"`. The server calls `ib.reqMarketDataType(3)` on connect so weekend/out-of-hours quotes fall back to delayed data instead of returning nulls.
| Env var | Default | Purpose |
|---|---|---|
| `IBKR_MAX_ORDER_NOTIONAL_PAPER` | `100000` | Per-order notional cap in paper (USD); `unlimited` to disable |
| `IBKR_MAX_ORDER_NOTIONAL_LIVE` | `25000` | Per-order notional cap in live (USD); `unlimited` to disable |
| `IBKR_MAX_DAILY_NOTIONAL_PAPER` | `1000000` | Daily cumulative cap in paper; `unlimited` to disable |
| `IBKR_MAX_DAILY_NOTIONAL_LIVE` | `100000` | Daily cumulative cap in live; `unlimited` to disable |
| `IBKR_HALT_FILE` | `~/.ibkr-mcp-server/HALT` | Path probed on every order; presence halts all order-mutating tools |
## Testing
```bash
uv run pytest # unit tests (default)
IBKR_INTEGRATION_TESTS=1 uv run pytest -m integration # against a running paper gateway
uv run python scripts/smoke.py # manual end-to-end smoke
```
TDQS
Scored across 14 tools
Each tool targets a distinct operation: order management (place, modify, cancel, list, bracket), data retrieval (account, positions, P&L, quotes, executions, historical bars), contract resolution (qualify, search), and connection status. No significant overlap.
All tools follow a consistent verb_noun pattern with snake_case (e.g., get_quote, place_order, list_open_orders). Naming is predictable and readable.
With 14 tools, the server is well-scoped for a trading API, covering orders, account info, market data, and contract resolution without being overloaded or sparse.
Core trading workflows are well-covered: full order lifecycle, account and position monitoring, market data, and contract lookup. Minor gaps like advanced order types or market depth are absent, but the essential surface is complete.