FSPN's analytics are available as tools inside Claude through a hosted MCP server: ask Claude about your week, your matchup, a trade, the waiver wire or a player, and it calls the same engines the app runs on, scoped to your league. Everything the app can work out, Claude can work out with you, in your own words. This page is the step-by-step; the wire-level detail (rate limits, error codes, the JSON API alongside it) is in the API reference, and the rest of the app is in the user guide.
What you need
- The Sync + API plan ($6/month), or a Hall of Fame seat, an Official's badge or the Owner's — those have it included. The plan is checked on every call, so a lapsed subscription pauses the connector until it is renewed.
- A way for Claude to prove it is you. There are two:
- Sign in and approve (OAuth). Claude Desktop's own connector dialog, the Claude and OpenAI connector directories and any MCP client that speaks OAuth 2.1 take the URL
https://fspn.ragealley.com/mcp, send you to FSPN to sign in, and show a consent page. Nothing to copy. What the app gets is read-only — every tool that reads, never the console — and it appears on Account → API & MCP as a connected app you can revoke. - A personal API token, for Claude Code, scripts and clients that take a header. Sign in, open Account → API & MCP → Create token, give it a name and pick a scope. The secret (
sa_…) is shown once — copy it then. Up to five live tokens per account.
- Sign in and approve (OAuth). Claude Desktop's own connector dialog, the Claude and OpenAI connector directories and any MCP client that speaks OAuth 2.1 take the URL
- For a personal token, the scope decides what Claude may do on your behalf:
read— every tool that reads: your week, the simulator, the trade finder, playoff odds, player cards, the digest, the games. This is the right choice for almost everyone (and the only thing an OAuth-connected app gets).read_write— everything inread, plus therun_commandtool (the data console:team-move,team-sync,fantasy-score,week-updateand the other CLI verbs) and, on the JSON API,POST /api/team/move,POST /api/team/import,/api/runand/api/run-stream. Pick this only if you want Claude to change your roster or run jobs.
Tokens and connected apps never reach the Account, billing or Ops pages, so a leaked token cannot change your plan or mint more tokens — but it can read your league, so treat it like a password and revoke it from the same card if it gets out.
Claude Desktop and the connector directories (OAuth)
This is the path with nothing to paste. It works in Claude Desktop's Add custom connector dialog, in the Claude web app's connector settings, from the Claude and OpenAI connector directories once FSPN is listed there, and in any MCP client that supports OAuth.
- Add a connector with the URL
https://fspn.ragealley.com/mcp(in Claude Desktop: Settings → Connectors → Add custom connector, name it FSPN). No header, no token. - The client discovers FSPN's authorization server by itself and opens a browser window on FSPN. Sign in if you are not already — the page brings you straight back afterwards.
- The consent page names the app (Claude Desktop, ChatGPT, …) and what it may do: read your leagues, teams, projections and the player data; never change your roster, run console commands, see billing or manage tokens. Press Approve (or Deny — the app is told and nothing is stored).
- Back in the client, the connector is connected; ask something that needs your league.
The app receives an access token that lives an hour and a refresh token that renews it for up to 30 days of inactivity, silently. Approving the same app again replaces its earlier connection; you can hold up to ten connected apps. Disconnect from Account → API & MCP — the row is labelled connected app and Revoke cuts it off at once; the client will ask you to connect again next time. If you see Sync + API needed instead of the consent page, the account approving has no plan that includes the API — get it on the Account page and connect again. A connected app is read-only by design; if you want Claude to change rosters or run jobs, use a read_write personal token in Claude Code instead (a write scope for connected apps is not offered yet).
Claude Code
- Register the server with the current CLI syntax for streamable HTTP with a header — keep it exactly, replacing
sa_…with your token:
claude mcp add --transport http fspn https://fspn.ragealley.com/mcp --header "Authorization: Bearer sa_…"
- Start (or restart) Claude Code and run
/mcp.fspnshould be listed as connected, with its tools. - Ask something that needs your league:
What's my win probability this week, and is there a bench swap that improves it?
Claude calls get_my_week (and simulate_matchup if it wants the distribution) and answers from your league's own numbers. A token is scoped to your first league unless you say otherwise; to point it at another of your leagues, register the URL with ?league=<league_id> on the end (GET /api/me with the same header lists your leagues and their ids).
Claude Desktop with a personal token (the bridge)
The connector dialog above is the way to go on Desktop. If you would rather use a personal token there — for a read_write scope, or on a machine where the browser sign-in is awkward — the connector goes through the mcp-remote bridge, a small stdio-to-HTTP relay that runs on your machine and adds the header for you. It needs Node.js (for npx).
- Open the config file: Settings → Developer → Edit Config (it opens
claude_desktop_config.json). - Add the server (merge with any
mcpServersyou already have):
{
"mcpServers": {
"fspn": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://fspn.ragealley.com/mcp",
"--header", "Authorization: Bearer sa_…"]
}
}
}
- Save and fully quit and reopen Claude Desktop. The tools appear under the connector list in a new chat.
Your token sits in that file in plain text. Keep the file private (it lives in your user profile, not in any synced folder), and revoke the token if you ever share the machine.
Other MCP clients
Any client that speaks MCP over streamable HTTP works the same way:
- Endpoint:
POST https://fspn.ragealley.com/mcp,Content-Type: application/json, one JSON-RPC 2.0 message per request (or an array as a batch). The reply is a plain JSON body; a notification is acknowledged with202and no body. - Auth: the
Authorization: Bearer sa_…header on every request — the only credential the endpoint reads (cookies are ignored). Nothing is kept between requests, so there is no session id to track. A client that supports OAuth gets the same kind of token by itself: a401nameshttps://fspn.ragealley.com/.well-known/oauth-protected-resource, which points at the authorization server (registration, the consent page, the token endpoint — the contract is in the API reference). - Protocol versions:
2025-06-18,2025-03-26and2024-11-05are accepted (aninitializenaming one of those gets it echoed back; anything else gets the newest); every response carries anMCP-Protocol-Versionheader. Methods:initialize,tools/list,tools/call,ping. - The server opens no stream of its own:
GET /mcpanswers405. - League:
?league=<league_id>on the URL, or anX-Leagueheader, picks one of your leagues; omitted, the first membership is used.
By hand, this lists the tools:
curl -X POST https://fspn.ragealley.com/mcp \
-H "Authorization: Bearer sa_…" -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
The tools
These are the 36 tools tools/list returns — the same set the local stdio server exposes, so anything written against one works against the other. Weeks and seasons are the NFL's; when a tool takes season and week, give the ones you are asking about.
My team and my league
Everything here reads the league the token is scoped to (your Sleeper or ESPN league as last synced).
get_my_week— Everything about my fantasy team this week: matchup with both lineups and win probability, waiver upgrades that beat a current starter, roster trends, and storylines. save=true stores the matchup for tracking. Parameters:season,week,save(required:season,week).get_team_matchup— Project the saved fantasy league's weekly matchup: best lineups for both sides, predicted winner, margin, and win probability. save=true stores the prediction for tracking. Parameters:season,week,save(required:season,week).simulate_matchup— Simulate my fantasy matchup thousands of times from each starter's floor-ceiling band: win probability, margin percentiles, blowout and nail-biter odds, and swing players (who moves my win probability most). Parameters:season,week,sims(required:season,week).get_slate— This week's NFL games as a fantasy watch guide: Elo win probability and implied spread next to the market line, environment flags (altitude, cold, wind, indoors), rostered fantasy points at stake, my starters and my opponent's starters in each game, and the must-watch game. Parameters:season,week(required:season,week).get_waiver_wire— Top projected players not rostered by anyone in the saved league, per position. Parameters:season,week,top(required:season,week).get_waiver_advice— Waiver advisor for my fantasy team: in FAAB leagues, bid suggestions per target from the remaining budget, the league's own bid history and how many rivals can afford to outbid; in priority leagues, claim / wait / skip with the chance the player clears the teams ahead of me. Each target carries this week's best-lineup gain and a rationale. Parameters:season,week,top(required:season,week).get_sleeper_edges— Where our projections disagree with Sleeper's for this week, net of the league-wide bias: my roster, the opponent, free agents we like far more (claim), others' players we like far more (trade for), and traps. Parameters:season,week,top(required:season,week).find_trades— Trade finder for my fantasy team: deals with any (or one) partner that improve both teams' best lineups rest-of-season — give/get lists, both sides' rest-of-season, this-week and playoff gains, a verdict (win-win / fair / ask) and a pitch line for the partner. Screened by marginal value, finalists evaluated exactly. Parameters:season,week,with,top(required:season,week).evaluate_trade— Trade analyzer for my fantasy team: both sides' best-lineup totals this week, rest-of-season, and in the fantasy-playoff weeks, with a verdict. give/get are comma-separated player names; with is the other team. Parameters:season,week,with,give,get(required:season,week,with,give,get).get_rest_of_season— Rest-of-season schedule strength for a fantasy roster: per-week opponent multipliers, ROS points, average and fantasy-playoff-week multipliers. names adds extra players (e.g. trade targets). Parameters:season,week,team,names(required:season,week).get_playoff_odds— Playoff odds for every team by simulating the remaining schedule: expected wins, P(playoffs), P(bye), P(#1 seed), and how this week's result swings my odds (must-win flag). Parameters:season,week,simulations,seed(required:season,week).get_all_play— All-play record and Luck index for my fantasy league from the synced results: each team's score against every other team every week, expected wins, luck (actual minus expected), points rank vs standings rank, luckiest and unluckiest teams, and a headline sentence for my team. Parameters:season.get_league_storylines— Fantasy-league storylines for a week (from the synced Sleeper league): toss-ups and mismatches, power rankings, streaks, unlucky losses, grudge rematches, all-in rosters, draft steals, hot and cold players. Parameters:season,week,top(required:season,week).get_weekly_digest— The weekly digest: matchup, power rank, playoff odds, top/unlucky scores, trends, upgrades, storylines — as data plus a share-ready SVG infographic. focus=league (broadcast card for the whole league, default) or team (my cockpit); format=card (1080x1350) or story (1080x1920). Parameters:season,week,focus,format(required:season,week).get_daily_matchup— Daily-sport league (sport nba|mlb): best lineup for every day of a window from the stored league doc (team import --sport nba), vs an opponent with win probability. Parameters:sport,start,end,team,opponent,week.
Players and projections
The shared NFL data the Tuesday job publishes, priced with your league's scoring.
get_weekly_projections— Full-week NFL fantasy projections (league scoring): top players per position and overall, with matchup multipliers, floor/ceiling ranges, usage trends, and injury flags. save=true stores them for later accuracy evaluation. Parameters:season,week,models,top_position,top_overall,save(required:season,week).get_player_card— Everything about one NFL player: this week's matchup projection with floor/ceiling, rank and percentiles within his position group, trend verdict and series, splits by opponent / opposing head coach / away venue, recent games, injury status, fantasy owner, and storylines. Parameters:name,season,week(required:name).search_players— Find real player names (avoids spelling errors in other tools). Case-insensitive substring match. Parameters:query,limit(required:query).get_player_trend— Is a player progressing or regressing? Give name (one player's series + verdict), or group (QB/RB/WR/TE/K/DST/LB/DB movers), or roster=true (my fantasy roster). Recent-vs-prior averages, trajectory slope, and season-over-season averages. Parameters:name,group,roster,seasons,window,top.get_performance_field— Compare every player in a position group against the field: per-game metrics (points, usage, yards, tackles, ...) with percentile ranks, plus floor/ceiling and recent form. Groups: QB, RB, WR, TE, K, DST, LB, DB. Parameters:group,seasons,min_games,top(required:group).get_draft_board— Season-long fantasy draft board in value-over-replacement order, with per-position boards, roster-mapped teams, schedule-strength, rookies, and snake-pick numbers. Parameters:season,league_size,slots,pick,top.get_window— Daily-sport week ahead (sport nba|mlb): each player's expected games in a date window × recency-weighted fantasy points per game, with a mild Elo opponent factor. Defaults to this Monday–Sunday. Filter by team or names (comma). Parameters:sport,start,end,team,names,top.
Games, series and ratings
The Elo models for NFL, NBA, MLB and soccer.
predict_game— Elo-driven game prediction. NFL supports weather/wind; soccer returns home/draw/away probabilities; MLB (sport=mlb) takes the two probable starters (home_pitcher, away_pitcher) and adjusts for them. Parameters:sport,home,away,weather,wind,league,neutral,home_pitcher,away_pitcher(required:home,away).predict_series— Best-of series odds (MLB wild card / division / LCS / World Series, or NBA playoffs): exact probability each side wins the series from the single-game Elo probabilities at each venue, the distribution of series length, and the next game's venue. wins = games already won, e.g. '2-1'. Parameters:sport,home,away,best_of,wins,pattern,home_pitcher,away_pitcher(required:home,away).get_postseason— MLB postseason board: every series in the stored schedule (wild card, division, LCS, World Series) with games played, current tally, our series odds for both sides and the next game. Parameters:season.get_research_packet— Broadcast-style pregame research packet (Markdown): model call, market line, form guide, head-to-head, players to watch, and granular storylines. Parameters:sport,home,away,season_type,weather,temperature(required:home,away).get_storylines— League-wide NFL/NBA/soccer narrative angles from stored results: streaks, form, head-to-head, hot/cold players. team narrows to one team. Parameters:sport,team,top,season_type.
Model accuracy and data quality
evaluate_projections— Score saved weekly projections against actual results: MAE and bias per position, week by week. Parameters:season,week(required:season).run_backtest— Temporal-holdout backtest of the projection models (frozen or walk-forward), optionally per position group. Parameters:train_through,test_years,mode,by_position,sample,position,seed.validate_data— Data-quality report before publishing anything: duplicate games, unscored rows, suspicious stat lines, with error/warning severity. Parameters:sport.get_status— Database contents: game/log/roster/projection counts per sport and trained-rating coverage.
Play
The daily games, same puzzle for the whole league.
play_redacted— Redacted — the daily mystery-player game (sport nfl|nba|mlb). Returns today's puzzle (or a practice one forpractice=seed) with clues revealed throughattemptmisses. Guess with guess_redacted (pass the same sport). Six guesses. Parameters:sport,date,practice,attempt.guess_redacted— Grade a Redacted guess: position/team/points tiles, the next clue, and the reveal on a win or the sixth miss. attempt counts this guess (1-6). Parameters:name,attempt,date,practice,sport(required:name,attempt).play_trivia— Stat Trivia (sport nfl|nba|mlb): ten multiple-choice questions generated from the database (season averages, best games, nemesis splits, defenses, records, final scores, Elo; NFL adds wind and your league's draft). Same set for the league each day; practice=seed for more. Answers withheld — use answer_trivia. Parameters:sport,date,practice.answer_trivia— Grade a Stat Trivia answer: q (0-based question index) and choice (0-3). Returns correct?, the right option, and the explanation. Parameters:q,choice,date,practice,sport(required:q,choice).
The console (read_write only)
run_command— Run any other CLI command by name with options — full parity with the terminal. Commands include: load-nflverse, load-espn, ratings-train, fantasy-score, fantasy-optimize, analyze-environment, analyze-coach, analyze-edge, ml-regression, ml-over, predict-player, research-storylines, research-player, research-validate, team-show, team-move, team-project, team-byes, team-evaluate, team-sync (Sleeper: options username or league-id, write-scoring), week-update. Returns the command's console output. Parameters:command,options(required:command).
Limits and safety
- 120 requests a minute per token (a sliding 60-second window). Past it the answer is
429with aRetry-After: 60header. Two tokens on one account count separately. - Request bodies over 256 KiB are refused with
413; a tool call is a few hundred bytes. - Every call is scoped to your league and your membership in it. Naming a league you are not a member of is
403; the Owner and Officials may look at any league. - Every
tools/callis written to the audit log asmcp.callwith the tool name (never the arguments) and the token that made it; the Officials read that log on/ops. Creating and revoking tokens is logged too (token.create,token.revoke). - Revoke from Account → API & MCP — personal tokens and connected apps alike. The connector stops at once. Signing out does not revoke tokens; deleting the account does. A connected app's access token expires after an hour and its refresh token after 30 days unused; a refresh token that is presented twice (someone else got a copy) revokes the whole connection.
- The hosted endpoint is hosted-only: on a local install
POST /mcpanswers401and points at the stdio server (python -m sports_analytics.main --db … mcp).
Troubleshooting
| You see | It means | Do this |
|---|---|---|
401 with error -32001 (and a WWW-Authenticate: Bearer header) | No token was sent, or the token is mistyped or revoked: That API token isn't valid — it may have been revoked. | Check the header is exactly Authorization: Bearer sa_…; if in doubt, create a new token and revoke the old one. |
403 with error -32003: The API needs the Sync + API plan. | The token is real but the plan has lapsed (or the account never had it). | Account → Plan & billing → Get Sync + API. Tokens survive and resume as soon as the plan is active. |
A tool result with isError: true saying This token is read-only — create one with the read_write scope to make changes. | You called run_command (a write) with a read token. The call itself was accepted (HTTP 200); the tool refused. | Create a read_write token if you really want Claude changing rosters or running jobs. |
429 with error -32029 | More than 120 calls in the last minute from this token. | Wait for Retry-After (60 seconds). Split heavy scripting across tokens if you must. |
400 with error -32700 | The body was not JSON. | Send Content-Type: application/json and a JSON-RPC 2.0 object. |
405 on GET /mcp | The endpoint is POST-only; there is no server-to-client stream. | Point the client at the URL as a streamable-HTTP server, not an SSE one. |
413 | The request body was over 256 KiB. | Trim the arguments. |
Error -32601 Unknown method | The method is not one of initialize, tools/list, tools/call, ping. | Resources, prompts and sampling are not offered. |
| The tools answer but the numbers are stale | The league is read as last synced. | Sync now on the Account page (Sync plan, once every five minutes per league), or wait for the Tuesday / Thursday / Sunday run. |
| Claude Desktop shows nothing after editing the config | The JSON did not parse, npx is not on the path, or Desktop was not fully quit. | Validate the file, install Node.js, then quit Desktop from the menu bar / tray and reopen it. |
| The connector's sign-in shows Sync + API needed | The account you signed in with has no plan that includes the API. | Account → Plan & billing → Get Sync + API (or sign in with the account that has it), then connect again. |
| That connection can't start — Unknown client_id or redirect_uri doesn't match | The client's registration expired or was made against another server (a stale cache after a reinstall, or a self-hosted copy). | Remove the connector in the client and add it again; it re-registers. |
| The connector worked, then asks you to connect again | The refresh token expired (30 days unused), you revoked the app on the Account page, or a refresh token was replayed and the connection was revoked for safety. | Approve again — a fresh connection replaces the old one. |
| The consent page's Approve goes nowhere | The browser blocked the redirect back to the app (a strict extension) or the app's callback window closed. | Retry from the client; the code is good for ten minutes and is single-use. |