Statos MCP Server
# @statospro/mcp
A [Model Context Protocol](https://modelcontextprotocol.io) server that connects
AI assistants — Claude Desktop, Claude Code, claude.ai — to a
[Statos](https://statos.pro) instance.
Statos tracks football odds across ~54 bookmakers and looks for legs where one
book's price disagrees with a devigged consensus of the books measured sharper
than it. This package wraps that surface in an AI-friendly tool layer, so an
assistant can answer "is anything mispriced today?" without you writing an HTTP
call.
```
npx -y @statospro/mcp@latest --api-token <token>
```
> **What this is not.** Statos used to serve a goal model's predictions, and
> earlier versions of this package exposed them as `list_picks`. That model was
> refuted on its own ledger — **−0.13% ROI across 17,060 settled picks at a
> 71.5% hit rate**, about as good as the market and losing the vig — and its
> endpoint was removed in Statos v1.44.0. What you get now is a **market
> measurement, not a forecast**: it reports that a price disagrees with other
> prices, never that an outcome is likely.
## Tools
**v0.4.0 — market surface.**
| Tool | Wraps |
|---|---|
| `list_candidates` | `GET /api/v1/worklist` |
| `get_match_candidates` | `GET /api/v1/worklist?match_id=` |
| `list_leagues` | `GET /api/v1/leagues` |
| `suppress_market` | `POST /api/v1/admin/market-suppressions` |
| `list_suppressions` | `GET /api/v1/admin/market-suppressions` |
| `unsuppress_market` | `DELETE /api/v1/admin/market-suppressions/:id` |
Scopes are gone. Statos is single-tenant and authenticates with one static
bearer token, so every tool is available to whoever holds it — including the
three **market-suppression** tools, which stop a (league × market) being emitted
for a window without a deploy. Treat the token accordingly.
### Removed in v0.4.0
| Tool | Why |
|---|---|
| `list_picks` | Called `/api/v1/suggestions`, deleted in Statos v1.44.0. Use `list_candidates`. |
| `get_match_picks` | Same endpoint. Use `get_match_candidates`. |
| `get_account` | Called `/api/v1/auth/me`, deleted in v1.43.0. Statos has no accounts — there is one identity and you are holding its token. |
`list_candidates` is **not** a rename of `list_picks`. A pick carried
`suggested_prob` — a model's belief about a match. A candidate carries
`fair_prob` — what the sharp books collectively price. The names differ because
the numbers do: keeping them would invite an assistant to carry model-shaped
reasoning onto values that fail in a completely different way.
## Quick start
### 1. Get the token
Statos is single-tenant. The token is whatever the server was configured with as
`STATOS_API_TOKEN`; there is no self-service key page. If you run the instance,
it is in your deployment environment.
### 2a. Claude Desktop
Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS)
or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
```json
{
"mcpServers": {
"statos": {
"command": "npx",
"args": ["-y", "@statospro/mcp@latest"],
"env": {
"STATOS_API_TOKEN": "<token>"
}
}
}
}
```
Restart Claude Desktop. The tools appear in the tools picker.
### 2b. Claude Code
```bash
claude mcp add statos -- npx -y @statospro/mcp@latest --api-token <token>
```
### 2c. Sandbox
```bash
claude mcp add statos-sandbox -- npx -y @statospro/mcp@latest \
--api-token <sandbox-token> \
--api-base https://api.sandbox.statos.pro
```
## Tool reference
### `list_candidates`
The legs the scanner currently considers mispriced.
| param | type | default | notes |
|---|---|---|---|
| `markets` | string[] | — | `over_under`, `corners`. Omit for both. |
| `min_edge_pct` | number | — | Percent, so `3` means 3%. The server applies its own floor; this only narrows further. |
| `in_quoted_range_only` | boolean | `false` | Drop legs whose fair price was extrapolated. |
| `limit` | integer | server default | Max 500. |
Each leg carries:
| field | meaning |
|---|---|
| `odds` / `bookmaker` | The price the edge was computed against. Betting elsewhere voids it. |
| `fair_prob` / `fair_odds` | The devigged sharp consensus. `fair_prob` is in `[0,1]`. |
| `edge_pct` | How much longer the price is than fair, in percent. |
| `books_in_consensus` | Sharp books behind the fair price. Reconstruction error scales about `1/√n`, so this is a sample size, not a decoration. |
| `in_quoted_range` | `false` means the fair price was extrapolated past every reference book's quoted ladder — the region with the largest held-out error. A `false` is a reason to distrust the edge. |
| `candidate_id` | The scan row. Cite it so a bet traces back to the run that produced it. |
The response also carries `run_id`, `strategy`, `scanned_at` and `price_basis`,
so a slate can be reproduced later.
**An empty list is the normal result.** Most fixtures are priced about where the
sharp consensus puts them; on a typical day the scanner finds a handful of legs
out of a hundred-odd it can price, and often none.
### `get_match_candidates`
Every leg priced for one fixture.
| param | type | notes |
|---|---|---|
| `match_id` | integer | The `match_id` from a `list_candidates` result. |
### `list_leagues`
Discover league IDs and names.
| param | type | default | notes |
|---|---|---|---|
| `continent` | string | — | `Europe`, `South America`, `North America`, `Africa`, `Asia`, `Oceania`, `International`. |
| `specialized_only` | boolean | `false` | Only `is_specialized=true` leagues. |
| `limit` | integer | 200 | Max 500. |
### `suppress_market` / `list_suppressions` / `unsuppress_market`
`suppress_market` takes `league_id` (0 = all), `market`, and `duration_hours`;
`list_suppressions` takes nothing; `unsuppress_market` takes an `id`.
## Configuration
| Env var | CLI flag | Default | Notes |
|---|---|---|---|
| `STATOS_API_TOKEN` | `--api-token <token>` | — | **Required.** `STATOS_API_KEY` and `--api-key` still work. |
| `STATOS_API_BASE` | `--api-base <url>` | `https://api.statos.pro` | Point at `https://api.sandbox.statos.pro` for sandbox. |
## Troubleshooting
- **`401`** — the token does not match the server's `STATOS_API_TOKEN`, or was
truncated on copy. Note the server rejects a token shorter than 32 characters
outright, so a half-pasted value fails the same way a wrong one does.
- **`401: missing or malformed Authorization header`** — the env var isn't
reaching the server process. Restart your MCP client after editing config.
- **`404` on `list_candidates`** — the Statos instance predates v1.44.0 and has
no `/worklist`. Upgrade the backend, or use `@statospro/mcp@0.3.0` against it.
- **No candidates** — usually a genuinely quiet day rather than a fault. Call
`list_candidates` with no filters to confirm the scan itself returned
something, and read `scanned_at` to check how fresh the sweep is.
## Versioning
The package version is independent of the Statos API version. The server sends
`User-Agent: @statospro/mcp/<version>` so backend logs can correlate, and that
version is read from `package.json` rather than restated in code — the two had
drifted before.
## Requirements
Node.js ≥ 20, and a Statos instance at v1.44.0 or later.
## License
MIT — see [LICENSE](./LICENSE).
TDQS
Scored across 7 tools
Each tool targets a distinct resource and action: account retrieval, suppression listing, pick listing, per-match pick retrieval, league listing, and suppression creation/cancellation. The only potential overlap (list_picks vs get_match_picks) is clearly differentiated by scope and description.
All tool names follow a consistent verb_noun pattern in snake_case (get_account, list_suppressions, list_picks, get_match_picks, list_leagues, suppress_market, unsuppress_market). Verb choice and structure are predictable throughout.
Seven tools is well-scoped for a betting suggestions server: read operations for account, leagues, and picks, plus admin suppression management. Each tool earns its place with no redundancy.
The server covers account info, discovery (leagues), picks retrieval (global and per-match), and a full suppression lifecycle (list, create, cancel). No obvious gaps exist for the stated purpose.