Skip to main content
Glama
DanielTomaro13

sportsdata-mcp

sportsdata-mcp

CI Release License: MIT sportsdata-mcp MCP server

Add to Cursor

Free & open source (MIT). Live sports data and cross-book betting odds as MCP tools — ~500 tools across 28 providers, in Claude Desktop, Cursor, or any MCP client. Zero config: install, run setup, and the full catalogue serves.

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

Related MCP server: AltSportsData MCP Server

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

Variable

Effect

SPORTSDATA_MCP_GROUPS

Comma-separated group list; overrides enabled_groups.

SPORTSDATA_MCP_CONFIG

Path to a config file (see resolution order above).

SPORTSDATA_MCP_MAX_BYTES

Global response-size cap in bytes for every provider that doesn't set its own max_response_bytes. 0 (the default) means no cap.

SPORTSDATA_LICENSE

Dormant — 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_PUBKEY

Only 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

Group

Tools

Notes

afl.public.core

22

Competitions, seasons, rounds, fixtures, ladders, match stats

afl.public.broadcasting

9

Broadcast regions, guides, providers

afl.public.content

8

News/articles, videos, photos

afl.premium.cfs

1

CFS premium ops — needs the anonymous x-media-mis-token

afl.premium.statspro

1

StatsPro ops — needs the x-media-mis-token

afl.premium.keyserver

1

HLS video URL signing

Sportsbet — sportsbet.com.au

Group

Tools

Notes

sportsbet.racing

15

Race meetings, racecards, results, futures, SRMs

sportsbet.sports

14

Sport events, markets, prices, SGMs

sportsbet.cross

12

Live status, commentary, ladders, promos, video

sportsbet.results

2

Resulted events by date

sportsbet.graphql

1

Persisted GraphQL gateway (apigw/sportsbook/graph)

Entain / Ladbrokes — ladbrokes.com.au

Group

Tools

Notes

entain.rest

13

Navigation quick-links and REST surfaces

entain.graphql

1

127 persisted GraphQL ops (gql/router)

entain.cdn

1

Contentful CMS entries (promotions, major-event nav)

PointsBet — pointsbet.com.au

Group

Tools

Notes

pointsbet.sports

10

Sports catalogue, competition/event feeds, full event markets, in-play, search

pointsbet.racing

11

Meetings, racecards, results, futures, SRMs, tips, form

pointsbet.content

3

Promotions, promo-code splash, + pointsbet_content_call over the static CMS/nav assets

TAB — tab.com.au

Group

Tools

Notes

tab.racing

9

Dates, meetings, racecards (fixed + parimutuel), form, next-to-go, jackpots, futures

tab.sports

9

Sports/competitions tree, full match markets + SGM, focused match markets, next-to-go, results, multi-builder

tab.discovery

4

Featured/live recommendations + tab_cms_call over the CMS content feeds

Unibet — unibet.com.au

Group

Tools

Notes

unibet.racing

1

unibet_racing_call — persisted-GraphQL: meetings, race cards, form, futures, specials

unibet.sport

3

unibet_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)

Group

Tools

Notes

betr.racing

8

Next-to-jump, today's/grouped racecards, race card, form, fluctuations, movers

betr.sport

7

Event types, competition categories, event markets, match detail, popular SGMs

betr.content

4

Promotions + featured racing + popular market links

Pinnacle — pinnacle.com (sharp odds)

Group

Tools

Notes

pinnacle.sports

13

Sports/leagues, full + highlighted + live + per-league matchups, carousel, matchup detail, straight + parlay markets (American-odds prices)

pinnacle.reference

4

Enums, market-label dictionary, teaser definitions, API status

Betfair Exchange — betfair.com.au (exchange odds)

Group

Tools

Notes

betfair.exchange

3

bymarket + byevent back/lay price feeds (the sharpest odds) + cash-out availability

betfair.navigation

1

bynode catalogue graph (sport → meeting → event → market)

betfair.inplay

5

Live scores, event details, timeline (single + batch), scores+broadcast

Dabble — dabble.com.au (iOS app backend)

Group

Tools

Notes

dabble.sport

5

Discover 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)

Group

Tools

Notes

supercoach.fantasy

6

One 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)

Group

Tools

Notes

nbl.basketball

14

Seasons, 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)

Group

Tools

Notes

wta.tennis

8

Official 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

Group

Tools

Notes

racingandsports.racing

3

Today's race meetings (all codes, verified) + sports match list + per-race odds (token)

Data Golf — datagolf.com (needs a key)

Group

Tools

Notes

datagolf.general

3

Player list, tour schedule, current event field

datagolf.predictions

11

DG 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.betting

3

Outright + matchup + all-pairings odds across ~13 books (incl. model line)

datagolf.historical

9

Archived 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)

Group

Tools

Notes

fanduel.racing

4

fanduel_racing_call (full-query GraphQL: featured/today races + odds, single-race card, tracks, pools, talent picks) + messages/quick-links/promotions

fanduel.sportsbook

2

fanduel_sb_call (REST: event pages + markets, in-play, promos, configs via the _ak key) + live scores

NRL — mc.championdata.com

Group

Tools

Notes

nrl.public.core

4

Champion 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

Group

Tools

Notes

nba.public.cdn

5

Open CDN JSON: today's scoreboard, full schedule, live box score + play-by-play, odds

nba.stats

2

nba_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

Group

Tools

Notes

espn.scores

5

Site API convenience endpoints: scoreboard, teams, standings, game summary, news

espn.site

1

espn_site_call — team detail, rosters, schedules, injuries, depth charts, transactions, history, athlete news, groups, rankings (10 ops)

espn.core

1

espn_core_call — the canonical $ref-linked model: events/competitions, odds, win-probability, plays, venues, drafts, coaches, calendar, transactions (37 ops)

espn.web

1

espn_web_call — site-wide search + common/v3 athlete views (7 ops)

espn.cdn

1

espn_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)

Group

Tools

Notes

openf1.reference

3

Grand Prix weekends (meetings), sessions (the fixtures feed), driver roster

openf1.results

5

Session classification, starting grid, drivers'/constructors' championship standings, overtakes

openf1.timing

5

Per-lap sector + speed-trap timing, pit stops, tyre stints, live gaps/intervals, track position

openf1.telemetry

2

Car telemetry (speed/throttle/brake/gear/RPM/DRS) + (x,y,z) location at ~3.7 Hz

openf1.live

3

Race-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)

Group

Tools

Notes

cricketaustralia.core

7

Fixtures (the /matches feed), competitions, tours/series, teams, player profiles (batch), venue lookup, competition ladder

cricketaustralia.match

3

Full scorecard (innings batting/bowling/wickets), run-graph series, live video streams

cricketaustralia.content

2

Pulselive 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_fixturescricketaustralia_scorecard?fixtureId=cricketaustralia_players?playerIds=.

MLB — statsapi.mlb.com (official Stats API, no key)

Group

Tools

Notes

mlb.reference

22

Sports/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.schedule

5

Games by date / range / team, plus postseason (schedule, series, tune-in) and tied games

mlb.game

10

Boxscore, linescore, play-by-play, v1.1 feed/live firehose, win-probability, context metrics, content, per-player game line, changes, uniforms

mlb.stats

9

Standings, season stats, one-player stats, league + team leaders, team-season stats, game pace, high/low records

mlb.extra

15

Draft (+ prospects), awards (catalogue + recipients), attendance, transactions, free agents, jobs (umpires/datacasters/scorers), Home Run Derby, All-Star ballots

mlb.meta

1

mlb_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)

Group

Tools

Notes

premierleague.core

7

Competitions, season structure, awards, the league table, current gameweek, geo

premierleague.teams

10

Teams (+ batch), squads, form (single + all-teams), team stats, next fixture, club metadata

premierleague.matches

8

Fixtures/results feed + match centre: detail, events, lineups, team stats (~200 Opta metrics), officials, commentary

premierleague.players

8

Player directory, profiles (basic/career/season), batch lookup, season + competition stats, metadata

premierleague.stats

2

Player + team stat leaderboards (sort by any Opta metric)

premierleague.content

8

Editorial 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_teamspl_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)

Group

Tools

Notes

laliga.core

6

Competitions, season instances (subscriptions), league table, rounds/matchweeks

laliga.teams

3

Season team list, single team, club squad

laliga.players

3

Every-player season stats (≈749, full Opta metrics), player profile + stats

laliga.matches

2

Matches 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)

Group

Tools

Notes

seriea.core

3

All competitions, the 41-season catalogue, single-season detail

seriea.season

6

League 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)

Group

Tools

Notes

kalshi.markets

6

Market catalogue + detail, order book, public trades, OHLC candlesticks (single + batch)

kalshi.events

9

Events, series catalogue (by category), single series, milestones, MVE combo collections, entity registry

kalshi.exchange

3

Exchange 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_eventskalshi_markets → orderbook/trades/candles by ticker. Prices are dollar-denominated.

Polymarket — polymarket.com (prediction markets, no key, geo-gated)

Group

Tools

Notes

polymarket.gamma

9

Markets/events/series/sports/tags catalogue + site search (the discovery plane)

polymarket.clob

6

Order book, best price, midpoint, spread, price history, CLOB catalogue

polymarket.data

2

Public 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 clobTokenIdspolymarket_book / polymarket_price_history.

X (Twitter) — api.x.com (needs a Bearer token)

Group

Tools

Notes

twitter.tweets

7

7-day search, volume counts, post lookup (batch + single), quote/repost/like engagement

twitter.users

6

Profile lookup (handle/id, batch), user timelines, mentions

twitter.trends

2

Trends 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.

  • AFLafl.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 raceIdpointsbet_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+venueMnemonictab_racing_race. Sports: tab_sporttab_competitiontab_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 Sportswww.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_sportspinnacle_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

Command

Purpose

serve

Start the MCP stdio server (default when no subcommand)

list-groups

Print every group with tool count + description

lint

Validate specs against the schema + capability catalogue (nonzero on failure)

doctor

Per-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)

version

Print 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.

  • contractpytest -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

Install Server
A
license - permissive license
B
quality
A
maintenance

Maintenance

Maintainers
Response time
1dRelease cycle
16Releases (12mo)
Commit activity

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/DanielTomaro13/sportsdata-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server