Leela MCP Server
by indulge
README.md
# Leela MCP Server (`lc0-mcp`)
An [MCP](https://modelcontextprotocol.io) (Model Context Protocol) server that
exposes the [Leela Chess Zero](https://lczero.org) neural-network chess engine
(**lc0**) to any MCP client. Once connected, a client (Claude, or any other MCP
host) can analyze positions, get best moves, evaluate lines, and manipulate
boards through a small set of tools.
> **Naming:** `lc0` is the binary name for **L**eela **C**hess **Zer**o, hence
> the `lc0-*` command and tool names in this repo.
This package is **just the MCP server**. The engine binary (`lc0`) and a network
weights file are **external dependencies** you provide — see
[Prerequisites](#prerequisites).
---
## Quick start
For the impatient — each step links to its detailed section below.
1. **Install Python ≥ 3.10** and [pipx](https://pipx.pypa.io)
(`python3 -m pip install --user pipx && python3 -m pipx ensurepath`).
2. **Get the `lc0` engine binary** — build it or download a release.
See [1. The `lc0` engine](#1-the-lc0-engine). Note its path.
3. **Get a network** (`*.pb.gz` weights file) and note its path.
See [2. A network (weights) file](#2-a-network-weights-file).
4. **Install this server:**
```bash
git clone https://github.com/indulge/leela-mcp-server.git
cd leela-mcp-server
pipx install .
```
5. **Connect it to your MCP client** with the two paths from steps 2–3.
For Claude Code:
```bash
claude mcp add -s user lc0-chess lc0-mcp \
-e LC0_PATH=/path/to/lc0 \
-e LC0_WEIGHTS=/path/to/network.pb.gz
```
See [Use with an MCP client](#use-with-an-mcp-client) for Claude Desktop, Cursor, etc.
6. **Verify:** `claude mcp list` shows `lc0-chess … ✔ Connected`, or run the
[smoke test](#run--test).
---
## Prerequisites
| Dependency | Required | How to get it |
|------------|----------|----------------|
| **Python** | ≥ 3.10 | system package / [python.org](https://www.python.org) |
| **`lc0` engine binary** | yes | build from source or download a release — see below |
| **A network weights file** (`*.pb.gz`) | yes | download — see below (a source build does **not** include one) |
| Python deps: `mcp[cli]`, `python-chess` | yes | installed automatically (see [Install](#install)) |
### 1. The `lc0` engine
lc0 is a separate project ([lczero.org](https://lczero.org) ·
[GitHub: LeelaChessZero/lc0](https://github.com/LeelaChessZero/lc0)).
- **Official prebuilt binaries are Windows-only.** On Linux/macOS, build from
source with `meson` + `ninja` (see lc0's
[build instructions](https://github.com/LeelaChessZero/lc0#building-and-running-lc0)),
or install via your package manager if it ships lc0.
- The build produces a binary, typically at `build/release/lc0`. Note that path —
you'll point `LC0_PATH` at it.
### 2. A network (weights) file
A source build of lc0 does **not** bundle a network, and lc0 will not start
without one.
- Download a `.pb.gz` network from the
[lc0 networks page](https://lczero.org/play/networks/bestnets/) or
[storage.lczero.org](https://storage.lczero.org/).
- Smaller nets (e.g. a distilled 256×10, ~37 MB) run acceptably on CPU; larger
nets (768×15, 170–380 MB) are stronger but want a GPU backend.
- Note the file path — you'll point `LC0_WEIGHTS` at it.
---
## Install
The server installs a `lc0-mcp` console command. Install it **for the current
user** with [pipx](https://pipx.pypa.io) (recommended — isolates the
dependencies and avoids PEP 668 "externally-managed environment" errors):
```bash
git clone https://github.com/indulge/leela-mcp-server.git
cd leela-mcp-server
pipx install .
```
This puts `lc0-mcp` on your `PATH` and pulls in its Python dependencies
(`mcp[cli]`, `python-chess`). Verify with `which lc0-mcp` — it should print a path
like `~/.local/bin/lc0-mcp`. (There's no `--help`; it's a stdio server that waits
for an MCP client to connect — see [Run / test](#run--test).)
Alternatives:
```bash
pipx install -e . # editable: track the working tree (for development)
pip install --user . # without pipx (may need a venv on PEP-668 systems)
```
To upgrade: `pipx reinstall lc0-mcp`. To remove: `pipx uninstall lc0-mcp`.
---
## Configure (environment variables)
The server is configured entirely through environment variables. **`LC0_PATH`
and `LC0_WEIGHTS` are effectively required** — set them to your engine binary
and network from [Prerequisites](#prerequisites). (As a convenience, if you place
the binary at `./lc0/build/release/lc0` and any `*.pb.gz` net in `./networks/`
relative to the server source, the defaults below find them — but for an
installed command you should set the paths explicitly.)
| Variable | Default | Meaning |
|----------|---------|---------|
| `LC0_PATH` | `./lc0/build/release/lc0` | path to the `lc0` engine binary |
| `LC0_WEIGHTS` | first `*.pb.gz` in `./networks/` | path to the network `.pb.gz` |
| `LC0_BACKEND` | auto-detect | force a backend (`eigen`, `blas`, `cuda`, …) |
| `LC0_THREADS` | `min(8, cpu_count)` | search threads |
| `LC0_DEFAULT_NODES` | `2000` | default search budget per call |
| `LC0_MAX_NODES` | `5000000` | hard cap per request |
| `LC0_MAX_MOVETIME_MS` | `60000` | hard cap per request |
---
## Use with an MCP client
`lc0-mcp` is a **stdio MCP server**: a client launches it as a subprocess and
talks JSON-RPC over stdin/stdout. Every stdio MCP client configures the same
three things — a **command**, optional **args**, and **env** vars:
```
command: lc0-mcp
args: (none)
env: LC0_PATH=/path/to/lc0
LC0_WEIGHTS=/path/to/network.pb.gz
```
A ready-to-edit JSON example is in [`examples/mcp-config.json`](examples/mcp-config.json).
Two concrete clients:
### Claude Code (CLI)
```bash
claude mcp add -s user lc0-chess lc0-mcp \
-e LC0_PATH=/path/to/lc0 \
-e LC0_WEIGHTS=/path/to/network.pb.gz \
-e LC0_DEFAULT_NODES=2000
```
Then `claude mcp list` should show `lc0-chess: … ✔ Connected`.
### Claude Desktop, Cursor, and other JSON-config clients
Most MCP hosts (Claude Desktop, Cursor, Windsurf, Zed, …) take the same
`command` / `args` / `env` shape in a JSON config file — only the file's location
differs (Claude Desktop: `claude_desktop_config.json`; Cursor: `.cursor/mcp.json`).
Add an entry under `mcpServers`:
```json
{
"mcpServers": {
"lc0-chess": {
"command": "lc0-mcp",
"env": {
"LC0_PATH": "/path/to/lc0",
"LC0_WEIGHTS": "/path/to/network.pb.gz"
}
}
}
}
```
Then restart (or reload) the client and it will launch `lc0-mcp` on demand.
> **If `lc0-mcp` isn't found:** the client's launch environment may not include
> your user bin directory on `PATH`. Use the absolute path instead — find it with
> `which lc0-mcp` (e.g. `~/.local/bin/lc0-mcp`) and put that in `"command"`.
---
## Tools
| Tool | What it does |
|------|--------------|
| `analyze_position(fen, nodes?, movetime_ms?, depth?, multipv=1)` | Evaluate a position; returns score (centipawns / mate), lc0 Win/Draw/Loss + expectation, and best line(s) in UCI + SAN. |
| `best_move(fen, nodes?, movetime_ms?, depth?)` | Single best move + resulting FEN + eval. |
| `play_move(fen, move)` | Apply a move (UCI or SAN); returns new FEN + board + game status. |
| `position_from_moves(moves, start_fen?)` | Build a position from a sequence of moves. |
| `legal_moves(fen)` | All legal moves (UCI + SAN). |
| `show_board(fen)` | Text + unicode board diagram and status. |
| `engine_info()` | Engine id, configured paths, and limits. |
`fen` accepts a FEN string or the literal `"startpos"`. The search budget
defaults to `LC0_DEFAULT_NODES`; pass `nodes`, `movetime_ms`, or `depth` to
override per call.
---
## Run / test
```bash
# Run the installed server standalone (stdio; it waits for an MCP client — Ctrl-C to stop)
LC0_PATH=/path/to/lc0 LC0_WEIGHTS=/path/to/network.pb.gz lc0-mcp
# Smoke test: drives the server as a real MCP client end-to-end.
# Requires LC0_PATH / LC0_WEIGHTS in the environment (or the default paths populated).
pip install -r requirements.txt # if running from source without installing
LC0_PATH=/path/to/lc0 LC0_WEIGHTS=/path/to/network.pb.gz python test_mcp.py
```
---
## Project layout
```
leela-mcp-server/
├── lc0_mcp_server.py # the MCP server (all tools)
├── pyproject.toml # packaging — defines the `lc0-mcp` command + deps (source of truth)
├── requirements.txt # convenience deps for running from source (mirrors pyproject)
├── test_mcp.py # end-to-end MCP smoke test
├── examples/
│ └── mcp-config.json # generic client config (placeholder paths)
├── LICENSE # MIT
└── README.md
```
---
## References
- **MCP** — protocol spec & SDKs: <https://modelcontextprotocol.io>
- **Leela Chess Zero** — project home: <https://lczero.org>
- **lc0** — engine source & build guide: <https://github.com/LeelaChessZero/lc0>
- **Networks** — weights downloads: <https://lczero.org/play/networks/bestnets/> · <https://storage.lczero.org/>
- **python-chess** — board/move/FEN library used here: <https://python-chess.readthedocs.io>
## License
MIT — see [LICENSE](LICENSE).
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues