euroleague-open-data

euroleague-open-data

An MCP server that lets an LLM query a DuckDB warehouse of EuroLeague and EuroCup basketball data in natural language, offering tools for player and team stats, shot charts, boxscores, and fantasy draft analysis without contacting upstream APIs.

Category
访问服务器

README

euroleague-open-data

An open EuroLeague / EuroCup basketball data warehouse, and an MCP server that lets an LLM query it in natural language.

Unofficial. Not affiliated with, endorsed by, or approved by Euroleague Basketball. Data originates from Euroleague Basketball and is retrieved from publicly accessible endpoints. For research and educational use. See DISCLAIMER.md.

Why this exists

The upstream EuroLeague API is undocumented, unversioned, and rate-limited at roughly 10 requests per minute by Cloudflare. That makes it unusable for interactive analysis: three questions in a row from one user would black out everyone else for five minutes.

So this project inverts the problem. A slow, polite, resumable crawler pulls the data into a local DuckDB warehouse once. Everything else — the MCP server, the Parquet exports — reads that snapshot.

The MCP server never contacts upstream. It holds no HTTP client. No amount of traffic to this project can generate load on Euroleague Basketball's infrastructure.

Status

Four seasons loaded: EuroLeague E2024 and E2025, EuroCup U2024 and U2025. Plus the announced 2026-27 EuroLeague squads, so the draft tools work before a game has been played.

Component State
Throttled crawler with permanent cache working
DuckDB warehouse, 8 base tables working
Validation suite, 8 reconciliation checks working
Derived analytics (TS%, eFG%, usage, Four Factors, shot zones) working
MCP server, stdio transport, 15 tools + 3 resources working
HTTP transport + landing page, Docker image build-tested working
Public deployment on Render (free instance, sleeps when idle) live
Full backfill (52 seasons, 12 122 games) not started, ~50h of crawling
Dataset publishing to GitHub Releases / HuggingFace not started

Quick start

Requires uv and Python 3.12+.

git clone https://github.com/McNews11/euroleague-open-data && cd euroleague-open-data
uv sync --extra etl

--extra etl pulls in the crawler's dependencies. Plain uv sync installs only what is needed to serve an existing warehouse, which is what the deployed image does — it keeps polars, pyarrow and httpx out of the container and halves its size.

Build the warehouse. The crawl is deliberately slow — about two hours for one season — and it is safe to interrupt and rerun, because every response is cached permanently.

uv run euroleague-etl --season E2025

Already have the cache and only changed the schema? Skip the network entirely:

uv run euroleague-etl --season E2025 --skip-crawl

Connect it to Claude

Claude Code

claude mcp add euroleague --env EUROLEAGUE_DB=$PWD/data/euroleague.duckdb -- $PWD/.venv/bin/python -m euroleague_open_data.mcp_server

Remote, for sharing with other people

A hosted deployment serves the same tools over HTTPS, so anyone can connect by URL with nothing installed — and it is the only way to use this from ChatGPT, which cannot run local MCP servers.

claude mcp add --transport http euroleague https://euroleague-open-data.onrender.com/mcp

It runs on a free instance that sleeps after 15 minutes idle, so the first request after a quiet spell takes about a minute. Everything after it is fast. To host your own, see docs/DEPLOY.md — the landing page at / fills in whatever hostname it is served from, so a fork needs no edit.

Note that ChatGPT custom connectors require a paid plan (Plus, Pro, Business, Enterprise or Edu) with Developer mode enabled. Claude Code and Claude Desktop work on any plan.

Claude Desktop

Add to claude_desktop_config.json:

{
  "mcpServers": {
    "euroleague": {
      "command": "/absolute/path/to/euroleague-open-data/.venv/bin/python",
      "args": ["-m", "euroleague_open_data.mcp_server"],
      "env": {
        "EUROLEAGUE_DB": "/absolute/path/to/euroleague-open-data/data/euroleague.duckdb"
      }
    }
  }
}

Things to ask it

Who had the best true shooting percentage in EuroLeague last season, minimum 20 games?
Compare Vezenkov and Nwora on efficiency and usage.
Which team had the best defensive rating, and which Four Factor drove it?
Show me Micic's shot chart by zone.
Which games are unreliable for lineup analysis?
Who changed clubs this summer, and which of them scored well last season?

That last one matters. The warehouse tracks its own completeness, so the model can say "this game has no shot data" instead of inventing a number.

Fantasy drafting

Built for BasketNews Fantasy draft mode: private leagues of 3–12 managers, 13-player rosters, unique squads, snake or reverse-snake order.

Fantasy points are recomputed exactly from boxscores, not estimated. Every term in the modern scoring system maps onto a stored field, so a player's score here is the score the game would award.

Ranking is by value over replacement, not by average. In a draft each manager gets a unique roster, so what decides a pick is how much better a player is than the next one available at the same position — and that depends on league size. An 8-team league and a 12-team league produce different boards from the same data.

uv run python -m euroleague_open_data.fantasy --teams 8 --scoring classic

Two things about scoring are worth knowing before you trust a late-round pick:

  1. The "classic" formula is not published for draft mode, so it was reverse-checked against the live site. Vezenkov, E2025 round 1: PIR 27 in a win scores 29.7 there, and 27 × 1.1 = 29.7 here. The PIR base and the win multiplier are confirmed. The loss multiplier is still inferred — and measured to be immaterial: rebuilding the board with 1.0 instead of 0.9 gives rank correlation 0.9996, an unchanged top 7, and an average move of 1.4 places.
  2. It matters less than it looks. Rank correlation between classic and modern is 0.99 and seven of the top eight are the same players. The disagreement shows up in the middle: one player moves 94th to 61st, which in an 8×13 draft is a different round.

Tools

Tool Purpose
search_players fuzzy name → canonical person_code
search_teams fuzzy name → canonical team_code, handles sponsor renames
get_player_stats season or career, totals / per-game / per-36, plus TS%, eFG%, usage
get_team_stats ratings and Four Factors, team and opponent
get_game_boxscore full game detail with completeness flags
get_shot_chart zone aggregates, optionally raw x/y coordinates
run_sql read-only DuckDB SELECT — the escape hatch for unanticipated questions
get_draft_board draft ranking by value over replacement, sized to your league, adjusted for minute pressure, with EuroCup and NBA arrivals converted and labelled
plan_snake_draft your picks in snake order, and who should survive until each
compare_draft_candidates head to head for a specific pick decision
get_player_fantasy_log game-by-game fantasy points, for form and role changes
get_coach_rotation how deep a coach's rotation runs — the ceiling on minutes
get_role_outlook minutes and production a club vacated, by position
get_transfers announced 2026-27 squads: who moved, who stayed, who is unsigned
get_squad_outlook a club's coming season: minutes freed, minutes claimed, who has no history

Resources: euroleague://schema, euroleague://coverage, euroleague://data-quality.

run_sql runs on a read-only connection, permits a single SELECT/WITH, caps rows, and cancels after 15 seconds.

Correcting what the data cannot know

The API lists who is on a roster. It never says why someone is absent, so a player who signed in the NBA looks identical to one still negotiating — and the first should not appear on a draft board at all. data/overrides.csv is where a human records the difference:

player,status,note
"DIALLO, ALPHA",left_league,signed in the NBA after Monaco left the EuroLeague
uv run euroleague-overrides

Rows that match no player, or match two, raise rather than being skipped — a correction that silently fails to apply is worse than none, because it looks like it worked.

Data quality

Validation runs as part of every ETL run and writes docs/data-quality-report.json, which is committed so regressions show up in git log.

Three findings worth knowing about, all documented in docs/api-notes.md:

  1. Shot coordinates and play-by-play begin at the 2007 season. Earlier seasons have boxscores only. This is a property of the source, not of this project.
  2. Period buckets and event sequence numbers disagree in roughly 40% of games. NUMBEROFPLAY is unique and reliable; the per-quarter arrays upstream returns are not. Affected games are flagged lineup_safe = false.
  3. Player identifiers differ across endpoints. Boxscores use TGB, the live feed uses PTGB. Normalisation is source-aware, and there is a regression test for it.

Coverage

Measured across all 52 seasons on 2026-08-10 (docs/coverage.json):

Segment Seasons Games Boxscore PBP + shots
EuroLeague E2000–E2006 1 563 yes no
EuroCup U2002–U2006 896 yes no
Both 2007–2025 9 059 yes yes
Total 52 12 122

Development

uv run pytest
uv run ruff check src tests
uv run mypy src

Licence and contact

Code is MIT — see LICENSE. The licence covers the code only. It grants no rights in the underlying match data, which belongs to Euroleague Basketball and its data partners. Commercial use of the data may require a licence from them.

Takedown: if you represent a rights holder and want this changed or removed, open a GitHub issue titled TAKEDOWN. We will respond within 7 days and will take published datasets down on request while any disagreement is discussed. No formal legal process is needed to get our attention.

Prior art

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选