Skip to main content
Glama
README.md
# lichess-mcp

[![Use with Claude](https://img.shields.io/badge/Use%20with-Claude-D97757?logo=claude&logoColor=white)](#claude-desktop)
[![PyPI](https://img.shields.io/pypi/v/lichess-mcp.svg)](https://pypi.org/project/lichess-mcp/)
[![Python](https://img.shields.io/pypi/pyversions/lichess-mcp.svg)](https://pypi.org/project/lichess-mcp/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

An [MCP](https://modelcontextprotocol.io) server that puts the [Lichess](https://lichess.org)
public API in front of any MCP-compatible agent, so you can ask about chess in plain language
instead of reading JSON.

> "How did Magnus do in blitz this week, and what did the engine think of his last loss?"

No API key, no account, no OAuth — every tool here uses Lichess' public endpoints.

<!-- Demo GIF: record a short Claude Desktop session and save it to docs/demo.gif, then
     uncomment the line below. -->
<!-- ![lichess-mcp in Claude Desktop](docs/demo.gif) -->

## What you get

| Tool | What it answers |
| --- | --- |
| `get_user_profile` | Ratings in every format, title, country, account stats, current win/loss streak |
| `get_recent_games` | Recent games with PGN, result, opponent, opening and accuracy; filter by format or colour |
| `get_game_analysis` | Per-side accuracy and ACPL, plus every blunder/mistake/inaccuracy with the engine's preferred move and the eval swing |
| `get_opening_stats` | Win/draw/loss and score% for one opening, for one player, as a given colour |
| `analyze_position` | Top engine lines for a FEN, from Lichess' cloud evaluation cache, rendered in SAN |
| `get_puzzle_of_day` | Today's daily puzzle: FEN, themes, rating, and the solution |
| `search_players` | Autocomplete a half-remembered username |
| `get_tournament_info` | Arena or Swiss tournament: time control, player count, games played, standings |

Plus a resource, `lichess://api-reference`, summarising the endpoints behind each tool, the
rate limits, and the known gaps — so an agent can read the manual instead of guessing.

### Output is shaped for reading, not parsing

Raw Lichess payloads bury the result of a game under ninety clock timestamps. Every tool
here returns a small dict with obvious keys:

```jsonc
// get_recent_games("DrNykterstein", count=1, format="blitz")
{
  "game_id": "kAdOQKeh",
  "url": "https://lichess.org/kAdOQKeh",
  "played_at": "2026-04-08T19:39:03+00:00",
  "format": "blitz",
  "time_control": "3+0",
  "opening": "Alekhine Defense: Sämisch Attack",
  "color": "black",
  "result": "win",
  "my_rating": 3145,
  "rating_change": 8,
  "my_accuracy": 93,
  "opponent": { "username": "respects_55", "rating": 2644, "accuracy": 89 },
  "pgn": "[Event \"Take Take Take Arena\"]\n..."
}
```

Engine lines come back as SAN, because `1... e5 2. g3 Nf6` is legible and `e7e5 g2g3 g8f6`
is not. Evaluations are normalised so a positive score always favours the side to move, and
forced mates are reported as `mate_in` rather than a huge centipawn number.

## Install

Requires Python 3.10+.

```bash
uvx lichess-mcp
```

or

```bash
pip install lichess-mcp
```

### Claude Desktop

Add this to your `claude_desktop_config.json`
(**macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`,
**Windows**: `%APPDATA%\Claude\claude_desktop_config.json`), then restart Claude Desktop:

```json
{
  "mcpServers": {
    "lichess": {
      "command": "uvx",
      "args": ["lichess-mcp"]
    }
  }
}
```

If you installed with `pip` rather than using `uvx`, point at the installed script instead:

```json
{
  "mcpServers": {
    "lichess": {
      "command": "lichess-mcp"
    }
  }
}
```

### Claude Code

```bash
claude mcp add lichess -- uvx lichess-mcp
```

## Try these

1. **"What's DrNykterstein's blitz rating, and is he on a streak right now?"**
   → `get_user_profile`

2. **"Show me my last 5 rapid games as Black and tell me which openings went badly."**
   → `get_recent_games`, then `get_opening_stats`

3. **"Pull up today's Lichess puzzle and give me the position — don't tell me the answer yet."**
   → `get_puzzle_of_day`

4. **"Here's a FEN from my game — `r1bqkbnr/pppp1ppp/2n5/4p3/2B1P3/5N2/PPPP1PPP/RNBQK2R b KQkq - 3 3`. What does the engine want to play?"**
   → `analyze_position`

5. **"Find every blunder in lichess.org/dmvL2duu and explain what each one missed."**
   → `get_game_analysis`

Openings, tournaments and player search work the same way: *"How do I score with the
Sicilian as White over my last 200 games?"*, *"Who won tournament W1OrjDLM?"*,
*"There's a player called something like 'penguin' — find them."*

## Configuration

Everything is optional.

| Variable | Default | Purpose |
| --- | --- | --- |
| `LICHESS_API_TOKEN` | unset | A [personal access token](https://lichess.org/account/oauth/token). Raises your rate limit and unlocks your own private data. Nothing here requires it. |
| `LICHESS_MCP_MIN_INTERVAL` | `1.0` | Seconds between requests. Lichess asks for 1 req/sec; lower it only with a token and a good reason. |
| `LICHESS_MCP_MAX_RETRY_AFTER` | `60` | Longest 429 back-off to sit out before reporting the rate limit to the caller. |
| `LICHESS_MCP_TIMEOUT` | `30` | Per-request timeout in seconds. |
| `LICHESS_MCP_HTTP_LOG_LEVEL` | `WARNING` | Set to `INFO` to log every outgoing request. |

## Being a good API citizen

Lichess is free, donation-funded, and asks clients for **at most one request per second**.
This server enforces that globally with an async rate limiter rather than trusting each tool
to behave, and backs off politely on HTTP 429. That means tools which scan many games
(`get_opening_stats` with a large `max_games`) are deliberately unhurried.

If you want the whole games database, take it from
[database.lichess.org](https://database.lichess.org), not from this server.

## Known limits

- **Computer analysis is opt-in on Lichess.** `get_game_analysis` and the `accuracy` fields
  only have data for games where a player requested analysis. Games without it return
  `analysis_available: false` and say so, rather than failing.
- **`analyze_position` reads a cache, not a live engine.** Lichess' cloud eval covers
  positions someone has already analysed — common openings and popular games. Novel
  positions return `cached: false` with an explanation.
- **`get_opening_stats` samples recent games.** Lichess' personal opening explorer now
  requires OAuth, so this aggregates the games export instead. Results describe the last
  `max_games` games (default 200, max 500), not a lifetime total.
- **Streaks are derived**, not fetched — Lichess has no streak endpoint, so
  `get_user_profile` computes the current run from the 30 most recent rated games.

## Development

```bash
git clone https://github.com/Anay704/lichess-mcp
cd lichess-mcp
python -m venv .venv && .venv/bin/pip install -e ".[dev]"
.venv/bin/pytest                             # offline, sub-second
.venv/bin/ruff check .
```

The suite stubs the HTTP layer, so the default run is fast and works offline. A handful of
tests that really call lichess.org are opt-in:

```bash
LICHESS_MCP_LIVE_TESTS=1 .venv/bin/pytest
```

## License

MIT — see [LICENSE](LICENSE).

Not affiliated with or endorsed by Lichess. Lichess is a free/libre, open-source chess
server; if you get value out of this, [consider donating to them](https://lichess.org/patron).

TDQS

A3.9/5.0

Scored across 8 tools

Disambiguation5/5

Each tool targets a distinct purpose: user profile, player search, recent games, game analysis, opening stats, daily puzzle, position evaluation, and tournament info. There is no overlapping functionality or ambiguity between tools.

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern: get_* for retrieval, search_players for search, and analyze_position for analysis. The verbs are clear and the nouns are specific, making the naming predictable and uniform.

Tool Count5/5

With 8 tools, the set is well-scoped for a chess data server. It covers core Lichess features without feeling bloated or sparse, and each tool contributes a necessary capability.

Completeness4/5

The tool set covers the main read-only Lichess workflows: user profiles, game retrieval, analysis, openings, puzzles, positions, and tournaments. Minor gaps exist, such as no direct way to fetch a user's full game history or a specific game by ID, but these can be worked around via recent games and analysis.

Maintenance

ActivitySlowing
ResponsivenessNo issues