sleeper-mcp
Enables Claude to interact with Sleeper fantasy football leagues via MCP tools for roster, waiver, matchup, and transaction queries, plus a dashboard for daily reports, live scoring, and game-day alerts.
README
sleeper-mcp
Connects Claude to a Sleeper fantasy football account two ways:
- MCP tools — ask Claude directly ("who should I start this week") and it pulls live Sleeper data via the tools below.
- Daily report dashboard — a site with matchup summary, a full recommended lineup (every starting slot, not just close calls), win probability, waiver targets, and trade suggestions, regenerated every morning (and again Sunday 11am). Also has a live matchup card (your lineup vs. theirs, live score, injury badges — refreshed straight from Sleeper, not the daily report) and a live standings card.
- Gameday alerts — a background watcher pushes a phone notification (via ntfy.sh) the moment one of your starters' injury status changes.
- Multi-user mode —
/settings.htmland/my.htmllet anyone with a Sleeper account sign up, connect their own username/league(s), and generate their own report on demand. See "Multi-user mode" below.
Sleeper's API is public and read-only, so this only ever reads league data; it can't submit waiver claims, propose trades, or set your lineup for you. Those actions still happen in the Sleeper app — this is for the research and reasoning that inform them.
Configured leagues (single-tenant mode)
Two league slots (keys butt-punt/no-fun — rename in src/config.js if you
like) set entirely via env vars: SLEEPER_USERNAME, SLEEPER_USER_ID,
SLEEPER_LEAGUE_BUTT_PUNT, SLEEPER_LEAGUE_NO_FUN. See .env.example. This
mode is for one operator's own account — for anyone else, use multi-user mode
below instead, no env vars or redeploys needed per person.
Tools
list_leagues— both leagues with current standingsget_my_roster— your starters/bench/taxi/IR + recordget_all_rosters— every team's roster, for scouting trade targetsget_matchup— your starters vs. your opponent's for a given week (defaults to current)get_waiver_wire— available free agents, ranked, filterable by positionget_trending_players— sitewide add/drop trends (not league-specific)get_transactions— recent waiver/trade activity in a league
Setup — local (stdio, this Mac only)
npm install
Register as a user-scoped MCP server (adjust the path to wherever you cloned this):
claude mcp add --scope user sleeper -- node /path/to/sleeper/src/index.js
Just ask Claude things like "who should I start this week in the Butt Punt League" or "any good waiver pickups at RB" — no need to invoke tools by name.
Setup — hosted (HTTP, behind Traefik)
src/http.js runs the same tools over the MCP Streamable HTTP transport
instead of stdio, so other devices (e.g. Claude Desktop on your phone) can
reach it too. docker-compose.yml has a Traefik label setup assuming
Docker-label discovery on a proxy network with a myresolver cert
resolver — adjust to your own reverse-proxy setup, or drop the labels:
block entirely and just publish a port if you don't use Traefik. Set
SLEEPER_DOMAIN in .env to your own hostname.
Requires SLEEPER_MCP_TOKEN in .env (the server refuses to start without
it) — every request to /mcp must send Authorization: Bearer <token>.
.env is gitignored; generate a token with:
node -e "console.log(require('crypto').randomBytes(24).toString('base64url'))"
To deploy/update, sync everything except local state to wherever Docker runs and rebuild:
rsync -av --delete --exclude .git --exclude node_modules --exclude data \
./ your-docker-host:/path/to/sleeper/
ssh your-docker-host "cd /path/to/sleeper && docker compose up -d --build"
To add it as a remote MCP server elsewhere:
claude mcp add --transport http sleeper https://your-domain/mcp \
--header "Authorization: Bearer <token>"
Daily report
automation/generate-report.mjs runs Claude Code headless (claude -p)
once a day with a tightly scoped tool surface — only the read-only sleeper
MCP tools plus WebSearch (for real injury/matchup context), no Bash/Write/
Edit — and --json-schema to force valid structured output matching
automation/report-schema.json. This runs under the Claude Pro plan, not
the pay-per-token API, so there's no separate billing (a report for both
leagues costs roughly $1–1.50 in notional usage against Pro's limits, not
an actual charge).
Scheduled via launchd (automation/com.michael.sleeper-report.plist,
symlinked into ~/Library/LaunchAgents), daily at 8:00 AM local time. If
the Mac is asleep at 8:00, launchd runs it as soon as the Mac wakes instead
of skipping it. Logs land in automation/logs/.
After generating, automation/deploy-report.sh rsyncs data/report.json
straight to the docker host's data/ volume — the container just reads
whatever's on disk on each request, so a new report doesn't need a
redeploy. To run it by hand:
./automation/run-daily.sh
Live data (no LLM, no daily-report lag)
/api/matchup?league=<key> and /api/standings?league=<key> hit Sleeper
directly on every request via getMatchupView()/getStandingsView() in
src/sleeperApi.js — same functions the MCP tools use. The dashboard's
Live Matchup and Standings cards poll these every 90s while the tab is
visible. Unauthenticated, same trust level as /api/report and the
dashboard itself — only /mcp (tool execution) requires the bearer token.
Gameday alerts
automation/watch-starters.mjs polls your current starters' injury_status
in both leagues and diffs against data/starter-status.json. On a real
change (to/from Questionable/Doubtful/Out/IR/PUP/Suspended), it pushes a
notification via ntfy.sh to the topic in .env's
NTFY_TOPIC — subscribe to that topic in the ntfy app (iOS/Android) or at
https://ntfy.sh/<topic> to receive them. Treat the topic name like a
shared secret — ntfy.sh topics are unauthenticated by default, so anyone
who knows it can read or publish to it.
Scheduled via launchd (automation/com.michael.sleeper-watch.plist) every
30 minutes, year-round — Sleeper's API is free and this is a cheap read, so
there's no real cost to running it in the off-season too. The first run
after a reset just seeds a baseline without alerting (avoids a flood of
"changes" for every player's pre-existing status). Run by hand:
node --env-file=.env automation/watch-starters.mjs
Multi-user mode
Separate from the single-tenant pipeline above — lets anyone with their own Sleeper account use this without touching env vars or redeploying.
src/db.js— SQLite via Node's built-innode:sqlite(Node 22+, zero native dependencies) atdata/app.db:users,sleeper_accounts,reportstables, plus a customexpress-sessionstore on the same DB./settings.html— email/password signup, then look up any Sleeper username's leagues and pick which to track./my.html— live matchup/standings (free, samegetMatchupView()/getStandingsView()as above) plus an on-demand "Generate my report" button.src/anthropicAgent.js— the report generator for this mode. It can't reuse headless Claude Code the way the single-tenant daily report does, since that only works under one personal Pro-plan login — so this is a hand-rolled tool-use loop against the raw Anthropic Messages API instead: the same Sleeper tools translated to Anthropic's format, the nativeweb_searchserver tool, and a forcedrecord_reporttool call whose input schema is the sameautomation/report-schema.jsonthe daily pipeline uses. RequiresANTHROPIC_API_KEY(console.anthropic.com) — real metered billing, no Pro-plan coverage. Each generation is rate-limited to once per 6 hours per user.
Only Sleeper is supported. Yahoo has an official OAuth Fantasy Sports API
and would be a reasonably clean addition; ESPN has no public API at all —
private leagues need espn_s2/SWID cookies scraped from a logged-in
browser session, which means storing real account credentials for other
people, not just another API key. Neither is implemented.
Known limitation
Sleeper's public player list is occasionally stale for deep veterans (a handful of long-retired players are still marked as active on a team). The waiver wire tool filters on Sleeper's own fantasy relevance ranking to push most of that noise out, but it isn't a second, independently-verified data source, so treat any surprising deep-bench name with suspicion.
Player data cache
data/players_cache.json caches Sleeper's full player list (~2000 players)
for 24 hours, per Sleeper's API guidance not to poll that endpoint more
often than that. Gitignored — regenerates automatically on first use each day.
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.