Skip to main content
Glama
palm8-dev

Kalshi Read-Only MCP Server

by palm8-dev

Kalshi Read-Only MCP Server (v0.1)

A tiny MCP server that lets an AI agent (Grok, Claude, anything that speaks MCP) read real Kalshi market data instead of guessing prices from web search.

It exposes exactly three tools:

Tool

What it does

kalshi_search_markets

Find markets by keyword, series, or event

kalshi_get_market

Full detail for one market ticker

kalshi_get_orderbook

Resting bid levels, best prices, depth totals

Read-only by design. The server only sends GET requests to Kalshi's public market-data endpoints. It has no Kalshi login, asks for no API keys, stores no credentials, and cannot place, change, or cancel an order.


1. What you need

  • Python 3.10 or newer (this project was built and tested on 3.14)

  • An internet connection (it calls https://api.elections.kalshi.com)

  • No Kalshi account

Related MCP server: Prediction Markets MCP Server

2. Install

From the project folder:

python -m venv .venv
.venv\Scripts\python.exe -m pip install -r requirements-dev.txt

requirements.txt holds just what the server needs to run (that is what a host installs). requirements-dev.txt adds the test tools on top.

On macOS/Linux the interpreter is .venv/bin/python instead.

3. Run the tests

.venv\Scripts\python.exe -m pytest

The tests use a fake Kalshi API (no network, no rate limits), so they run in about a second and always give the same answer.

4. Start the server

.venv\Scripts\python.exe server.py

You will see:

Kalshi read-only MCP server 0.1.0
  MCP endpoint : http://127.0.0.1:8000/mcp
  Health check : http://127.0.0.1:8000/healthz
  • MCP endpoint: http://127.0.0.1:8000/mcp — this is the URL an MCP client connects to.

  • Health check: http://127.0.0.1:8000/healthz — open it in a browser to confirm the server is alive.

Useful flags and environment variables:

Option

Default

Meaning

--port 9000

8000

Port to listen on (also PORT env var)

--host 0.0.0.0

127.0.0.1

Listen on all interfaces, not just this machine

MCP_AUTH_TOKEN

unset

If set, callers must send Authorization: Bearer <token>

KALSHI_API_BASE

Kalshi production

Point at a different Kalshi base URL

Stop the server with Ctrl+C.

5. Give it a public HTTPS URL

Grok connects over the internet, so 127.0.0.1 is not reachable from it.

This repo includes render.yaml, so Render can configure itself.

  1. Push this project to a GitHub repository.

  2. In Render: New → Blueprint, pick the repo, and confirm.

  3. Render prompts for MCP_AUTH_TOKEN. Paste a long random string — or leave it blank to run without auth. Generate one with: python -c "import secrets; print(secrets.token_urlsafe(32))"

  4. Wait for the first build, then open https://<your-service>.onrender.com/healthz to confirm it is alive.

  5. Your MCP URL is https://<your-service>.onrender.com/mcp.

Know this about the free tier: Render spins a free service down after 15 minutes with no traffic, and waking it takes roughly a minute. The first tool call after an idle period can therefore time out inside Grok. Two ways around it:

  • Point a free uptime monitor (UptimeRobot, cron-job.org) at /healthz every 10 minutes. That keeps the service awake and fits inside the 750 free instance hours per month for a single service. /healthz needs no auth, so this works even with a token set.

  • Or upgrade to Render's paid Starter tier, which never sleeps.

Temporary alternative: a tunnel from your own PC

Useful for a quick test; the server stays on your machine.

cloudflared tunnel --url http://localhost:8000     # winget install --id Cloudflare.cloudflared
ssh -R 80:localhost:8000 nokey@localhost.run       # no install, uses built-in ssh

Both hand you a random HTTPS URL that changes every restart, and both stop working when your PC sleeps. localhost.run also drops idle tunnels and rotates its hostnames while connected.

Protecting a public URL

Anything on the open internet gets scanned, and a permanent hostname gets found far sooner than a random tunnel one. Set a token so only your agent can call it:

set MCP_AUTH_TOKEN=pick-a-long-random-string
.venv\Scripts\python.exe server.py

Callers then need the header Authorization: Bearer pick-a-long-random-string. /healthz stays open so uptime checks and Render's own health probe keep working.

6. Connect it to Grok

  1. Start the server and the tunnel (or deploy it).

  2. In Grok, add a Custom MCP connector.

  3. Paste the full MCP URL, including /mcp: https://your-public-host.example.com/mcp

  4. If you set MCP_AUTH_TOKEN, add the Authorization: Bearer <token> header in the connector's auth settings.

  5. Save. Grok should list three tools: kalshi_search_markets, kalshi_get_market, kalshi_get_orderbook.

The server uses the streamable HTTP transport in stateless mode, which is what remote MCP connectors expect, and it survives restarts and load balancers because no session state is kept between requests.


Tool reference

kalshi_search_markets

Parameter

Default

Notes

query

""

Keywords; all words must appear

series_ticker

none

Restrict to one series, e.g. KXEPLGAME

event_ticker

none

Restrict to one fixture, e.g. KXEPLGAME-26SEP18BRECFC

status

open

open, closed, settled, unopened, any

limit

20

Max markets returned (1–100)

max_pages

8

Pages scanned per series (1–25)

Kalshi has no server-side text search, so this server filters locally:

  1. Your words are matched against the ~14,000 Kalshi series (competitions).

  2. The best-matching series are searched for open events (fixtures).

  3. Each event's markets are matched against your words, including the event title — so liverpool finds the win, tie, spread, and totals markets for "Ipswich Town vs Liverpool".

Every response carries a coverage block (events_scanned, pages_fetched, total_matches_found, scan_complete). If scan_complete is false, more data existed than the request budget allowed — narrow the search with series_ticker for an exhaustive answer.

Handy soccer series tickers:

Series

Competition

KXEPLGAME

English Premier League — match winner

KXEPLSPREAD / KXEPLTOTAL

EPL spread / total goals

KXUCLGAME

UEFA Champions League

KXLALIGAGAME

La Liga

KXSERIEAGAME

Serie A

KXBUNDESLIGAGAME

Bundesliga

KXLIGUE1GAME

Ligue 1

KXMLSGAME

MLS

kalshi_get_market

Takes an exact ticker. Returns prices, volume, open interest, liquidity, settlement rules, strike details, and timestamps, plus the parent event's title.

kalshi_get_orderbook

Takes an exact ticker and optional depth (1–100, default 10). Returns YES and NO bid levels sorted best-price-first, top-of-book prices, level counts, and totals, plus a price snapshot of the market itself.

Kalshi returns an empty order book with HTTP 200 for tickers that do not exist, so this tool checks the market first. market_not_found and is_empty: true are therefore two different, honest answers.


How to read the JSON

{
  "ok": true,
  "market": {
    "ticker": "KXEPLGAME-26SEP04IPSLFC-LFC",
    "prices": {
      "yes_bid": { "dollars": 0.66, "cents": 66 },
      "derived": {                       // computed here, NOT quoted by Kalshi
        "yes_mid_dollars": 0.665,
        "yes_mid_implied_probability_percent": 66.5
      }
    }
  },
  "retrieved_at": "2026-09-04T07:20:11Z",
  "source": "https://api.elections.kalshi.com/trade-api/v2/markets/..."
}

Three rules the server follows so an agent can trust the numbers:

  1. Quoted vs computed are separated. Anything under a derived key was calculated by this server (mid price, implied ask from the opposite bid). Everything else came from Kalshi as-is.

  2. Missing means null. If Kalshi does not send a price or volume, the field is null. The server never estimates, interpolates, or carries values over.

  3. Failure is explicit. Errors return "ok": false with a machine-readable code (market_not_found, event_not_found, invalid_parameter, rate_limited, timeout, network_error, http_error), never a plausible-looking empty result.

Prices are dollars per contract. A Kalshi contract pays $1.00 if YES resolves true, so 0.66 means a 66% implied probability. Cents are included alongside every price because Kalshi's own UI quotes cents.


Project layout

kalshi-mcp/
├─ server.py          MCP server: tool definitions + web app
├─ tools.py           The three tools and all JSON shaping
├─ kalshi_client.py   HTTP GETs, timeouts, retries, error mapping
├─ tests/
│  ├─ conftest.py     Fake Kalshi API used by every test
│  ├─ test_tools.py           Tool behavior
│  └─ test_mcp_server.py      Real MCP protocol round-trips
├─ requirements.txt
└─ README.md

Troubleshooting

Symptom

Cause / fix

rate_limited errors

Kalshi throttles public data. Wait a few seconds; the client already retries twice with back-off.

Search returns 0 results

Add series_ticker. A bare keyword search is best-effort — check the coverage block.

Grok cannot connect

Make sure the URL ends in /mcp and is HTTPS, and that both the server and the tunnel are running. Open /healthz in a browser first.

401 unauthorized

MCP_AUTH_TOKEN is set on the server; add the matching Authorization: Bearer header in Grok.

Search feels slow the first time

The series catalogue (~16 MB) is downloaded once and cached in memory for 15 minutes.

Limits of v0.1

  • Read-only market data. No trading, no portfolio, no account data — by design.

  • No WebSocket streaming; every call is a fresh REST read.

  • Keyword search is best-effort and says so; series/event-scoped search is exhaustive.

  • The series catalogue cache lives in memory, so a restart re-downloads it.

Data source

Kalshi public Trade API v2 — https://api.elections.kalshi.com/trade-api/v2 (docs: https://docs.kalshi.com). Market data is Kalshi's; this server only reformats it. Nothing here is trading advice.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.

No tool schema history has been recorded yet.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • A
    license
    B
    quality
    D
    maintenance
    Enables comprehensive read access to Polymarket prediction market data, including live prices, order books, and market statistics. It allows AI assistants to analyze events, search markets, and monitor real-time signals without requiring API keys or authentication.
    22
    1
    MIT
  • A
    license
    Not graded
    quality
    F
    maintenance
    Unified access to prediction market data from Kalshi and Polymarket, enabling natural language queries for real-time odds, orderbooks, and trade history.
    17
    12
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Provides access to US-regulated prediction-market data from Kalshi, enabling queries on market events without authentication.
    15
    MIT

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/palm8-dev/kalshi-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server