DanielTomaro13/sportsdata-mcp

πŸƒ Sports🟒 Verified Active
0 Views
0 Installs

🐍 🏠 🍎 πŸͺŸ 🐧 - Live sports data and cross-book betting odds as 500 tools across 28 providers β€” 10+ bookmakers (Sportsbet, TAB, Betfair, Pinnacle, FanDuel…), official league feeds (NBA, MLB, EPL, AFL, NRL, F1 telemetry), racing, and Kalshi/Polymarket prediction markets. Zero-config setup, no API keys for most providers. MIT.

Quick Install

One-Click IDE Configuration
claude_desktop_config.json
{
  "mcpServers": {
    "danieltomaro13-sportsdata-mcp": {
      "command": "npx",
      "args": [
        "-y",
        "danieltomaro13-sportsdata-mcp"
      ]
    }
  }
}
Or

Using an AI coding agent (Claude Code, Cursor, etc.)? Copy a ready-made prompt that tells it to fetch the setup instructions and install this server for you.

Documentation Overview

sportsdata-mcp

CI Release License: MIT sportsdata-mcp MCP server

Add to Cursor

Ask your AI which bookmaker is paying more β€” and get a real answer.

Free & open source (MIT). ~500 tools across 28 providers in Claude Desktop, Cursor, or any MCP client. uvx sportsdata-mcp serve and you're done.

You: Which book has the best price on Parramatta v Penrith, and how big is the spread?

Claude queries five books at once and comes back with:

BookEelsPanthers
Betfair1.186.20
BetR1.175.00
PointsBet1.174.80
Pinnacle1.174.78
Sportsbet1.194.75

Real captured odds. The same bet on Penrith pays $6.20 at Betfair and $4.75 at Sportsbet β€” a 30% spread on identical risk. That gap is invisible unless something is reading every book at once.

What this one is for: comparing prices, not just fetching scores. Plenty of sports MCP servers will get you fixtures and standings. This one is built around disagreement between books β€” eleven bookmakers, the Betfair exchange, and two prediction markets (Kalshi, Polymarket) side by side on the same market, plus fourteen official league/stats feeds. Deep on AU/NZ books (Sportsbet, TAB, Ladbrokes, PointsBet, BetR, Dabble) and on racing β€” thoroughbred, greyhound and harness with tote pools and exchange money β€” which most catalogues skip entirely. Capability tags make providers interchangeable, so "compare odds across books" is one question rather than twenty-eight integrations.

Built on this server

Two live terminals, both open source, both running on nothing but these tools:

sportsdata-ai.com/sports β€” prediction markets + exchange as a de-vigged sharp line, every book measured against it.

Sports board

sportsdata-ai.com/board β€” racing money flow: which runners are firming, fair price vs the field, win%/ROI scorecard.

Racing board

Try these once it's installed

  • "Compare head-to-head odds across every book for tonight's NRL games."
  • "Which AFL games have the biggest price disagreement between bookmakers?"
  • "What does Betfair imply for the Panthers vs what Sportsbet is offering?"
  • "Show me Pinnacle's line on every MLB game today."
  • "Pull the ladder and last five results for Hawthorn."

An MCP server that exposes sports-data APIs (bookmakers, league/governing-body feeds, aggregators) as tools, configurable so you only load the tool groups you need. A capability-tag system makes tools from different providers interchangeable wherever they answer the same question β€” so the model can compare odds across bookies or stats across data sources with one discovery call.

The catalogue spans bookmakers, league/governing-body feeds, and stats aggregators, and it keeps growing. New providers are added by dropping a YAML spec into src/sportsdata_mcp/specs/ β€” the engine needs no code changes β€” so the exact provider and tool counts move over time. Run sportsdata-mcp list-groups for the live inventory, and three meta-tools (group discovery, capability lookup, resource listing) are always on regardless of what you enable.

Install

One-liner (any MCP client config, via uv):

uvx sportsdata-mcp serve        # or: pip install sportsdata-mcp

Prebuilt app (no Python needed): grab the latest release (macOS + Windows), unzip, and run sportsdata-mcp setup β€” it writes the config for Claude Desktop / Cursor for you. The macOS build is unsigned for now: right-click β†’ Open the first time.

From source:

git clone https://github.com/DanielTomaro13/sportsdata-mcp.git
cd sportsdata-mcp
pip install -e .                # add ".[dev]" for the test + lint toolchain

Quickstart

sportsdata-mcp version          # print version info
sportsdata-mcp list-groups      # see every available tool group
sportsdata-mcp lint             # validate the packaged specs
sportsdata-mcp doctor           # probe enabled groups for reachability + auth
sportsdata-mcp serve            # start the MCP stdio server (default command)
sportsdata-mcp update-specs     # OTA-refresh provider specs (signed bundle); --clear reverts

Provider endpoints drift (e.g. Entain rotates its GraphQL persisted-query hashes). update-specs fetches a signed spec bundle and applies it into an overlay under ~/.sportsdata/spec-overlay, which the loader prefers over the packaged copy β€” so a drift fix doesn't need a whole new app build. The bundle is Ed25519-verified against a baked key (a product build refuses an unsigned/forged bundle; anti-rollback refuses a stale replay). Publish one with scripts/publish-spec-bundle.py; point --url / $SPORTSDATA_SPEC_FEED_URL at the asset. Restart the server after applying.

Enable tool groups with a config file or the SPORTSDATA_MCP_GROUPS env var:

SPORTSDATA_MCP_GROUPS="afl.public.core,sportsbet.racing,entain.graphql" sportsdata-mcp serve

See examples/ for Claude Desktop / Claude Code config snippets, a worked cross-bookie odds-comparison prompt, and an NBA shot-chart + box-score walkthrough that shows the nba_stats_call dispatcher pattern end to end.

Configuration

Config is resolved in this order (first hit wins):

  1. --config <path> flag
  2. $SPORTSDATA_MCP_CONFIG
  3. ./sportsdata-mcp.yaml
  4. ~/.config/sportsdata-mcp/config.yaml
  5. built-in defaults
# sportsdata-mcp.yaml
enabled_groups:
  - afl.public.core
  - sportsbet.racing
  - entain.graphql

providers:                      # all optional; sensible defaults apply
  sportsbet:
    request_timeout_seconds: 30
    rate_limit_rps: 10          # sustained requests/sec (token bucket)
    max_response_bytes: 0       # 0 = no cap (default); set a positive byte count to guard context

secrets: {}                     # for authenticated providers; prefer env vars in prod

A provider whose auth reads env: SOME_VAR is satisfied by the real environment variable first, then by a secrets: { SOME_VAR: "..." } entry of the same name (a local-dev convenience β€” keep real secrets in the environment in production).

Environment variables

VariableEffect
SPORTSDATA_MCP_GROUPSComma-separated group list; overrides enabled_groups.
SPORTSDATA_MCP_CONFIGPath to a config file (see resolution order above).
SPORTSDATA_MCP_MAX_BYTESGlobal response-size cap in bytes for every provider that doesn't set its own max_response_bytes. 0 (the default) means no cap.
SPORTSDATA_LICENSEDormant β€” the product is free; nothing requires a licence. The signed-entitlement machinery remains for anyone self-hosting gated premium feeds (see below).
SPORTSDATA_ENTITLEMENT_URL / SPORTSDATA_ENTITLEMENT_PUBKEYOnly relevant with the dormant entitlement gate above. Normally unset.

The (dormant) entitlement gate

This project used to be a paid product. It's free now β€” no licence exists or is needed, and every group serves by default β€” but the signed-entitlement machinery (Ed25519-verified feed grants, offline caching, 15-min revalidation) is kept dormant rather than deleted: it's tested, harmless when unset, and useful to anyone self-hosting this server who wants to gate premium feeds for their own users. Set SPORTSDATA_LICENSE + SPORTSDATA_ENTITLEMENT_URL against your own issuing service to activate it; leave them unset (the default) and nothing changes.

Keyed feeds. A few providers need an upstream credential you supply yourself β€” e.g. DATAGOLF_KEY for DataGolf, X_BEARER_TOKEN for Twitter/X. Everything else needs no key at all.

Meta-tools (list_available_groups, list_tools_by_capability, list_resources) are always registered regardless of what is enabled, so a fresh install can still guide the model to turn groups on.

On the response-size cap. There is no cap by default β€” every tool returns whatever the upstream API sends. If you want to guard the model's context window you can opt in to a cap: precedence is providers.<id>.max_response_bytes > SPORTSDATA_MCP_MAX_BYTES > the default (0, unlimited). Be aware that very large payloads (e.g. Sportsbet's full *_event_markets firehose, ~2 MB) won't fit in Claude's ~200 K-token context regardless β€” for those, prefer a narrower tool such as sportsbet_sports_card with includeTopMarkets: true.

Tool groups

Run sportsdata-mcp list-groups for live counts and descriptions.

AFL β€” api.afl.com.au

GroupToolsNotes
afl.public.core22Competitions, seasons, rounds, fixtures, ladders, match stats
afl.public.broadcasting9Broadcast regions, guides, providers
afl.public.content8News/articles, videos, photos
afl.premium.cfs1CFS premium ops β€” needs the anonymous x-media-mis-token
afl.premium.statspro1StatsPro ops β€” needs the x-media-mis-token
afl.premium.keyserver1HLS video URL signing

Sportsbet β€” sportsbet.com.au

GroupToolsNotes
sportsbet.racing15Race meetings, racecards, results, futures, SRMs
sportsbet.sports14Sport events, markets, prices, SGMs
sportsbet.cross12Live status, commentary, ladders, promos, video
sportsbet.results2Resulted events by date
sportsbet.graphql1Persisted GraphQL gateway (apigw/sportsbook/graph)

Entain / Ladbrokes β€” ladbrokes.com.au

GroupToolsNotes
entain.rest13Navigation quick-links and REST surfaces
entain.graphql1127 persisted GraphQL ops (gql/router)
entain.cdn1Contentful CMS entries (promotions, major-event nav)

PointsBet β€” pointsbet.com.au

GroupToolsNotes
pointsbet.sports10Sports catalogue, competition/event feeds, full event markets, in-play, search
pointsbet.racing11Meetings, racecards, results, futures, SRMs, tips, form
pointsbet.content3Promotions, promo-code splash, + pointsbet_content_call over the static CMS/nav assets

TAB β€” tab.com.au

GroupToolsNotes
tab.racing9Dates, meetings, racecards (fixed + parimutuel), form, next-to-go, jackpots, futures
tab.sports9Sports/competitions tree, full match markets + SGM, focused match markets, next-to-go, results, multi-builder
tab.discovery4Featured/live recommendations + tab_cms_call over the CMS content feeds

Unibet β€” unibet.com.au

GroupToolsNotes
unibet.racing1unibet_racing_call β€” persisted-GraphQL: meetings, race cards, form, futures, specials
unibet.sport3unibet_kambi_call over the Kambi offering API (groups, events, bet offers, in-play, bet-builder) + live stats + odds ladder

BetR β€” betr.com.au (BlueBet platform)

GroupToolsNotes
betr.racing8Next-to-jump, today's/grouped racecards, race card, form, fluctuations, movers
betr.sport7Event types, competition categories, event markets, match detail, popular SGMs
betr.content4Promotions + featured racing + popular market links

Pinnacle β€” pinnacle.com (sharp odds)

GroupToolsNotes
pinnacle.sports13Sports/leagues, full + highlighted + live + per-league matchups, carousel, matchup detail, straight + parlay markets (American-odds prices)
pinnacle.reference4Enums, market-label dictionary, teaser definitions, API status

Betfair Exchange β€” betfair.com.au (exchange odds)

GroupToolsNotes
betfair.exchange3bymarket + byevent back/lay price feeds (the sharpest odds) + cash-out availability
betfair.navigation1bynode catalogue graph (sport β†’ meeting β†’ event β†’ market)
betfair.inplay5Live scores, event details, timeline (single + batch), scores+broadcast

Dabble β€” dabble.com.au (iOS app backend)

GroupToolsNotes
dabble.sport5Discover any competition (active list / name lookup / sports), then its fixtures (embedded markets + decimal odds) + the full per-fixture book (400+ markets + Pick'em props)

The Australian social-betting app's backend, read directly. Reached by posing as the iOS app β€” the spec bakes the app's User-Agent + x-device-id + x-app-version so the public feeds return JSON anonymously. AU-only and Cloudflare-fronted (403s from non-AU IPs, like the other AU books). Works for any competition β€” dabble_active_competitions lists the ~269 currently-bettable ones across all sports. Read-only odds β€” no bet placement. Composes with the other books via sport.event_markets / sport.prices.

SuperCoach β€” supercoach.com.au (News Corp / Champion Data fantasy)

GroupToolsNotes
supercoach.fantasy6One uniform surface across all 7 games (afl/nrl/epl/nba/nbl/nfl/bbl) Γ— 2 modes (classic + draft): competition state, the full per-player feed (price + ppts1 projection + ownership + matchup; draft adds predraft_rank), fixtures (with H2H odds), club + single-player catalogues, leagues

News Corp / Champion Data's salary-cap fantasy game. Every feed lives under /{year}/api/{sport}/classic/v1/… β€” pass sport (one of the seven) and year (the season key: current calendar year for afl/nrl, currently 2025 for the others, which run across the new year). No auth, not geo-blocked (runs in CI). The core supercoach_players feed is per-round and large (~1–3 MB); use ppts1 (the real projection), not ppts. Adds the fantasy / projections angle via stats.fantasy_projections alongside Data Golf. See documentation/SuperCoach.md.

NBL β€” nbl.com.au (Australian National Basketball League)

GroupToolsNotes
nbl.basketball14Seasons, teams, ladder, schedule (scores), players + rosters, per-player season stats + game-log box scores, team stats, season stat leaders (sortable), and news

The league's own site data API β€” a Redis-cached proxy ("rosetta") over Genius Sports stats at prod.rosetta.nbl.com.au/get/…. No token, but referer-gated (403s without an nbl.com.au Origin + Referer β€” both baked into the spec). Every response is enveloped {type, count, source, data:[…]}. Season-scoped by year (the season start year: 2025 = NBL26, current); stat-leaders takes the season UUID from nbl_seasons. Distinct from the SuperCoach nbl fantasy feed β€” this is the official box-score source. See documentation/NBL.md.

WTA β€” wtatennis.com (Women's Tennis Association, official)

GroupToolsNotes
wta.tennis8Official WTA API: singles/doubles rankings, player catalogue + profiles + match history, tournament calendar + per-edition results + entry lists (seeds)

The WTA's official data API (api.wtatennis.com/tennis/…) β€” public Spring REST, no auth/key, no geo-block, runs in CI. Rankings need type+metric (rankSingles+singles or rankDoubles+doubles); tournaments are keyed by tournamentGroup.id + year (Australian Open = group 901). Fills the tennis gap on the stats side, composing with the bookmakers' live tennis markets. See documentation/WTA.md. (ATP has no equivalent open API β€” atptour.com is Cloudflare bot-protected β€” so it isn't modelled.)

Racing and Sports β€” racingandsports.com.au

GroupToolsNotes
racingandsports.racing3Today's race meetings (all codes, verified) + sports match list + per-race odds (token)

Data Golf β€” datagolf.com (needs a key)

GroupToolsNotes
datagolf.general3Player list, tour schedule, current event field
datagolf.predictions11DG rankings, pre-tournament (+ archive) + in-play model probabilities, skill + approach-skill ratings, player/live SG decompositions, live strokes-gained, live hole stats, DFS projections
datagolf.betting3Outright + matchup + all-pairings odds across ~13 books (incl. model line)
datagolf.historical9Archived raw round data, event-level results (finishes/earnings/points), historical bookmaker odds (outrights + matchups) and DFS results

Needs a Data Golf API key in the DATAGOLF_KEY env var (a personal subscription key β€” sourced via the static_query auth scheme, never stored in the repo).

FanDuel β€” fanduel.com (US)

GroupToolsNotes
fanduel.racing4fanduel_racing_call (full-query GraphQL: featured/today races + odds, single-race card, tracks, pools, talent picks) + messages/quick-links/promotions
fanduel.sportsbook2fanduel_sb_call (REST: event pages + markets, in-play, promos, configs via the _ak key) + live scores

NRL β€” mc.championdata.com

GroupToolsNotes
nrl.public.core4Champion Data match centre: competitions, fixture, per-match player stats, app settings

Plus the nrl://stats/definitions resource (dictionary of every NRL stat code).

NBA β€” cdn.nba.com + stats.nba.com

GroupToolsNotes
nba.public.cdn5Open CDN JSON: today's scoreboard, full schedule, live box score + play-by-play, odds
nba.stats2nba_daily_lineups + nba_stats_call, the dispatcher over the 138-endpoint /stats/ API

nba_stats_call fronts the whole stats.nba.com /stats/ analytics surface (player/team dashboards, box scores v2+v3, shot charts, play-by-play, leaders, standings, draft, hustle, tracking, …). Browse every operation, its required params and its defaults in the nba://stats/operations resource.

ESPN β€” espn.com JSON feeds

GroupToolsNotes
espn.scores5Site API convenience endpoints: scoreboard, teams, standings, game summary, news
espn.site1espn_site_call β€” team detail, rosters, schedules, injuries, depth charts, transactions, history, athlete news, groups, rankings (10 ops)
espn.core1espn_core_call β€” the canonical $ref-linked model: events/competitions, odds, win-probability, plays, venues, drafts, coaches, calendar, transactions (37 ops)
espn.web1espn_web_call β€” site-wide search + common/v3 athlete views (7 ops)
espn.cdn1espn_cdn_call β€” the CDN live core feed: scoreboard/game/boxscore/playbyplay (4 ops)

All ESPN tools are parametric over sport + league slugs (e.g. football/nfl, basketball/nba, soccer/eng.1), so the five groups cover every league ESPN carries. Browse each dispatcher's operations in its espn://{site,core,web,cdn}/operations resource.

OpenF1 β€” api.openf1.org (Formula 1, no key)

GroupToolsNotes
openf1.reference3Grand Prix weekends (meetings), sessions (the fixtures feed), driver roster
openf1.results5Session classification, starting grid, drivers'/constructors' championship standings, overtakes
openf1.timing5Per-lap sector + speed-trap timing, pit stops, tyre stints, live gaps/intervals, track position
openf1.telemetry2Car telemetry (speed/throttle/brake/gear/RPM/DRS) + (x,y,z) location at ~3.7 Hz
openf1.live3Race-control messages (flags/SC/incidents), team-radio clips, weather

Free, no-auth public REST surface (auth: none). Scope feeds by session_key / meeting_key (both accept the literal latest) and driver_number; discover keys with openf1_sessions / openf1_meetings first.

Cricket Australia β€” cricket.com.au (no key)

GroupToolsNotes
cricketaustralia.core7Fixtures (the /matches feed), competitions, tours/series, teams, player profiles (batch), venue lookup, competition ladder
cricketaustralia.match3Full scorecard (innings batting/bowling/wickets), run-graph series, live video streams
cricketaustralia.content2Pulselive CMS: video/text/audio/playlist content list + curated playlists

Two no-auth hosts (apiv2.cricket.com.au/web + the Pulselive CMS). The apiv2 endpoints carry jsconfig=eccn:true by default so they return the documented camelCase shape; flow is cricketaustralia_fixtures β†’ cricketaustralia_scorecard?fixtureId= β†’ cricketaustralia_players?playerIds=.

MLB β€” statsapi.mlb.com (official Stats API, no key)

GroupToolsNotes
mlb.reference22Sports/leagues/divisions/conferences, teams (+ single, affiliates, history, uniforms), rosters, alumni, coaches, personnel, players (profile, batch, search, season catalogue, changes feed), venues, seasons (current + full history)
mlb.schedule5Games by date / range / team, plus postseason (schedule, series, tune-in) and tied games
mlb.game10Boxscore, linescore, play-by-play, v1.1 feed/live firehose, win-probability, context metrics, content, per-player game line, changes, uniforms
mlb.stats9Standings, season stats, one-player stats, league + team leaders, team-season stats, game pace, high/low records
mlb.extra15Draft (+ prospects), awards (catalogue + recipients), attendance, transactions, free agents, jobs (umpires/datacasters/scorers), Home Run Derby, All-Star ballots
mlb.meta1mlb_meta β€” the /{type} lookup for every enum (positions, statTypes, gameTypes, pitchCodes, …)

The official MLB Stats API the MLB-StatsAPI library wraps, read directly (no key) β€” comprehensive coverage of the public surface. sportId=1 is MLB; discover ids with mlb_teams / mlb_schedule / mlb_player_search, then drill into a game or player. Most tools accept the API's hydrate string to embed related objects in one call.

Premier League β€” premierleague.com (no key)

GroupToolsNotes
premierleague.core7Competitions, season structure, awards, the league table, current gameweek, geo
premierleague.teams10Teams (+ batch), squads, form (single + all-teams), team stats, next fixture, club metadata
premierleague.matches8Fixtures/results feed + match centre: detail, events, lineups, team stats (~200 Opta metrics), officials, commentary
premierleague.players8Player directory, profiles (basic/career/season), batch lookup, season + competition stats, metadata
premierleague.stats2Player + team stat leaderboards (sort by any Opta metric)
premierleague.content8Editorial content/search, latest+popular news/video, broadcasting schedule

The private JSON APIs that power premierleague.com, read directly (no key, no cookies) across three hosts (the SDP stats platform, the editorial/ broadcast api.premierleague.com, and static config on resources.premierleague.com). Underlying data is Opta. Premier League = competition 8; season id is the starting year (2025 = 2025/26). Flow: pl_teams β†’ pl_matches β†’ a match id β†’ pl_match/pl_match_stats; pl_standings for the table. Unofficial/undocumented β€” respect the ~5 rps rate limit. The SDP wire params (_limit, _sort, kickoff>/kickoff<) are exposed under clean tool names (limit, sort, kickoff_after/kickoff_before).

LaLiga β€” apim.laliga.com (public key shipped)

GroupToolsNotes
laliga.core6Competitions, season instances (subscriptions), league table, rounds/matchweeks
laliga.teams3Season team list, single team, club squad
laliga.players3Every-player season stats (β‰ˆ749, full Opta metrics), player profile + stats
laliga.matches2Matches feed + single-match detail

The private JSON API behind laliga.com (Azure APIM), read directly. Underlying data is Opta. A public Ocp-Apim-Subscription-Key is shipped as a working default, so it runs out of the box β€” but the key rotates; override it with LALIGA_SUBSCRIPTION_KEY (env or secrets:) when reads start 401-ing (re-harvest from laliga.com's __NEXT_DATA__). A "subscription" is a season instance (slug laliga-easports-2025 = 2025/26); detail endpoints are keyed by slug. Pairs with the Premier League provider for cross-league football comparison via the shared stats.ladder / sport.fixtures_by_date / stats.player_season tags.

Serie A β€” api-sdp.legaseriea.it (no auth)

GroupToolsNotes
seriea.core3All competitions, the 41-season catalogue, single-season detail
seriea.season6League table (overall/home/away), the 20 teams, every-player + team Opta stats (paginated), all 380 matches, match lineups

The public SDP JSON API behind legaseriea.it, read directly (no auth). Underlying data is Opta. The Serie A competition id is baked in, so you only ever supply a seasonId (discovered from seriea_seasons; seasonName like 2025/2026). Player stats return identity and ~279 Opta metrics in one call (no squad endpoint), paginated 30/page with category=General|Goalkeeping. Completes the big-three football leagues alongside Premier League + La Liga via the shared stats.ladder / sport.fixtures_by_date / stats.player_season tags.

Kalshi β€” kalshi.com (prediction markets, no key)

GroupToolsNotes
kalshi.markets6Market catalogue + detail, order book, public trades, OHLC candlesticks (single + batch)
kalshi.events9Events, series catalogue (by category), single series, milestones, MVE combo collections, entity registry
kalshi.exchange3Exchange status, trading schedule, announcements

The CFTC-regulated US event-contract exchange. Market data is public β€” no key required; optionally set KALSHI_API_KEY_ID + KALSHI_PRIVATE_KEY(_PATH) and every request is RSA-signed for Kalshi's higher authenticated rate limits (needs pip install "sportsdata-mcp[kalshi-auth]"). Trading surfaces stay out of scope (read-only provider). Id chain: kalshi_series_list(category) β†’ kalshi_events β†’ kalshi_markets β†’ orderbook/trades/candles by ticker. Prices are dollar-denominated.

Polymarket β€” polymarket.com (prediction markets, no key, geo-gated)

GroupToolsNotes
polymarket.gamma9Markets/events/series/sports/tags catalogue + site search (the discovery plane)
polymarket.clob6Order book, best price, midpoint, spread, price history, CLOB catalogue
polymarket.data2Public trade tape + top holders

The largest crypto prediction market. All read endpoints are anonymous β€” the wallet keys Polymarket's SDKs use are for order placement only (out of scope). ⚠️ Geo-gated: Polymarket drops connections at the network edge from restricted jurisdictions (verified: AU IPs time out on every host) β€” run from an unrestricted region or VPN. Flow: polymarket_events β†’ a market's clobTokenIds β†’ polymarket_book / polymarket_price_history.

X (Twitter) β€” api.x.com (needs a Bearer token)

GroupToolsNotes
twitter.tweets77-day search, volume counts, post lookup (batch + single), quote/repost/like engagement
twitter.users6Profile lookup (handle/id, batch), user timelines, mentions
twitter.trends2Trends by location (WOEID) + project usage/cap monitor

The X API v2 read surface β€” no anonymous tier, so a Bearer token is required: env X_BEARER_TOKEN first (an operator can ship a deployment-wide token for all its users), then the config secrets: block (each user their own). The env var holds the bare token; the spec adds Bearer . Mind your tier's monthly read cap (twitter_usage); the spec throttles ~0.5 req/s and never auto-retries 429s. Write/user-context surfaces (posting, DMs, follows) are out of scope. Flow: twitter_user_by_username("AFL") β†’ id β†’ twitter_user_tweets; search with X operators ("Storm" lang:en -is:retweet).

Cross-provider comparison

Every tool is tagged with provider-agnostic capability slugs (e.g. sport.event_markets, racing.race_card). Tools sharing a slug answer the same question and are directly comparable across providers. The discovery flow:

  1. list_tools_by_capability("sport.event_markets") β†’ every enabled tool exposing it
  2. Call each provider's tool concurrently with the resolved event ids
  3. Compare the raw snapshots (schemas are not normalised β€” the model reconciles them)

See examples/comparator-prompt.md for a full "compare Storm v Cowboys odds across bookies" walkthrough.

Per-provider notes

  • Sportsbet β€” anonymous public APIs; no secrets needed. REST events are keyed by integer eventId; a persisted-GraphQL gateway is exposed via sportsbet_graphql_call (browse sportsbet://graphql/operations).
  • Entain / Ladbrokes β€” a persisted-GraphQL gateway; the model supplies an operation name + variables (discover them in entain://graphql/operations). Hashes can drift when the front-end bundle ships; refresh them with sportsdata-mcp refresh-hashes entain.
  • AFL β€” afl.public.* is anonymous. afl.premium.* mints an anonymous x-media-mis-token automatically; some premium endpoints still return 401 for anonymous callers.
  • NRL β€” the anonymous Champion Data match-centre CDN (mc.championdata.com), the same static JSON the official nrl.com match centre reads. No secrets, no cache-buster params needed. Resolve a competitionId from nrl_competitions (e.g. 12999 = 2026 NRL Premiership), a matchId from nrl_fixture, then pull per-player match stats from nrl_match; decode stat codes via nrl://stats/definitions.
  • NBA β€” two surfaces, no secrets. cdn.nba.com is wide open (it even serves JSON as text/plain, which the client accepts). stats.nba.com sits behind Akamai, which black-holes any request missing a full browser header bundle β€” the spec ships that bundle in provider.default_headers, so it just works. Akamai also rate-limits hard, so the spec's defaults block throttles NBA to ~1 req/2.5 s, sets a 45 s timeout, and retries transient 429/5xx with exponential backoff (all overridable via providers.nba.*). The /stats/ family is one dispatcher (nba_stats_call): pick an operation (the path segment, e.g. leaguedashplayerstats) and pass query_params β€” each operation already carries NBA's full default param set, so you override only what matters. Most responses are column-oriented (resultSets:[{name, headers, rowSet}]}); v3 box scores are nested.
  • ESPN β€” four public hosts, no auth, no API key: site.api.espn.com (scores, teams, standings, news, summaries), sports.core.api.espn.com (the canonical $ref-linked model β€” odds, win-probability, plays, venues, drafts, coaches), site.web.api.espn.com (search + athlete views) and cdn.espn.com (the live core feed, needs ?xhr=1). Nearly every URL is .../sports/{sport}/{league}/{resource}, so the tools take sport + league as parameters and cover every ESPN league parametrically β€” NFL, NBA, MLB, NHL, college, soccer (eng.1, esp.1, …), golf, racing, tennis, MMA and more. Discovery: espn_scoreboard(sport, league) β†’ an event id β†’ espn_game_summary or the deep espn_core_call(event_*) ops. The spec throttles to ~5 req/s and retries transient 429/5xx (overridable via providers.espn.*). Note the core API path uses leagues/{league} (plural); core list responses are lazy {count, items:[{$ref}]} envelopes β€” follow the refs for detail.
  • PointsBet β€” anonymous public APIs, no secrets. api.au.pointsbet.com serves the sportsbook (sports + racing); pointsbet.com.au serves static CMS/nav assets via the pointsbet_content_call dispatcher. Sports discovery: pointsbet_sport_competitions(sportKey) β†’ a competition key β†’ pointsbet_event(eventKey) for the full market book. Racing: pointsbet_racing_meetings(startDate, endDate) β†’ a raceId β†’ pointsbet_racing_race. Many feeds return a top-level JSON array.
  • TAB (Tabcorp) β€” anonymous public data, no secrets. api.beta.tab.com.au sits behind Akamai (the spec ships a browser header bundle + ~2.5 rps throttle, like NBA); cmsapi.tab.com.au serves CMS feeds via tab_cms_call. Every endpoint needs a jurisdiction (defaults to NSW). The API is HATEOAS and name-based β€” paths embed sport/competition/match/venue names with spaces (…/AFL Football/competitions/AFL/matches/Adelaide v Geelong), which the HTTP layer percent-encodes; pass raw names. Racing: tab_racing_meetings(date) β†’ raceType+venueMnemonic β†’ tab_racing_race. Sports: tab_sport β†’ tab_competition β†’ tab_match for the full market book.
  • Unibet β€” anonymous AU data, no secrets, two surfaces. Racing is persisted-GraphQL (unibet_racing_call, the graphql_persisted dispatcher) at rsa.unibet.com.au β€” race ids are eventKeys like 202606040200.T.AUS.hawkesbury.1; the endpoint enforces Apollo CSRF so a Content-Type: application/json header is sent. Sport is the Kambi offering API (unibet_kambi_call over *.kambicdn.com, market AU): group tree, events, bet offers, in-play, bet-builder. Browse ops in unibet://{racing,sport}/operations.
  • BetR β€” anonymous AU data, no secrets. BetR runs on the BlueBet platform, so the API is web20-api.bluebet.com.au β€” a flat REST surface covering racing (next-to-jump, grouped racecards, race cards, form, fluctuations) and sport (event types β†’ categories β†’ markets, SGMs). The betr.com.au Next.js _next/data/{buildHash} blobs are skipped (fragile per-deploy hash; the API serves the same data).
  • Racing and Sports β€” www.racingandsports.com.au racing/form data, no auth. racingandsports_todays_racing (/todays-racing-json-v2) is the verified feed β€” today's meetings across thoroughbred/harness/greyhound, by country. The site is behind Cloudflare, which whitelists that feed but JS-challenges the other paths from datacenter IPs (they work from a residential/browser IP); the form/fields/ results are HTML pages, and GetOdds needs a per-race token, so only the JSON feeds are modelled.
  • Betfair Exchange β€” anonymous, the open read-only web APIs keyed by the public _ak query param. The crown jewel is betfair_market_prices (ero …/bymarket) β€” exchange back/lay prices, the sharpest reference odds. Discover market ids by walking betfair_navigation (scan …/bynode, e.g. EVENT_TYPE:7 = Horse Racing) down to MARKET nodes; live scores/details come from the ips in-play service. string_csv id params take a list. (The apieds racing widgets are Cloudflare-gated from datacenter IPs and the appsync GraphQL needs a session, so they're out of scope β€” racing is covered via navigationβ†’bymarket.)
  • Pinnacle β€” anonymous, no key. The Arcadia "guest" API (guest.api.arcadia.pinnacle.com) β€” the open feed the web sportsbook reads. Sports only (sharp-odds book, no racing); prices are American odds. Flow: pinnacle_sports β†’ pinnacle_sport_matchups(sportId) β†’ pinnacle_matchup_markets(matchupId). The provider sends Pinnacle's public web-client X-API-Key, which unlocks the full per-sport + per-league matchup lists and the parlay markets.
  • FanDuel (US) β€” anonymous US data, no secrets, two surfaces under one provider. Racing is the first full-query GraphQL provider: fanduel_racing_call POSTs the literal query text (the graphql_query dispatcher kind, sibling to the persisted-hash graphql_persisted), with boilerplate variables (brand/product/device/profile) baked as per-op default_variables β€” most calls need none, override only what varies ({results: 12}, {trackCode, raceNumber}). Sportsbook is REST (fanduel_sb_call) keyed by the static public _ak web key, region NJ. The two halves need different Origin headers, so the sportsbook dispatcher overrides Origin + x-sportsbook-region over the racing-origin provider default. Browse ops in fanduel://{racing,sportsbook}/operations. (US data β€” composes with other US sources via capability tags.)

CLI reference

CommandPurpose
serveStart the MCP stdio server (default when no subcommand)
list-groupsPrint every group with tool count + description
lintValidate specs against the schema + capability catalogue (nonzero on failure)
doctorPer-provider reachability + auth + REST-contract probe (nonzero on failure)
refresh-hashes <provider>Refresh persisted-query hashes from the live front-end bundle (--dry-run to preview)
versionPrint version info

-v / --verbose enables DEBUG logging (and un-silences httpx/httpcore).

Contributing

See documentation/ADDING_A_PROVIDER.md for the full guide, with separate playbooks for adding a bookmaker vs a sports website / data API. In short, adding a provider is a spec-only change in the common case:

  1. Write src/sportsdata_mcp/specs/<provider>.yaml (copy an existing spec).
  2. Tag each tool with capability slugs from specs/_capabilities.yaml; add a new slug there if none fits (two providers sharing a slug makes them comparable).
  3. sportsdata-mcp lint β€” must pass.
  4. sportsdata-mcp doctor (with the new groups enabled) β€” probes it live.
  5. pytest -m "not live" β€” offline suite; drop the marker filter to run live tests.
  6. Add a row to tests/contract/test_api_contracts.py so the new provider's documented response shape is verified live on every PR (see below).
pip install -e ".[dev]"
pytest -m "not live"      # offline suite (the CI gate)
pytest -m contract        # live response-contract checks (see below)
ruff check .

CI

Every push/PR runs three jobs (.github/workflows/ci.yml):

  • test β€” ruff, sportsdata-mcp lint, and the offline suite (pytest -m "not live") across Python 3.11–3.13. The deterministic gate.
  • contract β€” pytest -m contract: live response-contract checks that hit each upstream API and assert it still returns the documented shape (top-level keys, and the documented keys on list items). It is resilient by design β€” it skips on anything outside our control (network errors, 5xx, 401/403/429, geo-blocks, a missing DATAGOLF_KEY, or an empty feed) and only fails on a genuine shape regression or a broken spec (wrong path/params β†’ 4xx). Bookmaker APIs that geo-block GitHub's runners simply skip there.
  • package β€” builds the wheel and proves the CLI loads the packaged specs from a clean install.

License

Proprietary and confidential. Copyright (c) 2026 Daniel Tomaro. All rights reserved. No use, copying, modification, or distribution is permitted without the owner's prior written consent β€” see LICENSE.

mcp-name: io.github.DanielTomaro13/sportsdata-mcp

Related MCP Servers

anup-shesh/garmin-local-mcp

🐍 🏠 🍎 πŸͺŸ 🐧 - Local-first Garmin data warehouse: incremental sync into SQLite you own, with server-side trends, lag-aware correlations, personal baselines, and anomaly detection returned as compact tables. Analysis keeps working offline when Garmin's API breaks, and a zero-auth FIT-bundle import fills gaps with no login at all.

πŸƒ Sports0 views
arturogarrido/claudinho

πŸ“‡ 🏠 🍎 πŸͺŸ 🐧 - Live scores, fixtures, standings, and read-only prediction-market signals for the 2026 World Cup. No API keys.

πŸƒ Sports0 views
Backspace-me/sportscore-mcp

πŸ“‡ ☁️ 🍎 πŸͺŸ 🐧 - Live scores, standings, top scorers, player stats, and knockout brackets for football, basketball, cricket, and tennis. Free public API, no key required.

πŸƒ Sports0 views
chessceo/chessceo-mcp

πŸ“‡ ☁️ 🏠 🍎 πŸͺŸ 🐧 - Chess data via chess.ceo β€” 11.7M+ games and 1.5M FIDE player profiles. 8 tools: player search, per-player opening preparation (opening tree with frequency + win rate), position statistics from the full game database, head-to-head records, and live tournament broadcasts. No API key. Remote server at https://mcp.chess.ceo/mcp or local via npx -y @chessceo/mcp.

πŸƒ Sports0 views

Engagement

Views
0
Installs
0
Upvotes
0

Views and upvotes are unique per visitor network (hashed IP). Installs count copy actions.

Status

Health: Active

Recent health check succeeded.

Last checked: 7/28/2026, 9:23:25 PM

Unclaimed listing (imported or pending owner verification). Claim it β†’
β˜… Spotlight Slot

Feature Your MCP Server

Get maximum visibility for your server across our directory, search results, and detail pages.

Spotlight Your Server

Own this project?

This directory is pre-filled from public sources. Claim via GitHub README, site badge, or DNS TXT to get the verified badge and attach your website.

Claim this listing

Promote this listing

Optional paid placement. Free listings stay free forever.

Share & Embed

Add our SVG badge (dark/light directory styles) or embeddable widget to your site.