Skip to main content
Glama
michaelc143

ESPN Fantasy MCP

by michaelc143

ESPN Fantasy MCP

A small, read-only Model Context Protocol server for ESPN Fantasy Football. It gives an MCP-compatible LLM access to normalized league snapshots, team rosters, and free-agent data.

This project calls ESPN's undocumented Fantasy read API. It is not an ESPN official developer integration, so endpoints and response shapes may change. Use it only with an account and leagues you are authorized to access, and review ESPN's terms before distributing or hosting it for other users.

What is included

The initial vertical slice exposes three read-only tools:

  • get_league_snapshot: settings, teams, rosters, schedule, and status.

  • get_team_roster: one normalized team and roster.

  • get_free_agents: free agents and waiver players, with a bounded result set.

Write operations such as adding, dropping, trading, or setting a lineup are intentionally not included. ESPN's mutation requests are undocumented and can make irreversible league changes; they should be added only with a preview, explicit confirmation, and strong validation.

Related MCP server: Sleeper Fantasy Football MCP

Requirements

  • Python 3.11+

  • uv or another Python package manager

  • An ESPN Fantasy league ID and season year

  • ESPN_S2 and ESPN_SWID cookies for private leagues

Install

uv sync --extra dev
cp .env.example .env

Fill in .env with your own values. Do not commit .env or put cookie values in prompts, tool arguments, logs, or source control.

For a private league, the cookies are the values named espn_s2 and SWID in your authenticated ESPN browser session. The server reads them only from environment variables. Public leagues may work without them.

Run

For a local MCP host that launches stdio servers:

uv run espn-fantasy-mcp

Example generic MCP configuration:

{
  "mcpServers": {
    "espn-fantasy": {
      "command": "uv",
      "args": [
        "run",
        "--directory",
        "/absolute/path/to/EspnMCP",
        "espn-fantasy-mcp"
      ],
      "env": {
        "ESPN_S2": "your-cookie-value",
        "ESPN_SWID": "your-cookie-value",
        "ESPN_BASE_URL": "https://lm-api-reads.fantasy.espn.com"
      }
    }
  }
}

The server does not need to know a default league: tools receive league_id and season explicitly, which makes accidental cross-league access less likely.

Test and lint

The tests mock ESPN and never contact the live service:

uv run pytest
uv run ruff check .

Architecture

MCP host / LLM
       |
       v
src/espn_mcp/server.py       Tool schemas and MCP transport
       |
       v
src/espn_mcp/espn.py         ESPN HTTP adapter and normalization
       |
       v
ESPN Fantasy read API        Undocumented, cookie-authenticated for private leagues

The adapter isolates ESPN-specific payloads from the MCP layer. If ESPN changes hosts, views, filters, or response shapes, update espn.py and its fixtures rather than changing tool contracts.

Available Tools

3 tools
get_free_agentsC

Find available free agents and waiver players. This is read-only.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo
seasonYes
league_idYes
positionsNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

C2.9/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description only states 'This is read-only,' which is a minimal behavioral disclosure. It does not explain what the tool actually returns, whether it filters or sorts results, or any side effects. Since there are no annotations to carry this burden, the description falls short of fully disclosing behavioral expectations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is extremely concise and to the point. It consists of two short sentences with no redundant information. The structure is clean and easily parsed.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Although an output schema exists (not shown), the description lacks contextual details about what results are returned and how parameters affect the search. It does not mention sorting, filtering, pagination, or any other behavior. Given the tool's moderate complexity (4 parameters), the description is incomplete for a user to fully understand its capabilities.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema provides no descriptions for parameters (0% coverage), and the description does not clarify any of them. Parameters like league_id, season, limit, and positions are left entirely unexplained. The description adds no meaning beyond the bare parameter names.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's function: 'Find available free agents and waiver players.' It uses a specific verb ('find') and identifies the resource (free agents/waiver players). This distinguishes it from sibling tools like get_league_snapshot and get_team_roster, which have different scopes.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no explicit guidance on when to use this tool versus alternatives. It does not mention any scenarios or conditions that would make this tool preferable to get_league_snapshot or get_team_roster. The 'read-only' note is implicit but does not help in tool selection.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_league_snapshotB

Get league settings, teams, rosters, schedule, and matchup data.

This is read-only. Use the league ID visible in the ESPN Fantasy URL.

ParametersJSON Schema
NameRequiredDescriptionDefault
seasonYes
league_idYes
scoring_period_idNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

B3/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden. It discloses that the operation is read-only, which is a key safety trait, and offers a hint on sourcing the league ID. However, it does not describe other behavioral aspects like return format, pagination, or any potential rate limits, though the output schema partially covers returns.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is two sentences with no filler. The primary purpose is front-loaded, and the read-only note is concise. Every word earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Despite having an output schema, the description fails to explain two of three parameters and offers no usage guidance. For a snapshot tool with multiple data types, this is insufficient to guide an agent effectively, especially without annotations.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate for all parameters. It provides a meaningful hint for league_id (source from URL) but says nothing about season or scoring_period_id, leaving their purpose and format unexplained.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb ('Get') and resource ('league snapshot') and enumerates the data types included (settings, teams, rosters, schedule, matchup data). It implicitly differentiates from siblings like get_team_roster (specific team) and get_free_agents (free agents) by covering the whole league, but does not explicitly contrast them.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance on when to use this tool versus the sibling tools. It only mentions that it is read-only and provides a hint about the league ID source, but does not state conditions for choosing this over get_team_roster or get_free_agents, nor any exclusions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_team_rosterB

Get one team's normalized roster and record. This is read-only.

ParametersJSON Schema
NameRequiredDescriptionDefault
seasonYes
team_idYes
league_idYes
scoring_period_idNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

B3.1/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full behavioral burden. It discloses the read-only nature, which is a meaningful safety signal. However, it adds nothing else — no mention of data volume, normalization specifics, or failure behavior. The output schema covers return format, so the read-only disclosure is the main contribution.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two short sentences with zero waste; the action is front-loaded and the read-only qualifier earns its place as behavioral disclosure. Could be slightly more informative without hurting conciseness, but as written it is efficient.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with 4 parameters, 0% schema coverage, and no annotations, the description is thin. The output schema documents returns, which helps, and parameter names are intuitive, but there is no guidance on parameter semantics or sibling differentiation. An agent would need to infer scoring_period_id's meaning and when this tool is preferable to the siblings.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0% and the description adds no parameter meaning whatsoever. The parameter names (league_id, season, team_id, scoring_period_id) are self-explanatory, which mitigates the gap somewhat, but scoring_period_id's role and the semantics of 'normalized' roster are left entirely to inference.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Get one team's normalized roster and record'), with scope clarified by 'one team's.' The 'normalized' qualifier adds specificity about the data transformation. It distinguishes from siblings by scope (a single team's data vs a league snapshot or free agents), though it doesn't name them explicitly.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The usage context is implied by the description — an agent can infer this is for retrieving a specific team's roster rather than league-wide or free-agent data. However, there is no explicit when/when-not guidance or named alternatives, so the agent must reason from the sibling tool names.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

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

  1. 3 tool updatesv0.1.0
    • First observedget_free_agents
    • First observedget_league_snapshot
    • First observedget_team_roster

TDQS

B3.4/5.0

Scored across 3 tools

Disambiguation5/5

Each tool has a clearly distinct purpose: league-wide snapshot, specific team roster, and free agent list. No meaningful overlap in functionality.

Naming Consistency5/5

All tool names follow the same 'get_' + noun pattern with snake_case, making them predictable and consistent.

Tool Count4/5

With only 3 tools, the set is minimal but fits a narrow read-only fantasy football use case. It is not bloated, though it borders on sparse.

Completeness3/5

The surface covers core read operations (league, team roster, free agents) but misses obvious fantasy football needs like standings, player details, or transaction history, leaving the domain partially incomplete.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    B
    maintenance
    Provides read-only access to Sleeper fantasy football leagues, enabling team snapshots, available players, matchups, trade context, and league history through standardized MCP tools.
    MIT
  • A
    license
    A
    quality
    C
    maintenance
    A read-only MCP server that supplies live context from Sleeper fantasy-football leagues, including rosters, trade analysis, draft picks, and league settings via Sleeper's public API.
    11
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Provides read-only, league-aware Sleeper fantasy football draft context to AI assistants, including live picks, rosters, and player availability over Streamable HTTP MCP.
    62 npm
    MIT
  • F
    license
    Not graded
    quality
    C
    maintenance
    Read-only MCP server that provides access to public Sleeper NFL fantasy data, including leagues, rosters, matchups, waivers, drafts, and player information. It enables AI clients to retrieve fantasy football details without requiring authentication or account modifications.
    -