Model Context Protocol server · Interactive Brokers
Reads IB's model greeks, its implied volatility surface and its what-if margin out of TWS, then reprices the whole portfolio across underlying shocks. It answers one kind of question: how bad does it get, at what shock, and what moves if you add a hedge.
It does not duplicate the official IBKR connector. Positions, balances, orders, trades and prices come from there. This fills the gap that one leaves.
Install
The snippets carry the current release, so there is no tag to look up. TWS or IB Gateway must be running on the same machine with the API enabled — the server talks to its local socket and never reaches IBKR over the internet.
…
…
| Client | Where the config goes |
|---|---|
| Claude Desktop | %APPDATA%\Claude\claude_desktop_config.json, or ~/Library/Application Support/Claude/ |
| Claude Code | claude mcp add |
| Cursor | ~/.cursor/mcp.json, or .cursor/mcp.json per project |
| VS Code | .vscode/mcp.json, keyed under servers with "type": "stdio" |
| Windsurf | ~/.codeium/windsurf/mcp_config.json |
Ports are 7496 TWS live, 7497 TWS paper, 4001 and
4002 for IB Gateway. If the configured one is wrong,
check_connection scans all four and says which is answering.
On a login managing more than one account, set IBKR_ACCOUNT: without it
every tool that reads positions refuses, rather than adding two accounts together.
Surface
Each carries the protocol's annotations, so a client can group them by permission. Six only read. The seventh is the one that touches IB's order path, and it stays inert unless you switch it on.
| Tool | What it returns |
|---|---|
| stress_portfolio | The P&L curve across underlying shocks, its trough, a per-position breakdown, and a reconciliation against NetLiquidation |
| stress_whatif | Three curves and three troughs: the portfolio, the portfolio plus hypothetical legs, and the difference |
| get_position_greeks | IB's model greeks for every option position, with both expiry dates and the forward its model used |
| get_vol_surface | IB's implied volatility grid for an underlying, by expiry and strike, with days to settlement |
| get_margin_summary | Margin and liquidity, split into the securities and commodities segments |
| check_connection | Which of the ways TWS can be unreachable is happening, and what to do about it |
| whatif_order gated | IB's own margin impact for a structure, per leg and cumulatively, with the SPAN offset between them |
Behaviour
Each of these produces a confidently wrong number if you skip it. All were found against live TWS.
AM-settled contracts can report the day before settlement, so two options expiring the same morning appear on two dates. IB's realExpirationDate settles it where available; both dates are always returned.
It is not implied by the expiry. On 18 December 2026 the ES quarterly is written on ESZ6 while the EW3 weekly expiring the same day is on ESH7. underConId is read, never inferred.
Contracts from IB's portfolio arrive with no exchange, and a market data request on one returns nothing at all — no ticks, no error. Left alone, an entire option book reports no greeks.
A contract IB would not price is listed with the reason, and the curve says how much of the book it is missing. Volatilities implied locally are labelled as such and never passed off as IB's.
At zero shock the portfolio is rebuilt from its positions — cash, securities at market, futures at their mark — and checked against NetLiquidation. Over 1% out, it returns reconciled: false with the residual.
If the curve is still falling at the end of the range, that minimum is the edge of the window and is labelled as one. Short-option books are concave, so their worst point sits at the boundary unless something is long gamma below it.
Limits
Risk Navigator's own volatility shock model is not published by any API. This rebuilds the curve from IB's inputs with a documented model; expect the shape to agree and the last few percent not to.
modelVsMarket rather than hidden.IBKR_RISK_FREE_RATE is your assumption, not a measurement.