Skip to main content
Glama
mayank-nayak

mcp-sports-server

by mayank-nayak
README.md
# Sports-Stats MCP Server

A [Model Context Protocol](https://modelcontextprotocol.io) server that exposes a
read-only SQLite database of **real NBA game data** to LLM clients (Claude Desktop,
etc.), enabling safe natural-language querying of the data.

The dataset is FiveThirtyEight's historical
[NBA Elo dataset](https://github.com/fivethirtyeight/data/tree/master/nba-elo):
**63,157 games across 69 seasons (1947–2015)**. `npm run seed` downloads it and
loads it into a clean relational schema.

## What it does

The server registers two tools that an LLM can call:

| Tool | Description |
|------|-------------|
| `list_schema` | Returns every table with its columns and types, so the model learns the data shape before querying. |
| `run_query` | Executes a single **read-only** `SELECT` and returns the rows as JSON. |

### Safety: read-only guardrails

`run_query` validates every statement before execution and rejects:
- anything that isn't a `SELECT` / `WITH` query,
- write & DDL keywords (`INSERT`, `UPDATE`, `DELETE`, `DROP`, `ALTER`, `CREATE`, `PRAGMA`, `ATTACH`, …),
- multiple chained statements (SQL-injection style `; DROP …`).

As a second line of defense, the database itself is opened in `readonly` mode.

## Data model

Two tables derived from the source dataset (the raw CSV stores two rows per game —
one per team — which the seed collapses into one clean row per game):

```
teams(id, franchise)                          -- e.g. ('LAL', 'Lakers')
games(id, season, game_date, is_playoffs, neutral,
      home_team_id → teams, away_team_id → teams,
      home_score, away_score)
```

## Setup

```bash
npm install
npm run build      # compile TypeScript -> dist/
npm run seed       # download the real dataset + populate sports.db (~18 MB download, cached)
```

## Use with Claude Desktop

Add this to your Claude Desktop config
(`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS),
then restart Claude Desktop:

```json
{
  "mcpServers": {
    "sports-stats": {
      "command": "node",
      "args": ["/Users/mayanknayak/Desktop/Personal Projects/mcp-sports-server/dist/index.js"]
    }
  }
}
```

You'll see a tools icon appear. Now ask natural-language questions:

- "Which franchise won the most games in the 1996 season?"
- "Show the 5 highest-scoring games of all time."
- "What's the Lakers' all-time playoff win percentage?"
- "Which team had the biggest average margin of victory in the 1980s?"

Claude will call `list_schema`, then write and run a `SELECT` via `run_query`.

## Smoke test (no Claude Desktop needed)

```bash
node test-client.mjs
```

Spins up the server over stdio and verifies tool discovery, schema introspection,
a real aggregate query, and that a `DELETE` is blocked by the guard.

## Stack

TypeScript · `@modelcontextprotocol/sdk` · `better-sqlite3` · `zod`

Maintenance

ActivityInactive
ResponsivenessNo issues