Skip to main content

oddsrail

The rail AI agents use to trade prediction markets.

An MCP server that gives any agent (Claude Code, Claude Desktop, or anything MCP-compatible) prediction-market access across Polymarket and Kalshi: market search, orderbooks, price history, positions, and order routing — with on-chain builder-code attribution on Polymarket — plus two premium signal tools (in-play overshoot/fade detection, resolution dispute-risk).

Free to use, and free of fees. oddsrail ships with a project builder code registered at 0 bps, so orders routed through it are attributed without adding a single basis point to anyone's trade. The project's income is a share of Polymarket's weekly builder reward pool — paid by Polymarket's own program, not by you. Running your own builder profile instead is one environment variable (ODDSRAIL_BUILDER_CODE), and server_info always tells you which code is in use. No fee tiers, no paywalled tools, no account required.

How oddsrail compares

Verified against each alternative directly (their repos, live endpoints, and registry entries — August 2026), not from their marketing:

oddsrail raw Polymarket API raw Kalshi API Crosswire Parsec
Both venues, one vocabulary ✅ find_markets single-venue single-venue pairs from a frozen 43-pair graph ✅ 5 venues
Book-walked cost + slippage ✅ quote_cost book only, math is yours book only top-of-book, covered pairs only preview on their infra
Order lifecycle (status/fills/kill switch) ✅ endpoints exist, with footguns¹ endpoints exist ❌ read-only ✅
Resolution criteria surfaced ✅ both venues buried in fields buried in fields ✅ deep — on 1 active pair ❌ zero UMA/rules tools
Trading signals ✅ overshoot + dispute-risk ❌ ❌ ❌ ❌
Open source / auditable ✅ MIT, full source n/a n/a ❌ 4-file listing shell, service proprietary ❌ closed
Self-hosted / non-custodial ✅ keys never leave your machine ✅ ✅ hosted only stores keys or holds a managed wallet
Cost to the trader 0 bps, free tools free free $0.02/call after 3/day SaaS $0–250/mo; builder program keeps 55–85% of fees
Safety defaults dry-run default, destructive-tool annotations, venue quirks pre-encoded² you discover them by rejection same n/a unknown (closed)

¹ The raw Polymarket API has the endpoints — and models rejections as ok:false return values, orders its books worst-first, ships a trades endpoint that returns the market's public tape, and enforces an undocumented $1 minimum notional. oddsrail exists because we hit every one of those and encoded the fix.

² Kalshi prices are dollar strings (integer cents were removed 2026-03), its orderbook is bids-only on both sides, and its current SDK requires Python ≥3.13. All normalised here.

Where the others are honestly ahead: Parsec covers 5 venues to our 2 and has websocket streaming; Crosswire's settlement-audit output on its one covered pair is deeper than our resolution_criteria, and it takes x402 micropayments natively; hosted services need zero install. Our bet is that a trading agent cares more about correctness, auditability, and keeping 100% of its economics than about any of those.

Quickstart

Python 3.11+ required.

pip install oddsrail
claude mcp add --transport stdio oddsrail -- oddsrail

Or from a clone, without installing:

python3 -m venv .venv && .venv/bin/pip install -r requirements.txt
claude mcp add --transport stdio oddsrail -- /abs/path/to/oddsrail/.venv/bin/python -m oddsrail.server

Then ask the agent: "search markets about the World Cup final and run the overshoot signal on the favorite".

How attribution works (CLOB V2, verified Aug 2026)

  1. Get your builder code (a bytes32) at polymarket.com → Settings → Builders. Set your fee rates there: taker up to 100 bps, maker up to 50 bps — additive on top of platform fees, settled to your builder wallet.
  2. export ODDSRAIL_BUILDER_CODE=0x... where the server runs.
  3. Every order any agent routes through place_order has the code placed in the V2 order struct's builder field before signing — attribution is on-chain, visible in every OrderFilled event on CTF Exchange V2.
  4. Verify with the builder_stats tool (public builder-trades endpoint + leaderboard).

If you skip this, orders carry the bundled oddsrail builder code (0xa576c5ce…, registered at 0 bps maker / 0 bps taker) — costing you nothing and funding the project. If you set your own, yours wins; the default is a default, not a lock-in.

Environment variables

Variable Default Meaning
ODDSRAIL_DRY_RUN 1 1 = orders are simulated and returned, never posted. Set 0 to trade.
ODDSRAIL_BUILDER_CODE project default Your bytes32 builder code. Overrides the bundled project default so attribution (and any reward-pool share) accrues to you instead.
POLYMARKET_PRIVATE_KEY unset Operator wallet key; required only for real trading. Never leaves this machine.
POLYMARKET_WALLET_ADDRESS unset Proxy/deposit wallet address, if the account uses one.

Status — live-verified 2026-08-23

All 10 callable tools were driven end-to-end through a real MCP client session against live Polymarket, from the Finland VPS (/opt/oddsrail). Verified working: search, market lookup, orderbook (9/65 levels), price history (361 pts), overshoot signal, dispute-risk, builder leaderboard, dry-run order, open orders, server info.

Field mappings were corrected against the real API during that run — the docs-guessed shapes were wrong in three places (outcomes is a dict keyed yes/no, search() nests markets inside events, and book/volume/ resolution data live in prices/metrics/state/resolution sub-objects).

⚠️ Network note

Polymarket API domains are blocked on Turkish networks (BTK) — local testing fails TLS with a block page. Run the server where Polymarket is reachable (the Finland VPS at /opt/oddsrail, a VPN, or any unblocked network). The signal logic and MCP layer are fully testable offline.

Kalshi (venue #2)

Kalshi is bring-your-own-key and single-tenant by design: the operator supplies their own API key, trades their own account, and this server caches nothing. That is deliberate — Kalshi's Developer Agreement limits API use to a member's own trading (§3), bars facilitating other members' trading (§3.2) and sublicensing the API (§3.7), and restricts storing/sharing API data (§3.1). A hosted multi-tenant Kalshi service would not be compliant; a self-hosted one is.

Attribution does not exist here. Kalshi Builder Codes are a Solana/DFlow/Jupiter integration — there is no builder or affiliate field anywhere on the REST API, so Kalshi order flow cannot be attributed or monetised the way Polymarket's can. Kalshi is in oddsrail for coverage and signal reach, not for routing revenue.

Two shapes on this API are easy to get wrong, so oddsrail normalises both:

  • Prices are dollar strings, not cents ("0.5600"), sizes are fixed-point strings ("10.00"); the legacy integer-cent fields were removed in March 2026. All arithmetic uses Decimal.
  • The orderbook is bids-only on both sides. yes_dollars and no_dollars are both bid ladders, ascending — so the best bid is the last element, and a NO bid at $0.99 is a YES ask at $0.01. kalshi_get_orderbook returns a conventional best-first bid/ask view of the YES book plus the raw ladders.

Order placement speaks natural terms — outcome (yes/no), action (buy/sell), price = probability of that outcome — and translates to Kalshi's YES-book bid/ask internally (buy NO @ 0.25 becomes ask @ 0.75). That translation is unit-tested, since it is the obvious place to ship an inverted-position bug.

Credentials: KALSHI_KEY_ID plus KALSHI_PRIVATE_KEY_PATH (PKCS#8 PEM) or KALSHI_PRIVATE_KEY. Set KALSHI_DEMO=1 to hit the demo environment. Read tools need no key at all.

Cross-venue tools

  • find_markets(query) — searches Polymarket and Kalshi in one call and returns one normalised shape per market: venue, market_id (the id that venue's order tool takes), title, yes/no price as probabilities in (0,1), best bid/ask, spread, 24h volume, close time, and trade_with naming the tool to call. Use this when you do not already know the venue.
  • quote_cost(venue, market_id, side, size) — what a size would actually cost, by walking the book rather than reading the top level. Returns average fill price, slippage vs best, notional, levels consumed, and whether the size is fillable at all — plus Polymarket's per-market fee schedule where it publishes one. Kalshi does not publish fees in its market payload, so they are reported as unknown rather than estimated.
  • compare_venues(query) — candidate same-event listings across venues. Not an arbitrage scanner. Matching an event across venues is an unsolved entity-resolution problem: naive title overlap cheerfully pairs a Brazilian election with a Ukrainian one and reports a 70-point "gap" that is fiction. Two gates apply (title similarity ≥ 0.5 and close dates within a week), so it usually returns nothing — which is the honest answer. A price delta between candidates is reported as yes_price_difference, never as profit.

Kalshi search

Kalshi has no text-search endpoint. oddsrail searches by event (the human-readable index, with with_nested_markets) rather than paging tens of thousands of machine-named markets, and matches on word boundaries — without that, "fed" matches "German Bundestag" and a Fed-rate query returns German election markets. Results carry truncated, because a bounded scan means an empty result is not proof a market does not exist.

Order lifecycle & discovery

  • order_status(order_id) — resting / partially_filled / filled / gone, with size_matched. The answer an agent needs after place_order.
  • my_fills(), my_positions() — the operator's executions and holdings, no address juggling. (Fills come from the Data API activity feed — the SDK's list_account_trades returns the market's public tape and is not used.)
  • cancel_all_orders() — kill switch: flatten every resting order at once.
  • resolution_criteria(venue, market_id) — the full resolution contract: what resolves YES, who resolves it, from which sources. Read it before trusting a price.
  • closing_soon(hours) — markets closing within N hours on either venue, where activity concentrates.

Workflow prompts

MCP prompts show up in clients as ready-made workflows, and they encode the order of operations that keeps an agent out of trouble — the sequencing is the expertise, which a flat tool list cannot convey.

  • /find_fade_setup(query, bankroll) — signal → book → cost → resolution → size → dry-run, with the rejection criteria at each step
  • /check_cross_venue_edge(query) — candidates → settlement audit → cost on both legs, and says plainly when the answer is "no edge"
  • /daily_review — positions, resting orders, fills, closing-soon, attribution

Risk & settlement

  • settlement_audit(polymarket_id, kalshi_ticker) — the check that decides whether a cross-venue price difference is an edge or a mismatch. Compares close times, resolution sources, UMA dispute status and market structure on live data with no pre-curated pair list, returning ok / caution / block with reasons — and listing the checks it did not perform.
  • position_size(bankroll, price, fair_value) — fractional-Kelly sizing, capped, refusing negative-edge bets, returning its own assumptions.

Tools (32)

  • search_markets, get_market, get_orderbook, price_history, get_positions — read-only, no keys
  • overshoot_signal — premium: fresh panic-jump detection + this market's historical reversion tendency (ported from the polymarket-wc analyzer)
  • dispute_risk — premium: transparent 0–100 heuristic for contested (UMA-dispute-prone) resolutions
  • place_order, cancel_order, open_orders — trading, dry-run by default. price is a probability in (0,1), size is in SHARES, and the exchange enforces a $1 minimum notional on marketable orders. Trading tools carry destructiveHint annotations so clients can gate them.
  • builder_stats — attribution verification + public builder leaderboard
  • find_markets, compare_venues, quote_cost — cross-venue (above)
  • server_info — config status, per-venue

Kalshi: kalshi_search_markets, kalshi_get_market, kalshi_get_orderbook, kalshi_get_trades, kalshi_balance, kalshi_positions, kalshi_open_orders, kalshi_place_order, kalshi_cancel_order.

Stack notes

  • Official unified SDK polymarket-client (0.6.x): AsyncPublicClient for data, AsyncSecureClient.place_limit_order(..., builder_code=...) for attributed orders. The legacy py-clob-client is archived and cannot attach builder codes — do not use it.
  • MCP SDK 2.0: MCPServer from mcp.server.mcpserver (the old mcp.server.fastmcp.FastMCP import is gone in 2.x).
  • Kalshi is on plain httpx + cryptography, not the official SDK: kalshi-python-sync requires Python >=3.13 and re-releases weekly in lockstep with the spec version. Auth is RSA-PSS(SHA256, salt=digest length) over str(unix_ms) + METHOD + path, where the path includes /trade-api/v2 and excludes the query string. Base URL is now external-api.kalshi.com.
  • x402 (planned): the official x402 PyPI package (v2.20+) can wrap MCP tools directly (x402.mcp, payment rides in tool-call _meta), but its MCP helpers currently target mcp 1.x — integrating means pinning mcp>=1.28,<2 or waiting for the 2.x-compatible release. Mainnet settlement needs a facilitator (Coinbase CDP: 1,000 free settlements/mo, then $0.001). Keep free tiers of both signals so registries can index the server.

What the builder economy looks like (live, 2026-08-23)

Pulled from the public leaderboard via builder_stats:

weekly all-time
#1 (betmoar) $2.31M $2.10B
median of top 25 $127K $88.2M
entry to top 25 $42K $36.8M

The instructive rows are the small-user ones: MagicMarkets routes $354K/week with 1 active user, Gate $1.11M/week with 2, PolymarketScan $277K with 3. Those are bot operators routing their own flow — oddsrail's exact target customer — and they show a single serious agent trader is worth real volume. Wallets (MetaMask, 37K users) dominate on user count, not on volume per user.

Roadmap

  1. Live smoke test from an unblocked network — done 2026-08-23, all tools pass
  2. Register builder code (polymarket.com → Settings → Builders), set fees to 0 bps at launch, export ODDSRAIL_BUILDER_CODE; first attributed order on a tiny size
  3. Kalshi as venue #2 — done 2026-08-23, 9 tools, verified live
  4. x402 paid wrapping for the two signals once the mcp-2.x conflict clears
  5. Registry listings: official MCP registry (mcp-publisher, PyPI mcp-name: marker), Smithery (needs public streamable-HTTP + a free tool for their scanner), Glama (glama.json)

Listing / distribution

  • GitHub: https://github.com/hmesutozsoy/oddsrail (public, MIT)
  • Glama: auto-crawls GitHub; glama.json in the repo root claims maintainership.
  • PyPI: https://pypi.org/project/oddsrail/ — pip install oddsrail
  • Official MCP registry: listed as io.github.hmesutozsoy/oddsrail (published 2026-08-30, status active). Re-publish after a version bump with mcp-publisher publish; keep server.json's version in step with pyproject.toml or the registry rejects it.
  • Smithery: requires a public HTTPS streamable-HTTP endpoint — available once oddsrail is hosted rather than run locally over stdio.

Release files for oddsrail 0.7.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for oddsrail 0.7.0
File Size Uploaded
oddsrail-0.7.0.tar.gz 37.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for oddsrail 0.7.0
File Interpreter ABI Platform
oddsrail-0.7.0-py3-none-any.whl Python 3 none any Details

Total release size: 79.4 kB

Release files / oddsrail-0.7.0.tar.gz

Download URL oddsrail-0.7.0.tar.gz
Size 37.4 kB
Tags Source
SHA-256 checksum
How to use checksums
f37eb6f5c142a4958bc60fa041e8de2a6de593219290994986ecbac33cb5bdc8
BLAKE2b-256 checksum
How to use checksums
1276763c1771fd2bbe0e1ffc20457d6c662b6cded618c77c73d132e748eea765
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.1

Release files / oddsrail-0.7.0-py3-none-any.whl

Download URL oddsrail-0.7.0-py3-none-any.whl
Size 42.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1dd471f704db00a6b16c423b63f4ca1578340b5b5d0a3ab182c7e6567867a978
BLAKE2b-256 checksum
How to use checksums
87b25d009cf5547958427423cb5ea9364f7dcb82fef587b4f7c66c9924f7e344
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.1

Release history Release notifications | RSS feed

0.19.0

2 release files

0.18.1

2 release files

0.18.0

2 release files

0.17.1

2 release files

0.17.0

2 release files

0.9.0

2 release files

0.8.1

2 release files

0.8.0

2 release files

This release

0.7.0 This release

2 release files

0.6.1

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page