Skip to main content
Glama
jmercer187

sleeper-mcp

by jmercer187
README.md
# sleeper-mcp

MCP server + AI draft simulator for [Sleeper](https://www.sleeper.app/) fantasy football.

> Unofficial project. Not affiliated with or endorsed by Sleeper. Uses the public Sleeper API read-only — no credentials required.

## What it does

- **MCP server (stdio)** exposing your Sleeper league, rosters, draft, and picks to any MCP client (Claude Code, Claude Desktop, opencode, etc.)
- **Full NFL player database** (~12k players) with local TTL caching and fast name/team/position search
- **Normalized draft state**: one clean JSON view of who's on the clock, what round, who has picked — works for snake, linear, keeper, and mid-draft states
- **Local draft simulator**: run AI-vs-AI mock drafts (snake or linear) against real Sleeper league settings, with pluggable agent strategies

## Installation

### Run directly with uvx (no install)

```bash
uvx sleeper-mcp          # starts the MCP stdio server
uvx sleeper-mcp --help   # CLI usage
```

### pip / uv

```bash
pip install sleeper-mcp     # or: uv add sleeper-mcp
sleeper-mcp                 # bare invocation = MCP server
sleeper-mcp search "josh allen"
```

### From source

```bash
cd /path/to/sleeper-mcp
uv sync --extra dev
uv run sleeper-mcp --help
```

## MCP setup

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

```json
{
  "mcpServers": {
    "sleeper": {
      "command": "uvx",
      "args": ["sleeper-mcp"]
    }
  }
}
```

Or with pip-installed binary:

```json
{
  "mcpServers": {
    "sleeper": {
      "command": "sleeper-mcp"
    }
  }
}
```

## MCP tools

| Tool | Description |
| --- | --- |
| `get_league` | League settings: roster positions, scoring, draft type |
| `get_rosters` | All rosters with players and owners |
| `get_draft` | Draft config: order, slots, type, status |
| `get_draft_picks` | All picks taken so far (or full completed draft) |
| `search_players` | Search the NFL player DB by name/team/position (cached locally) |
| `get_player` | Full player record by Sleeper player ID |
| `get_draft_state` | **Normalized on-the-clock view**: current round, pick number, team on the clock, their roster so far, remaining picks per team |
| `refresh_players` | Force-refresh the local player cache |

Example — ask your AI client: *"Who's on the clock in my Sleeper league and what have they drafted?"* and it will call `get_draft_state`.

## CLI

```bash
sleeper-mcp league 1234567890          # league settings
sleeper-mcp rosters 1234567890         # all rosters + owners
sleeper-mcp draft 1234567890           # draft config (league ID or draft ID)
sleeper-mcp picks 1234567890           # raw picks
sleeper-mcp state 1234567890           # normalized on-the-clock state (JSON)
sleeper-mcp search "buf qb"            # search players (name/team/position tokens)
sleeper-mcp player "Josh Allen"        # player by name or ID
sleeper-mcp refresh-players            # force player cache refresh
sleeper-mcp simulate --help            # AI-vs-AI mock draft
```

### `get_draft_state` output (abridged)

```json
{
  "league_id": "1234567890",
  "draft_id": "1234567891",
  "draft_type": "snake",
  "status": "in_draft",
  "current_round": 3,
  "pick_number": 22,
  "team_on_clock": {
    "roster_id": 10,
    "user_id": "u1",
    "display_name": "Alice",
    "slot": 1
  },
  "team_on_clock_roster": [
    {"player_id": 4984, "full_name": "Josh Allen", "position": "QB", "round": 2}
  ],
  "teams": [ ... ],
  "picks_taken": 21,
  "total_picks": 240
}
```

## Architecture

```
MCP client (Claude Code, etc.)
        │ stdio / JSON-RPC
        ▼
sleeper_mcp.server   FastMCP tools (8)
        │
        ├── SleeperClient (async httpx) ──► api.sleeper.app  (public, read-only)
        ├── PlayerDatabase  ──► local TTL file cache (~12k players)
        ├── build_draft_state()  normalizes league+rosters+draft+picks
        └── DraftSimulation  in-memory snake/linear simulator (Python API + CLI)
```

- **No auth**: the Sleeper public API requires no keys for league/draft/player reads.
- **Caching**: player DB cached to disk (default TTL 24h); draft state cached in memory (default 30s) so repeated MCP calls during a live draft stay fast without hammering the API.

### Configuration (env vars)

| Variable | Default | Purpose |
| --- | --- | --- |
| `SLEEPER_API_BASE_URL` | `https://api.sleeper.app/v1` | API base |
| `SLEEPER_PLAYER_CACHE_TTL` | `86400` | Player cache TTL (seconds) |
| `SLEEPER_PLAYER_CACHE_PATH` | platform cache dir | Player cache file location |
| `SLEEPER_STATE_CACHE_TTL` | `30` | In-memory draft-state TTL (seconds) |
| `SLEEPER_HTTP_TIMEOUT` | `30` | HTTP timeout (seconds) |

### Sleeper API limitations handled

- `/v1/players/nfl` returns a dict keyed by player ID; `team` vs `team_abbr` field inconsistency is normalized.
- `pick_no` is overall in recent payloads but per-round in older ones — both are detected and normalized.
- Keeper/irregular draft orders get an approximate on-the-clock note when the order can't be fully derived from the API.

## AI-assisted live drafting

Point your MCP client at the server, then during a real Sleeper draft ask things like:

- "Who's on the clock and what do they have so far?"
- "Search for available RBs on Buffalo and rank them by position scarcity in my league settings."
- "Show me my roster after this pick — who's my best flex?"

The agent composes `get_draft_state` + `search_players` + `get_league` to reason about value. The server is read-only; it never takes picks for you.

## AI-vs-AI mock drafts

`examples/ai_vs_ai_draft.py` runs full simulated drafts using real league settings (or defaults) and the local player DB:

```bash
uv run python examples/ai_vs_ai_draft.py --teams 4 --rounds 3
uv run python examples/ai_vs_ai_draft.py --league 1234567890 --strategy mixed
uv run python examples/ai_vs_ai_draft.py --type linear --out draft.json --verbose
```

Eight heuristic agents ship in the box: `position_priority`, `aggressive_qb`, `zero_rb`, `hero_rb`, `wr_heavy`, `upside`, `conservative`, `contrarian`. Write your own by implementing one method:

```python
from examples.ai_vs_ai_draft import DraftAgent, DraftSimulation

class MyAgent(DraftAgent):
    name = "my_agent"

    def pick(self, sim: DraftSimulation, team_id: int) -> str:
        available = sim.available_players()
        # ... your strategy ...
        return chosen_player.player_id
```

The simulator (`sleeper_mcp.simulator.DraftSimulation`) is also importable directly for custom harnesses — it enforces snake/linear order, slot eligibility (including FLEX/SUPER_FLEX), and duplicate-player rejection.

## Development

```bash
uv sync --extra dev
uv run pytest -q        # fully mocked; no network required
uv run sleeper-mcp --help
```

Layout:

```
sleeper_mcp/
  client.py       async httpx client + error hierarchy
  models.py       Pydantic models
  cache.py        player DB cache + search
  slots.py        slot assignment (starters/FLEX/SUPER_FLEX/bench)
  draft_state.py  normalization: on-the-clock, order, picks
  simulator.py    in-memory draft simulation
  server.py       FastMCP tools (stdio)
  cli.py          typer CLI; bare invocation = MCP server
tests/            pytest suite with httpx.MockTransport fakes
examples/         ai_vs_ai_draft.py
```

## Roadmap / intentionally deferred

- External data providers (ADP rankings, projections, news) — the search API is the extension seam; plug in any source
- Batch multi-draft runner / league-wide tournament simulation
- Streamable HTTP transport (stdio is sufficient for local MCP clients today)

## License

MIT — see [LICENSE](LICENSE).

TDQS

A3.9/5.0

Scored across 8 tools

Disambiguation4/5

Most tools target distinct resources and actions, and descriptions clarify the intended use of get_draft_state as a composite live-draft tool versus get_draft and get_draft_picks. There is some overlap because get_draft_state bundles draft metadata, recent picks, and drafted player IDs, so an agent may need to decide when the narrower get_draft/get_draft_picks tools are preferable.

Naming Consistency5/5

All tool names follow a consistent snake_case verb_noun pattern: get_league, get_rosters, get_draft, get_draft_picks, search_players, get_player, get_draft_state, refresh_players. The convention is predictable throughout, with clear resource-oriented names.

Tool Count5/5

Eight tools is well-scoped for a Sleeper fantasy football league/draft helper. Each tool has a clear role, and the set avoids unnecessary duplication or excessive surface area.

Completeness4/5

The surface covers the core fantasy football league and draft workflows: league settings, rosters, draft metadata, draft picks, player search/lookup, a live draft state aggregate, and cache refresh. Minor gaps remain around related league data such as matchups, standings, transactions, or user league discovery, but the primary draft-assistance purpose is well covered.

Maintenance

ActivityMaintained
ResponsivenessNo issues