使用说明
MCP Server for Chess
This project provides a Model Context Protocol (MCP) server for chess tools over HTTP. It exposes Stockfish analysis, Maia human-like move prediction, Lichess game lookup, and FEN board rendering.
Quick Start
Try It Now - Public Cloud Run Instance
No installation is needed. Connect an MCP client that supports Streamable HTTP to the hosted Cloud Run instance:
MCP endpoint
https://mcp-chess-54127830651.europe-central2.run.app/mcp
Claude Desktop config (~/Library/Application Support/Claude/claude_desktop_config.json on macOS):
{
"mcpServers": {
"mcp-chess": {
"url": "https://mcp-chess-54127830651.europe-central2.run.app/mcp"
}
}
}
Restart Claude Desktop, then ask chess questions like:
- "What is the best move in this FEN?"
- "What move would a 1500-rated human likely play here?"
- "Show this FEN as a board."
Smoke test with MCP Inspector
npx -y @modelcontextprotocol/inspector \
--cli \
--transport http \
https://mcp-chess-54127830651.europe-central2.run.app/mcp \
--method tools/list
Smoke test Maia3
npx -y @modelcontextprotocol/inspector \
--cli \
--transport http \
https://mcp-chess-54127830651.europe-central2.run.app/mcp \
--method tools/call \
--tool-name whatMoveWouldHumanPlay \
--tool-arg fen='rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1' \
--tool-arg rating=1500
Do not test the MCP endpoint by opening it in a browser. MCP over HTTP uses JSON-RPC POST requests and session headers, so a browser GET /mcp is not a valid request.
HTTP Endpoint
The server uses the Quarkiverse MCP HTTP transport. The local Streamable HTTP endpoint is:
http://localhost:8080/mcp
The public Cloud Run endpoint is:
https://mcp-chess-54127830651.europe-central2.run.app/mcp
Prerequisites
- Java 25 or later (required for building the application)
- Docker (required for running the containerized application)
Building the Application
The project includes a multi-stage Dockerfile that installs the official Stockfish release binary and Maia3 as part of the container build process.
To build the application:
./mvnw package
The Dockerfile pins Stockfish to sf_18 and Maia3 to the maia3-79m model by default. Build the container for linux/amd64, which is the intended Cloud Run target. On Apple Silicon or other non-amd64 hosts, build in Cloud Build or use a Docker setup that can execute amd64 images.
Build the image and override versions when needed:
docker build \
--platform linux/amd64 \
--build-arg STOCKFISH_REF=sf_18 \
--build-arg STOCKFISH_RELEASE_ASSET=stockfish-ubuntu-x86-64.tar \
--build-arg MAIA3_REF=main \
--build-arg MAIA3_MODEL=maia3-79m \
--build-arg MAIA3_BAKE_CHECKPOINT=true \
--build-arg TORCH_INDEX_URL=https://download.pytorch.org/whl/cpu \
--build-arg TORCH_PYPI_FALLBACK=false \
-f src/main/docker/Dockerfile.jvm \
-t shelajev/mcp-chess:0.0.1 .
To try the smaller Maia3 model, build with --build-arg MAIA3_MODEL=maia3-5m. The image uses the PyTorch CPU wheel index by default to avoid pulling CUDA packages into a Cloud Run image. TORCH_PYPI_FALLBACK=true can help in restricted build environments, but it may produce a much larger image. MAIA3_BAKE_CHECKPOINT=false skips baking the Hugging Face checkpoint; Maia3 will then download its model at runtime unless you provide MAIA3_CHECKPOINT. The Java tool reads MAIA3_MODEL, MAIA3_CHECKPOINT, MAIA3_DEVICE, MAIA3_UCI, and MAIA3_TIMEOUT_SECONDS at runtime, so the model command can be tuned without changing the source.
Running the Container
Once the image is built, you can run it with:
docker run -p8080:8080 shelajev/mcp-chess:0.0.1
or you can use the pre-built version:
docker run -p8080:8080 olegselajev241/mcp-chess:latest
This will start the MCP server and expose it on port 8080.
Testing the Image
Build the image for the same platform used by Cloud Run:
docker build \
--platform linux/amd64 \
-f src/main/docker/Dockerfile.jvm \
-t mcp-chess:api-test .
Run it locally:
docker run --rm -p 8080:8080 mcp-chess:api-test
Initialize an MCP session:
SESSION_ID=$(curl -sS -D - -o /tmp/mcp-init.json -X POST http://localhost:8080/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"curl","version":"test"}}}' \
| awk -F': ' 'tolower($1)=="mcp-session-id" {gsub("\r","",$2); print $2}')
List the exposed tools:
curl -sS -X POST http://localhost:8080/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-H "Mcp-Session-Id: $SESSION_ID" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'
Call Stockfish:
curl -sS -X POST http://localhost:8080/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-H "Mcp-Session-Id: $SESSION_ID" \
-d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"findBestMove","arguments":{"fen":"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"}}}'
The expected tool list is boardFromFen, findBestMove, lastGames, randomGame, and whatMoveWouldHumanPlay.
Connecting to the Server
Local MCP endpoint:
http://localhost:8080/mcp
Public Cloud Run MCP endpoint:
https://mcp-chess-54127830651.europe-central2.run.app/mcp
This project no longer includes the stdio transport. It also does not require the old SSE transport endpoint for normal MCP access.
See Quick Start for Claude Desktop and MCP Inspector examples.
Deploying to Cloud Run
This can be deployed to Google Cloud Run meaningfully as an HTTP MCP server. The container binds to 0.0.0.0 and uses the PORT environment variable with a local default of 8080, which matches Cloud Run's container contract.
Build and push an image, then deploy it:
gcloud artifacts repositories create mcp \
--repository-format=docker \
--location=REGION \
--description="MCP chess images"
gcloud builds submit \
--config cloudbuild.yaml \
--substitutions _REGION=REGION,_REPOSITORY=mcp,_SERVICE=mcp-chess,_MAIA3_MODEL=maia3-79m
The Cloud Build config builds and pushes REGION-docker.pkg.dev/PROJECT/mcp/mcp-chess:latest, then deploys it to Cloud Run.
To deploy manually instead:
gcloud builds submit --tag REGION-docker.pkg.dev/PROJECT/REPOSITORY/mcp-chess:latest
gcloud run deploy mcp-chess \
--image REGION-docker.pkg.dev/PROJECT/REPOSITORY/mcp-chess:latest \
--region REGION \
--allow-unauthenticated \
--memory 2Gi \
--cpu 2 \
--concurrency 2 \
--timeout 60s \
--set-env-vars LICHESS_API_TOKEN=optional-token
Recommended Cloud Run settings:
- Keep concurrency low. Stockfish is CPU-bound and each request starts an engine process; Maia keeps one warm Python process per instance and serializes Maia calls.
- Try 2 CPU and 2 GiB memory first for Stockfish plus the default Maia3 79M model. Raise memory to 3 GiB or 4 GiB only if Cloud Run reports OOMs or Maia cold-start failures.
- Treat the service as stateless. The container has Stockfish, Maia3, and the selected Maia3 checkpoint baked into the image.
- Consider authentication before exposing it publicly; the tools can consume external Lichess quota and CPU.
- CORS is disabled by default. Browsers are not MCP clients, so leave it off unless a browser page calls the endpoint. To enable it, set
MCP_CORS_ENABLED=trueandMCP_CORS_ORIGINSto an explicit origin list; Quarkus allows every origin when the origin list is left unset.
The checked-in cloudbuild.yaml deploys with --allow-unauthenticated so MCP clients can reach the endpoint without Google identity tokens. If you remove that flag, clients must send a valid Cloud Run identity token in the Authorization header before MCP traffic reaches the server.
Maia3 is started lazily and kept as a warm UCI process inside each Cloud Run instance. Calls to the Maia tool are serialized per instance so multiple HTTP requests do not interleave commands on the same Python process. This is compatible with Cloud Run: the process lives as long as the container instance lives, and it is shut down when the instance is terminated. If you want consistently warm Maia latency, configure --min-instances; otherwise the first Maia request on a cold instance pays the model load cost.
Available Tools
The MCP server provides several tools for chess analysis and interaction with chess platforms:
Stockfish Tools
- findBestMove
- Description: Analyzes a chess position using the Stockfish engine to find the best move.
- Parameters:
fen: FEN notation of the chess position to analyze.
Lichess Tools
-
lastGames
- Description: Fetches the last games from lichess.org by a given username.
- Parameters:
username: The username to fetch the games for.n: How many games to fetch, clamped to 1-100.
-
randomGame
- Description: Fetches a random game from lichess.org by a given username.
- Parameters:
username: The username to fetch the games for.days: How many days back to look for games.
-
boardFromFen
- Description: Returns a text visualization of a chess board from a position given in FEN notation.
- Parameters:
fen: FEN notation of the chess position to display.
Maia Tools
- whatMoveWouldHumanPlay
- Description: Uses the Maia3 chess engine to predict what move a human player would make in a given position.
- Parameters:
fen: FEN notation of the chess position to analyze.rating: Elo rating to condition Maia3 with, clamped to 0-5000.
FEN Validation
Every tool that takes a fen argument validates it against a strict allowlist before any engine
starts: eight ranks of piece letters and digits, a side to move, castling rights, an en passant
square, and optional halfmove and fullmove clocks. Anything else is rejected with an MCP error and
no subprocess is spawned.
This matters because the Stockfish tool interpolates the FEN into a command string that is run by a
shell and then by expect. Validating the input is what keeps shell and expect metacharacters out of
that command; do not relax it to a substring check or an escape pass. FenTest covers the accepted
syntax and a set of injection payloads, FenGuardTest asserts that each tool refuses them without
starting an engine, and StockfishEngineTest runs the real engine end to end and checks that a
payload leaves no trace on the filesystem. The engine test is skipped when stockfish and expect are
not on the PATH, so a plain checkout still builds.
