@sportmonks/football-mcp-server
v1.5.0
Published
[BETA] MCP server for Sportmonks Football API 3.0 — companion tool for AI-assisted football data exploration. Not a production API client.
Readme
Sportmonks Football MCP Server
** BETA** — This server is currently in beta. Functionality may change and edge cases may be unhandled. It is a companion tool for AI-assisted exploration of the Sportmonks Football API, not a production API client. Use it to chat with your data, prototype, and learn the API shape; for production workloads keep using the Sportmonks Football API 3.0 directly.
We actively welcome feedback — see the Feedback section below.
Model Context Protocol (MCP) server for the official Sportmonks Football API 3.0.
This server exposes focused Sportmonks Football API tools for search, player/team/league/coach/referee/venue lookup, squads, fixtures, standings, seasons, topscorers, odds, season statistics, per-fixture player statistics, match pressure index, transfers, match news, match facts, commentary key moments, team of the week, and team rivals.
Features
- Focused MCP tools instead of a broad raw API surface
- Typed input schemas plus runtime validation on every tool
- JSON output for every tool response, including error responses
- Descriptive errors with a
how_to_fixfield for the LLM - Pagination metadata (
returned,cap,possibly_more,date_window) on every list-style tool so the LLM knows when results were truncated - Built on official Sportmonks Football API 3.0 endpoints
- Player current-team resolution iterates candidate clubs and rejects national-team relations, so
current_teamreturns the player's club rather than their country - Unknown ids surface as typed
not_founderrors instead of all-null rows or silent empties — across the entity lookups (get_player,get_team,get_league,get_coach,get_referee,get_venue), the fixture lookups (get_fixture_details,get_match_preview),get_standings,get_historic_seasons,get_rivals, andget_totw; partial-coverage tools still return a clean empty result for real-but-uncovered ids - Types and states are loaded on startup and reused for shared mappings (broad and detailed positions are resolved to readable names via this cache)
Configuration
| Variable | Required | Default | Description |
| --- | --- | --- | --- |
| SPORTMONKS_API_TOKEN | Yes | — | Your Sportmonks API token from MySportmonks |
| SPORTMONKS_LOG_FILE | No | <os.tmpdir()>/sportmonks-football-mcp.log | Absolute path for the local tool-call log. Set to off, none, or empty to disable file logging |
| SPORTMONKS_DEBUG_URLS | No | off | Set to 1, true, yes, or on to log each outbound Sportmonks URL to stderr (with api_token redacted). Off by default |
Sportmonks authentication follows the official api_token query parameter approach documented at https://docs.sportmonks.com/v3/welcome/authentication.
Installation
The published npm binary works with any MCP client. Each example below fetches the package on first run (via npx -y) and starts the server; no manual install required.
Claude Code CLI
claude mcp add sportmonks-football \
--env SPORTMONKS_API_TOKEN="your-token" \
-- npx -y @sportmonks/football-mcp-serverClaude Desktop
{
"mcpServers": {
"sportmonks-football": {
"command": "npx",
"args": ["-y", "@sportmonks/football-mcp-server"],
"env": {
"SPORTMONKS_API_TOKEN": "your-token"
}
}
}
}Cursor
{
"mcpServers": {
"sportmonks-football": {
"command": "npx",
"args": ["-y", "@sportmonks/football-mcp-server"],
"env": {
"SPORTMONKS_API_TOKEN": "your-token"
}
}
}
}VS Code
{
"servers": {
"sportmonks-football": {
"command": "npx",
"args": ["-y", "@sportmonks/football-mcp-server"],
"env": {
"SPORTMONKS_API_TOKEN": "your-token"
}
}
}
}Install from source
For contributing or running against unreleased changes:
npm install
npm run build
SPORTMONKS_API_TOKEN="your-token" node dist/index.jsPoint your MCP client at the compiled entrypoint (node /absolute/path/to/dist/index.js) instead of the npx command.
Available Tools
Response shapes
List-style tools (search, get_matches, get_squad, get_standings, get_topscorers, get_odds, get_season_stats, get_fixture_lineup_stats, get_transfers, get_match_facts) return a { data, meta } envelope where meta contains returned, cap, and possibly_more. Use meta.possibly_more to detect upstream or local truncation. get_matches also exposes meta.date_window for non-live timeframes, the two stats tools (get_season_stats, get_fixture_lineup_stats) expose meta.stat_types with the applied stat filter, get_match_facts exposes meta.basis, meta.category, and meta.scope with the applied filters, and get_odds sets meta.empty_reason on empty results to say why they are empty.
get_pressure_index also returns a { data, meta } envelope, but meta carries returned, cap, possibly_more, and the applied mode; its data is mode-dependent (summary aggregates or a per-minute timeline) rather than a flat list. get_fixture_news returns a { data, meta } envelope too, with data carrying prematch/postmatch article arrays and meta = { timing, returned: { per type } }. get_rivals returns a { data, meta } envelope where meta = { team_id, returned }. get_commentaries returns a { data, meta } envelope where data is a flat list of key-moment lines ({ minute, comment, is_goal }) and meta = { fixture_id, returned }. get_totw returns a flat object { league_id, round, formation, players, meta } (the 11-player selection in players) where meta = { returned, scope, league_id, round_id }.
Single-entity tools (get_player, get_team, get_league, get_coach, get_referee, get_venue, get_match_preview, get_fixture_details) and get_historic_seasons return a JSON object or array directly without an envelope.
search
Search for entities in the Sportmonks database.
Inputs:
query(required)type(optional):player,team,league,coach,referee,venue,all(default)
Output:
{ data, meta }envelope;datacontains up to 25 items sorted alphabetically by name- Each item contains
id,entity_type(player,team,league,coach,referee, orvenue),name, andcountry(countrymay benullwhen Sportmonks doesn't provide one; useful for disambiguating generic names like the dozen leagues called "Super League") metais{ returned, cap, possibly_more }(capis 25);possibly_moreistruewhen more matched upstream (across the queried entity endpoints) than the 25 returned
get_player
Get player details by id.
Inputs:
id(required)
Output:
- JSON object with
id,name,position,nationality,date_of_birth, andcurrent_team
get_team
Get team details by id.
Inputs:
id(required)
Output:
- JSON object with
id,name,country,venue, andcoach({ id, name }for the current manager;nullwhen no active coach is recorded)
get_league
Get league details by id.
Inputs:
id(required)
Output:
- JSON object with
id,name,country,current_season_id, andcurrent_season_name
get_coach
Get coach details by id.
Inputs:
id(required)
Output:
- JSON object with
id,name,nationality,date_of_birth, andcurrent_team({ id, name }for the coach's active appointment;nullwhen none is recorded). Mirrorsget_player's shape
get_referee
Get referee (match official) details by id.
Inputs:
id(required)
Output:
- JSON object with
id,name,nationality, anddate_of_birth. Mirrorsget_coach's shape minuscurrent_team(referees have no team).nationalityfalls back to the referee'scountrywhen the dedicated nationality field is empty (it usually is)
get_venue
Get the full venue (stadium) record by id.
Inputs:
id(required)
Output:
- JSON object with
id,name,city,country,capacity,surface,address, andcoordinates({ latitude, longitude }, ornullwhen not recorded) - This is the full record; a team's venue (in
get_team) stays lean (name only). Address and coordinates are included here because a direct venue lookup wants the complete picture
get_rivals
Get a team's traditional rivals.
Inputs:
team_id(required)
Output:
{ data, meta }envelope; each rival containsrival_team_id,rival_team_name, andrival_short_code.metais{ team_id, returned }- Coverage is partial — a real team with no rival data returns an empty list, not an error (an unknown team id returns
not_found)
get_squad
Get the current or historic squad for a team.
Inputs:
team_id(required)season_id(optional)
Output:
{ data, meta }envelope; each row containsplayer_id,name,position,position_id,detailed_position,detailed_position_id, andjersey_number
get_matches
Get matches for a team or league.
Inputs:
id(required)type(required):team,leaguetimeframe(optional):live,historic,upcoming(default)
Output:
{ data, meta }envelope; each match containsid,home_team,away_team,starting_at,state, andleague- For
upcomingandhistorictimeframes,meta.date_windowreports the{ start, end }range queried
Built-in limits:
upcoming: next 14 days, max 20 fixtureshistoric: last 30 days, max 20 fixtureslive: max 20 fixtures
get_match_preview
Get a compact match preview for a fixture id.
Inputs:
id(required): fixture id
Output:
- JSON object with
id,home_team,away_team,starting_at, andlast_5_h2h_matches last_5_h2h_matchescontains up to 5 previous H2H fixtures withdate,home_team,away_team,home_score,away_score, andresult_info
Constraint:
- only works for fixtures that have not started yet
get_fixture_details
Get detailed fixture data with optional expansions.
Inputs:
fixture_id(required)includes(optional): subset oflineups,events,statistics,predictions,xg,referees,tv_stations
Output:
- Base fixture object always includes
id,home_team,away_team,starting_at,state,league, andscores lineups(when requested): each row containsplayer_id,player_name,team_id,jersey_number,position,detailed_position, andtype(lineuporbench).team_idlets you split home vs away without inferring from orderevents(when requested): each event containsminute,type,player_name,related_player_name,result, andinfostatistics(when requested): grouped per team withteam_id,team_name, and astatsobject keyed by stat namepredictions(when requested): a curated object withhome_win,draw,away_win,btts,over_2_5, andvalue_bets. Probabilities are percentages on a 0–100 scale as Sportmonks returns them;bttsandover_2_5carry only the positive direction (the inverse is derivable).value_betslists only bets Sportmonks flags as genuine value opportunities (is_valuetrue upstream) — it is an empty array for most fixtures (~99% in practice), including fixtures where the model evaluated a candidate but found no value. Each value bet containsbet(1X2 notation:"1"home,"X"draw,"2"away),bookmaker,fair_odd,odd, andstake. Sportmonks exposes ~35 prediction types; this include curates the four most useful (fulltime result, BTTS, over/under 2.5, value bets) — fields arenull(or[]) when a type is missingxg(when requested): a list of one object per team withteam_id,team_name,xg(Expected Goals), andxg_on_target(Expected Goals on Target / xGoT). Populated for finished and live fixtures with coverage; an empty array for upcoming fixtures or fixtures without xG data. Sportmonks exposes 10+ xG-family metrics; this include curates only xG and xGoTreferees(when requested): the match officials — a list of{ referee_id, name, type }wheretypeis the resolved role (e.g.Referee,1st Assistant,2nd Assistant,4th Official, VAR). Empty when officials aren't publishedtv_stations(when requested): the broadcasters showing the fixture grouped by country — an object mapping country name to a sorted list of station names, with the station's url in parentheses when it has one (e.g."Argentina": ["DGO (https://www.directvgo.com/)", "DIRECTV Sports"]). Sportmonks lists one pivot row per station per broadcast country (a World Cup final carries ~400 rows / ~250 stations across ~120 countries) and duplicate channel records; this include groups, dedupes within each country, and answers "where can I watch in X" by key lookup. An empty object when no broadcasters are listed (coverage varies by competition/region)
Constraint:
predictionsrequires a Sportmonks subscription with the predictions add-on; without it the whole call returns anauthentication_errorexplaining how to retry without predictions
get_standings
Get the standings table for a league. Tries the live endpoint first; if no live standings are returned (including 404 responses for competitions between phases), falls back to season-based standings using the league's current season.
Inputs:
id(required)
Output:
{ data, meta }envelope; each standing row containsposition,team,group,round,played,won,drawn,lost,gd, andpointsgroupis{ id, name }for group-stage competitions (World Cup, Champions League league phase, MLS conferences) andnullfor single-table leagues; rows are ordered by group then position so each group reads 1→N.roundis{ id, name }for the matchday the table reflects (nullif absent)
get_historic_seasons
Get all seasons for a league, sorted from most recent to oldest.
Inputs:
league_id(required)
Output:
- JSON array with
id,name,is_current,finished,starting_at, andending_at
get_topscorers
Get season topscorers, assisters, or card leaders.
Inputs:
season_id(required)type(required):goals,assists,cardslimit(optional): default10, max25
Output:
{ data, meta }envelope; each row containsposition,player,team, andtotal
get_odds
Get pre-match or premium betting odds for a fixture. Inplay odds are not supported.
Inputs:
fixture_id(required)type(optional):prematch(default),premiummarket_id(optional): filter to a single market (e.g.1for Fulltime Result)bookmaker_id(optional): filter to a single bookmakerlimit(optional): default50, max200
Output:
{ data, meta }envelope; each entry containsbookmaker_id,bookmaker_name,market_id,market_name,label,name,value,total,handicap,stopped, andlast_updatednamedisambiguates multi-outcome markets — the scoreline for Correct Score ("7-0"), the player for Goalscorers/player props — whenlabelalone (1/2/Draw,Anytime,3.5) is ambiguous;nullwhen the market needs no disambiguationvalue,total, andhandicapare decimal strings as Sportmonks sends them;total/handicapcarry the line for markets like Goal Line or Asian Handicap- When
datais empty,meta.empty_reasonsays why:fixture_has_no_odds,fixture_has_no_premium_odds,filters_matched_no_odds(unknown or unofferedmarket_id/bookmaker_id),premium_odds_not_available(premium coverage is time-limited, so past fixtures lose it; with filters set, the filters may also be the cause), orno_odds_returned
Built-in limits:
- up to
limitentries (default 50, max 200), sorted by market then bookmaker, rounded down to whole market+bookmaker blocks so a 1X2 block is not cut mid-way (returnedcan be slightly under the limit; if a single block alone exceeds the limit it is plainly sliced instead of returning nothing). An unfiltered fixture can carry thousands of odds upstream, so narrow withmarket_idand/orbookmaker_id— or raiselimit— whenmeta.possibly_moreistrue
Constraint:
type='premium'requires a Sportmonks subscription that includes the premium odds feed; without it the tool returns anauthentication_errorexplaining the tier requirement
get_season_stats
Get seasonal statistics for a player or team.
Inputs:
entity_id(required): player id or team identity_type(required):player,teamseason_id(required): useget_historic_seasonsto find onestat_types(optional): stat names to return instead of the per-entity-type defaults, e.g.["goals", "big_chances_created"]; unknown names are rejected with avalidation_errornaming the offenders
Output:
{ data, meta }envelope; each row containsentity_id,entity_name,entity_type,season_id,season_name, and astatsobject keyed by snake_case stat name- Player rows also contain
team({ id, name }) — a player's season stats are per club, so a mid-season transfer yields one row per club - Stat values mirror the upstream data: simple counters are unwrapped to plain numbers; richer stats (e.g.
goalswith penalty split, team stats with home/away splits,ratingwith average/highest/lowest) stay objects — so the same stat key can be a number for one entity and an object for another, depending on what Sportmonks tracks. Null subfields upstream never computes (e.g.shots.on_target_pct,shot_frequency) are stripped; a value left with a single subfield unwraps to a plain number metais{ returned, cap, possibly_more, stat_types };meta.stat_typesalways reports the applied stat filter, whether default or user-supplied- An empty
dataarray means the entity exists but recorded none of the requested statistics for that season (e.g. the season has not started, or a player made no appearances)
Default stat filters:
- player:
goals,assists,minutes_played,appearances,shots_on_target,passes,key_passes,tackles,rating - team:
goals,goals_conceded,team_wins,team_draws,team_lost,cleansheets,shots,pass_stats,ball_possession
Notes:
- stat names are the snake_case of the Sportmonks type name (
"Shots On Target"→shots_on_target); spellings like"Shots On Target"or"shots-on-target"are normalized automatically - stat availability varies by league, entity type, and data tier — and Sportmonks omits zero-value stats entirely — so a stat missing from
statsmeans "not tracked or zero"; the two cases cannot be distinguished upstream. A player with no minutes in the season (or an unstarted season) yields no row — "no stats" is always an emptydataarray - teams have no
shots_on_targetorpassesstat types upstream; shots-on-target lives insideshotsand pass numbers insidepass_stats
get_fixture_lineup_stats
Get player-level statistics for a fixture — both squads, including bench.
Inputs:
fixture_id(required)player_ids(optional): filter to specific playersstat_types(optional): stat names to return instead of the defaults, e.g.["rating", "passes", "shots_total"]
Output:
{ data, meta }envelope; each row containsplayer_id,player_name,team_id,team_name,type(lineuporbench), and astatsobject keyed by snake_case stat namemeta.stat_typesalways reports the applied stat filter, whether default or user-supplied- An empty
dataarray means the fixture exists but has no lineup data (not announced yet, or not covered for the league) — or, whenplayer_idsis set, that none of the requested players are in the lineups
Default stat filter:
goals,assists,minutes_played
Built-in limits:
- max 60 player rows (an international friendly with extended benches produced 49, so real fixtures are never truncated)
Notes:
- the stat filter is pushed upstream (an unfiltered fixture carries ~900 stat entries across ~60 types), so narrow
stat_typesrather than post-filtering large responses - stat names are the snake_case of the Sportmonks type name, normalized like
get_season_stats - Sportmonks omits zero-value stats, so a missing stat means "not tracked or zero"; under the default filter (which includes
minutes_played) abenchplayer with an emptystatsobject did not come on — with a narrower override, empty may just mean none of the requested stats were recorded
get_pressure_index
Get the Sportmonks Pressure Index for a fixture — a proprietary real-time metric scoring which team is dominating, minute by minute. Use it to describe momentum swings and periods of dominance.
Inputs:
fixture_id(required)mode(optional):summary(default) ortimeline
Output (both modes return a { data, meta } envelope; meta is { returned, cap, possibly_more, mode }):
summary:datais{ teams, swings }.teamsis[home, away], each withteam_id,team_name,peak_pressure,average_pressure, anddominance_share(% of recorded minutes that team led).swingsis the top momentum-swing minutes (lead changes, most decisive first by pressure, then chronological), each withminute,team_id,team_name, andpressuretimeline:datais{ teams, timeline }.teamsis[home, away](team_id,team_name);teams[0]is thehomekey andteams[1]theawaykey.timelineis the cleaned per-minute series sorted by minute, each entry{ minute, home, away }with the redundantid/fixture_idstripped
Built-in limits:
- timeline capped at 150 minute-entries (a full 90' match is ~94; covers extra time), with
meta.possibly_moreflagging truncation. Summary aggregates the whole recorded series
Notes:
- works for live (partial series) and finished (full series) fixtures; returns an empty series for upcoming fixtures or fixtures without pressure data
- pressure is a relativity metric — only one team has positive pressure at a time, so
dominance_sharevalues reflect who led each minute and a tied (both-zero) minute counts toward neither - team names are resolved from the fixture participants returned by the same call (no extra lookup)
get_transfers
Get football transfers: latest market activity, transfers for a team or player, or transfers within a date range. Confirmed transfers and rumours share one shape, selected via type.
Inputs:
id(optional): team or player id to scope toentity_type(team|player): required whenidis providedtype(confirmed|rumour): defaultconfirmed; rumours require a subscription add-ontimeframe(latest|date_range): defaults tolatestwhen anidis provided; for an unscoped query (noid) it must be set explicitly. Unscoped confirmedlatestmeans transfers dated within the last two weeks plus already-announced moves dated up to a week ahead; a scoped query returns the team's/player's most recent transfers with no date windowstart_date/end_date(YYYY-MM-DD): required whentimeframe=date_range; the window must not exceed 31 days (the Sportmonks API limit for transfer date ranges)
Output:
{ data, meta }envelope, max 25 results; each entry containsid,player({ id, name }),from_team({ id, name }),to_team({ id, name }),type(confirmed/rumour),transfer_kind(resolved transfer type, e.g.Transfer,Loan,End of loan),fee, anddatefeeisnullfor undisclosed deals (never0)- results are sorted newest-dated first (duplicated upstream rows are removed) before the 25-cap
Constraints / validation:
- requires either an
id(withentity_type) or an explicittimeframe— a bare call with neither is rejected idwithoutentity_typeis rejected- a
date_rangewindow longer than 31 days is rejected (Sportmonks caps transfer date ranges at 31 days) date_rangecannot be combined with anid— there is no date-scoped team/player endpoint, so the combination is rejected rather than silently ignoring the windowdate_rangecannot be combined withtype='rumour'— the Sportmonks rumour between endpoint matches records by update time, not rumour date, so results would be wrong or emptytype='rumour'without the rumours add-on returns a clearauthentication_errortelling you to retry withtype='confirmed'or upgrade
Notes:
- rumours have no dedicated "latest" feed, so an unscoped
timeframe=latestrumour query reads the full rumour feed (still capped at 25) - rumour-only fields (probability, source, currency) are dropped to keep one uniform shape across both types
get_fixture_news
Get match news for a fixture: expert-written pre-match previews and AI-generated post-match reports.
Inputs:
fixture_id(required)timing(optional):prematch(default, richest narrative),postmatch, orboth
Output:
{ data, meta }envelope.datacarries the requestedprematchand/orpostmatcharrays; each article hasid,title,type,league_id, andbodybodyis the article's lines sorted by id (their publish order — the API returns them shuffled) and joined into one readable prose string; per-lineid/newsitem_id/typeare droppedmetais{ timing, returned: { prematch?, postmatch? } }, echoing the timing and per-type article counts- A fixture with no news returns empty arrays and zero counts (not an error); an unknown fixture id returns
not_found
Notes:
- pre-match coverage is limited to top European competitions (UCL, Premier League, La Liga, Bundesliga, Serie A, Ligue 1); post-match coverage is broader
- pre-match previews are published ahead of kickoff; post-match reports become available after full time (exact lead/latency windows are not guaranteed)
- standalone (not a
get_fixture_detailsinclude) because each article is several paragraphs of prose — bundling it into every fixture call would inflate token usage
get_match_facts
Get pre-calculated Match Facts (structured match insights) for a fixture.
Inputs:
fixture_id(required)basis(required):h2h(the head-to-head matchup between the two teams) orteam(each team's recent form) — request them separately, not togethercategory(optional): one or more ofstatistics,streaks,players,coaches,statistic_comparisons
Output:
{ data, meta }envelope; each fact hastype_id,category,participant,natural_language(the sentence), anddata(the raw object). Both the sentence and the raw data are always returnedmetais{ returned, cap, possibly_more, basis, category, scope }, echoing the applied filters (categoryisnullwhen no category filter is applied;scopereports which scope was served)
Fixed filters (always applied):
- only facts with a non-null
natural_languagevalue (filtered MCP-side across all pages) league_matches-scoped facts when available; on cup/international fixtures (e.g. the World Cup) where league-scoped sentences are absent, falls back toall_matches-scoped facts —meta.scopereports which scope was served, andall_matchessentences aggregate across competitions
Built-in limits / notes:
- the raw dataset is 400-800 facts per fixture across many pages; after filtering a rich fixture yields ~85-90 facts per basis. Capped at 100 (
possibly_moreflags truncation) statistic_comparisonsexists only forbasis='team'; requesting it withbasis='h2h'returns a clear validation error- requires the Match Facts add-on; coverage varies by league, so a fixture with no facts returns an empty list (an unknown fixture id returns
not_found)
get_commentaries
Get the key moments of a fixture's text commentary — goals and cards.
Inputs:
fixture_id(required)
Output:
{ data, meta }envelope; each line is{ minute, comment, is_goal }, sorted chronologically.is_goalistruefor a goal andfalsefor a cardminuteis the numeric match minute; stoppage time folds in as a string like"45+9"metais{ fixture_id, returned }
Built-in limits / notes:
- returns only the
is_importantlines (goals + cards) — typically ~10-25 per match. A full play-by-play is ~110 lines; returning all of it is deferred until the server has general pagination - coverage varies by competition, so a fixture with no commentary returns an empty list (an unknown fixture id returns
not_found)
get_totw
Get the latest Team of the Week for a league — the 11 highest-rated players of the most recently completed round, per Sportmonks' player-rating model (best per positional role, max 3 per team, in the formation with the highest combined rating).
Inputs:
league_id(required)
Output:
{ league_id, round, formation, players, meta }playersis the 11-player selection sorted byformation_position(goalkeeper first); each is{ formation_position, position, player_id, player_name, team_id, team_name, team_short_code, rating }.player_nameis the trimmed display name;positionis the readable role (Goalkeeper,Defender,Midfielder,Attacker);ratingis numericformation(e.g."4-3-3") and theroundsummary ({ id, name, starting_at, ending_at, finished }) are surfaced once at the top levelmetais{ returned, scope, league_id, round_id }(scopeis always"latest")
Built-in limits / notes:
- always the latest TOTW (by-round and all-leagues variants are not exposed; the
roundsummary tells you which round the selection is from) - requires the Team of the Week add-on; coverage is round-based competitions only (no knockouts/playoffs), so a real league with no TOTW returns an empty
playerslist withreturned 0— a clear empty result, not an error (an unknown league id returnsnot_found)
Resources
The server exposes two MCP Resources. Fetch them via resources/read (or @-mention in clients that support it, e.g. Claude Desktop, Cursor).
| URI | MIME type | Content |
| --- | --- | --- |
| sportmonks://documentation | text/plain | Server overview: tool list, behavior notes, links to official Sportmonks docs |
| sportmonks://openapi | application/json | Official Sportmonks Football OpenAPI spec (fetched fresh on every read) |
Claude Code does not auto-load resources — users attach them explicitly via @-mention when needed.
Observability
The server writes one JSON line per tool call to stderr and to a local log file, including tool name, arguments, duration, and outcome. See OBSERVABILITY.md for the log format, default paths per OS, and debugging flows.
Error Format
All tool errors are returned as JSON with this shape:
{
"ok": false,
"error": {
"type": "validation_error",
"message": "The 'id' field must be a positive integer.",
"how_to_fix": "Call the tool again with 'id' set to a positive integer such as 501 or 19735.",
"details": null
}
}Example Prompts
You don't need to know any Sportmonks ids — the server will look them up. Try any of these:
Finding things
- Search for "Barcelona" — is it a team, a league, or both?
- Find the player ID for Erling Haaland
- Search for leagues named "Premier League"
Teams & players
- Get the profile for Manchester City
- Show me the current squad for Liverpool
- Who was in Arsenal's squad during the 2021/22 season?
- Get the profile for Vinicius Jr — which team is he at?
- Who is the current coach of Manchester City?
- Find the coach Pep Guardiola and tell me which club he manages
- Look up the referee Anthony Taylor
- Who are the match officials for fixture 123456?
- Find the venue Old Trafford — capacity, surface, and location
- Who are Liverpool's traditional rivals?
Fixtures
- Show me Real Madrid's upcoming fixtures for the next two weeks
- What were Chelsea's results over the last 30 days?
- Are there any live matches right now?
- Get the full details for fixture 123456 including lineups and events
- What were the xG and xGoT for both teams in fixture 123456?
- Where can I watch fixture 123456 — which TV channels are broadcasting it?
- Who controlled fixture 123456 and when did the momentum swing? Use the pressure index
- Give me the pre-match preview for fixture 123456
- Summarise the post-match report for fixture 123456
- Give me the head-to-head match facts for fixture 123456
- What are the team-form streaks and statistic comparisons for fixture 123456?
- What were the key moments — goals and cards — in fixture 123456?
- Who made the Premier League team of the week?
Standings & seasons
- Show me the current Premier League standings
- What seasons has the Champions League had? Give me the most recent ones first
- Who are the top 10 goal scorers in Serie A this season?
- Show me the top 5 assist providers and top 5 yellow card recipients in the Bundesliga
Odds
- What are the match-winner odds for England's next fixture?
- Which bookmaker offers the best odds on a home win in tomorrow's Madrid derby?
Season statistics
- How many goals and assists did Haaland record in the 2025/26 Premier League season?
- Compare Manchester City's goals scored and conceded at home vs away last season
Player match stats
- How many minutes did each Iceland player get in the Argentina friendly?
- Get the player ratings and passes for both teams in yesterday's final
Transfers
- Show me the latest confirmed transfers
- What transfer rumours are linked to Real Madrid right now?
- List all transfers in January 2026
Briefings (using prompts)
- Give me a pre-match briefing for the upcoming El Clásico — use the
match_previewprompt - Run the
team_overviewprompt for Manchester United - Generate a
league_overviewfor the Premier League
Building with the API
- Fetch a sample fixture with lineups and events using
get_fixture_details, then write me a TypeScript interface for the response shape - Use
get_standingsto get the current La Liga table and render it as a formatted markdown table - Build a function that takes a team name, searches for it, then fetches its next 5 fixtures
Feedback
This server is in beta and the team actively welcomes feedback. If you encounter issues, unexpected behavior, or have ideas for improvements, please email [email protected] and include:
- The specific tool that was called and its arguments
- What you expected versus what actually happened
- Your AI client (Claude Desktop, Claude Code, Cursor, etc.) and the version of this package
That detail helps us reproduce and fix issues quickly.
Development
npm install
npm run build
npm test