Install
$ agentstack add mcp-nirholas-robinhood-mcp ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
robinhood-mcp
An execution toolkit for the official Robinhood Crypto Trading API, delivered over the Model Context Protocol.
Robinhood's crypto API exposes four order types and nothing else: market, limit, stop_loss, and stop_limit. There is no bracket, no OCO, no trailing stop, no TWAP, no iceberg, no scale-in ladder, no scheduled DCA, no portfolio rebalance. Those are the order types people actually want, and every one of them has the same requirement: something has to keep working after the request returns. A trailing stop that exists only for the duration of one tool call is not a trailing stop. This package synthesizes the thirteen order types Robinhood lacks and runs them as durable background jobs that survive the agent disconnecting, the server restarting, and the machine rebooting. Around that sit ~73 tools across 8 opt-in modules: quotes and holdings, FIFO cost basis and realized P&L, a trade journal, risk limits with an enforced kill switch, and integration diagnostics.
> This is not Robinhood Chain, and it is not Robinhood equities. It targets the brokerage Crypto Trading API at trading.robinhood.com, documented at docs.robinhood.com/crypto/trading.
The signing bench — nirholas.github.io/robinhood-mcp
The project site is an interactive Ed25519 signing bench. You assemble a real Robinhood trading request and produce a genuine signature in your own browser — the math runs client-side on Robinhood's published example key, nothing is sent anywhere — then watch it disprove Robinhood's own documentation: their published test vector verifies against a Python dict repr, not the JSON body every client actually sends. It is a single self-contained page in [docs/](docs/), so it deploys anywhere static.
Deploy your own copy in one click:
[](https://deploy.workers.cloudflare.com/?url=https://github.com/nirholas/robinhood-mcp) [](https://app.netlify.com/start/deploy?repository=https://github.com/nirholas/robinhood-mcp) [](https://vercel.com/new/clone?repository-url=https://github.com/nirholas/robinhood-mcp&project-name=robinhood-mcp&output-directory=docs)
| Target | How | | --- | --- | | GitHub Pages | Settings → Pages → Source: main / /docs. Serves at nirholas.github.io/robinhood-mcp/. No build step. | | Cloudflare Pages | The button above, or wrangler pages deploy docs. Config in [wrangler.toml](wrangler.toml). | | Google Cloud | gcloud storage cp -r docs/* gs:/// behind an HTTPS load balancer, or Cloud Run with any static server rooted at docs/. | | Netlify / Vercel | The buttons above. Publish directory is docs/ ([netlify.toml](netlify.toml), [vercel.json](vercel.json)). |
Safety
Robinhood publishes no sandbox. There is no paper account, no test environment, no dry-run host. Every order this package can place is real money in a real brokerage account, and there is no way to rehearse. Read this section before you set ROBINHOOD_CRYPTO_ENABLE_TRADING=1.
Every guard below is enforced in code, inside the single Executor that all orders pass through, never in a tool description. An instruction in a prompt is a suggestion; a thrown error is not.
| Guard | What it does | | --- | --- | | Separate binary | The robinhood-mcp data server registers no tool that can trade. Attaching it to a general-purpose assistant cannot result in an order. | | Explicit opt-in | The trading server and the daemon refuse to start unless ROBINHOOD_CRYPTO_ENABLE_TRADING=1. It cannot be launched by accident. | | Per-order USD cap | Every order is priced before submission and rejected above ROBINHOOD_CRYPTO_MAX_ORDER_USD (default $100). It fails closed: if the USD value cannot be determined up front, the order is rejected rather than assumed cheap. | | Cumulative USD cap | ROBINHOOD_CRYPTO_MAX_DAILY_USD refuses once the process has committed that much in total. | | Symbol allowlist | With ROBINHOOD_CRYPTO_SYMBOL_ALLOWLIST set, only those pairs may trade. Everything else is refused. | | Buy-only | ROBINHOOD_CRYPTO_BUY_ONLY=1 rejects every sell order. | | Guarded vs autonomous | In the default guarded mode an order tool returns the exact request it would send plus a priced estimate, and places nothing until a second call carries confirm: true. ROBINHOOD_CRYPTO_AUTONOMOUS=1 skips that gate for unattended strategies. | | Kill switch | risk_kill_switch_engage halts all execution and pauses every running job. Persisted to disk, read on every order and never cached, checked inside the Executor. |
Two things about those controls are worth stating plainly, because they are the ones people get wrong.
Autonomous removes the human, not the ceiling. The per-order cap, the cumulative cap, the allowlist and the buy-only flag all still apply in autonomous mode. An unattended agent is exactly the case where a runaway order is most likely and least observed.
The kill switch binds the daemon too. It lives beside the Executor rather than inside the risk tool module, because robinhood-mcp-daemon builds its own Executor and loads no tool modules at all. A halt installed by a module would leave the one unattended process in the system still placing orders. Because the state is read from disk on every order rather than cached, throwing the switch from an MCP session stops a daemon that has been running since before the switch existed. It fails closed: a row that cannot be parsed reads as engaged, because resuming trading over a corrupted row is exactly the failure a kill switch exists to prevent.
Two limits are honest about what they are not. The cumulative cap is a per-process, in-memory counter that resets on restart: it is a runaway-loop brake, not an accounting system or a hard budget. And a durable job's total spend is not capped at all. Per-order caps apply to each order a job places, but a TWAP with 50 slices under a $100 per-order cap can commit $5,000. Size jobs deliberately and check risk_status before starting anything unattended.
Finally, scope the API key itself. Robinhood lets you pick permissions per credential: if you only want reads, do not grant order placement, and the key cannot trade no matter what software holds it. IP allowlisting is also available and worth using.
Install
Requires Node.js 22.5 or newer (the durable job store uses node:sqlite).
Nothing to install ahead of time: MCP clients launch the servers with npx.
1. Create a credential
npx robinhood-keygen
This prints a fresh Ed25519 keypair as JSON on stdout, with instructions on stderr. Then:
- Register the public key at robinhood.com/account/crypto. This page exists on web classic only, not the mobile app and not the new web UI.
- Robinhood issues an API key in return. That is
ROBINHOOD_CRYPTO_API_KEY. - Keep the private key as
ROBINHOOD_CRYPTO_PRIVATE_KEY. It is a base64-encoded 32-byte Ed25519 seed, not a 64-byte expanded keypair and not PKCS#8. Several Ed25519 libraries exportseed‖publicKey; pasting that gives 64 bytes and fails every request. Treat the private key like a password: it authorizes trades, and this package never stores or transmits it anywhere except in the signature it computes locally.
If you already have a keypair from elsewhere, verify_keypair (in the dev module) checks that a private key matches the public key you registered, without sending anything anywhere.
2. Add the server to your MCP client
Two binaries ship in the package, and they are separate on purpose.
| Server | Binary | What it can do | | --- | --- | --- | | Data | robinhood-mcp | Quotes, holdings, account, order history, cost basis, P&L. Cannot place an order. | | Trading | robinhood-mcp-trading | Everything above, plus real orders, synthetic order types, and risk controls. |
Read-only. This is the one to start with, and the one to attach to a general-purpose assistant. Paste into claude_desktop_config.json (Claude Desktop), .mcp.json (Claude Code), or your client's equivalent:
{
"mcpServers": {
"robinhood-crypto": {
"command": "npx",
"args": ["-y", "robinhood-mcp"],
"env": {
"ROBINHOOD_CRYPTO_API_KEY": "rh-api-...",
"ROBINHOOD_CRYPTO_PRIVATE_KEY": "your-base64-seed"
}
}
}
}
Ask your assistant: "What is my Robinhood crypto buying power, and what is BTC trading at?"
Trading. A separate entry, because it places real orders:
{
"mcpServers": {
"robinhood-crypto-trading": {
"command": "npx",
"args": ["-y", "robinhood-mcp-trading"],
"env": {
"ROBINHOOD_CRYPTO_API_KEY": "rh-api-...",
"ROBINHOOD_CRYPTO_PRIVATE_KEY": "your-base64-seed",
"ROBINHOOD_CRYPTO_ENABLE_TRADING": "1",
"ROBINHOOD_CRYPTO_MAX_ORDER_USD": "50",
"ROBINHOOD_CRYPTO_MAX_DAILY_USD": "500",
"ROBINHOOD_CRYPTO_SYMBOL_ALLOWLIST": "BTC-USD,ETH-USD"
}
}
}
}
Set the caps to numbers you are comfortable losing. They are the only thing standing between a misunderstood instruction and a real fill.
You can run both entries at once. The data server is safe to leave attached permanently; add the trading server when you intend to trade.
If you have not set credentials yet
The data server still starts. Rather than exiting (which most MCP clients surface only as an opaque "server failed to start"), it comes up with a single get_setup_status tool that reports what is missing and how to fix it. If that is the only tool you see, credentials are not configured: set ROBINHOOD_CRYPTO_API_KEY and ROBINHOOD_CRYPTO_PRIVATE_KEY, then restart the server and the full tool set appears.
The trading server is deliberately stricter and refuses to start without ROBINHOOD_CRYPTO_ENABLE_TRADING=1, so it can never be launched by accident.
Modules
The toolkit is larger than any one agent should see at once. Every tool spends context and adds a wrong option to choose between, which measurably degrades tool selection, so capabilities ship as modules the operator turns on with ROBINHOOD_MCP_MODULES. Only the selected ones are registered.
| Module | What it does | Default | Places orders | | --- | --- | --- | --- | | market | Quotes, holdings, account details, trading pairs, order history. | on | no | | orders | The four native order types as individual tools, plus the generic place_order and cancel_order. | on | yes | | algo | The thirteen synthetic order types, run as durable background jobs. | on | yes | | portfolio | FIFO cost basis, realized P&L and tax lots, allocation, slippage, volatility, risk-based sizing. | on | no | | risk | Exposure, concentration, drawdown, pre-trade checks, and the enforced kill switch. | on | yes | | journal | Fills, round-trip trades, win rate and profit factor, daily summaries, stale open orders, CSV export, reconciliation. | off | no | | ops | Health checks, clock skew, key verification, rate-limit state, API version, supervisor and unsettled-intent status. | off | no | | dev | Connection diagnostics, signature explanation, keypair generation and verification, client codegen, environment check. | off | no |
risk is classified as order-placing because engaging the kill switch pauses running jobs. It cannot itself submit an order.
How selection works:
- Unset loads the default set (
market,orders,algo,portfolio,risk), minus anything the server cannot host. That is 52 tools on the trading server and 16 on the data server. - A comma-separated list loads exactly those:
ROBINHOOD_MCP_MODULES=market,orders,risk. allloads everything: 73 tools on the trading server.
An unknown module name fails startup rather than being silently dropped, because a typo that quietly removes tools is worse than a server that refuses to start.
Loading everything degrades tool choice. all is useful for a builder exploring the surface and a poor choice for a production agent. Pick the modules the task actually needs. If an agent reports a capability as impossible, have it call list_modules: the tool names the exact ROBINHOOD_MCP_MODULES value that would enable what is missing, so a disabled module is never mistaken for a capability the toolkit lacks.
The data server refuses to load a mutating module (orders, algo, risk) and fails at startup if asked to, so an operator can never believe they enabled execution when they did not.
Full parameter reference for all 73 tools: [docs/tools.md](docs/tools.md).
The thirteen synthetic order types
Each of these is built on top of Robinhood's four primitives by a supervisor that keeps advancing it over time. Every one is started by its own tool with a typed schema, so a missing or misnamed parameter fails at the schema boundary instead of inside the job.
| Order type | Tool | What it does | Key parameters | | --- | --- | --- | --- | | TWAP | algo_twap_start | Splits one large order into evenly spaced slices over a duration, so the entry averages across the window instead of paying a single spread. | total_quantity, slices, duration_minutes, optional limit_price guard | | Trailing stop | algo_trailing_stop_start | Follows the market with a stop that ratchets in your favour and never retreats, exiting the full size when price retraces past the trail. | quantity, exactly one of trail_percent / trail_amount, optional activation_price | | Bracket | algo_bracket_start | Enters a position and attaches a take-profit and a stop-loss that cancel one another, so the trade is never left with only one exit. | entry_type, entry_price, take_profit_price, stop_loss_price | | OCO | algo_oco_start | Rests a take-profit and a stop against an existing position and cancels whichever does not fill. | quantity, take_profit_price, stop_price, optional stop_limit_price | | DCA | algo_dca_start | Buys or sells a fixed amount on a repeating schedule for a set number of occurrences. | interval_hours, occurrences, one of quote_amount_per_buy / asset_quantity_per_buy, optional max_price | | Ladder | algo_ladder_start | Places a series of resting limit orders across a price range, to scale into or out of a position as price moves. | total_quantity, levels, start_price, end_price, distribution | | Iceberg | algo_iceberg_start | Works a large order behind a small visible slice, refilling as each slice fills, so the book never shows the full size. | total_quantity, visible_quantity, max_duration_minutes, optional limit_price | | Chase | algo_chase_start | Rests a limit order and reposts it as the book moves away, to fill without crossing the spread. | quantity, max_chases, offset_bps, optional limit_price bound | | Grid | algo_grid_start | Rests buys below and sells above across a price range, replacing each fill with its opposite one level away, banking the spacing on every round trip. | lower_price, upper_price, grid_levels, quantity_per_grid, optional max_cycles / max_duration_minutes / stop_price | | Momentum | algo_momentum_start | Enters when price breaks out of its recent range by a set margin, then exits when it retraces a set fraction of the move it reached. | side, quantity, lookback_ticks, breakout_pct, exit_pct, max_duration_minutes | | Mean reversion | algo_mean_reversion_start | Fades a price that has stretched a set number of standard deviations from its recent mean, and closes the trade when it reverts toward that mean. | quantity, lookback_ticks, entry_z, exit_z, side_mode, max_duration_minutes | | Accumulate | algo_accumulate_start | Buys
…
Source & license
This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: nirholas
- Source: nirholas/robinhood-mcp
- License: Apache-2.0
- Homepage: https://github.com/nirholas/robinhood-mcp
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.