Skip to main content
Glama
dumkoder

Statos MCP Server

by dumkoder
README.md
# @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

A4.4/5.0

Scored across 7 tools

Disambiguation5/5

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.

Naming Consistency5/5

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.

Tool Count5/5

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.

Completeness5/5

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.

Maintenance

ActivityMaintained
ResponsivenessNo issues