stockfish-mcp
v1.0.0
Published
MCP server that gives an LLM the best move for a chess position (FEN or PGN) via Stockfish.
Maintainers
Readme
stockfish-mcp
A tiny, opinionated Model Context Protocol server that gives an LLM the best move for any chess position — powered by Stockfish.
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.
{
"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 pointSTOCKFISH_PATHat the binary). This project does not bundle Stockfish.
Install Stockfish:
# 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:
{
"mcpServers": {
"stockfish": {
"command": "npx",
"args": ["-y", "stockfish-mcp"]
}
}
}Or run it from a local checkout:
{
"mcpServers": {
"stockfish": {
"command": "node",
"args": ["/path/to/stockfish-mcp/index.mjs"]
}
}
}From the command line (manual testing)
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):
// 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:
// 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:
// 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:
{
"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, 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.
A note on Stockfish: Stockfish itself is licensed GPL-3.0. 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.
