MCP server documentation

Version 1.0.0 · Last updated 27 October 2026

Overview

The HoopMetrics MCP server exposes NBA statistics, schedules, game projections and the HoopMetrics model’s picks to any Model Context Protocol client. All tools are read-only.

https://mcp.hoopmetrics.app
PropertyValue
Endpointhttps://mcp.hoopmetrics.app (the host root)
TransportStreamable HTTP — JSON-RPC 2.0 over POST; GET with Accept: text/event-stream for the stream
Server namehoopmetrics 1.0.0
AuthenticationOAuth 2.1 bearer token (authorization code + PKCE S256), dynamic client registration
Scopesstats:read (all tools), picks:read (picks)

Connecting

Paste https://mcp.hoopmetrics.app into your client. Step-by-step setup for Claude, Claude Code, Cursor and VS Code is on the MCP server page. Opening the endpoint in a browser redirects to that page; MCP clients are served the protocol.

Authentication

The server follows the MCP authorization spec. Clients do not need to be configured with any of the URLs below — they are discovered:

  1. A request without a token gets 401 with WWW-Authenticate: Bearer resource_metadata="…".
  2. The protected resource metadata (RFC 9728) names the resource https://mcp.hoopmetrics.app and the authorization server https://hoopmetrics.app/api/auth.
  3. The client reads the authorization server metadata (RFC 8414), registers itself (RFC 7591) and runs the authorization code flow with PKCE, passing resource=https://mcp.hoopmetrics.app.
  4. The user signs in to HoopMetrics (or creates a free account) and approves the requested scopes on the consent screen.
  5. The client calls the endpoint with Authorization: Bearer <access token>. Tokens are signed JWTs with issuer https://hoopmetrics.app/api/auth and audience https://mcp.hoopmetrics.app; refresh tokens are issued with offline_access.
EndpointURL
Protected resource metadatahttps://mcp.hoopmetrics.app/.well-known/oauth-protected-resource
Authorization server metadatahttps://hoopmetrics.app/.well-known/oauth-authorization-server/api/auth
Issuerhttps://hoopmetrics.app/api/auth
Authorizationhttps://hoopmetrics.app/api/auth/oauth2/authorize
Tokenhttps://hoopmetrics.app/api/auth/oauth2/token
Dynamic client registrationhttps://hoopmetrics.app/api/auth/oauth2/register
Revocationhttps://hoopmetrics.app/api/auth/oauth2/revoke
JWKShttps://hoopmetrics.app/api/auth/jwks

Scopes

stats:readRequired for every call. Players, teams, games, schedules, leaderboards and game projections.
picks:readThe model’s picks: get_picks and the picks / prop_lines sections of get_game.

Tools

Parameters below are generated from the schemas the server registers, so they always match what your client sees in tools/list. Results come back as one text content block holding JSON.

Start with search to turn a name into a slug, then call get_player or get_team. Games are addressed by game_id from list_games or get_team.

list_games

stats:readGames on a date

Schedule and results for one date (YYYY-MM-DD, default today UTC). Returns game_id for get_game.

PARAMETERS

date
string
Date, YYYY-MM-DD
format YYYY-MM-DD

RETURNS

date, count and games[]: game_id, game_date, game_time_utc, status (scheduled / live / final), period, scores, and both teams' id, abbreviation, name, slug and colour.

EXAMPLE REQUEST
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "list_games",
    "arguments": {
      "date": "2026-10-27"
    }
  }
}

get_game

stats:readGame detail

One game by game_id: teams, status, score, model projection, and (with picks:read) the model's picks and player prop lines.

The picks and prop_lines sections need picks:read; without it they read “requires picks:read scope”.

PARAMETERS

game_idrequired
string
NBA game id, e.g. 0022600123
sections
("projection" | "picks" | "prop_lines")[]
Which optional sections to include (default all). prop_lines is large (every line of every player); leave it out when only the model's picks are needed.

RETURNS

game; projection (model version, home_win_prob, projected points for each side, projected_total, projected_spread, injury adjustments in points); picks.game (team markets), picks.players (player props) and picks.prop_lines (every line offered for every player).

EXAMPLE REQUEST
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "get_game",
    "arguments": {
      "game_id": "0022600128",
      "sections": [
        "projection",
        "picks"
      ]
    }
  }
}

get_player

stats:readPlayer profile

Bio, season averages and recent game log for a player (slug from search).

PARAMETERS

slugrequired
string
Player slug, e.g. nikola-jokic
season
string
Season like 2026-27 (default: current)
format YYYY-YY
games
integer
How many recent games (default 12)
1–40

RETURNS

player (bio, team, position, draft), season, season_stats (per-game averages, shooting splits, rebounds split, fouls drawn, plus-minus) and recent_games[] (box score line per game, newest first).

EXAMPLE REQUEST
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "get_player",
    "arguments": {
      "slug": "nikola-jokic",
      "games": 10
    }
  }
}

get_team

stats:readTeam profile

Record, team stats, roster and latest games for a team (slug from search).

PARAMETERS

slugrequired
string
Team slug, e.g. boston-celtics
season
string
Season like 2026-27 (default: current)
format YYYY-YY

RETURNS

season, team (record, per-game team stats and league ranks), roster[] with each player's per-game averages, and recent_games[] — the team's 10 latest games by date, scheduled ones included.

EXAMPLE REQUEST
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "get_team",
    "arguments": {
      "slug": "boston-celtics"
    }
  }
}

get_leaderboard

stats:readSeason leaders

Top players by a per-game stat for a season.

PARAMETERS

stat
"pts" | "reb" | "ast" | "stl" | "blk" | "fg3m" | "min"
Per-game stat to rank by
default "pts"
season
string
Season like 2026-27 (default: current)
format YYYY-YY
limit
integer
How many players (default 25)
1–100

RETURNS

season, stat and leaders[] in rank order — player slug and name, position, team, games played and the per-game value.

EXAMPLE REQUEST
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "get_leaderboard",
    "arguments": {
      "stat": "ast",
      "limit": 10
    }
  }
}

get_picks

picks:readModel picks

Current value picks from the HoopMetrics model: game markets (moneyline, totals, handicap) and top player props, ordered by edge, each with the bookmaker selection behind its price (sb_event_id / sb_market_id / sb_selection_uuid). Requires picks:read.

PARAMETERS

player_limit
integer
Player prop picks to return (default 30)
1–100
only_value
boolean
Return only picks with verdict = value (drops pass / longshot / low-sample). Longshot = positive EV but price > 4.0 or model < 25%

RETURNS

note, game_picks[] and player_picks[], ordered by edge: game_id, market, selection, line, price, model_prob, implied_prob, edge, verdict, sample_games, the bookmaker selection ids (sb_*) and price_captured_at. Player picks add the player, projection, hit rates (last 5, last 15, season, head-to-head) and the opponent's rank against that stat.

EXAMPLE REQUEST
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "get_picks",
    "arguments": {
      "only_value": true,
      "player_limit": 20
    }
  }
}

Data & picks

Every answer is a snapshot of the HoopMetrics database, not a live feed. Lines and picks refresh about every 30 minutes on game days, scores every couple of hours, box scores every morning, and the injury report shortly before each line refresh. Seasons are written 2026-27; the current season is the default everywhere.

Picks are research, not advice. They compare the model’s probability with a bookmaker’s price; the fields that describe a pick are:

FieldMeaning
edgemodel_prob × price − 1. 0.05 means the model expects a 5% return per unit at that price.
model_probThe model's probability that the selection wins.
implied_prob1 / price — what the price implies, margin included.
priceDecimal odds at price_captured_at.
price_captured_atWhen that price was read (UTC). Re-check the live price before acting on a quote older than a few hours.
sb_event_id · sb_market_id · sb_selection_uuidIdentify the bookmaker event, market and selection the price came from. null when the selection is no longer offered.
sample_gamesCurrent-season games behind the estimate.
game_time_utcTip-off in UTC. game_date is the US calendar date of the game.

Verdicts

verdictMeaning
valuePositive edge with enough current-season sample to back it.
passNo edge at the current price.
longshotPositive expected value, but price above 4.0 or model probability under 25%.
low-samplePositive edge that the current-season sample does not yet confirm.
injury-riskPlayer props only: the injury report gives the player a 10–50% chance of missing the game, so the pick is never marked value. Above 50% there is no pick at all.

Errors

ResponseWhen
401 invalid_requestNo Authorization header. Start the OAuth flow from WWW-Authenticate.
401 invalid_tokenExpired or malformed token, or one issued for another resource (issuer or audience mismatch). Refresh or re-authorize.
403 insufficient_scopeThe token lacks stats:read.
Tool result isErrorUnknown slug or game_id, or a picks tool called without picks:read. The text says which.

Server instructions

The server sends these instructions to the client on initialize:

INSTRUCTIONS
HoopMetrics is NBA research: box scores, season splits, schedules and a v1 betting model priced against bookmaker lines.
Every figure is a snapshot from the HoopMetrics database, not a live feed; carry the game_date / season through to what you report.
Identity first: call search to turn a name into a slug, then get_player / get_team with that slug. Games are addressed by game_id (from list_games or get_team).
Picks (get_picks, and the picks section of get_game) are model output with an "edge" = model probability × decimal price − 1; they need the picks:read scope and are research, not advice.
Every pick carries the bookmaker selection behind its price — sb_event_id (event id), sb_market_id and sb_selection_uuid — plus price_captured_at, the time that quote was read. They are null when the selection has left the offer; re-check the live price before acting on a quote older than a few hours.

Changelog

  • 1.0.0 — seven read-only tools, OAuth 2.1 with dynamic client registration, endpoint at the root of mcp.hoopmetrics.app.