Skip to main content
Glama
ibkr-mcp

ibkr-mcp

by zionto

ibkr-mcp-server

MCP server for Interactive Brokers — Flex Web Service account historical analysis.

Overview

This server exposes IBKR's Flex Web Service as MCP tools so that Claude (or any MCP-compatible client) can retrieve and analyse your account history without manual report downloads.

Related MCP server: IBKR MCP Server

Prerequisites

  1. An Interactive Brokers account.

  2. A Flex Query configured in Account Management with the desired sections (Trades, OpenPositions, CashTransactions, EquitySummaryInBase, FIFOPerformanceSummaryInBase).

  3. A Flex Web Service token generated in Account Management → Reports → Flex Queries → Manage → Generate Token.

Setup

# Install with uv (recommended)
uv sync

# Or with pip
pip install -e .

Copy .env.example to .env and fill in your credentials:

cp .env.example .env
# edit .env with your IBKR_FLEX_TOKEN and IBKR_FLEX_QUERY_ID

Running the server

# stdio transport (for Claude Desktop / mcp CLI)
ibkr-mcp

# Or via uv
uv run ibkr-mcp

Claude Desktop config (local stdio)

Add to ~/Library/Application Support/Claude/claude_desktop_config.json:

{
  "mcpServers": {
    "ibkr": {
      "command": "uv",
      "args": ["run", "--project", "/path/to/ibkr-mcp", "ibkr-mcp"],
      "env": {
        "IBKR_FLEX_TOKEN": "your_token",
        "IBKR_FLEX_QUERY_ID": "your_query_id"
      }
    }
  }
}

claude.ai MCP connector (remote SSE)

claude.ai (Teams / Enterprise) supports custom MCP connectors over HTTPS. The server must be publicly reachable; use the steps below.

1. Deploy the server

# Option A — Docker (recommended)
docker build -t ibkr-mcp .
docker run -d \
  -e IBKR_FLEX_TOKEN=your_token \
  -e IBKR_FLEX_QUERY_ID=your_query_id \
  -e MCP_TRANSPORT=sse \
  -e MCP_AUTH_TOKEN=your_secret_token \
  -p 8000:8000 \
  ibkr-mcp

# Option B — uv directly (on a VPS / cloud VM)
MCP_TRANSPORT=sse \
MCP_AUTH_TOKEN=your_secret_token \
IBKR_FLEX_TOKEN=your_token \
IBKR_FLEX_QUERY_ID=your_query_id \
uv run ibkr-mcp

2. Put HTTPS in front (required by claude.ai)

Use any reverse proxy that terminates TLS. A minimal Caddy example:

your-domain.com {
    reverse_proxy localhost:8000
}

Or use a managed tunnel for quick testing:

ngrok http 8000   # gives you https://xxxx.ngrok.io

3. Add to claude.ai

  1. Open claude.ai → Settings → Integrations → Add custom integration

  2. Fill in:

    • Name: IBKR

    • URL: https://your-domain.com/sse (note the /sse path)

  3. Under Authorization, select Bearer token and paste the value of MCP_AUTH_TOKEN

  4. Click Save — the tools appear automatically

Health check: GET https://your-domain.com/health returns {"status":"ok"} and bypasses bearer auth, so you can verify connectivity without credentials.

Available Tools

Flex Web Service protocol

Tool

Description

flex_send_request

Initiate a Flex query; returns a reference_code

flex_get_statement

Download a completed report by reference code (polls until ready)

flex_run_query

Convenience: send + poll + download in one call

XML parsing

Tool

Description

flex_account_info

Account metadata (account ID, type, currency, date range)

flex_parse_trades

Trade records — filterable by symbol, asset category, date

flex_parse_positions

Open position records

flex_parse_cash_txns

Cash transactions (dividends, interest, fees, etc.)

flex_parse_equity

Daily NAV / equity summary time series

flex_parse_fifo_pnl

FIFO realized and unrealized P&L by symbol

Analysis

Tool

Description

flex_analyze_pnl

Aggregate realized P&L from trades; group by symbol, month, year, asset category

flex_analyze_dividends

Summarize gross dividends and withholding taxes by symbol

flex_analyze_portfolio_history

Portfolio value over time with total-return calculation

Typical workflow

1. flex_run_query()                    → xml_content
2. flex_account_info(xml_content)      → account metadata
3. flex_analyze_pnl(xml_content)       → realized P&L by symbol
4. flex_analyze_dividends(xml_content) → dividend income summary
5. flex_analyze_portfolio_history(xml_content) → NAV time series

Flex Query setup guide

Your query must include these Sections for full tool coverage:

  • Account Information

  • Trades (select Executions)

  • Open Positions

  • Cash Transactions

  • Equity Summary in Base Currency

  • FIFO Performance Summary in Base Currency

Recommended date period: Last N days or Custom Date Range covering your analysis window.

Development

uv sync --dev
pytest tests/

Available Tools

13 tools
flex_account_infoA

Extract account metadata from a Flex XML report.

Args: xml_content: Raw Flex XML string (from flex_run_query or flex_get_statement).

Returns JSON array of account info objects, one per FlexStatement in the report.

Inputs: xml_content (required). Returns: result

flex_analyze_cash_utilizationA

Calculate free cash as a percentage of Net Liquidation Value (Net Liq), after deducting cash reserved for short (sold) put obligations.

When a put is sold, the seller must be able to purchase the underlying if assigned. The worst-case cash reservation per contract is:

strike × multiplier × |quantity|

This tool answers: "Of my total Net Liq, how much of my cash is actually free — not spoken for by open short-put assignments?"

Uses the most recent equity-summary row for Net Liq and cash, and the current open-positions snapshot for short puts.

Returns JSON: net_liq — total portfolio value in base currency gross_cash — cash balance from equity summary reserved_for_puts — worst-case assignment obligation of all short puts free_cash — gross_cash − reserved_for_puts free_cash_pct_net_liq — free_cash / net_liq × 100 (the headline %) cash_pct_net_liq — gross_cash / net_liq × 100 (for reference) currency — base currency of the account as_of_date — equity summary report date used short_puts — per-position breakdown of the reservation

Inputs: xml_content (required). Returns: result

flex_analyze_dividendsA

Summarize dividend income and withholding taxes from cash transactions.

Args: xml_content: Raw Flex XML string.

Returns JSON with total gross dividends, total withholding tax, net dividends, and a per-symbol breakdown.

Inputs: xml_content (required). Returns: result

flex_analyze_pnlA

Aggregate realized P&L from trade records in a Flex XML report.

Only closing trades (open_close contains 'C') contribute to realized P&L.

Args: xml_content: Raw Flex XML string. group_by: Dimension to aggregate by — "symbol", "month", "year", "asset_category", or "buy_sell" (default "symbol"). asset_category: Filter by asset category e.g. STK, OPT, FUT (optional). from_date: Earliest trade date, YYYY-MM-DD (optional). to_date: Latest trade date, YYYY-MM-DD (optional).

Returns JSON with totals and a breakdown list sorted by realized_pnl descending.

Inputs: xml_content (required), to_date, group_by, from_date, +1 more. Returns: result

flex_analyze_portfolio_historyA

Show how the portfolio NAV changed over time and compute a return figure.

Return methods

twr (default) — Time-Weighted Return via daily chain-linking. Eliminates the timing/size effect of external cash flows. Use this to evaluate the strategy independent of cash-flow decisions.

mwr — Money-Weighted Return via Modified Dietz. Reflects the investor's actual experience: large deposits before a rally boost the return; large withdrawals before a rally reduce it. Use this to evaluate overall portfolio performance.

Args: xml_content: Raw Flex XML string. metric: Which equity component to track — "total", "cash", "stock", "options", "futures", "bonds" (default "total"). return_method: "twr" (default) or "mwr".

Returns JSON with a time-series, annualised and period returns, and the total net external cash flow over the period.

Inputs: xml_content (required), metric, return_method. Returns: result

flex_get_statementA

Download a completed Flex report using its reference code.

Polls automatically until the report is ready or the timeout is reached.

Args: reference_code: Reference code returned by flex_send_request. token: Flex API token (overrides IBKR_FLEX_TOKEN env var). poll_interval_seconds: Seconds between polling attempts (default 5). max_wait_seconds: Maximum seconds to wait for the report (default 120).

Returns JSON: {"xml_content": "<FlexQueryResponse...>..."}

Inputs: reference_code (required), token, max_wait_seconds, poll_interval_seconds. Returns: result

flex_parse_cash_txnsA

Extract cash transaction records from a Flex XML report.

Common transaction_type values: Dividends, "Withholding Tax", "Broker Interest Paid", "Broker Interest Received", "Other Fees".

Args: xml_content: Raw Flex XML string. transaction_type: Filter by transaction type substring (case-insensitive, optional). symbol: Filter by symbol (optional). from_date: Earliest date, YYYY-MM-DD (optional). to_date: Latest date, YYYY-MM-DD (optional).

Returns JSON: {"count": N, "total_amount": "...", "transactions": [...]}

Inputs: xml_content (required), symbol, to_date, from_date, +1 more. Returns: result

flex_parse_equityA

Extract daily NAV / equity summary rows from a Flex XML report.

Args: xml_content: Raw Flex XML string. from_date: Earliest report date, YYYY-MM-DD (optional). to_date: Latest report date, YYYY-MM-DD (optional).

Returns JSON: {"count": N, "equity_summary": [...]}

Inputs: xml_content (required), to_date, from_date. Returns: result

flex_parse_fifo_pnlA

Extract FIFO realized/unrealized P&L summary from a Flex XML report.

Args: xml_content: Raw Flex XML string. symbol: Filter by ticker symbol (case-insensitive, optional). asset_category: Filter by asset category e.g. STK, OPT, FUT (optional).

Returns JSON: {"count": N, "total_realized": "...", "total_unrealized": "...", "rows": [...]}

Inputs: xml_content (required), symbol, asset_category. Returns: result

flex_parse_positionsA

Extract open position records from a Flex XML report.

Args: xml_content: Raw Flex XML string. symbol: Filter by ticker symbol (case-insensitive, optional). asset_category: Filter by asset category e.g. STK, OPT, FUT (optional).

Returns JSON: {"count": N, "positions": [...]}

Inputs: xml_content (required), symbol, asset_category. Returns: result

flex_parse_tradesA

Extract trade records from a Flex XML report.

Args: xml_content: Raw Flex XML string. symbol: Filter by ticker symbol (case-insensitive, optional). asset_category: Filter by asset category e.g. STK, OPT, FUT (optional). from_date: Earliest trade date to include, YYYY-MM-DD (optional). to_date: Latest trade date to include, YYYY-MM-DD (optional).

Returns JSON: {"count": N, "trades": [...]}

Inputs: xml_content (required), symbol, to_date, from_date, +1 more. Returns: result

flex_run_queryA

Send a Flex query and wait for the complete XML report.

Combines flex_send_request + flex_get_statement in one call.

Args: query_id: Flex Query ID (overrides IBKR_FLEX_QUERY_ID env var). token: Flex API token (overrides IBKR_FLEX_TOKEN env var). poll_interval_seconds: Seconds between polling attempts (default 5). max_wait_seconds: Maximum seconds to wait for the report (default 120).

Returns JSON: {"xml_content": "<FlexQueryResponse...>..."}

Inputs: token, query_id, max_wait_seconds, poll_interval_seconds. Returns: result

flex_send_requestA

Initiate a Flex query. Returns a reference_code to use with flex_get_statement.

Args: query_id: Flex Query ID (overrides IBKR_FLEX_QUERY_ID env var). token: Flex API token (overrides IBKR_FLEX_TOKEN env var).

Returns JSON: {"reference_code": "...", "status": "..."}

Inputs: token, query_id. Returns: result

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A4.2/5.0

    Scored across 13 tools

    Disambiguation5/5

    Each tool targets a distinct action or report section. The only potentially overlapping pair is flex_parse_fifo_pnl and flex_analyze_pnl, but their descriptions clearly differentiate raw extraction vs. aggregated analysis. Similarly, flex_run_query explicitly wraps the two-step send/get process.

    Naming Consistency4/5

    All tools share the flex_ prefix and almost all follow a verb_noun pattern (send_request, parse_trades, analyze_pnl). The sole exception is flex_account_info, which uses a noun-only suffix. This is a minor inconsistency that does not hinder readability.

    Tool Count5/5

    13 tools is within the ideal 3-15 range for a domain-specific MCP. Each tool covers a distinct stage of the Flex report lifecycle or a different report section, so none feel redundant.

    Completeness5/5

    The server covers the full workflow: initiate query (flex_send_request), retrieve XML (flex_get_statement, flex_run_query), parse core sections (trades, positions, cash, equity, FIFO P&L), and analyze (PnL, dividends, cash utilization, portfolio history). No obvious missing operations for the stated purpose.

    Related MCP Connectors

    Related MCP Servers

    • F
      license
      Not graded
      quality
      D
      maintenance
      Enables LLM clients to interact with Interactive Brokers Trader Workstation for automated trading workflows. Supports market data retrieval, portfolio management, and order execution through the TWS API.
      5
      -
    • A
      license
      Not graded
      quality
      D
      maintenance
      Provides AI models with secure access to Interactive Brokers trading data and functionality, enabling account management, market data retrieval, and trading operations through natural language interactions.
      18
      MIT
    • F
      license
      Not graded
      quality
      C
      maintenance
      Connects Claude AI to Interactive Brokers accounts to enable real-time portfolio tracking, position management, and historical market data retrieval. It also integrates financial news and sentiment analysis from multiple sources, including Finnhub and IB native feeds.
      -
    • A
      license
      B
      quality
      B
      maintenance
      Provides 32 trading analysis tools for AI-powered market analysis, including real-time data, technical indicators, options Greeks, scanners, and Interactive Brokers portfolio management, all accessible via natural language in Claude Desktop.
      36
      406
      MIT