Local chess review tool: PGN -> Stockfish analysis -> mistakes explained in words, driven from Claude Code via MCP.
Strong foundation with generally well-described tools and clear schemas. 8 tools all have detailed descriptions (avg 180+ chars) explaining purpose, parameters, and return behavior. All parameters have types and descriptions. Tool names follow verb_noun convention (analyze_game, fetch_games, get_engine_line, etc.). Output schemas are partially documented in descriptions. Main gaps: no explicit output schema definitions in code visible, missing error handling guidance, no parameter validation constraints (enums), and no indication of idempotency or destructiveness annotations. This is a domain-specific server (chess analysis) that demonstrates above-average care in documentation, descriptions are concrete, motivate why each tool exists, and explain dependencies between tools (e.g., 'Hand the returned pgn to analyze_game'). However, it falls short of A-grade because: (1) no JSON Schema output specs visible in tool registrations, (2) parameter enums not formalized (e.g., sensitivity, source, difficulty accept strings without declared legal values), (3) no tool annotations (readOnlyHint, destructiveHint, idempotentHint), (4) error recovery guidance absent. Score reflects strong baseline (descriptions, naming, parameters) with noticeable gaps on modern patterns and schema rigor.
Analyse a full game from PGN and find the player's mistakes. Mistake sensitivity adapts to skill: stronger players get smaller win%-drop cutoffs (subtler errors flagged) and a slightly deeper sweep. If `elo`/`sensitivity` are omitted, the reviewed side's Elo is read from the PGN (normalized for Lichess vs Chess.com, whose scales differ).
Fetch one Lichess game by its id or URL; returns its `pgn` (+ metadata) for analyze_game. Accepts a bare game id ("abcd1234") or a full URL (e.g. https://lichess.org/abcd1234/black). Hand the returned `pgn` to `analyze_game` to review it.
Fetch a Lichess user's recent games (newest first) so the user doesn't paste PGNs. Returns a list of games with `game_id`, players, ratings, `result`, `speed`, `opening`, `date`, and the full `pgn`. Show the user the list and let them pick one, then call `analyze_game` with the chosen game's `pgn`. Public games need no auth; heavy users can set LICHESS_TOKEN to avoid IP rate limits.
Evaluate a position (optionally after a candidate move) and return engine lines. This is the grounding for "why?" follow-ups. Without `move`, it returns the best move and principal variation for `fen`. With `move` (UCI like "g1f3" or SAN like "Nf3"), it also returns how that move is classified and the engine's refutation / expected continuation after it — i.e. concretely *why* it is good or bad.
No JSON Schema output schemas documented. Tool descriptions mention returns (e.g., 'Returns a list of games', 'Returns engine lines') but no formal output schema is visible in the code. LLMs cannot plan downstream tool calls or extract fields with confidence.
Parameter values not constrained as enums. Many parameters accept a controlled set of strings (player: 'white'|'black'|'auto', sensitivity: 'casual'|'default'|'strong'|'master', source: 'lichess'|'your_games', difficulty: 'easier'|'harder') but are defined as free-form strings. LLMs may hallucinate invalid values.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | C | 60 | 2026-07-28+ | v2 |
Return a player's saved coaching profile: recurring patterns across all analysed games. Aggregates the persisted game history into accuracy, win/loss/draw counts, mistake rates, the most common mistake *motifs* (e.g. hung_piece, pawn_grab, missed_capture), which game phase leaks the most win%, per-opening results, and the most recent games. Use this to give personalised, trend-aware coaching ("you keep hanging pieces in the endgame") instead of judging a single game in isolation.
Move the review cursor to mistake #index and return the position before it. Use the `index` values from `analyze_game`'s mistake list. Returns the FEN one move before the mistake so narration (and the web board) stays in sync.
Serve the next tactics puzzle to solve, near the user's puzzle rating. Terminal parity with the web board: this sets the SAME shared puzzle session the board uses, so you can drive a solve from Claude Code and it stays in sync. Present the returned position to the user (side to move + "find the best move"), let them find a move, then call `solve_puzzle`. The solution is deliberately NOT returned.
Submit a move attempt in the active puzzle. Returns the verdict (correct, wrong, checkmate) and refutation if it's wrong.
No error recovery guidance. Tools have no documented error cases or recovery instructions. E.g., what should the LLM do if a game_id is not found? If a puzzle is not active? If Stockfish is not available? Bare error codes provide no guidance.
No tool annotations (readOnlyHint, destructiveHint, idempotentHint). All tools appear read-only (no side effects), but this is not explicitly declared. Modern MCP clients use annotations to decide retry logic and caching. Absence forces clients to assume worst case.
Parameter constraints not documented. E.g., 'depth' in get_engine_line accepts integer but no min/max stated. 'max' in fetch_games has no documented bounds. LLMs may pass 0, negative, or absurdly large values.
Ambiguous move format acceptance. get_engine_line and solve_puzzle accept 'UCI or SAN' but do not clarify which is preferred, or how the tool disambiguates if both are valid. This invites format errors.