Skip to main content
Glama
README.md
# polymarket-book-mcp

An MCP server that reads Polymarket order books, market metadata, and price
history.

**No private key, no trading, read-only.** This package never imports
`py_clob_client`, `web3`, or `eth_account`, never reads a wallet key, and
never sends a POST or DELETE to any order endpoint. It only issues GET
requests to Polymarket's public Gamma and CLOB APIs, the same endpoints the
website itself uses to show you a book. There is no code path in here that
could place, modify, or cancel an order, because the client that would do
that was never written.

## Why another one

A survey of existing Polymarket MCP servers turned up two patterns: some
require a wallet private key to be configured just to read a public order
book, which is a strange amount of trust to ask for a read-only operation;
others expose market search or trade history but ship no order-book tool at
all, which is the one piece of data a market-making or quoting workflow
actually needs first. This package exists to fill that specific gap: order
books, cleanly parsed, with no signing key anywhere in the dependency tree.

## Install

```bash
uv venv
uv pip install -e .
```

## Usage with Claude Code / Claude Desktop

Point the config at the venv's Python interpreter directly:

```json
{"mcpServers": {"polymarket": {"type": "stdio",
  "command": "/absolute/path/to/polymarket-book-mcp/.venv/bin/python",
  "args": ["-m", "polymarket_book_mcp.server"]}}}
```

Or run it straight from GitHub with `uvx`, no local clone or install step:

```bash
uvx --from git+https://github.com/sunnywlad/polymarket-book-mcp polymarket-book-mcp
```

## Tools

| Tool | Parameters | Description |
|---|---|---|
| `search_markets` | `query`, `limit=10`, `include_closed=False` | Keyword search via Polymarket's real search backend. Results are interleaved across matching events, so a query like `bitcoin` returns several distinct events rather than one price ladder's worth of strikes. |
| `get_order_book` | `token_id`, `depth=10` | Live order book for one outcome token: best bid/ask, mid, spread, top N levels per side with cumulative size and notional. The central tool. |
| `get_market` | `url_or_slug` | Resolve a market URL or slug into its question, condition_id, per-outcome token ids, tick size, LP reward band, and status flags. |
| `get_quote` | `token_id` | Cheap price check: best_bid, best_ask, mid, spread only. |
| `get_price_history` | `token_id`, `interval="1d"`, `fidelity=60` | Historical price series for one outcome token, plus a min/max/first/last summary. |

## Data sources

Every network call this server makes, so you can verify it yourself:

- `GET https://gamma-api.polymarket.com/public-search` — market/event search (backs `search_markets`).
- `GET https://gamma-api.polymarket.com/markets?slug=...` — slug to conditionId (backs `get_market`).
- `GET https://clob.polymarket.com/book?token_id=...` — live order book (backs `get_order_book`, `get_quote`).
- `GET https://clob.polymarket.com/markets/{condition_id}` — market/token metadata (backs `get_market`).
- `GET https://clob.polymarket.com/prices-history?market=...` — historical prices (backs `get_price_history`).

Both APIs are public and require no authentication for these read paths.

## Notes on the Polymarket API

A few behaviors worth knowing if you're building on these endpoints
yourself, each reproduced directly against the live API:

- **`clobTokenIds` and `outcomes` are JSON-encoded strings, not arrays.**
  A Gamma market object contains `"clobTokenIds": "[\"5144...\", \"6848...\"]"`,
  a string that happens to look like a JSON array, not an actual array. Code
  that calls `.map` or iterates on it directly will throw or silently do
  nothing. It needs a `json.loads` first, with a fallback for malformed or
  missing values.
- **`GET /markets?search=...` is a no-op.** It silently ignores the `search`
  parameter and returns an unrelated page of markets. The only endpoint that
  actually implements search is `GET /public-search?q=...`, the one behind
  the website's search box.
- **The order book's price levels are not sorted the way you'd expect.**
  `GET /book` returns both `bids` and `asks` in ascending price order. For
  asks that puts the best price (lowest ask) first, which looks correct and
  invites indexing `asks[0]`. For bids, ascending order puts the best price
  (highest bid) *last*: `bids[0]` is the worst bid in the book, not the
  touch. Both sides need an explicit sort before you can trust `[0]`.
- **`events_status=active` does not fully filter.** `/public-search` still
  returns closed markets when asked for active ones, so the market's own
  `closed` flag has to be re-checked client-side.
- **One-sided books are normal, not errors.** Illiquid, closed, or resolving
  markets routinely return an empty `bids` or `asks` array with HTTP 200.
  Best bid, best ask, mid, and spread all have to degrade to null rather
  than raising, and mid/spread need *both* sides to mean anything.
- **`/prices-history` takes a token id in a parameter named `market`.**
  Passing an actual condition_id there returns an empty history with no
  error, which reads like a market with no trading rather than a bad call.

## Note on the MCP Python SDK version

The `mcp` dependency is pinned `>=1.9,<2` deliberately. SDK 2.0.0 removed the
`Server.list_tools` decorator, so any server written against the 1.x
low-level API dies at startup with
`AttributeError: 'Server' object has no attribute 'list_tools'`. If you are
debugging that error in another MCP server, an unbounded `mcp>=1.0.0` pin in
its dependencies is very likely the cause.

TDQS

A4.1/5.0

Scored across 5 tools

Disambiguation4/5

Tools are mostly distinct: search_markets finds markets, get_market resolves a specific market, get_order_book provides full depth, get_quote provides a quick price snapshot, and get_price_history gives historical data. The only potential overlap is get_quote versus get_order_book, but the descriptions clearly differentiate a lightweight price check from full depth.

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern with lowercase and underscores: search_markets, get_market, get_order_book, get_quote, get_price_history. This is highly predictable and maintains a uniform style throughout.

Tool Count5/5

Five tools is a well-scoped set for a market data server, covering search, resolution, current pricing (both depth and quick quote), and historical data. Each tool earns its place without unnecessary redundancy or bloat.

Completeness4/5

The server covers the core market data lifecycle: discovering markets, resolving identifiers, retrieving current order book depth, getting quick quotes, and accessing price history. Minor gaps like trade history or volume data exist, but the set is adequate for the stated purpose of a book-focused MCP.

Maintenance

ActivityStale
ResponsivenessNo issues