PitBridge
Join the waitlist

Connecting Claude to NinjaTrader 8 is not the hard part. Getting the two to talk takes a small config block. The hard part is doing it in a way where a bad tool call cannot quietly send a real order to a funded account. This walks through the whole path: install the local bridge, write a safe config, wire Claude to it, connect the link, and confirm that a guardrail engine on your machine blocks an order that breaks your limits before NinjaTrader ever sees it.

The pieces you need

Three things make the connection. NinjaTrader 8, running as usual with its own accounts and data. A local bridge, the PitBridge daemon, which runs an MCP server, a guardrail engine and a small NinjaTrader AddOn that carries orders into the platform. And an MCP client, which is the thing Claude runs inside, either Claude Desktop or Claude Code.

The bridge is the important piece. It is the only component that both speaks to Claude and speaks to NT8, and it is where every order is checked. Nothing about this setup asks you to send your account details to a hosted service. The local-first bridge keeps the MCP server, the guardrail engine and your keys in one process on your own machine.

PitBridge order pathAn order intent travels from the AI agent through the PitBridge daemon to the guardrail engine, which either allows it through to NinjaTrader 8 or blocks it with a stated reason.AI AGENTmcp / rest clientPITBRIDGE DAEMONlocalhost:8873GUARDRAIL ENGINE12 rules activeNINJATRADER 8Sim101ALLOW buy 2 MES. all rules pass.BLOCK buy 10 MES. size 10 > max_contracts_per_order 2.

Example decisions: ALLOW buy 2 MES, all rules pass. BLOCK buy 10 MES, size 10 exceeds max_contracts_per_order 2.

The order route: the agent proposes, the guardrail engine adjudicates, and only an allowed order reaches NinjaTrader 8. The block is shown as plainly as the allow.

Before you start: what you need

Four things, once. You will not need any of them again after the first run.

  • macOS, or the Windows box where NinjaTrader 8 runs, with the uv package manager installed.
  • The PitBridge daemon repository checked out. Every daemon command runs from the daemon directory.
  • NinjaTrader 8 with a Sim101 account. Real routing into NT8 is milestone M4. On a Mac you exercise the whole safety kernel against a bundled fake AddOn instead.
  • An MCP-capable client: Claude Desktop or Claude Code.
Prerequisites. The fake AddOn stands in for the real C# AddOn so you can test the safety kernel with no Windows box and no market data.

Install uv with the one-line installer, then sync the daemon and check the version. This build runs in paper and simulation only: arm-live refuses, because the live-execution plugin ships separately in a later milestone.

bash
# from the daemon directory
curl -LsSf https://astral.sh/uv/install.sh | sh
cd pitbridge/daemon
uv sync --extra dev          # create the venv, install deps
uv run pitbridge --version   # prints "pitbridge 0.1.0"

Install and verify. You can also put the CLI on your PATH with uv tool install, after which the uv run prefix can be dropped.

Write a minimal, safe config

PitBridge reads one TOML file that describes the daemon bind, your account, and its guardrails. Keep it under a scratch PITBRIDGE_HOME so nothing touches your real ~/.pitbridge. This config defines a single Sim101 paper account with realistic limits.

config.toml
[daemon]
bind = "127.0.0.1"            # localhost only
port = 8873
paired_mode = false
pairing_token = "pb_pair_demo"   # the AddOn presents this token

[accounts.sim]
nt_account = "Sim101"        # the simulated NT account name
mode = "paper"               # read_only | paper | live
profile = "eval"             # eval | funded

[accounts.sim.guardrails]
instruments = ["MES 09-26"]      # allowlist
max_contracts_per_order = 5
max_position = 10
daily_loss_halt = 400
trading_window = { tz = "America/New_York", open = "09:30", close = "16:00" }

A real config.toml. Keys match the daemon schema. mode is read_only, paper or live, and live is refused in this build.

Every guardrail you can set

The block above is the short version. The full guardrail surface, under [accounts.sim.guardrails], is the definitive reference below. Every setting is optional: leave one out and that check simply does not run.

settingvaluewhat it enforces
instrumentslist of contractsAllowlist. Only the listed contracts may be traded, matched on the exact string including expiry.
max_contracts_per_orderinteger >= 1Rejects any single order larger than this.
max_positioninteger >= 1Caps the net position per instrument and account.
daily_loss_haltdollars > 0Halts the day once day P&L reaches minus this amount.
profit_lockdollars > 0Banks the day once day P&L reaches plus this amount.
trading_windowtz, open, closeOrders are allowed only inside this session window, early-close aware.
max_ordersper_minute, per_hour, per_dayRate limits on order submission over each rolling window.
cooldown_after_loss_sseconds >= 0Pauses new entries for this long after a losing execution.
cooldown_after_order_sseconds >= 0Pauses new entries for this long after any order.
duplicate_window_sseconds >= 0Rejects an identical account, instrument, side and qty inside the window.
human_confirmoff | first_order | every_live_order | size_increaseHolds a matching order for CLI approval instead of submitting it.

Two settings live at account level rather than inside the guardrails block. day_halt_sticky defaults to true, so a hit loss limit or profit lock ends the trading day rather than being recomputed each check. on_link_down chooses what happens when the AddOn link drops: none, notify, or flatten_on_reconnect.

Start on a simulation account

Before an agent touches anything funded, keep the bridge pointed at a NinjaTrader simulation account such as Sim101, exactly as the config above does. Better still, start in read-only mode so the agent can read state and reason out loud without any write tools exposed at all. When you are ready to let it place orders, do it against the simulation account first and watch how it behaves across a full session.

Start the daemon, and the one thing not to do

The daemon has two surfaces. pitbridge run gives the REST and WebSocket surface. pitbridge mcp gives the MCP stdio surface that Claude drives. The important part: the MCP stdio server is itself the daemon, and it serves the AddOn link on the same port. When you wire Claude in the next section, Claude launches pitbridge mcp for you. You do not separately run pitbridge run.

Point Claude at the local MCP server

Claude Desktop reads a small configuration file that lists the MCP servers it should connect to. You add one entry for PitBridge that runs the daemon’s mcp command over stdio and points it at your config. Replace the two absolute paths with your own checkout and config location.

claude_desktop_config.json
{
"mcpServers": {
  "pitbridge": {
    "command": "uv",
    "args": [
      "run", "--directory", "/ABSOLUTE/PATH/TO/pitbridge/daemon",
      "pitbridge", "mcp",
      "--config", "/ABSOLUTE/PATH/TO/pitbridge-test/config.toml"
    ],
    "env": { "PITBRIDGE_HOME": "/ABSOLUTE/PATH/TO/pitbridge-test" }
  }
}
}

Claude Desktop config. Runnable once you fill in your own absolute paths.

Claude Code registers the same server from a shell, with one command:

bash
claude mcp add pitbridge -- \
uv run --directory /ABSOLUTE/PATH/TO/pitbridge/daemon \
pitbridge mcp --config /ABSOLUTE/PATH/TO/pitbridge-test/config.toml

Claude Code one-liner. Same server, registered from the command line.

Where the config file lives

On macOS the Claude Desktop file is at ~/Library/Application Support/Claude/claude_desktop_config.json. On Windows it is under %APPDATA%\Claude\. One gotcha catches most people: Claude Desktop launches these configs with a minimal PATH, so a bare uv in command can fail to resolve. If the server will not start, use an absolute path to uv, for example ~/.local/bin/uv.

Restart the client fully and Claude sees nine tools. Five read state and never change anything. Four request a change, and every place_order still passes the guardrail engine.

reads5

  • get_accounts
  • get_positions
  • get_orders
  • get_account_state
  • get_guardrail_status

Return state. They never change anything.

writes4

  • place_order
  • cancel_order
  • close_position
  • flatten_account

Request a change. Every place_order still passes the guardrail engine.

The nine tools an MCP client sees at connect time. There is no tool to lift the kill switch, arm live, or change a guardrail: those are operator-only on the CLI.

There is deliberately no tool to lift the kill switch, arm live trading, or change a guardrail. Those are operator-only and live on the CLI. This is the difference between an agent that can look and propose, and an agent wired straight into a live order router.

Until an AddOn connects and sends its first account state, the daemon blocks every order with LINK_DOWN. This is the reconcile-first rule: no queueing orders against a dead link. On a Mac you provide that link with the fake AddOn, pointed at the same port and token as your config.

bash
uv run python ../tools/fake_addon.py \
--host 127.0.0.1 --port 8873 \
--token pb_pair_demo --account Sim101

The fake AddOn provides the link so Claude’s orders have somewhere to go.

The fake AddOn is deliberately dumb: it auto-acks and fills orders, replies to snapshots, and keeps the link fresh. Its fills are synthetic and instant, with no market data and no slippage, so it proves the safety kernel, not trading performance. On a Windows box with NinjaTrader, the real C# AddOn from milestone M4 replaces it and routes orders into the platform.

Verify the connection

Ask Claude in plain language, “what accounts do I have?” and “what is my guardrail status?”. It calls get_accounts and get_guardrail_status, and you should see link_up: true. Cross-check the same picture from the CLI at any time.

bash
uv run pitbridge status --config "$PITBRIDGE_HOME/config.toml"

PitBridge status
daemon    : bind 127.0.0.1:8873 (paired_mode=false)
kill      : released
accounts  :
  - sim [Sim101] mode=paper profile=eval link_up=true day_halted=false
open orders (0):
  (none)

The status command. link_up=true and kill released means the link is fresh and orders will be adjudicated normally.

What safely means here

Safely does not mean the agent is smart. It means the agent cannot do something you did not permit, no matter what it decides. Between Claude and NinjaTrader sits the guardrail engine, and it checks every place_order against the limits you configured: a per-order contract cap, a position cap, a daily loss halt, an instrument allowlist, and a kill switch you can hit at any time. The agent never sees around these checks. It calls the tool, the engine adjudicates, and only an allowed order reaches NT8.

This is why the connection is built the way it is. A language model can be told to respect a limit, but a told limit is advice. A limit enforced in the daemon, out of the model’s reach, is a control. The guardrails page lists exactly what the engine refuses, and the security model covers where each part runs and how the local token works.

A blocked order, on purpose

The moment that shows the setup working is a blocked order. Ask Claude in plain language to check the account, buy two contracts, then buy twenty. It issues three tool calls. The first two pass. The third breaks the per-order contract cap you configured, and the engine refuses it before NinjaTrader sees anything.

agent tool calls
# claude checks state, then places two orders against the sim account
# fills are synthetic sim fills from the fake AddOn (Rule 4.41 applies)
get_account_state                                              -> link_up: true, day_pnl: 0.0, day_halted: false
place_order account=sim instrument="MES 09-26" side=BUY qty=2  -> SUBMITTED, then FILLED filled_qty=2
place_order account=sim instrument="MES 09-26" side=BUY qty=20 -> BLOCKED reason_code=MAX_CONTRACTS_PER_ORDER

Example agent tool calls against the local daemon. Real tool names and reason codes. Fills are synthetic simulation fills, not live results. The block returns as a structured result Claude can read and explain, not an error that kills the chat.

A refused order is the system doing its job, not an error to hide. The block comes back as a typed reason_code, so the agent can read it, explain why the order stopped, and move on. When you connect Claude to NinjaTrader 8 this way, you are not trusting the model to stay inside your risk. You are making it impossible for the model to leave.

Every reason an order can be refused

Every block returns a stable, machine-readable reason_code the agent can read and explain. The evaluation order is frozen: the kill switch is always checked first, and human confirmation is always last, so a config change cannot reshuffle the safety chain. Below are the codes the engine emits.

reason_codewhat it means
KILL_SWITCHKill switch engaged. New entries are blocked. Release is CLI-only.
LINK_DOWNAddOn link is down or has not sent first state. Orders hard-reject.
INSTRUMENT_NOT_ALLOWEDInstrument is not in the account’s allowlist.
MAX_CONTRACTS_PER_ORDEROrder qty exceeds the per-order contract cap.
MAX_POSITIONProjected net position exceeds max position for that instrument and account.
OUTSIDE_TRADING_WINDOWOutside the configured session window.
NO_TRADE_DAYHoliday-calendar closure, or a conservative half-day policy.
DAILY_LOSS_HALTDay P&L is at or under minus your daily_loss_halt.
PROFIT_LOCKDay P&L is at or over plus your profit_lock. The day is banked.
COOLDOWN_AFTER_LOSSInside the cooldown window since the last losing execution.
COOLDOWN_AFTER_ORDERInside the cooldown window since the last order.
MAX_ORDERS_PER_MINUTE / HOUR / DAYOrder rate limit for that rolling window has been reached.
DUPLICATE_ORDERSame account, instrument, side and qty inside the duplicate window.
HUMAN_CONFIRM_*Confirmation is required before this order can proceed.
INVALID_QTYQty is not an integer of at least 1. Over REST a malformed payload is rejected SCHEMA_INVALID before any guardrail runs, so a negative BUY cannot become a disguised SELL.

The kill switch and getting flat

The kill switch is the blunt instrument. Run uv run pitbridge kill --reason "..." and every new entry is blocked with KILL_SWITCH. De-risking is deliberately still allowed under a kill, so cancel_order, close_position and flatten_account still work and you can always get flat. Releasing the kill is CLI-only with uv run pitbridge unkill. Claude has no tool to lift it, by design. The engine enforces the limits you configure; it does not promise any trading outcome.

Optional: require human confirmation

If you want a person in the loop, set human_confirm to first_order, every_live_order, or size_increase in the guardrails block. A matching order comes back PENDING_CONFIRM instead of being submitted. Approve it from the CLI with uv run pitbridge confirm <order_id>, which re-checks the kill switch, the halt state and the guardrails at confirm time. It auto-rejects after 60 seconds if you do nothing.

Explain and audit every decision

Every request, decision and outcome is written to an append-only, hash-chained audit log. Take an order_id and run uv run pitbridge audit why <order_id> to see the request, the guardrail decision, and the submit result for exactly why an order passed or blocked. Run uv run pitbridge audit verify to prove the whole chain is intact and untampered. It reports chain OK with the entry count, and an anchor file catches anyone deleting the tail of the log.

Troubleshooting

Most first-run problems come from the link or the client PATH, not the guardrails. This table maps the common symptoms to a cause and a fix.

symptomcausefix
Every order returns LINK_DOWNNo AddOn connected, or it has not sent first account state.Start the fake AddOn, or the real AddOn, on the same port.
Claude shows no PitBridge toolsClient not restarted, or uv path wrong.Fully quit and reopen the client. Use an absolute path to uv.
Server fails to start in Claude DesktopMinimal PATH cannot resolve a bare uv.Put an absolute uv path in the command field.
port already in use or double behaviorRunning pitbridge run and pitbridge mcp on the same port.Run only one surface.
INSTRUMENT_NOT_ALLOWED on a valid contractContract is not in the instruments allowlist.Add the exact string including expiry, for example MES 09-26.
OUTSIDE_TRADING_WINDOW unexpectedlyLocal time differs from trading_window.tz.Widen the window or check the timezone.
Valid order blocked KILL_SWITCHThe kill is still engaged from earlier testing.Run pitbridge unkill.
INVALID_QTY or SCHEMA_INVALIDZero, negative, or non-integer qty.Send a qty of at least 1.

A note on hosted alternatives

There is more than one way to give an agent access to NinjaTrader. CrossTrade, for example, is a hosted MCP path. PitBridge takes a different stance: it is local-first and guardrail-first, so the MCP server, the checks and your keys stay on your machine and the block is the product. Which one fits depends on whether you want the bridge in the cloud or on your own hardware.

When your NinjaTrader setup is supported, tell us on the waitlist which platform and prop firms you use. PitBridge is trading infrastructure, not financial advice: it enforces the limits you configure and does not promise any trading outcome.

Read the pillar: NinjaTrader 8 MCP bridge

Questions

Do I need to write code to connect Claude to NinjaTrader 8?

No. Claude Desktop and Claude Code connect to an MCP server with a small config block. You point the client at the local PitBridge daemon and the tools appear in the session.

Is my NinjaTrader account or API key exposed to a cloud service?

No. The bridge runs on your machine. The MCP server, the guardrail engine and your credentials stay in the local daemon, and no order is relayed through a PitBridge server.

Do I need a paid Claude plan to do this?

Claude Code needs a paid Claude plan such as Pro, Max, Team or Enterprise. The MCP config block itself is the same on Claude Desktop. PitBridge does not sell or resell any Claude subscription; you bring your own client.

Can I use a funded or prop-firm account?

Start on a simulation account such as Sim101. For a funded account you must check the firm's automation policy yourself, because rules change and vary by firm: some prohibit agent or automated order entry, some permit it with conditions. Firms like Apex or Topstep are only examples to verify, not endorsements. Verify with the firm before you connect anything live.

Can I do the same thing with ChatGPT or Claude Code?

Yes. Any MCP-capable client can connect the same way. The bridge does not care which model is on the other end, because the guardrail engine checks every order regardless of the client.

Why don't the tools appear after I edit the config?

Fully quit and reopen the client so it re-reads the config. Check that the JSON is valid. If the server still fails to start, use an absolute path to uv in the command, because the client launches with a minimal PATH.

Does this work on a Mac without NinjaTrader installed?

Yes for exercising the safety kernel. On a Mac you run the daemon against a bundled fake AddOn that gives synthetic fills, so you can see guardrails, the kill switch and the audit log work. Real order routing into NinjaTrader 8 is Windows plus the M4 AddOn.

Which account should I start on?

Start on a NinjaTrader simulation account such as Sim101, in read-only or paper mode. Move an agent to a live account only after you have watched how it behaves and set your limits.

PitBridge is in development. NinjaTrader 8 is first.

Tell us your platform and we email you when your setup is supported. Nothing else.