Skip to main content
Glama
fantasy-yolo

fantasy-yolo

by gagandaroach

fantasy-yolo — ESPN Fantasy Football MCP server and CLI

Manage your ESPN Fantasy Football team from Claude, or any MCP client, or your terminal. An MCP server and a matching CLI that read your league's live data and make the moves you tell it to: set your lineup, add and drop players, submit waiver claims.

CI ESPN canary License: MIT Python 3.13

You: anything wrong with my lineup this week?

Agent: Nothing flagged across your 9 starters. Two bench players project higher than the starter at their position: Kyler Murray (17.34) over Jordan Love (17.28) at QB, and Trey Smack (9.21) over Harrison Butker (9.05) at K.

You: start Smack. Then grab Kaleb Johnson and drop Darnold.

Agent: Preview: Smack BE → K, Butker K → BE. Add Kaleb Johnson (RB, GB), drop Sam Darnold (QB, SEA). Confirm? … Both applied by ESPN.

The same tools from the terminal — every answer carries a one-line summary and says which season and week it resolved:

$ fy check-lineup
{
  "summary": "nothing flagged across 9 starters",
  "provenance": { "season": 2026, "week_resolved": 3, ... },
  "alerts": [],
  "starters_checked": 9
}

$ fy player "Saquon Barkley"
{
  "summary": "Saquon Barkley (RB, PHI) is rostered by Gridiron Gremlins",
  ...
}

The one thing to understand first

The human is the brain. This server has no optimizer, no projections model, and no opinion about which players are good. It answers questions accurately and executes instructions carefully. It will tell you a starter is projected zero points; it will never tell you to bench him.

That is a deliberate constraint, not a missing feature. See Why no optimizer.

Related MCP server: Sleeper MCP Server

Quick start

Needs Python 3.13 and uv.

git clone https://github.com/gagandaroach/fantasy-yolo
cd fantasy-yolo
uv sync

Then follow docs/setup.md — two cookies out of your browser and a small config file naming your league. Five minutes. Check it with:

uv run fy roster

Claude Code

claude mcp add fantasy-yolo -- uv run --directory /path/to/fantasy-yolo fantasy-yolo-mcp

Claude Desktop, Cursor, and other MCP clients

Add this to your client's MCP config — for Claude Desktop that is claude_desktop_config.json (Settings → Developer → Edit Config):

{
  "mcpServers": {
    "fantasy-yolo": {
      "command": "uv",
      "args": ["run", "--directory", "/path/to/fantasy-yolo", "fantasy-yolo-mcp"]
    }
  }
}

No credentials in either — deliberately. They live in a chmod 0600 file the config points at.

Writes are off until you set write_enabled: true in your config. Start read-only; turn writes on once you trust what you see.

Tools

Every tool is available identically over MCP and on the CLI, generated from one definition, so the two cannot drift apart.

Reading — always available

Tool

CLI

What it answers

get_roster

fy roster

Your roster with slots, opponents, projections, injury designations

check_lineup

fy check-lineup

One pre-kickoff check: empty slots, byes, players out, already-locked, projected zero

get_matchup

fy matchup

Who you're playing, projected score both sides, who's yet to play

get_league

fy league

Rules, key dates, waiver system, standings

get_budgets

fy budgets

What every team can still spend, not just you

find_players

fy find-players

Search the pool, ranked on a basis you name

get_player

fy player NAME

One player, and which team rosters them

get_team

fy team NAME

Another team's roster

get_position_counts

fy position-counts

Every team's positional shape, for trade shopping

get_all_rosters

fy all-rosters

Every team's roster in one call, filterable by position

get_transactions

fy transactions

The league's transaction log, filterable

get_pending

fy pending

Your claims and trades ESPN hasn't processed yet

Writing — off by default

Set write_enabled: true in your config to turn these on. While it's false they are not registered at all, so a model cannot see or offer them.

Preview

Execute

Effect

preview_lineup

execute_lineup

Set your starting lineup

preview_add_drop

execute_add_drop

Add a free agent, drop a player, or both

preview_waiver

execute_waiver

Submit a waiver claim with a FAAB bid

preview_cancel_waiver

cancel_waiver

Cancel a pending claim

Trades are designed but not yet built.

How writes are made safe

ESPN's transaction API has no dry run and no idempotency key. A retried write genuinely submits the move a second time. Everything below exists because of those two facts.

Two calls, one code. Every write is split. preview_* shows the change in player names and returns a short-lived, single-use code bound to that exact transaction. execute_* refuses to run without it. An approval given for one add cannot be spent on another, and a retried execute is refused rather than double-submitted.

Being honest about what that guarantees. It makes a write impossible as a side effect of a question, and forces the preview into the transcript. It does not guarantee a human read it — a model can call preview and execute in the same turn. The real human-in-the-loop guarantee is your MCP client's approval prompt on a tool marked destructiveHint. We set that annotation; your client decides what to do with it.

Nothing is ever retried. Retries are disabled at the HTTP adapter, because requests and urllib3 retry by default and this is the one platform where that silently double-submits a waiver claim. A timeout is reported as unknown — never as success, never as failure — with your roster re-read so you can see for yourself.

Your roster is re-read immediately before every write. If anything moved since the preview — you set a lineup on your phone — the write is refused rather than silently clobbering it.

Legality is checked client-side, since ESPN won't tell you in advance: roster space, slot eligibility, whether a player is actually free or actually yours, your bid against your remaining budget and the league minimum, and whether your league even uses FAAB.

It can only ever touch your own team. isLeagueManager and isActingAsTeamOwner are hard-wired false with no parameter exposing them, and your team id is pinned in config.

Every write is logged — intent before the request leaves, outcome after — so an interrupted write still leaves something to reconcile against. Credentials are redacted from every line.

What this server does not know

Stated in the server's own instructions, so a model driving it is told up front rather than left to invent:

  • No projections beyond ESPN's own number. There is no model here.

  • No injury news, no news feed, no reporter text.

  • No timestamps on injury designations. ESPN reports a status word and nothing else. It will catch a player ruled out on Wednesday; it will not catch a Sunday-morning inactive.

  • No ranking of players on merit.

A missing projection is returned as null, never as 0.0 — because ESPN really does project some players at zero, and conflating the two invites a confident wrong conclusion.

Why no optimizer

Removing the optimizer doesn't remove decision-making from the product — it moves it to you, where the context is. An automated optimizer on this platform would also be operating with no dry run and no undo, which is a bad combination for irreversible moves.

There's a second reason worth naming: if you drive this with an LLM, the decision-making relocates into a general model with no current fantasy data. That model will fill silence with confident, plausible, wrong detail. Hence the section above — telling it loudly what it doesn't know is the compensating control.

Notes on ESPN's private API

Documented here because it is otherwise scattered across gists and half-wrong repos. All verified against live ESPN.

Fact

Detail

Read host

lm-api-reads.fantasy.espn.com

Write host

lm-api-writes.fantasy.espn.com — one POST .../transactions/ endpoint for everything

Auth

espn_s2 + SWID cookies. No OAuth, no API keys, no developer program

X-Fantasy-Role

Not an auth signal. Reads NONE on an authenticated 200 just as on an unauthenticated 401

Dry run

Does not exist. VALIDATE / PREVIEW / DRY_RUN all 400

Idempotency

Does not exist. idempotencyKey and executionKey both 400

Body parsing

Strict, and happens before auth — so an unauthenticated POST returns 401 for a valid shape and 400 for an invalid one. A free, side-effect-free schema oracle

Wire slot names

fromLineupSlotId / toLineupSlotId. ESPN's client calls them fromSlotId/toSlotId internally and renames on the way out; sending the internal names 400s

Errors

Match TRAN_* by prefix — the real constants ship in _ONE / _PLURAL / _LM variants

Three shapes most public implementations get wrong, all confirmed correct here by ESPN's own parser:

  • A future-week lineup is FUTURE_ROSTER, not ROSTER.

  • A standalone drop is ROSTER, not FREEAGENT.

  • The trade verbs (TRADE_ACCEPT, TRADE_DECLINE, …) do exist.

A daily canary checks that ESPN still parses the exact payloads we'd send. ESPN tightened leagueHistory in Aug 2025 and split read/write hosts in Apr 2024, both without notice.

Design

src/fantasy_yolo/
  espn/       vendored ESPN client (football), ours to fix
  read/       read wrappers + the raw fetches espn-api misses
  write/      payload builders, never-retry client, error interpretation
  policy/     lineup planner, legality, confirm tokens, audit log
  tools/      the 20 tools
  cli.py      Typer frontend      ─┐ both generated from one registry,
  mcp/        MCP frontend        ─┘ so they cannot drift

Status

Reads are working and verified against a live league.

Writes are proven. A six-item batched lineup change against a live 14-team league returned 200 EXECUTED on 2026-09-08. The confirmation token survived across two separate CLI processes, the re-read-before-write guard held, and the audit log captured intent and outcome with credentials redacted.

Add/drop is proven. An add paired with a drop was applied by ESPN on 2026-09-24; an add onto a full roster was rejected by ESPN and reported as such, with nothing changed.

Waiver claims are built and their payloads validated by ESPN's own parser, but have not yet been fired authenticated. Trades are designed, not built.

Before you use this

Unofficial. Not affiliated with, endorsed by, or sponsored by ESPN or Disney. It drives a private, undocumented API that can change or break without notice.

It runs on your machine, with your credentials, on your own account. It operates no service and redistributes nothing.

"Runs locally" is not "stays local." Everything this server returns is handed to whatever MCP client and model you configure — for most people, a hosted service. There is no telemetry and no network connection other than to ESPN, but that is not the same as your data staying on your machine.

What you're risking. ESPN's terms restrict unofficial automated access. ESPN can suspend or terminate your account and your league, and neither this project nor its author can restore anything they take. Read Disney's Terms of Use and ESPN's Fair Play and Conduct rules and decide for yourself. Note ESPN ships its own "Auto Control" AI that makes roster moves — automation is not itself against the spirit of the game — but that is not permission to drive the private API from outside.

espn_s2 is a full ESPN/Disney session cookie, not a scoped token. Leaking it is account compromise. Never paste it into an issue, a chat, or a screenshot.

Contributing

Issues and PRs welcome — see CONTRIBUTING.md. Security reports go through SECURITY.md, privately. Changes are in CHANGELOG.md.

Licence

MIT. Permissive: use it, change it, ship it, sell it. The only thing it asks is that the copyright notice travels with the code.

It covers this project's own code. It grants no rights to ESPN's data, marks, or API, and it is not permission from ESPN.

Vendored code

src/fantasy_yolo/espn/ is a vendored copy of the football half of espn-api by Christian Wendt, also MIT. Its licence is preserved verbatim at LICENSE-espn-api, as MIT requires when redistributing.

It is vendored rather than depended on so defects in the read path can be fixed directly. The first was a POSITION_MAP that could not convert a lineup slot label back to its id for bench, IR or flex — which made lineup writes impossible. Our changes are marked fantasy-yolo: in comments, and its own test suite runs in our CI.

Available Tools

2 tools
get_matchupC
Read-only

Who you are playing this week, and the projected score for both sides.

Inputs: week, league. Returns: summary, opponent, provenance, my_projected, +1 more

get_rosterB
Read-only

Your roster: lineup slots, NFL opponents, projections and injury designations.

Injury designations are exactly what ESPN reports. There is no timestamp and no news text behind them, so do not describe how recent one is (B-02).

Inputs: week, league. Returns: bench, summary, starters, provenance, +2 more

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    B3.3/5.0

    Scored across 2 tools

    Disambiguation5/5

    get_matchup and get_roster target completely different resources: one provides weekly opponent and projected scores, the other provides team roster details. There is no overlap or ambiguity between them.

    Naming Consistency5/5

    Both tools follow the exact same get_<noun> snake_case pattern, making the naming predictable and easy to understand.

    Tool Count3/5

    Two tools is borderline: they cover the core read-only queries for a fantasy matchup and roster, but the server is sparse compared to the typical footprint of a fantasy football tool.

    Completeness2/5

    The server is entirely read-only and limited to matchup and roster information. There are no tools for lineup changes, player news, league standings, or transactions, which are significant gaps for fantasy football workflows.

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      B
      maintenance
      Connect ESPN & Yahoo fantasy leagues to AI assistants via MCP. Read-only tools for rosters, standings, matchups, free agents, and league info across football and baseball.
      23
      MIT
    • F
      license
      A
      quality
      D
      maintenance
      Enables natural language interaction with Sleeper Fantasy Football API data, allowing queries about leagues, players, matchups, draft results, and trade analysis.
      13
      25
      -
    • A
      license
      Not graded
      quality
      B
      maintenance
      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.
      135 npm
      MIT
    • F
      license
      B
      quality
      C
      maintenance
      Provides MCP-compatible LLMs with read-only access to normalized ESPN Fantasy Football league snapshots, team rosters, and free-agent data.
      3
      -