sleeper-mcp
# 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
Scored across 8 tools
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.
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.
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.
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.