Skip to main content
Glama
SergioCastro02

NBA MCP Server

README.md
# NBA MCP Server

[![CI](https://github.com/SergioCastro02/nba-mcp-server/actions/workflows/ci.yml/badge.svg)](https://github.com/SergioCastro02/nba-mcp-server/actions/workflows/ci.yml)
[![Python](https://img.shields.io/badge/python-3.11%2B-blue)](pyproject.toml)
[![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE)

A [Model Context Protocol](https://modelcontextprotocol.io) server that gives any
MCP-compatible LLM client (Claude Desktop, Cursor, VS Code, a custom LangGraph
agent) live access to NBA stats, standings, rosters, scores and box scores.

```
"Compare LeBron James and Kevin Durant's playoff scoring."
"Who led the league in assists in 2023-24?"
"What was the final score and top scorers of game 0042300405?"
```

The LLM answers these by calling tools on this server — no hard-coded data, no
scraping in the prompt.

---

## Why this project exists

Most "LLM + API" demos wire one API to one chatbot with glue code. MCP replaces the
glue with a protocol: write the server once, and every compatible client can use it.
This repo is a small, production-shaped example of that — caching, error handling,
a clean data/protocol split, tests, and two transports (stdio + HTTP).

## Tools

| Tool | What it does |
|------|--------------|
| `find_player(name)` | Resolve a player name → `player_id` |
| `find_team(name)` | Resolve a team name / city / abbreviation → `team_id` |
| `list_teams()` | All 30 teams with ids |
| `player_career_stats(player_id)` | Career + playoff averages + season-by-season |
| `league_standings(season)` | Standings by conference for a season |
| `team_roster(team_id, season)` | Roster with position, age, experience |
| `stat_leaders(season, stat, limit)` | Per-game leaders (points/rebounds/assists/…) |
| `scoreboard(game_date)` | Games and scores for a date (defaults to today) |
| `game_boxscore(game_id)` | Final score + top 5 scorers per side |

Every tool returns `{"ok": true, "data": ...}` or `{"ok": false, "error": "..."}` so
the model can recover from a bad name instead of hallucinating.

### Resources

| URI | Contents |
|-----|----------|
| `nba://teams` | The 30 teams with ids (JSON) |
| `nba://glossary` | What each stat field means (Markdown) |

### Prompts

| Prompt | Arguments |
|--------|-----------|
| `scouting_report` | `player_name`, `focus` (overall/offense/defense/playoffs) |
| `compare_players` | `player_a`, `player_b` |

Both walk the model through calling the tools and grounding every claim in the
returned numbers.

## Architecture

```
MCP client (Claude Desktop / Cursor / LangGraph agent)
        │  JSON-RPC over stdio or streamable HTTP
        ▼
  server.py         ── tool schemas (docstrings + type hints), thin error wrapping
        ▼
  nba_client.py     ── normalizes nba_api's 80-column rows into small LLM-friendly dicts
        ▼
  cache.py          ── TTL disk cache (scores 5 min, season stats 6 h, rosters 30 d)
        ▼
  nba_api  ──►  stats.nba.com
```

`nba_client.py` has no MCP dependency on purpose — the same layer will feed the RAG
pipeline and agents in the companion project.

## Install

```bash
git clone https://github.com/SergioCastro02/nba-mcp-server
cd nba-mcp-server
python -m venv .venv && . .venv/Scripts/activate   # or source .venv/bin/activate
pip install -e ".[dev]"
```

## Run

```bash
nba-mcp-server                          # stdio (for Claude Desktop / Cursor)
nba-mcp-server --http                   # streamable HTTP on http://localhost:8000/mcp
nba-mcp-server --http --host 0.0.0.0    # bind all interfaces (containers)
```

`GET /healthz` returns `{"status": "ok"}` for load balancers and container probes.

### Claude Desktop

Add to `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "nba": {
      "command": "nba-mcp-server"
    }
  }
}
```

(Use the absolute path to the `.venv` binary if it isn't on your PATH.)

### MCP Inspector

```bash
npx @modelcontextprotocol/inspector nba-mcp-server
```

### End-to-end demo

Launches the server as a subprocess and drives it over stdio, exactly like a real
client would — resolves names to ids, compares two players' playoff stats, pulls
season leaders and a box score:

```bash
python examples/mcp_client_demo.py
```

## Deploy to AWS

Terraform stack for ECS Fargate + ALB + CloudWatch in [`infra/`](infra/README.md).

## Development

```bash
ruff check .
pytest
```

## Roadmap

- [x] `resources` for static reference data (`nba://teams`, `nba://glossary`)
- [x] `prompts` (`scouting_report`, `compare_players`)
- [x] IaC to deploy the HTTP transport to AWS ECS Fargate — see [`infra/`](infra/)
- [ ] Wire continuous deployment (OIDC + push job) and stand up a public demo URL
- [ ] Publish to PyPI

This server is the tool layer for a larger project: a multi-agent NBA analysis
platform (LangGraph orchestration + RAG over news/recaps + AWS Bedrock).

## Data source & limits

Data comes from `stats.nba.com` via [`nba_api`](https://github.com/swar/nba_api).
It is unofficial and rate-limited; the disk cache absorbs most repeat calls.
Not affiliated with the NBA.

## License

MIT

TDQS

A3.9/5.0

Scored across 9 tools

Disambiguation5/5

Each tool targets a distinct NBA entity or query type: player lookup, team lookup, team enumeration, player stats, standings, roster, stat leaders, scoreboard, and box score. Even the closest pair, find_team and list_teams, are clearly differentiated as search versus full list.

Naming Consistency3/5

Tool names mix imperative verb-led forms like find_player, find_team, and list_teams with noun/resource forms like player_career_stats, league_standings, and game_boxscore. The names are readable and consistently snake_case, but the verb/noun pattern is not applied uniformly.

Tool Count5/5

Nine tools is a well-scoped size for an NBA data server, covering the major stat and score queries without redundancy. Each tool earns its place in the set.

Completeness4/5

The tool set covers the core read-only NBA workflow: player and team lookup, player stats, rosters, standings, stat leaders, scoreboard, and box score summaries. Gaps such as full box score details, player game logs, or a season schedule are minor and can be worked around.

Maintenance

ActivityMaintained
ResponsivenessNo issues