Skip to content

Prepare safe signal-to-trade demo for Stanford#81

Draft
KillerQueen-Z wants to merge 6 commits into
BlockRunAI:mainfrom
KillerQueen-Z:feat/stanford-trading-demo
Draft

Prepare safe signal-to-trade demo for Stanford#81
KillerQueen-Z wants to merge 6 commits into
BlockRunAI:mainfrom
KillerQueen-Z:feat/stanford-trading-demo

Conversation

@KillerQueen-Z

Copy link
Copy Markdown
Contributor

Summary

  • add MCP safety annotations for all tools and a read-only Polymarket positions/orders surface
  • serialize paid Trading requests and validate Predexon request contracts before payment
  • use Polymarket's direct CLOB endpoint, remove geoblock-bypass guidance, and add a packaged signal-to-trade Skill plus Stanford runbook
  • expand the Trading profile to the nine tools needed for discovery, evidence, portfolio checks, and dry-run order construction

Validation

  • 264 tests passing
  • TypeScript typecheck and production build passing
  • Skill validation and npm package dry-run passing
  • clean Claude Code and Codex CLI MCP installation verified
  • live concurrent price/candlestick/order-book/smart-money flow completed without same-wallet x402 conflicts
  • live $1 FOK Polymarket order dry-run completed without signing or submission
  • retired market-data route rejected before payment with a zero-spend session ledger

Safety / demo scope

No new live order was submitted. Polymarket's official US geoblock prevents a confirmed trade from the Stanford venue, so the live presentation is designed to show current signals and an executable order dry-run, with explicit confirmation still required for any future write.

Draft for review.

KillerQueen-Z and others added 4 commits July 26, 2026 23:59
…ier from blocking valid work

Four defects found reviewing this branch, each verified against the gateway
source or reproduced directly.

markets-validation: the Gamma blocklist rejected `search`, `sort`, `end_after`,
and `end_before` on polymarket/markets{,/keyset}. All four are spec-backed
Predexon filters (blockrun/src/lib/predexon.ts POLYMARKET_MARKET_PARAMS, where
P_SEARCH is documented as "this param is `search` here, NOT `q`"), so the
validator refused the exact query the demo needs — open BTC markets ending
after a date, sorted by liquidity — before payment. Narrowed to the params
that really are Gamma-only: active, closed, order, ascending.

markets-validation: markets/listings was hard-blocked as "no longer exposed",
but the gateway still registers it (predexon.ts:155), maps its params (:348),
routes it (:527), and advertises it as a priced Tier-1 endpoint in the x402
manifest. Unblocked and restored in the tool description and skill tables.

errors: `/5[0-9]{2}/` matched any bare 5xx-shaped number, so "max_tokens 512 is
above the limit" was reported as a temporary outage with "try again in a few
minutes" — hiding a validation bug behind retry advice. A 5xx now has to be
labelled as a status (adjacency required) or carry an HTTP reason phrase.

errors: dropping "api error after payment" from isServerError sent post-payment
upstream failures with no parseable status into the payment branch, telling
users to fund a wallet that was never the problem. Restored for the non-4xx case.

payment-serialization: the queue is process-global and each waiter awaited its
predecessor unconditionally, so one x402 call that never settled would wedge
every paid tool until restart. Waiters now give up after a bounded wait
(BLOCKRUN_PAYMENT_QUEUE_MAX_WAIT_MS, default 120s) — losing serialization on one
call beats a permanently stuck server.

Tests updated where they asserted the old behavior; 274 pass, typecheck and
build clean.

@VickyXAI VickyXAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed on the branch: npm run typecheck, npm test (270 → 274 pass), and npm run build are all green. The annotations split (blockrun_polymarket_read) and the market-buy depth / min-size work in orders.ts are genuinely good and I'd take them as-is.

I found four defects and pushed fixes for them to this branch (5fe6237). One larger item below is a product decision, not a bug, and I've left it alone.


1. Product decision that shouldn't ride along in a demo PR

src/utils/polymarket/constants.ts flips the default CLOB host from BlockRun's Finland egress to https://clob.polymarket.com. That isn't demo scaffolding — it removes the shipped 0.30.3 default for every existing user, and US/UK users lose order placement entirely.

Meanwhile the README this PR edits still leads with:

Read the odds and place the bet, from one self-custody wallet.the ability to place real, USDC-settled bets on Polymarket

After this merge that's false by default for a large share of users. The Regions section was updated honestly, but the headline claims weren't reconciled with it.

Also: deploy/finland-egress/ is still in the tree. Every doc reference to it was stripped, so the script that deploys the egress now sits in the repo undocumented, contradicting the policy this PR states.

This may well be the right call for a Stanford stage — but it's a decision about the product's flagship capability, currently landing as line 40 of "Prepare safe signal-to-trade demo." Suggest splitting it out and deciding it on its own, then reconciling the copy and the deploy/ directory either way.

2. Fixed: the validator rejected valid Predexon params

GAMMA_STYLE_MARKET_PARAMS blocked search, sort, end_after, end_before on polymarket/markets{,/keyset}. All four are spec-backed Predexon filters — see blockrun/src/lib/predexon.ts POLYMARKET_MARKET_PARAMS, where P_SEARCH carries the comment "this param is search here, NOT q".

Net effect: the exact query this demo wants — open BTC markets ending after a date, sorted by liquidity — was refused before payment with a message claiming Predexon doesn't accept it. test/markets-validation.test.ts asserted that behavior, locking it in.

Narrowed the blocklist to what is genuinely Gamma-only (active, closed, order, ascending) and replaced the test with one that pins the opposite: Predexon's own filters must pass through.

3. Fixed: markets/listings is still a live paid endpoint

The validator returned "Predexon no longer exposes 'markets/listings'", but the gateway still registers it (predexon.ts:155), maps its params (:348), routes it (:527), and advertises it as a priced Tier-1 endpoint in the x402 .well-known manifest (route.ts:580).

One of the two repos is wrong and they can't both ship. Since the gateway is what users are paying against, I unblocked it and restored it in the tool description and skill tables. If it really is retired upstream, the gateway needs a matching PR first.

4. Fixed: formatError sold validation bugs as outages

Broadening hasStatus("500") to /5[0-9]{2}/ matched any bare 5xx-shaped number. Reproduced:

IN : Model rejected: max_tokens 512 is above the limit
OUT: … This is a temporary API issue. … Try again in a few minutes.

512 is everywhere in LLM errors (token limits, embedding dims). A 5xx now has to be labelled as a status — with adjacency required, so context length 512 exceeded doesn't qualify — or carry a standard HTTP reason phrase.

5. Fixed: post-payment outages told users to fund their wallet

Dropping "api error after payment" from isServerError meant a post-payment failure with no parseable status fell through to the payment branch:

IN : API error after payment: upstream provider unavailable
OUT: … your wallet needs funding. Run blockrun_wallet action:"setup" …

That's precisely the confusion this file exists to prevent. Restored for the non-4xx case; the 4xx tightening you added is kept and still tested.

6. Fixed: one hung call could wedge every paid tool

serializePaidRequest is a module-global promise chain and each waiter awaited its predecessor unconditionally, with no timeout. A single x402 call that never settles blocks every subsequent markets / surf / rpc / defi / price call for the life of the process — restart is the only recovery.

Waiters now give up after a bounded wait (BLOCKRUN_PAYMENT_QUEUE_MAX_WAIT_MS, default 120s). Losing serialization on one call beats a permanently stuck server.


Left for you — not changed

  • Serialization covers 5 of the paid tools. chat, search, exa, image, video, music, speech, modal, phone all still fire concurrently from the same wallet, so the x402 race this guards against is still reachable. I did not extend it, because serializing the 60–180s async media tools would be actively worse. Worth deciding deliberately: either scope the guard to the fast paid-data tools and say so, or find a wallet-level fix.
  • destructiveHint: true on read-only paid data tools (markets, search, exa, surf). In the MCP spec that hint means irreversible mutation; clients honoring it will prompt for approval on every data query — which cuts against this demo. "Costs money" and "destructive" aren't the same axis. Consider readOnlyHint: true plus documenting cost, unless per-call approval is the intent.
  • Smart-money cohort thresholds (≥100 trades / ≥$1000 PnL / ≥0.15 ROI) are invented client-side from one observed 400, with no override. The gateway has no param spec for that path, so this is guesswork that will reject legitimate narrower cohorts.
  • Candlesticks interval is now mandatory. Validating the value when present is safe; requiring it may break a working server-side default. Worth confirming against a live call.
  • docs/ added to npm files ships the internal Stanford runbook to every npx @blockrun/mcp consumer.
  • Tool count 19 → 20 desyncs from blockrun/brand-numbers.json and awesome-blockrun/brand-numbers.json, both still at 19. Needs companion PRs at release time.

…hange into its own PR

The default CLOB host flip (Finland relay -> direct clob.polymarket.com) removes
order placement for every US/UK user by default — the repo's headline capability
— and it was landing as a side effect of demo prep. Reverted here so this PR
ships only the annotations, the read-only tool, pre-payment validation, and the
demo material. The egress question gets decided on its own merits.

Restored to main: CLOB_HOST default, setup's blocked-region guidance, the 403
mapClobError text, the tool description, the README Regions paragraph and env
table row, the setup guide's §3 and troubleshooting rows, and the skill's
regions section (plus the test that asserted the new 403 wording).

Kept from this branch, since they are independent of the default: the
blockrun_polymarket_read flow through the docs and skill, markets/search-based
discovery, and client.ts's comment cleanup.

The Stanford runbook now sets POLYMARKET_CLOB_HOST=https://clob.polymarket.com
explicitly on the presentation machine — with the Finland default restored,
setup would otherwise report a permitted region from the venue and the demo's
region-honesty claim would not hold. Also fixed the Claude Code install snippet,
which passed POLYMARKET_MAX_SESSION_USD without its own -e flag.

package.json: drop `docs` from files, so the internal Stanford runbook stops
shipping to every npx consumer (verified: 0 docs entries in npm pack).

274 tests pass, typecheck and build clean.
@VickyXAI

Copy link
Copy Markdown
Contributor

Pushed 2b6a181 — the egress change is now split out per the review.

Reverted to main (the parts tied to the default host): CLOB_HOST, setup's blocked-region guidance, mapClobError's 403 text, the tool description, the README Regions paragraph + env row, the setup guide's §3 and troubleshooting rows, and the skill's Regions section — plus the test asserting the new 403 wording. US/UK trading keeps working on this branch.

Kept, since none of it depends on the default: the blockrun_polymarket_read flow through the docs and skill, markets/search-based discovery, and the client.ts comment cleanup.

The egress question now lives in #83 with the full list of surfaces that have to move together, including deploy/finland-egress/ — this PR stripped every doc reference to it but left the directory in the tree.

Two things worth a look:

The Stanford runbook now sets POLYMARKET_CLOB_HOST=https://clob.polymarket.com explicitly on the presentation machine. With the Finland default restored, setup would report a permitted region from the venue and the demo's region-honesty claim wouldn't hold. This also makes the demo independent of however #83 is decided.

The Claude Code install snippet passed POLYMARKET_MAX_SESSION_USD without its own -e flag, so the session cap would have silently not been set on the demo machine. Fixed.

Also dropped docs from package.json files — the internal runbook was shipping to every npx @blockrun/mcp consumer (verified: 0 docs entries in npm pack).

274 tests pass, typecheck and build clean.

Still open from the review, all yours: serialization covering only 5 of ~12 paid tools, destructiveHint on read-only paid data tools, the smart-money cohort thresholds, the mandatory candlesticks interval, and the 19→20 brand-numbers desync with blockrun / awesome-blockrun.

…nverified API contracts

Closes the four items left open by the review of this branch.

Annotations described price, not effect. `destructiveHint` means "irreversible
update" and is only meaningful when `readOnlyHint` is false — it says nothing
about money, and MCP has no cost hint. Marking blockrun_markets/search/exa/surf
destructive because they settle $0.0095 makes every annotation-honoring client
demand approval for a plain lookup, which cuts hardest against the research and
demo flows this branch adds. Spend control already lives in the budget ledger
(BLOCKRUN_BUDGET_LIMIT, reserveBudget, per-agent delegation).

Reassigned by what a tool actually does:
- paid reads (markets, search, exa, surf, defi, price) -> readOnly + openWorld
- generative (chat, image, video, music, speech, realface) -> write, not
  destructive; the shape MCP defines for a benign write
- rpc -> destructive. It was `paidPrivate`; eth_sendRawTransaction can broadcast
  a signed transaction, so this is an upgrade, not a relaxation
- modal -> destructive. Executes arbitrary code in a sandbox
Verified over stdio tools/list, not just at the registration call: 20 tools, 4
destructive (modal, phone, polymarket, rpc). Tests now pin the full matrix and
fail if a new tool ships unannotated — which under the spec would silently
default to destructive.

Serialization scope made deliberate rather than accidental. Added the two fast
paid data tools that were missed (search, exa) and documented why the
long-running ones are excluded: queueing a 5ms price lookup behind a 3-minute
video render trades a rare settlement race for a guaranteed stall. The race is
still reachable across a media call and a data call; that needs a wallet/nonce
fix, not a queue.

Two validator rules were enforcing guesses, and a wrong pre-payment rule blocks
an endpoint users are paying for:
- candlesticks: "1h fails" is observed, "interval is mandatory" is not. Now
  validates the value only when one is supplied.
- smart-money: the observed failure was the UNFILTERED call. Now requires any
  recognized cohort filter instead of invented magnitudes (min_trades >= 100,
  PnL >= $1000, ROI >= 0.15), which would have rejected a 7d window or a
  20-trade cohort with no override.

278 tests pass, typecheck and build clean.
@VickyXAI

Copy link
Copy Markdown
Contributor

Pushed 905b2cf — the four remaining code items from the review are done. 278 tests pass, typecheck and build clean.

Annotations now describe effect, not price

destructiveHint means "irreversible update" and is only meaningful when readOnlyHint is false. It says nothing about money, and MCP has no cost hint — so marking markets/search/exa/surf destructive because they settle $0.0095 made annotation-honoring clients demand approval for a plain lookup. That cut hardest against the research and demo flows this PR adds. Spend control already lives in the budget ledger.

Reassigned by what each tool actually does. Two are now stricter than before:

  • rpc was paidPrivate (non-destructive) — but eth_sendRawTransaction can broadcast a signed transaction. Now destructive.
  • modal executes arbitrary code in a sandbox. Now destructive.

Verified over a real stdio tools/list, not just at the registration call — 20 tools, exactly 4 destructive (modal, phone, polymarket, rpc). A new test fails if any tool ships unannotated, which under the spec would silently default to destructive.

Serialization scope is now a decision, not an accident

Added the two fast paid data tools that were missed (search, exa). Documented why the long-running ones stay out: queueing a 5ms price lookup behind a 3-minute video render trades a rare settlement race for a guaranteed stall. The race is still reachable across a media call and a data call — that needs a wallet/nonce fix, not a queue. Said so in the module rather than leaving it to be rediscovered.

Two validator rules were enforcing guesses

A wrong pre-payment rule blocks an endpoint users are paying for, so both now match what was actually observed:

  • candlesticks"1h" failing is observed; interval is mandatory is not. Now validates the value only when one is supplied, so a server-side default still works.
  • smart-money — the observed failure was the unfiltered call. Now requires any recognized cohort filter instead of invented magnitudes (min_trades >= 100, PnL >= $1000, ROI >= 0.15), which would have rejected a 7d window or a 20-trade cohort with no override.

brand-numbers 19 → 20 — deliberately NOT changed here

Not an oversight. blockrun/src/lib/brand-numbers.ts:139 is hand-asserted precisely so this repo fails first, and bumping it now would have blockrun.ai advertising 20 tools before npm ships them. Three ordered steps at publish time:

  1. npm publish this repo at the version containing the 20th tool
  2. blockrun/src/lib/brand-numbers.ts:139mcp: { tools: 20 }, deploy
  3. In awesome-blockrun: node scripts/sync-brand-numbers.mjs --refresh (its CI runs --check offline against the committed snapshot, so it will fail until this is done)

Everything from the review is now either fixed here or tracked in #83.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants