stockfish-mcp
by slothingaway
README.md
# stockfish-mcp
A tiny, opinionated [Model Context Protocol](https://modelcontextprotocol.io) server that gives an LLM the **best move for any chess position** — powered by [Stockfish](https://stockfishchess.org).
Hand it a position as **FEN** or **PGN** (or nothing, for the starting position) and it returns the best move, the engine's evaluation, and — optionally — the predicted best line, as clean structured JSON. One tool. One call. No UCI knowledge required.
```json
{
"turn": "black",
"bestmove": { "uci": "f6e4", "san": "Nxe4" },
"ponder": { "uci": "d2d4", "san": "d4" },
"score": { "type": "cp", "value": -371, "perspective": "side to move" },
"evaluation": "White is better (+3.71)",
"fen": "r1bqk2r/ppp2ppp/2np1n2/P3p3/1PB1P3/5N2/2PP1PPP/RNBQ1RK1 b kq - 0 7"
}
```
## Requirements
- **Node.js ≥ 18**
- **Stockfish** installed and on your `PATH` (or point `STOCKFISH_PATH` at the binary). This project does **not** bundle Stockfish.
Install Stockfish:
```sh
# Debian / Ubuntu
sudo apt install stockfish
# macOS (Homebrew)
brew install stockfish
# or download a binary from https://stockfishchess.org/download/
```
## Usage
### With any MCP client (Claude Desktop, etc.)
Add to your client's MCP config:
```json
{
"mcpServers": {
"stockfish": {
"command": "npx",
"args": ["-y", "stockfish-mcp"]
}
}
}
```
Or run it from a local checkout:
```json
{
"mcpServers": {
"stockfish": {
"command": "node",
"args": ["/path/to/stockfish-mcp/index.mjs"]
}
}
}
```
### From the command line (manual testing)
```sh
npm install
npm start # speaks MCP over stdio
npm test # end-to-end smoke test (needs stockfish installed)
```
## The `analyze` tool
| Parameter | Type | Default | Description |
| ------------- | ------- | ------- | ----------- |
| `position` | string | start position | A FEN string or PGN/move list. Omit for the starting position. |
| `depth` | integer | `18` | Search depth in plies. Higher = stronger but slower. |
| `movetime` | integer | — | If given, search this many milliseconds instead of a fixed depth. |
| `includeLine` | boolean | `false` | Include the engine's predicted best line (PV) in SAN. |
### Response
| Field | Description |
| ------------ | ----------- |
| `turn` | `"white"` or `"black"` — side to move in the given position. |
| `bestmove` | `{ uci, san }` — the best move (`null` if the position is already terminal). |
| `ponder` | `{ uci, san }` or `null` — the reply the engine expects. |
| `score` | `{ type: "cp" \| "mate", value, perspective: "side to move" }` — raw engine score. |
| `evaluation` | Human-readable, White-perspective summary, e.g. `"White is better (+3.71)"`, `"Mate in 1 for Black"`, `"roughly equal"`. |
| `fen` | The FEN actually analyzed (after PGN conversion / normalization). |
| `line` | *(only with `includeLine`)* Numbered SAN principal variation, truncated with `…`. |
### Examples
**FEN (black to move):**
```json
// analyze({ "position": "r1bqk2r/ppp2ppp/2np1n2/P3p3/2B1P3/5N2/2PP1PPP/RNBQ1RK1 b kq - 0 7", "depth": 14 })
{
"turn": "black",
"bestmove": { "uci": "f6e4", "san": "Nxe4" },
"ponder": { "uci": "d2d4", "san": "d4" },
"evaluation": "White is better (+3.71)"
}
```
**PGN:**
```json
// analyze({ "position": "1. e4 e5 2. Nf3 Nc6 3. Bb5 a6", "depth": 12 })
{
"turn": "white",
"bestmove": { "uci": "b5c6", "san": "Bxc6" },
"ponder": { "uci": "d7c6", "san": "dxc6" },
"evaluation": "White is better (+0.36)"
}
```
**Best line + mate detection:**
```json
// analyze({ "position": "6k1/5ppp/8/8/8/8/8/R6K w - - 0 1", "includeLine": true })
{
"turn": "white",
"bestmove": { "uci": "a1a8", "san": "Ra8#" },
"evaluation": "Mate in 1 for White",
"line": "1. Ra8#"
}
```
## Configuration
Environment variables:
| Variable | Default | Description |
| ------------------------- | ----------- | ----------- |
| `STOCKFISH_PATH` | `stockfish` | Path to the Stockfish binary. |
| `STOCKFISH_TIMEOUT_MS` | `60000` | Per-search safety timeout, in milliseconds. |
| `STOCKFISH_DEFAULT_DEPTH` | `18` | Search depth used when the caller omits `depth`. |
Example:
```json
{
"mcpServers": {
"stockfish": {
"command": "npx",
"args": ["-y", "stockfish-mcp"],
"env": { "STOCKFISH_PATH": "/usr/games/stockfish", "STOCKFISH_DEFAULT_DEPTH": "20" }
}
}
}
```
## How it works
The server spawns a fresh `stockfish` process for each `analyze` call, sends `position fen …` followed by `go depth N` (or `go movetime N`), reads the engine's `info` lines to capture the latest score and principal variation, and resolves on `bestmove`. Moves are converted to SAN with [`chess.js`](https://github.com/jhlywa/chess.js), which also handles PGN→FEN conversion and FEN validation.
Two UCI subtleties worth noting (both handled here): `go` is **asynchronous**, so stdin is left open and no `quit` is sent before the search finishes — closing the pipe would abort it. And `score cp`/`score mate` are reported from the **side-to-move**'s perspective, so we flip them when Black is to move to produce a consistent White-perspective evaluation.
## AI usage in this project
Entirely written by Qwen3.8-Preview-Max
## License
[MIT](LICENSE).
**A note on Stockfish:** Stockfish itself is licensed [GPL-3.0](https://github.com/official-stockfish/Stockfish/blob/master/LICENSE). This project does not include, link against, modify, or distribute Stockfish — it only *spawns* a Stockfish binary that you install separately and communicates with it over the public UCI protocol via a pipe. The two are separate programs, so this wrapper is independently licensed under MIT. You are responsible for obtaining Stockfish and complying with its license.
TDQS
A4.4/5.0
Scored across 1 tool
Disambiguation5/5
Only one tool exists, so there is no possibility of confusion or overlap with other tools.
Naming Consistency5/5
With a single tool, naming is trivially consistent and fits a clear verb-noun pattern (analyze_? but the tool name is just 'analyze', which is appropriate).
Tool Count3/5
A single tool is borderline for a chess engine server; it bundles position setup and search, which works but limits flexibility (e.g., no separate depth control or multiple analysis modes).
Completeness4/5
The tool covers the main use case of analyzing a position with evaluation and best move. It accepts FEN/PGN, making it versatile, but lacks advanced options like depth parameter or multi-move output.
Maintenance
ActivitySlowing
ResponsivenessNo issues