catalora-mcp
OfficialProvides trading integration with Robinhood for settled execution via chain mode, though not yet implemented; currently supports local paper trading.
Click on "Install 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., "@catalora-mcpRun a pre-trade check on TSLA for 500 shares"
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.
catalora-mcp
Eight tools that let an AI agent trade under limits it cannot talk its way around.
An MCP server for trading agents. Works with Claude, Codex, Cursor, or anything else that speaks the Model Context Protocol.
{
"mcpServers": {
"catalora": {
"command": "npx",
"args": ["-y", "@catalora/mcp"],
"env": {
"CATALORA_MODE": "local",
"CATALORA_MANDATE": "./mandate.json",
"CATALORA_JOURNAL": "./fills.jsonl",
"CATALORA_MARKS": "./marks.json",
"CATALORA_STARTING_EQUITY": "100000"
}
}
}
}Drop that into claude_desktop_config.json, .mcp.json for Claude Code, or the equivalent
for Codex or Cursor. Ready-made copies are in examples/.
The eight tools
Deliberately small. An agent handed forty tools reasons worse than one handed eight — it spends its planning budget choosing between tools instead of choosing between trades.
Tool | Does | Why it earns a slot |
| Ticker whitelist, leverage ceiling, drawdown cap, position and order limits | The agent knows its boundaries before it plans, not after it is refused |
| open / pre / post / closed / halted, plus hours to next open | The input almost no agent has, and the largest avoidable loss there is |
| Reference price with a staleness flag | Tells the agent when a price is a last print rather than a live one |
| NAV, cash, positions, headroom against every limit | Full state in one call, instead of inferring from remembered orders |
| Drawdown remaining, notional remaining, NAV floor | Lets the agent brake before it is stopped |
| Checks the mandate, then executes. Refuses on breach | The enforcement point |
| Full or partial exit | Always available, even in a symbol the mandate no longer permits |
| Realised P&L, drawdown, Sharpe, hit rate, time in market | What the agent benchmarks against |
A ninth tool needs a written argument for why the agent reasons better with it, not why
the data is useful. test/tools.test.ts fails the build at nine. A documented promise that
nothing enforces erodes through ordinary, well-meaning pull requests.
Related MCP server: edgex-mcp
Resources and prompts — capability without a ninth tool
The obvious way to add capability would be to break the eight-tool cap. But a tool is for doing something, and a resource is something you read that does not change when you read it. Modelling them correctly means the surface grows and the promise holds.
Resources — attach to a conversation once, up front:
URI | What |
| The limits as prose, written for a system prompt |
| The same, machine-readable |
| NAV, positions, and headroom against every limit |
| Full track record, provenance first |
| P&L by month — the view a lucky month cannot hide in |
| How much of the record is one ticker |
| Every drawdown, its depth, and whether it recovered |
| Closures, half-days, and the next five sessions |
Resource templates — parameterised, read any instance:
Template | What |
| Session state and whether the price is live |
| Auction times and the MOC/MOO cutoffs |
| Limit up/down bounds at a reference price |
| When a trade on that date settles |
Attaching the mandate costs nothing and means the model plans inside its limits rather than discovering them by being refused.
Prompts — checklists the operator invokes by name:
pre_trade_check— session, mandate, room, quote staleness, in that order. Stops at the first step that says stop, and never submits.daily_review— what moved, what it cost, what tomorrow's calendar looks like. Explicitly refuses to congratulate you on one good day.risk_report— every limit ranked by how close it is to binding, tightest first.position_sizing— works a size back from the drawdown budget rather than from NAV, because the budget is what is actually scarce. Says "there is no room for this" when that is the honest answer, instead of shrinking it to something pointless.postmortem— separates a good decision with a bad outcome from a bad decision, because those need opposite responses and conflating them is how a process gets changed for no reason. One page, no consolation.record_critique— reads the record as a sceptic. Ends with the strongest argument that it is luck, and is told not to soften it.
A REST shim over the same eight tools
For agents that are not MCP clients — a Python script, a cron job, a custom runtime.
curl -s localhost:8787/tools
curl -s -X POST localhost:8787/tools/submit_order \
-d '{"ticker":"NVDA","side":"buy","quantity":100}'
curl -s "localhost:8787/resources/catalora%3A%2F%2Fmandate"It calls exactly the same callTool the MCP server does, so the two surfaces cannot drift.
A refusal comes back 200 with ok: false — the request was correct and the answer is no;
a 4xx would tell the caller to fix a request that has nothing wrong with it.
Binds to 127.0.0.1 and has no authentication. That is only safe because of the binding.
The kill switch
CATALORA_READ_ONLY=1submit_order and close_position refuse. Everything that reads keeps working, because an
operator who has just pulled this switch will want to see where the book stands.
It fails closed: anything but an explicit 0, false, no or empty counts as on. An
operator who sets CATALORA_READ_ONLY=yes and gets live trading because the parser wanted
"true" has been failed by the tool at the exact moment they were relying on it.
It disables exits too. That is deliberate — "stop everything, now" is a different intent from "stop opening", and it needs its own way to say it.
Auditing what the agent tried
CATALORA_AUDIT=./audit.jsonlOff unless set. A track record says what an agent traded; the audit log says what it tried, including the refusals — and an agent that submitted forty breaching orders before finding a compliant one has told you something its P&L never will.
Arguments are recorded; results are not. A result can contain the whole portfolio, and the arguments are the part that reflects what the agent chose.
Refusals are answers, not errors
This is the part that decides whether an agent recovers or spins.
REFUSED — 1 mandate violation:
[POSITION_SIZE_EXCEEDED] The resulting NVDA position would be $300,000.00, 30.0% of
$1,000,000.00 NAV, above the 25.0% per-position cap ($250,000.00). At $100.00 the
largest permissible order is 500 units.
The largest order that would pass every constraint is 500 units of NVDA (submitted 3000).An agent that gets "mandate breach" resubmits the same order. An agent that gets the
constraint, the actual value, the limit, and the exact quantity that would work fixes it on
the next turn.
Every response carries both prose and structure: the model reasons over the sentence and
computes with structuredContent.
When no smaller order would help, it says so — "Do not retry it smaller."
Local mode, and what it is not
Today this server runs entirely on your machine. The mandate is a JSON file, fills go to an append-only journal, and the track record is computed from that journal.
submit_order journals a paper fill. It does not route anywhere. Every receipt says so:
PAPER FILL — journalled locally at the reference price. Nothing was sent to a venue
and no capital moved. Assume zero slippage and infinite liquidity, because that is
what was simulated.A locally computed track record is self-reported. It carries
provenance: "local-journal" and every response leads with the unverified notice. There is
no code path here that converts it into a verified one, and there never will be — if there
were, "verified" would stop meaning anything.
That is not a limitation to work around. It is the honest description of what a local record is: the operator controls the input file.
What this server refuses to fabricate
A price it does not have. No marks file entry means an error, not a guess. An agent cannot tell a made-up quote from a real one.
A mandate. No mandate file means the server refuses to start the agent. An agent with no declared limits is exactly what this exists to prevent.
A statistic it cannot compute.
null, never0. "Could not measure" and "measured zero" are different facts.Slippage. A marks file is a list of prices, not a book.
Chain mode
Not implemented. The CATALORA contracts ship through Robinhood, and until their ABIs are frozen there is nothing to write against.
The stub is real, though, and it throws rather than falling back to local mode. An operator who asked for settled execution and silently got paper fills would not find out until it mattered.
The Driver seam exists on day one for that reason: LocalDriver today, ChainDriver when
the contracts land, same tool handlers, same schemas, same responses, one env var to switch.
No chain type or contract ABI may leak past that interface into a tool handler. Retrofitting
a seam after eight handlers have grown their own I/O is the rewrite this design avoids.
Configuration
From the environment only. Never a CLI flag — flags land in shell history, and one of these values will eventually be a key that can move money.
Variable | Default | Meaning |
|
|
|
|
| Path to the mandate |
|
| Path to the append-only fill journal |
| — | JSON map of |
| — | Capital base. Without it, drawdown and Sharpe are unmeasured |
| — | Chain mode only. Never logged, never echoed |
|
| Chain mode only |
| — | Append-only log of every tool call. Off unless set |
| — | Refuse every order-placing tool. Fails closed |
|
|
|
Everything is validated at startup rather than at the first tool call. An agent that discovers a missing key three tool calls into a plan has already wasted the plan.
The agent key is absent from any logged or returned config, not masked. A masked secret still leaks its length and prefix, and a field that exists at all invites someone to widen it.
stdout is the protocol stream
The single most important rule in this repository.
A stray console.log anywhere in the process writes into the JSON-RPC stream and corrupts it.
The failure mode is not a helpful parse error — it is a client that appears to hang, or one
that works right up until something logs mid-tool-call. This cost real debugging time on a
previous MCP server.
So:
Everything here logs to stderr.
console.log/info/debug/warn/error/traceare redirected to stderr at startup, so even a dependency that logs cannot corrupt the stream.A test spawns the real server, exercises all eight tools over a real stdio transport, and asserts that every byte of stdout parses as a JSON-RPC message. CI runs the same check against the built binary.
Getting started
mkdir my-agent && cd my-agent
curl -O https://raw.githubusercontent.com/cataloraxyz/catalora-mcp/main/examples/mandate.json
curl -O https://raw.githubusercontent.com/cataloraxyz/catalora-mcp/main/examples/marks.jsonEdit mandate.json to the limits you actually want, point your MCP client at the directory,
and ask your agent:
Check the session state for NVDA, then tell me what your mandate allows.
A good first sign that it is wired up correctly: ask it to buy far more than the mandate permits and watch it come back with the exact size that would have worked.
Built on
@catalora/market-session— session state@catalora/mandate— the enforcement rules@catalora/track-record— performance math
All three are zero-dependency and usable on their own. The MCP server is a thin surface over them, which is why the interesting logic is tested there rather than here.
Development
pnpm install
pnpm test # 147 tests, including a real end-to-end MCP session
pnpm typecheck
pnpm lint
pnpm build
pnpm dev # run from source over stdioNote on dependencies.
package.jsondeclares ordinary semver dependencies on@catalora/*and is publish-ready. Until those land on npm,pnpm-workspace.yamlredirects them to sibling checkouts, and CI checks the sibling repos out to build them. Both blocks are commented and have a removal trigger. This shape is what broke CI across a previous repo fleet, so it is scoped and temporary on purpose — the fix is publishing, not more scaffolding.
See CONTRIBUTING.md.
Part of CATALORA
CATALORA is the allocation layer for AI agents trading tokenized stocks: agents trade under a mandate a contract enforces, build a track record nobody can fake, and raise outside capital against it.
This server is the integration surface. Local mode works with none of that — no account, no key, no contract — and the same eight tools point at the contracts when they ship.
agents ──▶ @catalora/mcp ─┐
│ @catalora/mandate what an agent may do
├──▶ @catalora/market-session when it may do it
humans ──▶ @catalora/cli ─┘ @catalora/track-record what it actually did
two surfaces three zero-dependency librariesThe eight tools are a thin shell over the three libraries below. Nothing this server does is unavailable to a program that imports them directly.
Package | Repo | The question it answers |
| What is this agent allowed to do? | |
| Is this ticker tradeable right now, and if not, when? | |
| What did it actually do, and who says so? | |
| The three libraries as eight tools, for any MCP client ← you are here | |
| The same three, from a terminal |
Web catalora.xyz · dApp dapp.catalora.xyz · Docs docs.catalora.xyz · Source github.com/cataloraxyz
License
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseAqualityBmaintenanceAn MCP server that enables autonomous AI agents to connect to Tastytrade for market scanning, option strategies, account management, and optionally placing trades with built-in safety controls.9MIT
- AlicenseAqualityDmaintenanceMCP server for EdgeX perpetual contract trading, providing 16 AI agent tools for market data, account management, and order execution.1729MIT
- Alicense-qualityCmaintenanceAn MCP server that lets an AI agent backtest, risk-check, and audit trading strategies, determining if a strategy is overfit or actually works.MIT
- Alicense-qualityAmaintenanceMCP server for AI agents to inspect and trade against a MetaTrader 4 terminal, with an offline mock mode for CI and demos.2MIT
Related MCP Connectors
MCP server exposing the Backtest360 engine API as tools for AI agents.
An MCP Server that provides identity verification and anti-fraud tools for AI agents via deepidv.
Crypto transaction firewall and risk tools for MCP agents.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/cataloraxyz/catalora-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server