MaxPreps
National high-school sports database (CBS Sports). No auth, no API key, no browser bridge — plain curl from anywhere, no signed-in tab.
Pages are Next.js SSR: every public page has a companion JSON route at
/_next/data/<buildId>/<path>.json. scripts/mpx.mjs resolves the buildId, fetches, and decodes.
# scripts/mpx.mjs sits next to this SKILL.md — point M at it.
# Standalone install: M=~/.claude/skills/maxpreps/scripts/mpx.mjs
# Shipped with maxpreps-mcp: M=<plugin-root>/skills/maxpreps/scripts/mpx.mjs
M=~/.claude/skills/maxpreps/scripts/mpx.mjs
node "$M" search "myers park"
node "$M" schedule nc/charlotte/myers-park-mustangs/football/25-26/schedule
mpx.mjs <kind> [path] — path is the site path without leading/trailing slashes.
With no path it decodes a payload on stdin. Kinds:
search "<query>"— schools + athletes (start here)teams <school-or-team-path>— every team path the school publishesschedule— games with scores, result, home/away (decodes the positional array)roster— players with jersey, position, height, class (decodes the positional array)stats— team stat leaders ·team— season info, record, standings, rankingsrankings— ranked leaderboard for a sport (see path note below)teamrankings/standings— one team's ranks; its conference tablestatcats/statleaders— stat leaderboard index; one board's ranked athletesschool— school profile + nearby schools ·athlete— one athlete's careerraw— undecodedpageProps, for anything without a dedicated kindbuildid— print the cached build id
Flags: --all keep isDeleted rows · --raw emit undecoded pageProps · --fresh (buildid) bypass cache.
Resolve before you fetch
Don't guess paths — two lookups, both cheap:
# 1. school -> canonicalUrl
node "$M" search "mater dei" | jq -r '.schools[] | "\(.name) (\(.city), \(.state)) \(.canonicalUrl)"'
# 2. school path -> real team paths
node "$M" teams ca/santa-ana/mater-dei-monarchs | jq -r '.[] | "\(.path) [\(.gender) \(.sport) \(.level)]"'
Team path grammar is <sport>[/girls][/jv|/freshman][/<yy-yy>]/<tab>, but the segments are
not predictable — the default gender varies by sport (golf/girls + golf/spring,
field-hockey is Girls with no gender segment). Always take paths from teams.
Tabs: schedule, roster, stats, rankings, standings. Omit the tab for the team home page.
Leaderboards live on their own path: [<st>/]<sport>[/<season>]/rankings/<page> — the trailing
page number is required (omitting it 404s) and each page holds 25 teams.
Seasons
Current season = no year segment. Prior seasons insert <yy-yy> before the tab:
.../football/25-26/schedule. Roughly 20 years of history; mpx teams <team-path> lists
every season with its year, or read .seasons from mpx team <team-path>.
Gotchas
buildIdrotates on every deploy.mpxcaches it for 6h and silently re-resolves on a 404, so this is handled — but a hand-rolledcurlagainst a stale id 404s. Use the script, ornode "$M" buildidfirst.- An out-of-season team looks broken. Before opening day the current season has an empty roster and no results. That is correct data, not a failure — check the prior season (
25-26) before reporting nothing. isDeletedrows are real and numerous. The 25-26 Myers Park football roster carries 87 entries, 63 of which the site renders; the rest are soft-deleted duplicates.mpxfilters them by default (matching the site) —--allkeeps them. Same for contests.resultStringis winner-first, not team-first. A loss reads"L 20-13"even though the team scored 13. For team-vs-opponent use the decodedteamScore/opponentScorefields, whichmpxorients correctly.homeAwayType:0= home,1= away,2= neutral site (decoded tohomeAway; any other value →unknown).classYearis a number: 9–12, decoded toclassYearLabel(Fr./So./Jr./Sr.).- Statewide scoreboards are not in the JSON.
/<st>/<sport>/scores/returns only page chrome — the game list is hydrated by a route that never fires server-side. Get scores per-team fromscheduleinstead; don't claim a state had no games. - Search is strict —
"myers park high"returns zero,"myers park"returns the school. Drop qualifiers and retry before concluding a school is absent.
Read-only and low-volume by design; there is no write path. See references/recipes.md for
field shapes, the positional key maps, and ready-to-run jq.
For the same data as typed MCP tools — usable from claude.ai or any client without this CLI — see maxpreps-mcp, which ships this skill.
