Pilot
dreamDEX Perps Bounty · an MCP server for agents

Your agent writes the order. Pilot holds it short.

Asked for a 0.0005 BTC short, Codex planned $40.11 against your $25 cap. Pilot refused it with nothing sent and named a size that fits: the 0.00031 BTC re-issue filled on dreamDEX (Somnia testnet).

Bay 1 · Codex CLI · 2026-10-24 02:41 UTCHideki · chain 50383
held short · 02:41:11
callsignCodex
marketBTCshort · IOC
size0.00050.00031
notional$40.11cap $25
Refused · nothing sent

maxNotionalUsd: the position this order leaves would be $40.11 notional, above the $25 cap per market
next safe action: reduce size to at most 0.00031 BTC

stop bar · your limitschecked before broadcast
cleared · 02:41:21
callsignCodex
marketBTCshort · IOC
size0.00031
notional$24.86cap $25
Cleared · filled

0x385c1486…8080 · success @ block 31576050 · filled 0.00031

15

refused with nothing sent: 15 rows in DEMO.md, 11 by your limits, 4 by pilot's own checks

DEMO.md · Summary
9/9

orders opened through the MCP tools, then closed flat on the read-back

DEMO.md · Summary
7

distinct rules that refused an order, each with a next safe action

DEMO.md · Refusals
2

agents that traded through it: Claude Code (Anthropic) and Codex CLI (OpenAI)

DEMO.md · Clients
Strip 01 · the run

One run, read from both sides of the stop bar.

On the left, the journal pilot wrote while Codex worked (refused 02:41:11, closed 02:41:24). On the right, the same fill as dreamDEX's own explorer shows it.

codex exec · pilot over stdiojournal
02:41:11.799plan_order BTC short 0.0005
refusal · refused_by pilot-policy · maxNotionalUsd
the position this order leaves would be $40.11 notional, above the $25 cap per market
next: reduce size to at most 0.00031 BTC
02:41:18.257plan_order BTC short 0.00031 → plan_6c9331077bd9
notional $24.86 · liquidation 234283.29, 189.78% from mark
02:41:21.757place_order plan_6c9331077bd9
sent · filled 0.00031 · block 31576050 · success
tx 0x385c1486…8080
02:41:24.347close_position BTC
sent · filled 0.00031 · position after 0 · block 31576075
Re-set from evidence/p3-mcp-run-codex-journal.jsonl rows 4–7: our layout, the journal's fields and words. Recorded on commit 0d18cec. Since commit 5c81bcb pilot values a short at the top bid it can fill at, so this order would now read about $40.51 and pilot would name a slightly smaller size.
markets explorer · hideki · tx 0x385c…8080
Two crops of one capture of dreamDEX's Hideki markets explorer for transaction 0x385c…8080: block 31576050, 24/10/2026 02:41:21 UTC; one sell order at limit 80209.4, filled, crossed on arrival.
dreamDEX's Hideki markets explorer, captured 2026-10-27: two crops of one page (the transaction, the order; how). It rounds the size to 0.0003; the journal row records 0.00031 filled. Open it live

Seven ways it said no, and what to do instead.

Every refusal names its rule and a next safe action the agent can act on. These are the real rows from DEMO.md; some are scripted probes, like a made-up plan id.

Your limits11 rows · policy.json
Claude Code · 04:09:54maxNotionalUsdlong 0.57 SOL with 0.2 open

the position this order leaves would be $79.80 notional, above the $25 cap per market

… close_position or a reducing order frees room

6rows
Codex CLI · 02:41:26allowedMarketslong 200 DOGE

DOGE is not on the allowed list (BTC, ETH, SOL)

pick one of BTC, ETH, SOL

4rows
scripted · test policy, plans only · 04:36:53minLiquidationDistancePctlong 3 SOL

liquidation would sit 12.80% from mark, closer than the 15% minimum

use a smaller size (less leverage)

1row
Pilot's own checks4 rows
Claude Code · 02:40:06minQuantitylong 0.04 SOL

… is below the SOL minimum 0.1

plan at least 0.1 SOL

1row
MCP Inspector · 02:43:13unknownPlanplan_0123456789ab

… plans live in this server's memory only and are one-shot

call plan_order first

1row
scripted · 04:17:32planExpiredplan_2b867222f225

… expired 2s ago (plans live 60s)

call plan_order again for fresh prices

1row
scripted · 04:17:32planAlreadyUsedplan_2b867222f225

… was already used: an earlier place_order refused it (planExpired) and sent nothing

call plan_order for a new plan

1row

15 rows: 11 under your limits, 4 under pilot's own checks. Three of the DOGE rows also failed minQuantity; each is counted once, under your limits. The 0.0005 BTC refusal in the hero is one of the six maxNotionalUsd rows. One more refusal came from the exchange itself, with the race injected on purpose (see Honest limits). Source: DEMO.md, all times 2026-10-24 UTC.

Strip 02 · how it holds

Give an agent the SDK and it can send any order. Pilot makes it wait for clearance.

Checked twice, sent once

plan_order runs the SDK's own previews against your policy and hands back a one-shot plan_id that expires in 60 s by default. place_order re-checks your policy, re-runs the liquidation and margin previews on live state and re-derives the limit price from the live book before it sends.

agenttested: Claude Code, Codex CLI
plan_orderSDK previews + your policy
plan_idone-shot · 60 s default
place_orderre-checked on live state
dreamDEXtx → journal
getBookTopsOnchaingetMaxPerpOrderSizepreviewPerpOrderMarginpreviewPerpLiquidationPriceplacePerpOrder@somnia-chain/markets-sdk 0.32.1

The cap is on the position

With 0.2 SOL open, Claude Code planned a 0.1 SOL add, about $10 and under the cap on its own. Refused, nothing sent: the position it leaves would be $30.80.

open · 0.2 SOLplanned at $20.54
add · 0.1 SOL$30.80 > $25

Limits no pilot tool can edit

They live in a file no pilot tool writes; policy only reads it. They carry over between sessions, and the daily cap counts across restarts.

~/.pilot/policy.json · the defaults "maxNotionalUsd": 25, "minLiquidationDistancePct": 15, "allowedMarkets": ["BTC", "ETH", "SOL"], "dailyOrderCap": 20, "maxSlippagePct": 1, "planTtlSec": 60

Closing is never blocked by your limits

close_position closes from the live on-chain size and your limits never stop it, not even an invalid policy file. When it cannot close (an empty book, the signer lock held elsewhere) it says why.

Every plan, refusal and transaction lands in an append-only journal. journal --card turns yours into a receipt to share:

My dreamDEX perps receipts via pilot (Somnia testnet) @DreamDEXSomnia: 9 orders opened, 9 closed, 2 brackets, 15 refused with nothing sent (11 by my limits). Latest fill: https://smk-dev-markets-explorer-…/tx/0x664861a4…85a5 https://github.com/edycutjong/dreamdex-pilot
Honest by design · from the README

What it does not do, in its own words.

“Testnet only. Nothing here has run on mainnet.”README · Honest limits
“One exchange refusal in DEMO.md is marked race injected: the limit price was lowered in the sender to make the order unfillable …”README · Honest limits
“Pilot guards its own tools, not the rest of the agent.”README · Honest limits

Is it real?
What was staged?

Are these real orders?
Yes, on the Somnia testnet that dreamDEX's docs call Hideki (chain id 50383). Every transaction in DEMO.md was re-read from the RPC after the run, and each order and close hash links to its fill on dreamDEX's Hideki markets explorer (brackets and cancels are checked by the RPC only). Testnet only, with a burner key.
Which agents can use it?
Tested with Claude Code and Codex CLI over stdio; other MCP clients should work. Claude Code (Anthropic) opened 7 of the 9 recorded orders and Codex CLI (OpenAI) the other 2; MCP Inspector made read-only calls. Pilot has 9 tools: markets, account, plan_order, place_order, close_position, bracket, cancel_all, policy, journal.
Can the agent change its own limits?
Not through pilot: no pilot tool writes the policy file. Claude Code and Codex CLI also have shell and file tools that could read the key or edit that file, so the README's Getting Started section starts Claude Code with no built-in tools and Codex in a read-only sandbox (which still leaves it a shell), each checked with a throwaway key. Keep the burner small.
What happens when an order breaks a limit?
Nothing is sent. The refusal names the rule and a next safe action ("reduce size to at most 0.00031 BTC", "pick one of BTC, ETH, SOL"), says so when no order fits, and goes into the journal.
What was scripted or staged?
The prompts were written for the runs, and some refusals are scripted probes, such as a made-up plan id. One exchange refusal is race injected. The liquidation rule was shown under a test policy that lifts the cap to $1000, plans only. Later live runs are kept out of the DEMO.md counts. The Codex run in the hero was recorded on commit 0d18cec; since 5c81bcb pilot values a short at the top bid, so the same order would now read about $40.51.
Can it always get me out?
close_position is never blocked by your limits, even by an invalid policy file. It can still stop, for example on an empty book or when another pilot server holds the signer lock, and it says why.
Can I try it without a key?
Yes. Reads work keyless when PILOT_ACCOUNT names a public address; the README's Getting Started section uses the demo burner, and its second call (a $60 SOL plan) is refused by the default $25 cap with nothing sent.
Clearance

Set your limits once. Let the agent trade inside them.

git clone https://github.com/edycutjong/dreamdex-pilot cd dreamdex-pilot && npm ci && npm test