ibkr-mcp-guard
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@ibkr-mcp-guardshow my account balance and positions, then draft a limit buy of 10 AAPL"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
ibkr-mcp-guard
IBKR (guarded): a small local MCP server that lets your AI assistant use Interactive Brokers' official MCP server through a safety guard. The assistant can read your account and market data and create order drafts. It can never send an order to the market.
An opt-in paper trading mode lets the assistant submit orders to an IBKR paper (simulated) account, so you can test strategies. Live accounts can never receive orders.
Unofficial. This project is not affiliated with, endorsed by, or supported by Interactive Brokers. It connects to IBKR's official public MCP server (
https://api.ibkr.com/v1/api/mcp-public), announced on 28 July 2026, using IBKR's own browser sign-in.
Runs locally over stdio. Needs Node.js 20 or newer and has no npm dependencies.
Installs by URL as a plugin in EnConvo, Claude Code and Codex, or runs with
npx.
Safety guarantees
Never requests order submission | It asks for the scopes |
Submit tools removed | Any upstream tool that places, submits, transmits or executes an order, or that requires |
Drafts only | Order instructions ( |
Asks before drafting | If your MCP client supports elicitation, the guard asks you to Approve or Cancel each draft before sending it. Otherwise your host's own tool-approval prompt applies, which is triggered by |
Tokens stay local | macOS: stored in your login Keychain. Elsewhere: stored in a file with mode |
Nothing else leaves your machine | The guard connects only to |
Tool classes:
Class | Examples | Behaviour |
read |
| Listed with |
write |
| Listed with |
draft |
| Listed with the DRAFT prefix and |
block |
| Never listed, never called. |
Set IBKR_MCP_GUARD_READONLY=1 to hide the write and draft tools as well.
Related MCP server: IBKR MCP Server
Install
EnConvo
Open Plugins → Install from URL and paste:
https://github.com/tivojn/ibkr-mcp-guardEnConvo reads .mcp.json and expands ${CLAUDE_PLUGIN_ROOT}.
Claude Code
As a plugin (this repo is its own marketplace):
claude plugin marketplace add https://github.com/tivojn/ibkr-mcp-guard
claude plugin install ibkr-mcp-guard@ibkr-mcp-guardInside Claude Code, the same commands are /plugin marketplace add tivojn/ibkr-mcp-guard followed by /plugin install ibkr-mcp-guard@ibkr-mcp-guard.
Or as a plain MCP server:
claude mcp add ibkr -- npx -y github:tivojn/ibkr-mcp-guardCodex
Add to ~/.codex/config.toml:
[mcp_servers.ibkr]
command = "npx"
args = ["-y", "github:tivojn/ibkr-mcp-guard"]
startup_timeout_sec = 60 # the first npx run downloads the packageIf you use a local clone, set command = "node" and args = ["/path/to/ibkr-mcp-guard/server/index.mjs"]. The repo also ships a .codex-plugin/plugin.json manifest for hosts that install Codex-format plugins by URL.
Any other MCP client
npx -y github:tivojn/ibkr-mcp-guard # stdio MCP server
# or from a clone:
node server/index.mjsFirst use
Ask your assistant to "sign in to IBKR", or ask any IBKR question, such as "what are my positions?".
Your browser opens IBKR's sign-in page. The consent screen should list read access and order-instruction (draft) access only, not order submission. Approve it.
The tab says Signed in to IBKR. Go back to the assistant and ask again.
The guard's own tools work before you sign in:
ibkr_sign_inopens the browser sign-in. It returns immediately and finishes in the background, and does nothing if you are already signed in.ibkr_statusshows whether you are signed in, your account ids (if IBKR reports them), the granted scopes, when the token expires, and whether order submission was granted (it should say no).ibkr_sign_outrevokes the tokens at IBKR and deletes them locally.ibkr_paper_log(only in paper trading mode) shows the latest paper-order audit entries.
Before you first sign in, the tool list shows only these three tools. After that, the guard caches IBKR's tool list, so the tools still appear when you are signed out. Calling one while signed out starts a sign-in.
Environment variables
Variable | Effect |
| Hide and refuse write and draft tools; reads only. Also turns paper mode off. |
| Paper trading mode: order submission to IBKR paper accounts only. |
| In paper mode, skip the guard's Approve/Cancel prompt before each paper order (for automated strategy runs). Has no effect outside paper mode. |
| Use the |
| Don't open a browser; the sign-in result includes the link to open yourself (useful over SSH). |
| Data directory (set by plugin hosts). Otherwise |
| Testing only: a different upstream MCP URL. A warning is logged to stderr. |
The data directory holds the token file (non-macOS, or with STORE=file), tools-cache.json (the last tool list, which contains no secrets) and, in paper mode, paper-orders.log.
Paper trading mode
Paper trading mode is for testing strategies on an IBKR paper trading account through IBKR's official connector. It is off by default. When it is off, nothing on this page applies and the guard behaves exactly as described above.
What it does
With IBKR_MCP_GUARD_PAPER=1:
The sign-in also asks for order submission. The guard requests
openid account-ids mcp.read mcp.write mcp.orders.submit. Only this code path can ask formcp.orders.submit; the default path still throws if anything asks for it.Changing modes needs a new sign-in. Each saved sign-in remembers which scopes it was made with. If you turn paper mode on or off, the old sign-in is not used, and the guard asks you to sign in again.
Order submission is gated to paper accounts. The tools that the default mode removes (place, submit, cancel or modify orders, and so on) are allowed only when every account the sign-in can see is a paper account, meaning its id starts with
DUorDF. Before every submit, the guard:reads the account ids again from IBKR, by calling the account tools (
get_account_positions,get_account_balances,get_account_summary,get_account_ordersand any other read tool whose name mentions accounts) and scanning their answers for account ids;refuses if it sees any non-paper id (
U…,F…,I…), or if it cannot find any account id at all;refuses if the order's arguments name an account that is not one of those paper accounts.
The result of this check is never cached. Each submit makes its own check.
Submit tools are shown only while the session is paper-only. They are listed with the description prefix "PAPER ACCOUNT ONLY — submits a simulated order to your IBKR paper account (DU…). Refused for live accounts." and
destructiveHint: true. The guard checks again on everytools/listand after each sign-in, and sendsnotifications/tools/list_changedwhen the answer changes. At other times the submit tools are hidden, and they are refused if called by name.Every order is confirmed. If your MCP client supports elicitation, the guard shows a plain read-back (account, side, quantity, symbol, order type, price if given, time in force) and asks you to Approve or Cancel. After you approve, it checks the accounts again before sending. Without elicitation, your host's own approval prompt applies, which is triggered by
destructiveHint. For automated strategy runs,IBKR_MCP_GUARD_PAPER_NO_CONFIRM=1skips the guard's prompt. Even then, orders still go only to paper accounts.Every attempt is logged. Each paper submit attempt (submitted, failed, refused or cancelled) is added as one JSON line to
paper-orders.login the data directory (file mode0600). Each line records the time, the tool, the decision and reason, the accounts seen, a read-back of the order and its arguments. Tokens are never written to the log. Use theibkr_paper_logtool (optionallimit, default 20) to see recent entries.ibkr_statusshows the paper state: whether paper mode is on, the accounts seen in a fresh read (each labelled paper or LIVE), whethermcp.orders.submitwas granted, and whether submits are allowed right now and why.
IBKR_MCP_GUARD_READONLY=1 overrides paper mode: the guard does not request the submit scope and no orders are submitted.
Get a paper login
In IBKR's Client Portal, open Settings → Paper Trading Account. Create (or reset) your paper account there and note its username. Paper accounts start with simulated funds, normally USD 1,000,000.
When the guard opens IBKR's sign-in page, set the login page's Live / Paper switch to Paper, then sign in with the paper username.
IBKR's official MCP connector does accept paper logins. A paper sign-in through this guard has been checked: the account summary showed the paper account's USD 1,000,000 net liquidation value and no positions. If you sign in with a live login while paper mode is on, the guard sees the live account id and refuses every submit.
Turn it on
Paper mode is set by an environment variable on the MCP server, so set it in your host's server configuration.
EnConvo: in the plugin's MCP server settings, add the environment variable IBKR_MCP_GUARD_PAPER with value 1 (and, if you want, IBKR_MCP_GUARD_PAPER_NO_CONFIRM = 1). Then restart the server.
Claude Code: add it as a separate MCP server with the variable set:
claude mcp add -e IBKR_MCP_GUARD_PAPER=1 ibkr-paper -- npx -y github:tivojn/ibkr-mcp-guardCodex (~/.codex/config.toml):
[mcp_servers.ibkr-paper]
command = "npx"
args = ["-y", "github:tivojn/ibkr-mcp-guard"]
env = { IBKR_MCP_GUARD_PAPER = "1" }
startup_timeout_sec = 60After you turn it on, ask the assistant to "sign in to IBKR", sign in with your paper login, and then run ibkr_status. It should show your DU… account labelled paper and "Paper order submission allowed now: yes".
Paper trading is simulated. Paper fills, prices and margin can differ from what would happen in a live account, and results in paper trading do not predict live results. This is not financial advice.
Troubleshooting
The browser didn't open. Use the link in the tool result, or set
IBKR_MCP_GUARD_NO_BROWSER=1. The sign-in must finish on the same machine, because IBKR redirects tohttp://127.0.0.1:<port>/callback."Sign-in timed out". The flow waits 10 minutes. Ask to sign in again.
"Sign-in did not match". You opened an old sign-in tab. Start a new sign-in.
No IBKR tools after signing in. Your client may ignore
notifications/tools/list_changed. Restart the MCP server or reload tools.Signed out unexpectedly. IBKR refused to renew the token (for example, it was revoked or expired). Sign in again.
Reset everything. Run
ibkr_sign_out. Then:On macOS, delete the Keychain items with service
ibkr-mcp-guard(Keychain Access, orsecurity delete-generic-password -s ibkr-mcp-guard -a https://api.ibkr.com/v1/api/mcp-public).Elsewhere, delete the data directory.
Logs. The server logs to stderr only. stdout carries MCP JSON-RPC.
Paper mode refuses every submit. Run
ibkr_status. "This sign-in can see non-paper (live) account(s)" means you signed in with a live login: sign out and sign in with your paper login. "No account ids could be read" means IBKR's account tools did not return any account id, so the guard cannot confirm that only paper accounts are visible."You need to sign in again" after changing paper mode. This is expected: a sign-in made in one mode is not used in the other.
Why Node's
fetch?api.ibkr.comasks for an optional TLS client certificate. Chromium-based HTTP stacks treat that as fatal, but Node'sfetchhandles it.
How it works
your AI host ──stdio (MCP)──▶ ibkr-mcp-guard ──HTTPS (MCP Streamable HTTP + OAuth bearer)──▶ api.ibkr.com/v1/api/mcp-public
│ policy: block / draft / write / read
└─ tokens: macOS Keychain or 0600 fileMCP server (stdio): newline-delimited JSON-RPC 2.0 with
initialize,ping,tools/listandtools/call.Protocol versions 2025-11-25, 2025-06-18, 2025-03-26 and 2024-11-05 are supported; 2025-06-18 is the default.
It sends
notifications/tools/list_changedafter sign-in and sign-out.When the client supports it, it uses
elicitation/createto confirm drafts.
Upstream client: MCP Streamable HTTP. It accepts JSON or SSE answers, tracks
Mcp-Session-Id, followstools/listpaging and applies timeouts. A 401 triggers one token refresh, and a second 401 starts a new sign-in.Sign-in (MCP authorization, OAuth 2.1):
Discovery from the
WWW-Authenticateresource_metadatavalue (RFC 9728), then the authorization-server metadata (RFC 8414, path-inserted URL first).Dynamic Client Registration (RFC 7591) as a public native client, with a loopback redirect
http://127.0.0.1:<port>/callback. The registered port is reused when it is free; otherwise the guard registers again.PKCE S256 (RFC 7636), plus a random
statethat is checked on the callback.The
resourceparameter (RFC 8707) is sent.Refresh tokens are rotated, and tokens are revoked on sign-out.
Development
npm test # node --test: offline, fake upstream + fake OAuth serverThe tests cover:
discovery using IBKR's real metadata strings
the DCR body, PKCE (RFC 7636 test vector), state mismatch, and the scope assertion
policy classification of the real tool names
paper mode: the default path never asking for
mcp.orders.submit, the paper scopes, mode changes needing a new sign-in, the DU/DF gate (live ids, unknown ids, accounts named in the order), tool visibility andlist_changed, Approve/Cancel,NO_CONFIRM, the audit log and statustools/list filtering and the draft prefix
the signed-out call and the 401 refresh
SSE parsing
the
0600token filean end-to-end stdio round trip (sign-in, list, call, block, elicitation) against a local fake MCP server
Disclaimer
This software is provided as is, under the MIT License, without warranty of any kind. It is not financial advice, and nothing it or your assistant outputs is a recommendation to buy or sell anything. AI assistants make mistakes: check every number and every draft yourself in IBKR before acting. You use it at your own risk and remain responsible for every action taken in your account. Interactive Brokers, IBKR and related marks belong to their owners.
License
MIT © 2026 Adam Cohen
Available Tools
3 toolsibkr_sign_inSign in to Interactive BrokersAIdempotent
Start signing in to Interactive Brokers in the user's browser (read + order-draft access only; order submission is never requested). Returns immediately; the user approves in the browser and then asks again.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds substantive context beyond the annotations: it discloses the requested permission scope (read + order-draft only, never order submission) and the asynchronous return behavior. With openWorldHint and non-destructive already declared, this covers the main agent concerns, though it says nothing about session lifetime or failure modes.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tight sentences. The action and permission scope are front-loaded, and the follow-up workflow ('user approves... then asks again') is placed where it is needed. No wasted text.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter, no-output-schema authentication trigger, the description covers the essentials: what starts, what access is granted, that it returns immediately, and what the agent should do next. Nothing needed to invoke it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so the baseline is 4. The description correctly implies no input is required and adds no misleading parameter claims.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Start signing in to Interactive Brokers') and scopes it to browser-based authentication. It is clearly distinct from ibkr_sign_out and ibkr_status by action, though it never names those siblings explicitly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The final clause explains the execution model: the call returns immediately, the user approves in the browser, and the agent must 'ask again' afterwards. That is real usage guidance for an async tool, but it offers no explicit when-not condition or named alternative.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ibkr_sign_outSign out of Interactive BrokersAIdempotent
Sign out of Interactive Brokers: revoke the tokens at IBKR and delete them from this computer.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare idempotent, open-world, non-read-only, non-destructive; the description adds the substantive detail that the tool operates on both sides (remote revocation plus local credential deletion), which is the key behavioral fact an agent needs. It does not clarify what happens if no session exists or that re-authentication is required afterwards, keeping it from a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single sentence with a colon that front-loads the action and then enumerates the two effects. No filler, nothing to trim.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter tool with no output schema and full annotation coverage, the description covers the essential effects. Minor omissions are the post-condition (agent must sign in again) and error behavior when not signed in.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so there is nothing for the description to disambiguate; baseline 4 applies. The description adds no parameter noise either.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Names the exact operation (sign out of Interactive Brokers) and specifies the two concrete effects: token revocation at IBKR and local token deletion. This clearly distinguishes it from ibkr_sign_in and ibkr_status, which are the only siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The purpose makes the use case obvious (when you want to end the IBKR session), but the description never states when this should be preferred over simply letting a session lapse, nor does it mention prerequisites such as requiring an active session. Usage is implied rather than guided.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ibkr_statusIBKR connection statusARead-onlyIdempotent
Show whether the user is signed in to Interactive Brokers, the account ids, granted scopes, token expiry, and confirm that order submission was not granted.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare the tool read-only, idempotent, non-destructive, and closed-world, so the safety profile is covered. The description adds behavioral value by specifying the returned status data and explicitly confirming that order submission was not granted, which is meaningful context beyond the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The definition is a single, front-loaded sentence that enumerates the returned fields without filler. Every clause contributes to describing the tool's output and scope.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description carries the burden of explaining return values and does so thoroughly: sign-in state, account ids, scopes, token expiry, and order-submission permission. For a zero-parameter status tool with full annotation coverage, nothing essential is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero input parameters, so there are no parameter semantics to explain; the baseline score of 4 applies. The schema and description are consistent, and no missing parameter information exists.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb ('Show') and resource ('signed in to Interactive Brokers') and enumerates the exact status fields returned: account ids, scopes, token expiry, and order-submission permission. This inherently distinguishes it from the sibling sign-in and sign-out tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied by the status-check nature of the tool, but the description does not explicitly say when to call it versus ibkr_sign_in or ibkr_sign_out, nor does it state prerequisites or exclusions. An agent can infer the use case, but the definition leaves the routing to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
3 tool updates
v0.2.0- First observed
ibkr_sign_in - First observed
ibkr_sign_out - First observed
ibkr_status
TDQS
Scored across 3 tools
The three tools map cleanly to distinct authentication lifecycle actions: sign in, inspect status, and sign out. There is no overlap in purpose, and the descriptions make the boundaries clear.
All tools use the same predictable prefix and verb pattern: ibkr_sign_in, ibkr_status, ibkr_sign_out. The convention is consistent and readable.
For an authentication/guard server, three tools are sufficient and each earns its place. There are no redundant or filler tools.
The server covers the full auth lifecycle: sign in, check current status and scopes, and sign out/revoke tokens. No obvious operations are missing for its stated purpose as a guard around IBKR access.
Maintenance
Related MCP Connectors
- Trade ItOAuthapp.tradeit
Connect brokerage accounts to AI assistants: holdings, orders, asset lookup, and approved trading.
Paper trading for AI: live quotes, indicators, and virtual trades on stocks, crypto, and forex.
AI agents backtest, paper trade and live trade via your broker. Free to connect; OAuth.
- FensoryOAuthcom.fensory
Trading MCP server for AI agents, with live market data, account reads and controlled execution.
Related MCP Servers
- AlicenseBqualityCmaintenanceEnables AI assistants to interact with Interactive Brokers trading accounts to retrieve market data, check positions, and place trades. Includes pre-configured IB Gateway and handles OAuth authentication automatically.1460 npm226MIT
- AlicenseNot gradedqualityDmaintenanceProvides 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.18MIT
- FlicenseNot gradedqualityDmaintenanceConnects AI assistants to Interactive Brokers for intelligent portfolio management, options analysis, risk monitoring, and automated trading strategy suggestions. Enables real-time account tracking, Greeks calculations, option chain analysis, and playbook-based risk adjustments through natural language.5-
- AlicenseBqualityDmaintenanceGives AI assistants real-time access to Interactive Brokers accounts via the Client Portal Web API, with 26 read-only tools for portfolio, market data, options, and scanner.3214 npm2MIT