Skip to main content
Glama
simonlokc

polymarket-mcp

by simonlokc
README.md
# polymarket-mcp

Clean, **NegRisk-resolved** Polymarket market & order-book data as an MCP server.
No API key, no database — stateless wrappers over the public Gamma and CLOB
APIs, with the messy parts (keyset pagination, NegRisk flags, token→outcome
resolution, book parsing) already handled for you.

Point any MCP client (Claude Desktop, Cowork, Cursor, …) at it and your agent
can query prediction-market data directly.

## Tools

| Tool | What it does |
|---|---|
| `polymarket_get_markets` | Top active markets by liquidity or 24h volume. Returns question, conditionId, **outcome→tokenId mapping**, negRisk flag, event, liquidity, volume, best bid/ask, spread. Filters: `limit`, `order_by`, `min_liquidity`, `contains`, `neg_risk_only`. |
| `polymarket_get_orderbook` | Live CLOB order book(s) for one or more outcome token ids. Sorted bids/asks + best bid/ask, spread, midpoint. |
| `polymarket_resolve_token` | Resolve a `token_id`, `condition_id`, or `slug` to its market and outcome mapping (which outcome the token is, its siblings). |
| `polymarket_resolve_token_onchain` | **On-chain-verified** resolution: derives `conditionId` + `outcomeIndex` from Polygon ground truth (CTF Exchange registry → ConditionalTokens position ids) instead of trusting the API. Returns block provenance + verification level (`fully_verified`, or `pair_only` for NegRisk). Requires `POLYGON_RPC_URL`. |

## Install & run

Requires **Node 18+** (uses the built-in `fetch`).

```bash
npm install
npm run build
```

Add to your MCP client config (e.g. Claude Desktop `claude_desktop_config.json`):

```json
{
  "mcpServers": {
    "polymarket": { "command": "node", "args": ["/absolute/path/to/polymarket-mcp/dist/server.js"] }
  }
}
```

Then ask your agent things like *"what are the most active Polymarket markets
right now?"* or *"get the order book for this token id"*.

### Try it without a client

```bash
npm run build && npm run smoke   # drives the server over stdio against live APIs
```

## Tests

```bash
npm test   # unit tests for the ABI encoders — pure, no network
```

The encoder tests are deliberately network-free so they run anywhere. The API
and on-chain paths are verified against live endpoints by hand (see `npm run
smoke`), since CI has no route to Polymarket or a Polygon RPC.

## Config (all optional)

| Env | Default |
|---|---|
| `GAMMA_BASE_URL` | `https://gamma-api.polymarket.com` |
| `CLOB_BASE_URL` | `https://clob.polymarket.com` |
| `FETCH_TIMEOUT_MS` | `15000` |
| `POLYGON_RPC_URL` | *(unset)* — required only for `polymarket_resolve_token_onchain`. Any Polygon RPC that serves `eth_call` works; see below |

### Picking a Polygon RPC

The tool only needs `eth_blockNumber` and `eth_call`, so a free keyless endpoint
is enough. Verified working without an API key:

```bash
export POLYGON_RPC_URL="https://polygon-bor-rpc.publicnode.com"
```

`https://polygon.drpc.org` and `https://1rpc.io/matic` also answer keyless.
Note that `https://polygon-rpc.com` and `https://rpc.ankr.com/polygon` now
return **401 / Unauthorized** and require an account.

Passing the optional `block` argument reads historical state, which needs an
**archive** node — most free endpoints reject it (publicnode returns *"Archive
requests require a personal token"*). The tool surfaces that as an error rather
than pretending the token is unregistered. Omit `block` to read `latest`.

## Notes

- **Read-only.** No trading, no orders, no keys. It reads public data.
- Every fetch has a hard timeout + backstop so a stalled upstream can't hang the
  server.
- This is a data-access tool, not trading advice. Prices and books are live
  public data; verify before acting on them.

## License

MIT