thorsenk/rffl-mcp-server
If you are the rightful owner of rffl-mcp-server and would like to certify it and/or have it hosted online, please leave a comment on the right or send an email to dayong@mcphub.com.
The rffl-mcp-server is a public-only ESPN Fantasy Football MCP server designed for OpenAI Agent Builder, featuring structured logging and cache observability.
rffl-mcp-server
ESPN Fantasy Football MCP server with authentication support for historical data access, structured logging, and cache observability.
Features
- Authentication support - Access private leagues and historical data (2011-2025) with ESPN cookies
- Public league support - Recent seasons (2023+) work without authentication for public leagues
- Full historical access - Simple matchups work for ALL years 2011-2025 with authentication ✨ NEW
- Structured JSON logging - FastMCP Cloud dashboard-friendly
- Configurable caching - Toggle cache and monitor performance (700-800x speedup)
- Multiple transports - stdio (dev), HTTP, or SSE
- Production ready - 94.1% health check pass rate, comprehensive testing
Quick Install (Cursor)
Click to install the deployed MCP server in Cursor:
This installs the live server at https://rffl-mcp-server.fastmcp.app/mcp with access to all fantasy football tools.
Quick start (local)
# Install dependencies
pip install -r requirements.txt
# Configure environment variables
# Create .env file with your ESPN credentials (required for historical data)
# Run the server
python rffl_mcp_server.py # stdio
MCP_TRANSPORT=http PORT=8080 python rffl_mcp_server.py # HTTP
Getting ESPN Authentication Cookies
REQUIRED for accessing historical data (2018-2022 seasons)
- Log into ESPN.com in your browser
- Open Developer Tools (F12 or right-click → Inspect)
- Go to the Application tab (Chrome) or Storage tab (Firefox)
- Under Cookies →
https://espn.com, find:espn_s2- Long alphanumeric stringSWID- Format:{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}
- Copy these values into your
.envfile
Deploy to FastMCP Cloud
- Push this repo to GitHub.
- Sign in to FastMCP Cloud with GitHub (free for personal servers).
- Create a new project from this repo.
- Entrypoint:
rffl_mcp_server.py:mcp - Environment variables (Project → Settings → Environment):
ESPN_LEAGUE_ID=323196(your league ID)ESPN_YEAR=2025(season year)ESPN_S2=your_espn_s2_cookie(for historical data)SWID={your_swid_cookie}(for historical data)ENABLE_CACHE=true(optional, default: true)LOG_LEVEL=INFO(optional, default: INFO)
- Deploy. Your MCP endpoint will look like:
https://<project>.fastmcp.app/mcp.
Note: Local development tools (.claude/, .cursor/) are automatically excluded from deployment via .gitignore. Only production code is deployed.
Environment Variables
| Variable | Default | Description |
|---|---|---|
ESPN_LEAGUE_ID | 323196 | ESPN fantasy football league ID |
ESPN_YEAR | 2025 | Fantasy football season year |
ESPN_S2 | None | ESPN authentication cookie (REQUIRED for historical data 2018-2022) |
SWID | None | ESPN authentication cookie (REQUIRED for historical data 2018-2022) |
ENABLE_CACHE | true | Enable/disable league caching (true/false) |
LOG_LEVEL | INFO | Logging level (DEBUG, INFO, WARNING, ERROR) |
ESPN_DEBUG | 0 | Enable ESPN API debug mode (0/1) |
MCP_TRANSPORT | stdio | Transport mode (stdio/http/sse) |
HOST | 0.0.0.0 | HTTP/SSE server host |
PORT | 8080 | HTTP/SSE server port |
Tools
Core Fantasy Football Data
get_league(league_id?, year?)- League metadata, settings, and teamsget_standings(league_id?, year?)- Teams ordered by standingsget_matchups(week?, league_id?, year?, include_lineups=false)- Weekly matchups with live scoring (simple: works 2011-2025, enhanced with lineups: 2019-2025)get_enhanced_boxscores(week?, league_id?, year?)- Enhanced boxscores with formatted lineup tables (starters + bench)get_power_rankings(week?, league_id?, year?)- Two-step dominance power rankingsget_teams(league_id?, year?)- Raw teams arrayget_scoreboard(week?, league_id?, year?)- Legacy scoreboard viewget_player_info(name?|player_id?, league_id?, year?)- Player lookup by name or ID
Observability & Cache Management
get_cache_stats()- Cache hit/miss statistics and statusclear_cache()- Force clear cache for fresh dataping()- Health check endpoint
Structured Logging
The server outputs JSON-formatted logs suitable for FastMCP Cloud's observability dashboard:
{"timestamp": "2025-10-09 18:45:23", "level": "INFO", "message": "Fetching league from ESPN API", "cache_hit": false, "league_id": 323196, "year": 2025}
{"timestamp": "2025-10-09 18:45:24", "level": "INFO", "message": "Successfully loaded league from ESPN", "league_id": 323196, "year": 2025, "duration_ms": 856, "status": "success"}
{"timestamp": "2025-10-09 18:45:25", "level": "INFO", "message": "get_matchups completed", "tool": "get_matchups", "week": 5, "duration_ms": 1243, "matchup_count": 6, "status": "success"}
Cache Behavior
- Cache enabled (default): League objects are cached in memory across tool calls
- Cache disabled (
ENABLE_CACHE=false): Every request fetches fresh data from ESPN - Cache statistics: Use
get_cache_stats()to monitor hit rate and performance - Cache clearing: Use
clear_cache()to force fresh data when needed
Important Notes
Authentication Requirements
- Recent seasons (2023+): Public leagues work without authentication
- Historical seasons (2018-2022): REQUIRES ESPN_S2 and SWID cookies
- Pre-2018 seasons (2017 and earlier): Accessible with authentication, but limited data
- Box scores: Available for 2019+, limited availability for 2018 and earlier
Data Availability by Year
| Year Range | Authentication | League Data | Simple Matchups (scoreboard) | Enhanced Boxscores (with lineups) | Notes |
|---|---|---|---|---|---|
| 2023-2025 | Optional (public leagues) | ✓ | ✓ | ✓ | Full access |
| 2019-2022 | Required | ✓ | ✓ | ✓ | ESPN API requires auth |
| 2011-2018 | Required | ✓ | ✓ | Limited | ESPN API limitation for enhanced data |
| 2010 and earlier | Required | ✓ | Limited | Limited | Historical data availability varies |
Key Insight: Use get_matchups() (default) or get_scoreboard() for simple matchup data - works for ALL years 2011-2025. Use get_matchups(include_lineups=True) or get_enhanced_boxscores() for detailed player lineups - only works for recent years (currently 2019-2025, rolling ~7 year window).
Other Notes
- Default league (323196) is the RFFL public league
- For load testing or guaranteed fresh data, disable caching with
ENABLE_CACHE=false - Cookies expire periodically - if you get auth errors, refresh your ESPN_S2 and SWID values
Example Usage
# Get current week matchups
get_matchups(week=5)
# Get league standings
get_standings()
# Check cache performance
get_cache_stats()
# Returns: {"enabled": true, "hits": 12, "misses": 3, "total_requests": 15, "hit_rate_percent": 80.0, "cached_leagues": 1}
# Force fresh data
clear_cache()
Testing Locally
# Install dependencies
pip install -r requirements.txt
# Set up authentication (create .env file with your ESPN cookies)
# See "Getting ESPN Authentication Cookies" section above for instructions
# Run with stdio transport
python rffl_mcp_server.py
# Run with HTTP transport
MCP_TRANSPORT=http PORT=8080 python rffl_mcp_server.py
# Test historical data access (2022)
ESPN_YEAR=2022 python rffl_mcp_server.py
# Test with caching disabled
ENABLE_CACHE=false python rffl_mcp_server.py
# Test with debug logging
LOG_LEVEL=DEBUG python rffl_mcp_server.py
Advanced FastMCP Cloud Features
Beyond the basic tools, FastMCP Cloud provides three powerful capabilities to extend your server: Resources, Resource Templates, and Prompts.
Resources
What they are: Resources expose static or dynamic data/content that MCP clients can access for context. Think of them as "readable assets" your server makes available.
Use cases for rffl-mcp-server:
-
League Documentation
- Expose
league-rules.mdasresource://league/rules - Scoring system explanations
- Historical league notes
- Expose
-
Schema Definitions
- Team structure schemas
- Player stat definitions
- API response formats
-
Cached Reports
- Pre-generated season summaries
- Historical comparisons
- League analytics dashboards
How to implement:
from fastmcp import FastMCP
mcp = FastMCP("rffl-mcp-server")
@mcp.resource("league://rules")
def get_league_rules():
"""Expose league rules as a resource"""
return {
"uri": "league://rules",
"name": "RFFL League Rules",
"mimeType": "text/markdown",
"text": "# League Rules\n\n## Scoring System\n..."
}
@mcp.resource("league://history/{year}")
def get_season_summary(year: int):
"""Dynamic resource for season summaries"""
# Fetch data for that year
league = League(league_id=323196, year=year, espn_s2=ESPN_S2, swid=SWID)
summary = f"# {year} Season Summary\n\nChampion: {league.standings()[0].team_name}..."
return {
"uri": f"league://history/{year}",
"name": f"{year} Season Summary",
"mimeType": "text/markdown",
"text": summary
}
In FastMCP dashboard: Resources appear automatically from your manifest after deployment.
Resource Templates
What they are: Parameterized resources that generate content dynamically based on URI patterns. Like resources, but with variable paths.
Use cases for rffl-mcp-server:
-
Dynamic Player Cards
player://stats/{player_id}- Generate player stat cards on-demandplayer://history/{player_id}/{year}- Historical player data
-
Team Dashboards
team://overview/{team_id}- Real-time team dashboardteam://matchup/{team_id}/{week}- Specific matchup analysis
-
Week-by-Week Reports
week://summary/{year}/{week}- Auto-generated week summariesweek://boxscore/{year}/{week}/{matchup_id}- Detailed boxscores
How to implement:
@mcp.resource_template("player://stats/{player_id}")
def player_stats_card(player_id: int):
"""Generate player stat card on-demand"""
league = _get_league()
player = league.player_info(playerId=player_id)
card = f"""
# {player.name} - {player.position}
**Team:** {player.proTeam}
**Total Points:** {player.total_points}
**Average:** {player.avg_points}
**Projected:** {player.projected_total_points}
"""
return {
"uri": f"player://stats/{player_id}",
"name": f"{player.name} Stats",
"mimeType": "text/markdown",
"text": card
}
In FastMCP dashboard: Templates appear with their URI pattern (e.g., player://stats/{player_id}).
Prompts
What they are: Pre-configured AI instructions that guide how MCP clients (like ChatMCP) interact with your tools. They improve natural language understanding and tool calling accuracy.
Use cases for rffl-mcp-server:
-
Improve Tool Discovery
- Teach AI when to use each tool
- Explain parameter meanings
- Provide usage examples
-
Handle Natural Language
- "Show me 2016 standings" →
get_standings(year=2016) - "Week 5 matchups" →
get_matchups(week=5) - "Last year's champion" →
get_standings(year=2024)+ extract winner
- "Show me 2016 standings" →
-
Add Fantasy Football Context
- Scoring system explanations
- Common abbreviations (RB, WR, PPR, etc.)
- League-specific rules
How to implement:
@mcp.prompt("fantasy-expert")
def fantasy_football_assistant():
"""Prompt to guide AI behavior"""
return {
"name": "fantasy-expert",
"description": "Fantasy football expert assistant for RFFL",
"arguments": [],
"prompt": """
You are a fantasy football expert assistant for the RFFL league (ID: 323196).
TOOL USAGE GUIDELINES:
- When user mentions a year (e.g., "2016", "last year"), extract it and pass as year parameter
- When user mentions a week (e.g., "week 5", "this week"), pass as week parameter
- Default to current season (2025) if no year specified
- Historical data (2018-2022) requires authentication
NATURAL LANGUAGE MAPPING:
- "standings" / "rankings" → get_standings(year=X)
- "matchups" / "games" / "scores" → get_matchups(week=X, year=Y)
- "boxscores" / "detailed scores" → get_enhanced_boxscores(week=X, year=Y)
- "power rankings" → get_power_rankings(week=X, year=Y)
- "player stats" / "lookup [player]" → get_player_info(name="player", year=Y)
COMMON ABBREVIATIONS:
- QB=Quarterback, RB=Running Back, WR=Wide Receiver, TE=Tight End
- PPR=Points Per Reception, FLEX=Flexible position slot
- IR=Injured Reserve, BYE=Team bye week
EXAMPLES:
User: "Show me 2016 standings"
→ Call: get_standings(year=2016)
User: "Week 5 matchups for 2022"
→ Call: get_matchups(week=5, year=2022)
User: "Who won last year?"
→ Call: get_standings(year=2024) → Extract rank 1 team
Always be helpful, concise, and fantasy-football savvy!
"""
}
@mcp.prompt("historical-analysis")
def historical_data_prompt():
"""Prompt for historical data queries"""
return {
"name": "historical-analysis",
"description": "Analyze historical RFFL data across multiple seasons",
"arguments": [
{"name": "start_year", "description": "First year to analyze", "required": True},
{"name": "end_year", "description": "Last year to analyze", "required": True}
],
"prompt": """
Analyze RFFL historical data from {start_year} to {end_year}.
STEPS:
1. For each year in range, call get_standings(year=YEAR)
2. Extract champion, runner-up, top scorer
3. Identify trends (repeat champions, scoring increases, etc.)
4. Summarize findings in a markdown table
Use get_matchups() and get_power_rankings() for deeper insights if needed.
"""
}
In FastMCP dashboard: Prompts appear with name, description, and can be invoked by AI clients.
Implementation Priority
Start with Prompts (Highest Impact):
- Create
fantasy-expertprompt to improve ChatMCP interactions - Test natural language queries immediately
- Iterate based on what ChatMCP still struggles with
Then Resources (Medium Effort, High Value):
- Add league rules/documentation as static resources
- Create season summary resources for each year
- Helps AI understand league context better
Finally Resource Templates (Advanced):
- Dynamic player cards
- On-demand team dashboards
- Best for power users who want deep integrations
Example: Adding Your First Prompt
1. Update rffl_mcp_server.py:
@mcp.prompt("fantasy-expert")
def fantasy_football_assistant():
return {
"name": "fantasy-expert",
"description": "Fantasy football expert for RFFL with smart tool calling",
"arguments": [],
"prompt": """
You are an expert RFFL fantasy football assistant.
When users mention years (2016, 2022, "last year"), extract and use as year parameter.
When users mention weeks (week 5, "this week"), extract and use as week parameter.
Tool mapping:
- "standings"/"rankings" → get_standings(year=X)
- "matchups"/"games" → get_matchups(week=X, year=Y)
- "boxscores" → get_enhanced_boxscores(week=X, year=Y)
Always include year in responses to avoid confusion.
"""
}
2. Commit and push:
git add rffl_mcp_server.py
git commit -m "Add fantasy-expert prompt for improved natural language"
git push origin main
3. FastMCP auto-deploys → Prompt appears in dashboard → ChatMCP uses it automatically!
Architecture
Built with:
- FastMCP - Model Context Protocol server framework
- espn-api - ESPN fantasy football API wrapper
- Python 3.8+ compatible
License
MIT
See Also
- - Complete API reference for all 11 tools
- - Developer guidance and architecture details
- - FastMCP Cloud deployment checklist
- - Comprehensive testing strategy
- - Current health status (94.1% pass rate)