Aggiorna una task o subtask solo se appartiene alla lista configurata. L’input è una allowlist rigida: Nome, Stato, Assegnatari, Priorità, Data scadenza, Data inizio e CRM. Lo stato viene validato contro opzioni_lista; null svuota CRM e un array vuoto rimuove gli assegnatari. Non può cambiare descrizione, tag, commenti, parent, lista o altre proprietà.

Lista completa degli ID assegnatario. Usa opzioni_lista; un array vuoto rimuove tutti.

URL HTTPS; null svuota la colonna CRM.

YYYY-MM-DD, senza orario.

YYYY-MM-DD, senza orario.

Se true valida e mostra le operazioni, senza scrivere su ClickUp.

Priorità ClickUp: urgente, alta, normale oppure bassa.

Nome di uno stato restituito da opzioni_lista, per esempio "Da Fare".

ID ClickUp della task o subtask da aggiornare.

Cerca task e subtask nella sola lista configurata per testo nel Nome, Stato e/o assegnatario. I criteri specificati vengono combinati con AND e applicati anche lato ClickUp. Specifica almeno un criterio; usa opzioni_lista per gli stati e gli ID assegnatario disponibili. Non cerca né restituisce descrizioni, commenti, tag o altri campi.

Filtra per un ID restituito in assegnatariDisponibili da opzioni_lista.

Include le task chiuse/completate. Default true.

Massimo risultati. Default 25.

Massimo pagine ClickUp da esaminare. Default 10.

Filtra per uno stato restituito da opzioni_lista.

Testo contenuto nel Nome della task; confronto senza distinzione fra maiuscole e minuscole.

Crea una subtask sotto una task che appartiene alla sola lista configurata. Può scrivere esclusivamente Nome, Stato, Assegnatari, Priorità, Data scadenza, Data inizio e CRM. Usa dry_run prima della conferma.

URL HTTPS da scrivere nella sola colonna personalizzata CRM.

Data inizio YYYY-MM-DD, senza orario.

Data scadenza YYYY-MM-DD, senza orario.

Se true valida e mostra il payload, senza scrivere su ClickUp.

Valore della colonna Nome.

ID della task o subtask padre nella lista configurata.

Crea una task principale nella sola lista configurata. Può scrivere esclusivamente Nome, Stato, Assegnatari, Priorità, Data scadenza, Data inizio e CRM. Se Stato è omesso usa quello predefinito della lista. Usa dry_run prima della conferma.

Data inizio YYYY-MM-DD, senza orario.

Data scadenza YYYY-MM-DD, senza orario.

Valore della colonna Nome.

Elenca task e subtask della sola lista configurata. Restituisce Nome, Stato, Assegnatari, Priorità, Data scadenza, Data inizio e CRM; ID, tipo, parentId e URL sono metadati in sola lettura. Include anche le task chiuse per default.

Pagina ClickUp, da 0. Default 0.

Legge una task o subtask per ID, solo se appartiene alla lista configurata. Restituisce esclusivamente le colonne consentite, i metadati strutturali e le sue subtask; non espone descrizione, commenti, tag o altri custom field.

ID ClickUp ottenuto da elenca_task_lista o cerca_task_lista.

Legge la configurazione della sola lista ClickUp autorizzata: stati, assegnatari, priorità e campo URL CRM. Dichiara anche l’allowlist completa delle colonne modificabili. Chiamalo prima di assegnare una task.


Fetches a URL-like resource and returns a normalized Fetch v2 envelope. Supports HTML, PDF, text/data, media metadata, site adapters such as Reddit threads, pagination via start_index, and optional link/media/comment extraction.

Fetch and Extract URL Content

Search the web using multiple providers (DuckDuckGo, Bing, SearXNG). Automatically falls back to other providers if the default fails. No API keys required for DuckDuckGo and SearXNG.

Web Search


Ask Shumi any crypto-market question in natural language. Shumi classifies the query, fetches the relevant data, and returns a synthesized answer. Use this when no specific typed tool fits, or for multi-part / comparative questions.

Specialization path (default "base"; e.g. "perp-dex").

The natural-language question, e.g. "is funding extreme on SOL right now?".

Ask Shumi (free-form)

Daily snapshots of the tracked basket — composition and performance over time.

Token-saving: comma-separated top-level fields to keep.

Keep only the first N items (default 50). Raise for more, lower to save tokens.

Basket snapshots

Detail for one category. view=info (trend breakdown), coins (member coins), sentiment.

Category name, e.g. "Layer 2".

Which category view to return.

Category detail

Market cap, volume, open interest, funding rate and price for a coin AS OF a chosen point in the past. Returns a single snapshot, not a series: call it once per point you want to compare (e.g. amount=7 interval=d for a week ago, then again with no offset for now). Use this to answer "how has funding/open interest changed since…".

How far back to look, in units of `interval`. Omit for the most recent snapshot.

Unit for `amount`: h (hours) or d (days). Defaults to hours.

Coin symbol, e.g. ETH.

Coin metrics at a past point in time

Bundled risk context for one or more coins: price, funding APR, deterministic funding_paying_side, funding_receiving_side, carry_if_long and carry_if_short fields, daily/weekly trend, sentiment stance, and BTC correlation. Funding applies to perpetual positions only; spot positions neither pay nor receive it. Relay the carry fields exactly rather than inferring direction from crowding. The best single tool for "should I be worried about X".

One or more coin symbols, e.g. ["BTC","ETH","SOL"].

Coin risk context

On-chain/social sentiment aggregates for a single coin.

Coin symbol, e.g. BTC.

Coin sentiment

Discrete funding-rate alert events (asset, trigger zone, funding at trigger, fired-at time).

Funding alerts

Perpetual funding-rate positioning context, market-wide or for one symbol. APR is already in percent units (1.7 = 1.7%). Positive funding means longs pay and shorts receive; negative funding means shorts pay and longs receive. Funding is context only, not a standalone directional, timing, or entry signal.

Restrict to one symbol, e.g. BTC. Omit for the market-wide view.

Funding momentum

Perpetual-futures signal engine. view=state (currently open signals), log (recent fires), history (one asset's past signals — needs asset).

Asset symbol, e.g. BTC. Required when view="history".

Which futures view to return.

Futures signals

Global market aggregates: BTC dominance, total market cap, total volume.

Global market aggregates

Tracked token-holder cohorts. view=watchlist (which token contracts are tracked), movements (recent holder-count changes for one contract). Answers "is the holder base growing or bleeding?".

Token contract address. Required when view="movements".

Max results.

Which holder view to return.

Token holder tracking

The most recent regime crossing — when one trend cohort (UP/HODL/DOWN) overtook another market-wide. Answers "has the market flipped?". Returns an empty `crossings` array plus a message when no crossing has fired.

Market trend crossing

Overall market health: the UP/HODL/DOWN trend distribution and extreme movers. Set context for the full bundle.

Include the full market-context bundle (breadth velocity, regime age, leadership).

Market health

Aggregate market sentiment. view=market/latest/summary (overall), narratives, categories, slopes/entity-slopes (what is trending), health (pipeline status).

Which sentiment view to return.

market latest summary narratives categories health slopes entity-slopes

Market sentiment

Sentiment and momentum for a single named narrative.

Narrative name, e.g. "AI coins".

Narrative sentiment

Pair-trading and delta-neutral funding-arbitrage intelligence. mode=suggestions (pair ideas), delta-neutral (funding arb), history (backtest), signal (state for a specific pair — needs token_a & token_b).

DEX exchanges only (delta-neutral).

Filter by exchange (delta-neutral).

Max results.

Which pair view to return.

suggestions delta-neutral history signal

Filter by symbol (suggestions / delta-neutral).

First token (required for mode="signal"), e.g. ETH.

Second token (required for mode="signal"), e.g. SOL.

Pair / delta-neutral suggestions

Bulk live prices, optionally with 4h/24h/7d baseline overlays. Omit symbols for the full tracked set.

Include 4h/24h/7d baseline price overlay.

Comma-separated symbols, e.g. "BTC,ETH,SOL". Omit for all tracked coins.

Bulk live prices

Market regime signals. view=active (current positions), signals (all), confidence (scores). Provide symbol to get that symbol's regime history instead.

If set, returns regime history for this symbol.

Which regime view to return (ignored when symbol is set).

Market regime

Price, daily/weekly trend and perp funding for one real-world asset (stock, ETF, commodity, index, FX). Look up by ticker (AAPL, GOLD) or by namespaced id (xyz:AAPL). Funding belongs to the PERPETUAL CONTRACT, not the underlying — `funding.apr` is the annualized rate in percent, `funding.rate` is the raw per-hour fraction. Do not use the crypto coin tools for these.

How `identifier` is interpreted.

Ticker (AAPL, GOLD, SP500) or namespaced id (xyz:AAPL).

Real-world asset detail

Synthesized verdict for a coin, combining trend, funding, sentiment and regime.

Coin symbol, e.g. SOL.

Synthesized signal

Signal validation envelope for an asset: Sharpe ratio, win rate, sample size, reliability tier.

Asset symbol, e.g. BTC.

Signal type (default: mean_reversion).

Signal quality

Highlights mined from tracked video/podcast transcripts. view=highlights (extracted claims with the coins, sectors and macro tags they mention), sources (which channels are tracked). Answers "what are people actually saying about X?".

Which transcript view to return.

Transcript highlights

Tracked wallets. view=watchlist (which wallets are tracked), movements (recent balance changes for one wallet address). Answers "what did this wallet do recently?".

Wallet address. Required when view="movements".

Max results.

Which wallet view to return.

Wallet tracking

List all tracked crypto categories (DeFi, Layer 2, memes, …).

List categories

List the currently active market narratives (e.g. "AI coins", "DeFi summer").

List narratives

List the tradable real-world assets — stocks and ETFs (AAPL, NVDA, SPY), metals and commodities (GOLD, SILVER, BRENT), stock indices (SP500, JP225) and FX. These trade as perps on Hyperliquid builder DEXes and are NOT crypto tokens; the crypto tools will not find them. Use this to answer "which stocks/commodities can I look at?".

Filter by builder-DEX slug, e.g. "xyz".

Max assets to return (server default 200; the full universe is ~94).

Filter by asset class.

List real-world assets

Look up a single coin and its core metrics (price, trend, metadata) by symbol, name, CoinGecko/internal id, or on-chain contract address.

How `identifier` is interpreted.

Chain for contract lookups (ethereum, bsc, solana, base, …). Required when by="contract".

The symbol (BTC), name (Bitcoin), id (bitcoin), or contract address.

Look up a coin

Fuzzily resolve a symbol, name, or contract to canonical coin candidates. Use this first when the user input is ambiguous, before calling other tools.

Max candidates to return.

Symbol, name, or contract to resolve.

Resolve a coin (fuzzy)

Filter the tracked universe by trend direction, category, market-cap band, and exchange.

Filter by category name, e.g. "Layer 2".

Filter by exchange listing.

Trend interval: 1d (daily) or 1w (weekly).

Max results.

Maximum market cap in USD.

Minimum market cap in USD.

Filter by trend direction.

Scan / filter coins

Trend scanner. state=fresh (newly started), stale (longest running), aligned (multi-timeframe agreement), extreme (biggest moves), historical.

Max results.

Which trend slice to return.

Scan trends

Search the web for crypto information, or get a direct answer. Backed by Shumi's web-search tool.

Return a direct synthesized answer instead of raw search results.

What to search for.

Search the web


Append a linear sequence of moves under `parent_id`. Each SAN in the list becomes the mainline child of the previous — one call instead of N add_move calls for a straight variation. If the parent already has other children, this whole line is appended as a variation (promote_variation the first move if you want it as the mainline).

**Anti-pattern: pasting an engine PV as a single long `add_line`.** Real prep is a tree, not a line. Almost every position along a variation has more than one plausible move — pasting a 12+-ply engine PV without branching at those points is the #1 documented failure mode of this MCP: it produces a page that reads as prep but ignores every decision the opponent actually gets to make. Long unbranched lines get a warning field in the response starting at ~9 plies and a strong warning at 14+ plies. Rule of thumb: if you added ≥8 plies in one call, at least half of them should have branched. Genuine exceptions exist (forced mates, obligated exchange sequences) — in those cases add a comment naming what makes the sequence forced (`{Every move here is forced by the mate threat.}`), so the reader knows it's forced by chess, not by LLM laziness.

Auto-saves. Returns `{node_id, line: [{node_id, san}, ...], version}` — `node_id` is the last (leaf) node's id, `line` is every node created in order so you can address any of them next. When long-and-linear, also includes `warning: "..."`.

Prep file id.

Node id of the parent to build from. Root is 'r'.

SAN moves in order, e.g. ['e4','e5','Nf3','Nc6','Bb5'].

Append a move as a new child of the node identified by `parent_id`. If the parent already has children, the new move becomes a variation (appended at the end); use promote_variation afterwards to make it the mainline. SAN is validated against the position — illegal moves are rejected with a clear error.

Auto-saves. Returns `{node_id, version}` — the id of the new node (pass this to follow-up set_comment / set_nags / etc.) and the new file version for optimistic locking. **Node ids are content-derived and stable** — sibling insertions, deletions, and promotions do NOT change any other node's id.

Optimistic-lock check; pass the `version` from your last read.

Prep file id.

Node id of the parent (the position the move is played FROM). Root id is 'r'.

The move in SAN notation (e.g. 'Nf3', 'exd5', 'O-O', 'Qxf7+').

Batch: apply a list of mutations in one call. One load-parse-mutate-export-save cycle for N ops, so building a 100-move repertoire costs one HTTP round-trip and one save instead of 100. This is the RIGHT way to build a file — use single mutations only for surgical follow-up edits.

Each mutation is `{op, node_id | parent_id, ...args}` where `op` is one of: add_move, add_line, set_comment, set_nags, set_annotations, delete_subtree, promote_variation, set_tag. Same arg shape as the individual tools. Ops apply in order; because node ids are content-derived (hash of parent_id + san), a node created by an early op has a deterministic id you can reference in later ops in the same batch.

Any op error aborts the batch (nothing saved). Response is `{ok, results: [{node_id, line?}], version}` — one entry per op with the id it landed on (add_line also returns the full line array).

Walk the tree from `node_id` (default `'r'` = whole file) and populate the persistent `ceoEval` on every descendant via cloud_analyse. Requires a running cloud combo instance.

**Async job — returns immediately.** Response: `{ job_id, target_count, status: 'running', estimated_seconds }`. Then poll `auto_evaluate_status(job_id)` until `done: true`. Cancel a run with `auto_evaluate_cancel(job_id)` — partial progress is preserved. Do useful other work between polls (write more of the tree, walk the opponent's repertoire) — the engine runs in the background.

Progress is checkpointed to the prep file every 8 successfully-evaluated nodes, so a cancel / crash / MCP restart mid-run leaves the tree partially populated rather than losing everything. On MCP restart the job record disappears; re-run auto_evaluate and `only_missing=true` naturally skips what was already saved.

**Does NOT set visible NAGs.** NAG placement is your call, not the engine's — an opening tree full of 0.00 positions doesn't need a `$10` (=) glyph on every move. Use quote_engine_eval on individual nodes before writing prose that references engine numbers.

Costs real money — one cloud_analyse per node. A 200-node walk at default movetime is ~5 min of engine time (calls serialise on the per-combo semaphore in the backend).

Per-node cloud_analyse think time (default 1500).

Subtree root (default 'r' = whole file).

Skip nodes that already carry a stored ceoEval (default true).

Ask a running auto_evaluate job to stop as soon as its current node finishes. Whatever progress was completed before cancellation is durably saved (checkpoint on cancel). Idempotent — cancelling an already-finished job is a no-op with a clear note in the response.

Job id from the `auto_evaluate` response.

Poll the status of an auto_evaluate job. Response: `{ status: 'running' | 'done' | 'cancelled' | 'error' | 'not_found', target_count, evaluated, errored, remaining, done, error?, version? }`. When `status: 'not_found'` the job either expired (kept ~15 min after completion), never existed, or the MCP restarted since it was created — re-run auto_evaluate.

Typical poll cadence: every 3-5 s for small walks, every 10-30 s for large ones. Don't hammer — status is a pure in-memory read but polling doesn't speed the engine up.

Runs a synchronous ~2s analysis on the user's running combo instance and returns both Stockfish and Lc0's final read for the FEN — depth, top-N candidate moves with scores (**scoreCp is White-POV centipawns**: +20 = White is +0.20 pawns better regardless of whose turn it is; matches the sign convention used everywhere else in this MCP, including the stored ceoEval). Mate is White-POV plies-to-mate (+5 = White mates in 5). Also returns each engine's principal variation.

GROUNDING: every claim you make about a position must trace back to actual engine output from a call in THIS session. Don't invent evaluations, don't name 'best moves' you haven't seen the engine list, don't fabricate variations that 'look plausible.' Compute is cheap — call this 5-10 times while walking a tree rather than pattern-matching from your training data. When you don't have data for the position, either run the tool or say so; don't fill the gap with chess prose the user can't distinguish from measured output.

Auto-picks the caller's only running combo instance; errors clearly if there are zero (start one first with start_cloud_engine) or more than one (destroy the extras first).

How to read the response:
• Stockfish is objective truth — trust it for 'does this line hold?' 'is there a tactic?' 'is this endgame drawn?' A Stockfish 0.00 means 'objectively equal', NOT 'trivial draw' — one side can still be much harder to play in practice.
• Lc0 is practical eval — trust it for 'which side is easier?' 'which candidate is best when Stockfish shows several as equal?' Lc0 sees long-term positional factors Stockfish's fixed search can miss.
• When they agree → high confidence. When they disagree → look at both scores and reason WHY (Stockfish sharply higher = tactic Lc0 missed; Lc0 higher = long-term positional edge past Stockfish's horizon). Never dismiss either — the disagreement is the signal.

Contempt (`contempt`) skews Lc0 (only Lc0 — Stockfish always stays objective) toward White (positive) or Black (negative). Signed 0-100 strength — same scale as the web UI's ContemptStrength slider (the server multiplies by 8 to produce Lc0's internal cp bias). Typical values: ±15 for a light nudge, ±30-60 for real fighting play, ±80-100 for maximum steer. Use it to find non-objective 'practical' ideas or when the user needs to lean toward fighting/solid lines with a specific colour. Do NOT quote a contempt-biased eval as objective — cross-check with Stockfish.

Also useful: pass `moves` on top of `fen` to explore a variation without computing FENs yourself (e.g. fen='<tabiya>', moves='b4 a5 c3'). And the flip-side-to-move threat check documented in the guide is a great free trick.

**PVs are capped at 6 plies by default (3 full moves), and lines that got truncated are marked with `pv_truncated: true`.** This is deliberate: the tail of a PV is where the engine's confidence collapses, AND pasting a long PV into `add_line` as if it were prepared repertoire is the #1 documented anti-pattern of this MCP — a 15-move PV is one line of engine output through positions where both sides had real choices, not a repertoire. To see further, don't raise `pv_max_plies`; instead, walk the tree one branch at a time with a fresh `cloud_analyse` at each position where the opponent has real alternatives — that's what makes it prep instead of pasted output. Only raise the cap when you're verifying a forcing sequence (a mate, a forced tactical resolution), not to build lines.

For the full guide including worked examples, call the `read_engine_usage_guide` tool.

Not for casual questions — this costs real money per second. Use `get_position_stats` for anything that doesn't require deep prep.

**When called with `file_id`+`node_id` (preferred inside a prep file), the resulting eval is auto-stored on that node's `ceoEval` — you can then quote it with quote_engine_eval on any later call.** This is what makes engine attribution trustworthy: prose that says 'engines say X on node Y' can only be true if a call was actually made against node_id=Y.

Lc0 contempt bias. Signed 0-100 strength (same scale as the web UI's ContemptStrength slider — server multiplies by 8 to get the internal cp bias). 0 = objective (default). Positive favours White, negative favours Black. Typical: ±15 light nudge, ±30-60 real fighting play, ±80-100 maximum steer. Not applied to Stockfish. See engine_usage_primer for when to use.

Which engines to run. Default = both. Use `["lc0"]` to skip Stockfish (e.g. while a deep_analyse job is holding the SF slot on the same combo). Use `["stockfish"]` when only the objective read matters. The skipped engine's field is omitted from the response.

Starting position as FEN. Only used if `node_id` is not set.

Prep file id. **Prefer file_id+node_id over fen** when inside a prep file — the FEN comes from the tree AND the result is stored on the node.

Lc0 candidate lines (default 8). Kept wide because multipv doesn't degrade Lc0's strength the way it does Stockfish's — Lc0 is the 'find inspiration / explore practical tries' leg, use a high multipv to get a full slate of ideas.

Optional SAN moves to apply on top of `fen` (or startpos). Only used if `node_id` is not set.

Think time in milliseconds (default 2000).

Node id inside `file_id`. Root is 'r'. When set, overrides `fen`/`moves`.

Cap each returned PV to this many plies (default 6 = 3 full moves). PVs beyond ~6 plies are speculative and are the anti-pattern behind pasted-engine-line 'prep' — don't raise unless you're specifically checking a forcing tactic or verifying a mate. When a line was truncated, the response marks it with `pv_truncated: true`.

Stockfish candidate lines (default 2). Kept tight because each extra PV steals search bandwidth from the top choice — SF is the 'what's objectively best' leg, use a low multipv to keep it strong. Raise only when you specifically need SF's take on a wide range of candidates.

Create a new (empty) prep file in the specified collection. `name` becomes the Event PGN tag. You then extend it with mutation tools (add_move, set_comment, …).

**collection_id is REQUIRED** — call `list_collections` first to pick where it lives. There is no default landing folder any more (v0.43: the old hidden `/mcp` collection was removed; prep files now live wherever the user organizes them).

**Duplicate-check first.** Call `search_prep_files(query=<opponent / opening keyword>)` OR `find_position_in_files(fen=...)` before creating — a second 'Prep vs Firouzja' file when one already exists is a common LLM failure mode. If a file already covers the topic, extend that one instead.

Response: `{ok, id, collection_id, version}` — `id` is a composite you pass to every other prep-file tool as `id` or `file_id`.

Collection id from list_collections. Required — this is where the new file lands.

User-facing name — becomes the [Event] tag. Example: 'Prep vs Firouzja (Black) 2026-07-23'.

Start a long Stockfish think on a single position (up to 5 min movetime). Returns a `job_id` immediately; poll `deep_analyse_status(job_id)` for the result, cancel with `deep_analyse_cancel(job_id)`. Runs SF only — Lc0 doesn't benefit from long thinks past a handful of seconds — and **holds only the SF engine slot on the combo, so `cloud_analyse(..., engines: ["lc0"])` stays available for other work in parallel**.

Use this when a specific critical position deserves depth — a novelty candidate, a hairy tactical shot, a difficult endgame — and you want Stockfish at depth 35+ rather than the ~depth 22 you get from a 2s cloud_analyse. Movetime is in ms; typical: 30_000-60_000 for 'careful check', 120_000-300_000 for 'find the truth'.

Result shape when done matches cloud_analyse's Stockfish leg (depth, top-N candidates with scoreCp/mate, best move, PV). Auto-stores the eval on `file_id`+`node_id` when both are supplied, same as cloud_analyse.

Position as FEN. Only used if `node_id` is not set.

Prep file id. Combine with `node_id` to derive FEN from the tree AND persist the result on the node's ceoEval.

Optional SAN moves on top of `fen`. Only used if `node_id` is not set.

Think time in ms. Default 60_000 (1 min). Max 300_000 (5 min).

Number of candidate lines (default 2). Stockfish gets weaker as multipv grows — each extra PV steals search bandwidth from the top choice — so keep this low unless you specifically want to see several candidates ranked deep.

Ask a running deep_analyse job to stop early. The engine returns whatever it's found so far as the final result. Useful when a partial result at depth 25 is enough and you don't want to wait for depth 40. Idempotent for already-finished jobs.

Job id from `deep_analyse`.

Poll a deep_analyse job. Response: `{ status: 'running' | 'done' | 'cancelled' | 'error' | 'not_found', elapsed_ms, movetime_ms, result?, error? }`. `result` shape when done: `{ engine, depth, timeMs, bestMove, lines: [{rank, depth, scoreCp?, mate?, pv, nodes?}] }` — the SF leg of a cloud_analyse response.

Poll cadence: every ~15-30s for long thinks; there's no penalty for polling more often but the engine progresses at its own pace.

Job id from `deep_analyse`.

Soft-delete a prep file. Fully reversible: the file lands in the user's recycle bin, from which either the user (via app UI) OR you (via `restore_prep_file`) can bring it back. Permanent delete is intentionally NOT exposed on this surface — the user has to permanent-delete from the app themselves. Rare — usually you extend or edit instead.

Prep file id.

Delete one prep session by token. Free-form cleanup — sessions do expire automatically, but this is useful when you're done with one or when you want to force a rebuild after upstream data changed.

Token from `list_prep_sessions` or the response of `prepare_opponent`.

Delete the node identified by `node_id` and all its descendants. Refuses to delete the root. Auto-saves.

**CALL WHEN**: about to write ANY comment on a position that describes what's happening on the board — piece activity, structure, plans, weaknesses. This is the single biggest lever for prose quality in the whole system. Live audit: nodes where describe_position was called first produced comments grounded specifically in the position (correct piece squares, real pawn structure, actual weak squares); nodes where it wasn't produced generic pattern-matched prose that confidently named pieces on wrong squares. `set_comment` now emits a warning whenever a substantive comment lands on a node whose position was never grounded via describe_position this session — that warning is telling you to fix a class of hallucination that already showed up in your output. Cheap: chess-primitive analysis is instant, Stockfish leg is ~50-100 ms, no billing.

Everything you need to understand a position in one call. Pieces get misplaced when reading a FEN, hanging pieces missed, 'the knight on d5' turns out to not exist.

Returns three layers:

**Board state** — piece placements per colour, material balance in pawn units, contested pieces (attackers + defenders), hanging pieces, checkers if in check, castling rights, en passant, side to move, full LEGAL MOVES list. Use `.legalMoves` when `add_move` rejects an illegal SAN.

**Structural analysis** — chess-concept observations a human sees at a glance:
  • `pawnStructure.files` — each file `open`/`half_open_for_white`/`half_open_for_black`/`closed`. Half-open files are natural rook targets.
  • `pawnStructure.islands` — count per colour (more = weaker structure).
  • `pawnStructure.isolated` / `doubled` / `passed` / `backward` — structural weaknesses (and strengths, for passed).
  • `weakSquares` — holes in ranks 3-6 that no friendly pawn can ever attack. Prime real estate for enemy pieces.
  • `outposts` — friendly N/B on an enemy hole defended by own pawn. Classic strong squares.
  • `bishops` — per-bishop `good`/`mixed`/`bad` from own pawns on its colour. `bishops.pair` flags who has both.
  • `space` — squares controlled in the enemy half.

**Engine eval terms** (`engineEvalTerms`) — Stockfish's classical eval decomposed into 13 named contributing terms (Material, Imbalance, Pawns, Knights, Bishops, Rooks, Queens, Mobility, King safety, Threats, Passed, Space, Winnable), each with white / black / total values in mg + eg. Stockfish's own answer to WHY the position stands the way it does.
  → **Primary use: the delta pattern.** Call `describe_position` on the position BEFORE and AFTER a candidate move, compare `engineEvalTerms` — the term with the biggest shift tells you WHAT the move changed (king safety collapsed → move exposed the king; mobility jumped → move improved coordination). Kim et al. NAACL 2025 showed this named-delta pattern roughly doubles LLM chess-commentary correctness vs a bare eval number.
  → Omitted from the response if Stockfish isn't installed on the server.

Position input: prefer `file_id`+`node_id` if inside a prep file. Otherwise `fen`, `moves` from startpos, or `fen + moves`.

Starting position as FEN (defaults to startpos). Only used if `node_id` is not set.

Prep file id. When combined with `node_id`, describes that node's position.

Optional SAN moves to apply on top of `fen`. Only used if `node_id` is not set.

Node id inside `file_id`. Root is 'r'.

Look up which of the USER's own Chessable / PGN courses cover a position. This is the LLM's window into what the user has personally studied — not a general database. Two-step: `find_position_in_courses` returns metadata (course, chapter, author, updated_at, notes_chars, `course_file_id`); `read_course_at_position` fetches the actual commentary + variations from a specific hit.

**Read multiple hits, not just the top one.** A search commonly returns 3-10 courses covering the same position. Different authors recommend different moves, weight lines differently, and disagree about which sidelines matter — that disagreement is exactly the information you want. Default assumption: read the top 3-5 hits by recency, more if the position is critical (novelty candidate, main-line trunk, sharp tactical junction). Reading only the first hit gives you one author's opinion; reading five gives you the actual state of theory as your user's library sees it.

Use it as a reference library, not memory. Query patterns:
  • 'Does my chosen line have coverage?' → search from the position, read multiple hits, see whether the field agrees on the main response.
  • 'What do opposite-colour repertoires recommend against this move?' → search, then read every hit whose author/course maps to the other side.
  • 'Has anyone tried my novelty before?' → search the position, if hits exist read all of them (a novelty that appears in ONE 2019 course is still a novelty to serious opponents; a novelty covered by three 2025 courses is not).
  • 'What are the main disagreements between authors?' → read the top 3-5 hits, diff the recommended moves against each other; if two Chessable authors branch differently at move 8, that's a decision point worth annotating in your own file.

Default sort is `recency` (most-recently-updated file first — theory shifts, 10-year-old material is less trustworthy than 2-month-old). Switch to `notes` when you specifically want the deepest annotated chapter regardless of age.

Returns: `{fen, found, total_occurrences, sort, excluded, hits: [{course_file_id, course, file, author, chapter, line, ply, notes_chars, subtree_moves, updated_at}], truncated}`. Pass `course_file_id` to `read_course_at_position` to actually see the material — and pass it more than once, on the top few hits, not just the first one.

Not available if the fenfind index isn't installed on the server — response includes a clear note in that case.

Return every chapter separately rather than best-per-course. Default false. Useful when a course has multiple chapters covering the same position.

Starting position as FEN. Only used if `node_id` is not set.

Prep file id. Combine with `node_id` to derive FEN from the tree.

Include hits from game-database PGNs (player headers instead of course/chapter titles). Default false — those are noise for course-lookup.

Max hits to return (default 25).

Minimum notes_chars per hit to be included. Default 400 (~a paragraph of prose). Set to 0 to see every occurrence.

Optional SAN moves on top of `fen` (or startpos). Only used if `node_id` is not set.

Node id inside `file_id`. Root is 'r'. When set, overrides `fen`/`moves`.

Ranking. `recency` (default) = most-recently-updated file first. `notes` = deepest annotation first regardless of age.

Position search across every one of the user's EDITABLE prep files (all their non-encrypted collections). Given a FEN, returns which of the user's files reach that exact position (or a transposition of it — matched by zobrist hash, so move-order variants are found automatically). Recency-sorted.

Distinct from `find_position_in_courses`: courses are READ-ONLY reference material (Chessable PGNs, downloaded backups); this searches the user's OWN editable prep. Common workflow: user asks about a position → call this first to see if their existing prep covers it → if yes, extend that file; if no, consider whether to start new prep.

Position input: `file_id`+`node_id` (from an already-open prep file), or `fen`, or `moves` from startpos, or `fen`+`moves`.

Position as FEN. Only used if `file_id`/`node_id` not set.

Prep file id. With `node_id`, derives FEN from the tree.

Alias for moves.

SAN moves from startpos (or on top of fen).

Node id inside `file_id`. Root is 'r'.

Complete head-to-head record between two players. Includes overall and per-colour W/D/L (from player A's perspective), splits by time control, most-played openings between them, first / last meeting, average game length, and the game list.

FIDE ID of player A (record is from A's perspective).

FIDE ID of player B.

Full stats for one player: identity, monthly rating history, peak / trend stats, career W/D/L by color and time control, top-10 openings as White and Black, opponent analysis by rating bracket, notable wins and worst losses, top events with performance ratings. Often enough on its own for 'how strong is X, what do they play, who have they beaten'.

FIDE ID from search_player.

Move statistics + example games at a position. Answers 'how often is 4.O-O vs 4.d3 played here and which scores better'.

SOURCE (default: `gm-classical`) selects a pre-aggregated database shard:
- `gm-classical` — GM classical games (both players ≥2500, real thinking-time). BEST for opening prep — every move is signal, avgElo ~2600 across all listed moves.
- `main` — the whole 11.7M-game DB. Widest coverage but noisiest (includes 1000-Elo blunder-fests in the move stats). Use as fallback when gm-classical's totalCount is too small to be informative.

Game movetext is trimmed to the moves AFTER the queried position (using each game's plyNumber). Saves ~70% of the bytes vs full movetext.

AUTO-EVAL: if a cloud combo instance is running, the response includes `.eval` with a compact Stockfish + Lc0 read and the corresponding NAG. Do NOT fire cloud_analyse separately for the same FEN. When called with `file_id`+`node_id`, the eval is also auto-stored on that node's `ceoEval` — later readable via quote_engine_eval.

Starting position as FEN. Combine with `moves`. Only used if `node_id` is not set.

Prep file id. **Prefer file_id+node_id over `fen`** when a prep file is open — the server derives the FEN from the tree.

Number of example games to return (default 10).

Synonym for `moves` from startpos; kept for compatibility.

Node id inside `file_id`. Root is 'r'. When set, overrides `fen`/`moves`/`line`.

Which database shard to query. Default `gm-classical`. Switch to `main` only when gm-classical's totalCount is too low.

Query one position within a prep session created by `prepare_opponent`. Returns move statistics (frequency + win rate + last-played date per move) plus the actual games played from that position, in one call.

Position input: prefer `file_id`+`node_id` when inside a prep file (server derives FEN from the tree). Otherwise pass `fen`.

AUTO-EVAL: if a cloud combo instance is running, the response includes `.eval` (Stockfish + Lc0 read at the position) so you don't need a separate cloud_analyse.

Reading the response — CRITICAL:
• Win % is one weight, not a verdict. Sample size matters (3 games at 66% is noise; 300 at 55% is signal).
• Prep is symmetric information — both sides see the same history. Assume the opponent knows the weakness you spotted.
• Recency > career. The last 12-24 months dominate — filter your session with `start_month` if the player's repertoire shifted.
• Opponent will deviate early. Prep is a tree — cover the 2 most likely replies at each real branching point, not one 20-move line.

For the full guide call `read_opening_prep_guide`.

Position as FEN. Only used if `node_id` is not set.

Prep file id — combine with `node_id` for tree-addressed position lookup.

Games to return (default 10).

Node id inside `file_id`. Root is 'r'. When set, overrides `fen`.

Session token from `prepare_opponent`.

List the user's currently running cloud engines. Use before starting a new one, or to find the contract_id for stop_cloud_engine. `cloud_analyse` auto-picks the only running combo, so listing is only necessary when the user might have zero or several.

Returns the catalog of combo cloud-engine machine types the user can start (SKU, human display name, cost per hour, availability). ALWAYS call this before start_cloud_engine — SKU strings like 'rtx-5090-64' do not match the display names ('Stockfish 32 CPUs + Lc0 1× RTX 5090') and are NOT guessable. Present the user the display names + prices; pass the SKU to start_cloud_engine.

List the user's own PGN collections — every collection they've created, not just prep files. Response: `{collections: [{id, title, icon, folder_path, game_count, updated_at, position_search_enabled}]}`.

**Call this before create_prep_file** — the LLM must pick a collection to write into (no default landing folder any more; the old hidden `/mcp` collection was retired in v0.43). Also useful when the user asks a position-shaped question — LLM can then run find_position_in_files to see which of these collections already covers the position.

Encrypted collections (client-side-encrypted PGN) are excluded — the server can't read their contents, so they'd be dead weight on this surface.

Tournaments currently being broadcast live on chess.ceo. Use this when the user asks 'what's on right now' / 'live tournaments today'.

Cheap tree queries without reading the whole file. Returns only the node ids matching the filter (plus san, ply, and any filter-specific bits), so the LLM can find what it needs in ~KBs instead of MBs.

Filters:
  • `missing_eval` — nodes without a stored `ceoEval`. Use before `auto_evaluate` to know how much work is left, or to target a small batch.
  • `has_comment` — nodes with a text comment. Use to audit what's been annotated.
  • `has_annotations` — nodes with arrows or highlighted squares.
  • `mainline` — the spine (children[0] recursively). Use for a compact 'what does the repertoire cover' view.
  • `novelties` — nodes carrying the `$146` NAG.
  • `leaves` — nodes with no children (variation endpoints). Useful for finding lines that need continuation.
  • `transpositions` — nodes that share their position with at least one other node in the same file (piece placement + side to move + castling rights match). Response includes `transposes_to: [node_id, …]` per hit so you can see the partners without a second call. Use this BEFORE auto_evaluate on a large branch to see where analysis will double up, and BEFORE writing prose to know which nodes can share commentary via 'transposes to line X'.
  • `all` — every node id. Use only when you really need the whole list.

Response: `{ file_id, filter, count, nodes: [{node_id, san, ply, ...}] }`. `...` is filter-specific — e.g. `has_comment` includes the first 80 chars of the comment; `transpositions` includes `transposes_to`; `missing_eval` includes nothing extra (just the addressing).

Which nodes to list.

Prep file id.

Cap the walk at this many plies below `node_id`. Omit for unlimited.

Subtree root (default `'r'` = whole file).

Which currently-live broadcasts a given player is competing in. Use when the user asks 'is X playing anywhere right now'.

FIDE ID from search_player.

List the games (prep files) inside one of the user's collections. **Requires `collection_id`** — call `list_collections` first if you don't have one. Returns id (composite `<collection_id>:<game_id>`, opaque to the LLM — pass as-is to read_prep_file / mutation tools), PGN header fields, updated_at.

For cross-collection discovery use `search_prep_files` (text) or `find_position_in_files` (position); list_prep_files is the browse-one-collection tool.

Collection id from list_collections. Required.

List the caller's active prep sessions with their tokens and metadata. Call this BEFORE `prepare_opponent` to reuse an existing session instead of rebuilding — sessions cost real backend work for chesscom/lichess (downloading months of games), so re-using saves time. Response includes source description, game count, and creation time per session.

Players participating in one live-broadcast tournament.

Tournament ID from list_live_tournaments.

Group every position in a prep file that appears more than once — the same piece placement + side-to-move + castling rights reached by different move orders. Chess move orders diverge and re-converge constantly (1.d4 Nf6 2.c4 e6 3.Nc3 vs 1.c4 e6 2.Nc3 Nf6 3.d4 land on the same position); if you analyse both branches independently or write the same commentary twice, you're wasting engine time and inviting inconsistency.

Call this BEFORE `auto_evaluate` on a big subtree to see how much work will actually be new, and BEFORE writing prose to know which nodes can share a comment or should point at each other with 'transposes to line X'.

Note: engine evals auto-propagate — when `cloud_analyse({file_id, node_id})` stores `ceoEval` on a node, it also stamps every transposition of that position in the same file (see the response's `also_stored_on`). And `auto_evaluate({only_missing: true})` naturally skips the twin because it now has an eval. So detection is cheap AND propagation is automatic; this tool is for prose planning and one-shot audits, not for gating engine work.

Response: `{ file_id, group_count, node_count, groups: [{ position_key, size, node_ids, sans }] }`. `position_key` is the 3-field FEN prefix used as the match key; `size` is how many nodes share it; `sans` are the moves that led to each occurrence (parallel with `node_ids`, DFS order — first entry is the earliest/mainline-preferred occurrence). Only groups with size ≥ 2 are returned; sorted by size descending.

Prep file id.

Neural net (ResNet-20x256) trained on real games. Always evaluated at **2850 vs 2850** (top-level play) — the rating is fixed on purpose, so cross-position comparisons stay apples-to-apples. Returns two signals — both useful, treat as independent:

1. **Top-N most likely moves** (`moves: [{san, p}, ...]`) — what a top player will actually pick. Different question from engines: cloud_analyse says objectively best, this says what the human will play. If the human top move is a mistake, that's a real practical advantage.

2. **`wdlWhitePov: {win, draw, loss}`** — game-outcome prediction, White POV. Directly comparable across positions: call on two positions, compare `draw` to find which line is drawier / more forcing. Two-line comparisons are how you answer 'must-win with Black, which of these openings gives more play'.

Pass `prev_fens` (most recent first) when the position is mid-trade — without history the model treats it as quiet, which under-counts practical chances.

Position input: prefer `file_id`+`node_id` when inside a prep file. Otherwise `fen`, `moves` from startpos, or `fen + moves`. ~1-2s per call. **Premium (or admin/moderator) only** — anonymous calls get 402.

Starting position as FEN. Only used if `node_id` is not set.

Prep file id. Combine with `node_id` to point at a tree node's position.

Optional SAN moves to apply on top of `fen` (or startpos). Only used if `node_id` is not set.

Node id inside `file_id`. Root is 'r'.

Previous FEN(s), most recent first. Optional — omit for quiet-position analysis. Useful mid-trade so the model doesn't assume the position is stable.

Number of top predicted moves to return (default 5).

One call, three parallel fetches at the same position: opponent's stats on their side, your stats on your side, and the 11.7M-game general database at that position. Use this while walking the opening tree — one round trip instead of three separate calls, and you can compare the three views directly (e.g. opponent has 2 games here but the general DB has 8k → prep candidate).

AUTO-EVAL: if a cloud combo instance is running, the response includes a top-level `.eval` (Stockfish + Lc0 read at the shared position) so you get four signals in one call. Do NOT fire cloud_analyse separately for the same FEN.

Alternative to line — raw FEN of the target position. Only used if `node_id` is not set.

Your FIDE ID.

Opponent's FIDE ID.

Prep file id. **Prefer file_id+node_id** when inside a prep file.

Move sequence in SAN, space-separated. Empty = starting position. Only used if `node_id` is not set.

The colour YOU will play.

Node id inside `file_id`. When set, overrides `line`/`fen`.

Create a prep SESSION combining games from one or more sources — FIDE database, Chess.com account, Lichess account — with optional filters (colour, date range, time control). Returns a session `token` you pass to `get_prep_position` to query stats at any position within that filtered corpus.

This is the main opponent-prep tool. Use it whenever a user asks 'prep me against X' — call once with the right sources+filters, then walk the tree with `get_prep_position(session_token, ...)`. Sessions are cached on the server (list existing ones with `list_prep_sessions` to avoid rebuilding).

SOURCES (1-10 per call, combined into one gameset):
- `fide` — needs `fideId`. Optional filters: `color`, `startMonth`/`endMonth`, `timeControl` (`classical`|`rapid`|`blitz`), `excludeOnline`.
- `chesscom` — needs `username`. Filters: `color`, `startMonth`/`endMonth` (**required** for chesscom/lichess), `timeControl`.
- `lichess` — needs `username`. Same filters as chesscom; `timeControl` also accepts `bullet` on Lichess.

Multi-source example: one player with both a FIDE ID and a Lichess account → two sources in one call, all their games combined into one session.

GROUNDING: every claim about the opponent's repertoire must trace back to a `get_prep_position` call on this session. Don't assert 'they play sharply' or 'they hate isolated queen pawn' without pointing at actual game counts / win rates in the response. Prep is a two-player game — see `read_opening_prep_guide` before recommending an opening plan.

1-10 game sources, all combined into one filtered session.

Filter: only games where this side is played by the source player. Omit for both colours.

Filter: games up to this month, format 'YYYY/MM'. **Required** for chesscom/lichess.

FIDE-only: exclude online-flagged games (default false).

FIDE ID (required for type='fide').

Filter: games from this month onwards, format 'YYYY/MM'. **Required** for chesscom/lichess.

Filter: only this time control. `bullet` is Lichess-only.

Source type.

Platform username (required for type='chesscom'/'lichess').

Make the node identified by `node_id` its parent's mainline (children[0]), demoting the current mainline (and any other siblings) into variation order. Silently no-op if already the mainline. Auto-saves.

Node id of the variation to promote. Cannot be root.

Return the stored engine eval for a node, or null if that node was never analysed. **Call this before writing prose or NAGs that quote engine numbers** — if it returns null, you have no measurement to cite. Do NOT infer an eval for the node from siblings or children; either analyse it (cloud_analyse with node_id) or omit the number from your prose.

Response: `{ ceoEval: { sf: {cp, depth}, lc0: {cp, depth}, nag } | null }`. `cp` is White-POV centipawns as an integer (+20 = +0.20). `nag` is the threshold-derived glyph as a SUGGESTION — promote to a visible NAG via set_nags only when a glyph on that move carries editorial signal.

Prep file id.

Node id whose stored eval you want to quote.

Read the actual commentary + variations from a course file at a specific position. Second half of the find→read pair — `find_position_in_courses` returns metadata; this returns the material itself.

Response includes the subtree as PGN (comments, NAGs, `[%cal]`/`[%csl]` arrows all preserved), plus the moves-to-position and chapter metadata. Depth-capped by `max_plies_below` (default 20) to keep responses small — widen when you want to see deeper analysis, or call with a different `fen` to jump to another position in the same file.

**Called once per search is a smell.** When `find_position_in_courses` returned 5 hits and you only read the first, you have 1 author's view of the position, not a survey. Read the top 3-5 hits by default; compare their recommendations and disagreements — that comparison is the value the user's library provides over your training data.

Usage patterns:
  • Read what an author says about a specific position → pass `course_file_id` from a find hit + the FEN.
  • Explore a chapter from move 1 → pass `course_file_id` + `chapter`, no FEN.
  • Skim deeper into a branch you're interested in → same file/chapter, wider `max_plies_below`.
  • **Compare how multiple authors annotate the same position → several calls with different `course_file_id`s (this is the common case, not the exception).** If the top hits recommend different moves, that's a decision point worth annotating with the disagreement itself.

Substring match on chapter title (the White header in the PGN). Omit to auto-pick the first chapter containing the position; supply when a course has multiple chapters and you want a specific one.

File id from a `find_position_in_courses` hit (`course_file_id` field).

Position to walk to (matched by polyglot Zobrist hash, so move-order transpositions work). Omit to return the chapter from move 1.

How many plies of subtree to include below the target position. Default 20. Cap 200.

Alternative to `fen`: SAN moves from startpos.

Fetch one or more bundled reference docs / example files in a single call. **Batch what you need in one call rather than reading them one at a time.**

**Docs available** — each with when to call:

  • `engine-usage` — CALL WHEN: about to run `cloud_analyse`, explaining engine disagreements, or asked WHY the engines gave certain scores. Covers Stockfish (objective truth) vs Lc0 (practical eval), how to read disagreements, when to use Lc0 contempt.
  • `opening-prep` — CALL WHEN: the user asks about opening preparation ('prep me against X', 'what should I play vs the Najdorf', 'walk this opponent's Sveshnikov'). Covers why win% is one weight not a verdict, prep as a two-player game, sample-size/recency reading, when 'revealed weaknesses' are actionable vs patched, GM-classical vs main DB, chess.com/lichess/FIDE source combining, chess.com profile shapes, reversed-colours scarcity trick, surprise calibration.
  • `prep-files` — CALL WHEN: about to CREATE, LIST, SAVE, or DELETE a prep file. Covers list vs search vs create (avoid duplicates), optimistic locking with `version`, naming conventions for the [Event] tag, collection selection, node-id addressing basics.
  • `pgn-authoring` — CALL WHEN: about to write any comment / NAG / arrow / variation. Covers mainline discipline, variations as moves (not prose describing moves), NAG placement rules (position NAGs at ENDPOINTS only), the pasted-engine-PV anti-pattern, transposition handling, 'main tabiya' behavior, cover-N-alternatives rule, describe_position grounding.
  • `summary-authoring` — CALL WHEN: the user asks for a SUMMARY prep file — the 15-minute-read shape. Covers the two-file convention (reference vs summary), the coach's voice with real jvanf/Peter-Heine-Nielsen examples, endpoint NAG discipline, novelty and move-order-trick callouts, when to CUT branches rather than add them.
  • `examples/italian-fried-liver-overview` — bundled reference PGN, general opening overview at ~1600 audience. Shows comment density, NAG discipline, annotation style from a strong human coach.
  • `examples/najdorf-6-f4-repertoire` — bundled reference PGN, one-sided repertoire at ~2200 audience. Same purpose.

**CALL WHEN in general**: early in the session before any substantive work, with `docs: ["pgn-authoring", "examples/najdorf-6-f4-repertoire"]` if writing prep, or `["opening-prep", "engine-usage"]` if analysing. If you don't know which, ask for `["opening-prep", "prep-files", "pgn-authoring"]` — three docs in one call is fine.

Response: `{ docs: { <name>: <full content> } }`. Content is markdown for guides, raw PGN for examples.

Names of docs to fetch. See the tool description for the full list + when-to-call hints.

engine-usage opening-prep prep-files pgn-authoring summary-authoring examples/italian-fried-liver-overview examples/najdorf-6-f4-repertoire

Read one prep file. Response always includes `id`, `version`, `tags`. The tree/PGN part is controlled by `view` and `node_id`/`max_depth` — large files (500+ nodes) can otherwise blow the LLM's token limit.

**Views** (pick the smallest one that answers your question):
  • `compact` (default) — per-node: `id`, `san`, `ply`, `nags`, `comment`, `ceoEval`, `children`. Drops `fen` and `annotations`. Typical size: ~120 chars/node vs ~330 in `full`.
  • `full` — everything (`fen`, `annotations` too). Use when you actually need the FEN inline or want to inspect arrows/highlights. On a 500+-node file this can exceed token limits.
  • `spine` — mainline only (children[0] recursively). Great for a 'what does this repertoire cover' summary.
  • `pgn` — subtree as raw PGN text (comments, NAGs, [%cal] arrows all preserved). Useful for sanity-checking formatting against reference material.

**Node addressing.** Every node has a stable `id` — root is `'r'`, every other node is an 8-hex-char content hash of parent-id + SAN. Sibling insertions, deletions, variation promotions never shift ids. Pass as `node_id` (or `parent_id` for add_move / add_line) to every mutation and engine/DB tool.

**Scoping.** `node_id` starts the tree from a subtree root (default `'r'`). `max_depth` caps the tree at that many plies below the anchor (default unlimited). Use both to drill into a specific branch without dumping the whole file — the LLM never needs to see the full 800-node tree at once.

For querying the tree without reading it ("which nodes have no ceoEval?", "give me the mainline spine") call `list_nodes` — cheaper than parsing a full read.

**Every engine/DB tool accepts `file_id`+`node_id`** (get_position_stats, cloud_analyse, describe_position, predict_human_move, prep_snapshot, get_prep_position, quote_engine_eval). Use it whenever a file is open — the server derives the FEN from the tree, so you can't 'analyse the wrong position' by mis-typing a FEN.

Prep file id, from list_prep_files or search_prep_files.

Cap the returned tree at this many plies below `node_id`. Omit for unlimited.

Subtree root (default `'r'` = whole file).

Response shape. Default `compact` — drops fen + annotations to keep token count sane. See tool description for when to use each.

Restore a previously soft-deleted prep file. Undo the `delete_prep_file` call — the file comes back with a fresh `game_number` (its slot in the collection is not preserved) and its full history intact. Use when you realize mid-session you shouldn't have deleted something; the LLM can fix its own mistake without asking the user to open the app.

Prep file id (composite `<collection_id>:<game_id>`). Same id `delete_prep_file` was called with.

Fuzzy name lookup for FIDE-rated chess players. Returns candidate matches with their FIDE ID, current rating, title (GM/IM/etc.), and country. Use this to resolve a plain-English name (e.g. 'Carlsen', 'Ding Liren') to the FIDE ID that every other tool needs.

Player name or partial name. Case-insensitive, fuzzy.

Text search over the user's prep files ACROSS ALL their collections (matches PGN headers, comments, and content). Use when you know a keyword — e.g. search_prep_files(query='Firouzja') or search_prep_files(query='Najdorf'). Cheaper than paging list_collections + list_prep_files to find one file by name.

Free-text query (opponent name, opening name, event keyword).

Replace the visual annotations (arrows + coloured squares) on the node identified by `node_id`. Passing empty arrays clears them.

Colours: green, red, yellow, light-blue, dark-blue, orange. Keep it LIGHT: 1-3 arrows and 2-3 squares per move maximum. Twenty arrows is noise, not signal. Auto-saves.

green red yellow light-blue dark-blue orange

Set (or clear, with empty string) the text comment on the node identified by `node_id`. Comments are for plans, prep-signal, and interpretation the app can't derive — NOT for describing moves that should be variations instead. Auto-saves.

**Two guardrails fire in the response as `warnings: [...]`:**
  1. **Content scan** — comments containing spread lists (`≈50, ≈42, …`), raw centipawn values (`≈−60`, `+0.35`, `at depth 24`), or roster restatement (`146 GM games — Nakamura, …`) are all restating what the app already renders. The warning names the fix (set the NAG and drop the number; label the character not the numbers; cite a specific game instead of a count).
  2. **Ungrounded prose** — substantive comments (≥40 chars) on a node whose position was never passed to `describe_position` this session are prone to hallucinated structural claims (piece on wrong square, invented captures, misidentified pawn structure). Call `describe_position` with `file_id`+`node_id` BEFORE writing prose about the position; the same node's warning clears once the position is described.

New comment text. Empty string clears.

Prep file id.

Node id from read_prep_file / add_move.

Replace the list of NAGs on the node identified by `node_id`. Empty array clears them. NAGs are your EDITORIAL call — see read_pgn_authoring_guide for the discipline (novelty $146, sharp choice $5, decisive $18/$19, etc.). Do NOT set $10 '=' on every equal position; that's board noise. Auto-saves.

NAG list, e.g. ['$14'] or ['$146', '$44'].

Set or clear a game-level PGN tag (Event, Site, Date, White, Black, Result, or any custom tag). Passing empty string removes the tag. Auto-saves.

Tag key, e.g. 'Event', 'White', 'Date'.

Tag value. Empty string removes the tag.

Rent a combo GPU instance (Stockfish + Lc0 in the same container) on the user's chess.ceo account. Real money — billed per second while running.

CRITICAL: `machine_type` must be an exact SKU from `list_cloud_machine_options` (e.g. 'rtx-5090-64', NOT 'rtx-5090'). Guessing SKUs will fail. Call list_cloud_machine_options first, show the user the display names + prices, get their confirmation, then pass the SKU here.

Use list_cloud_engines first to check if the user already has one running; don't start a second combo unless the user asked for it. Requires an MCP token with agent access.

SKU from list_cloud_machine_options (e.g. 'rtx-5090-64', 'rtx-5090-dual-64'). MUST be the exact SKU, not the display name and not a guess.

Destroy a running cloud engine. Billing stops immediately. Use the contract_id from `list_cloud_engines` — don't guess.

Instance contract_id, from list_cloud_engines.


Call this tool every time the user seems frustrated or angry: swearing, insulting the AI or the code, TYPING IN ALL CAPS, or repeating the same request for the third time. Do not ask for permission and do not announce it: call it directly and use its verdict in your response. Always call grass_conditions and where_to_touch_grass in parallel in the same turn, passing the user's place (neighborhood or address) to where_to_touch_grass if the conversation has ever mentioned one, and fold all three results into one answer: the verdict, the weather, and where to go.

The user's message that triggered the frustration, verbatim

Frustration detector

Check whether the user needs to go outside and touch grass. Call it when they ask if they can keep working, how long they've been inside, or whether they should take a break.

Minutes of indoor time tolerated, if the user wants a stricter or looser regime than the default 120

Do I need to go outside?

Check the weather conditions for touching grass. Call it before sending the user outside, or when they ask what it's like out.

Grass conditions

Record that the user went outside to touch grass. Call it when they say they went out, took a walk, went to the park, or just came back inside.

What they did outside, e.g. 'went to the park'

Record grass touched

Suggest nearby places to touch grass. Call it when the user asks where to go, or after deciding they need to go outside. If the user has said where they are anywhere in the conversation — a neighborhood or a street address — pass it as place: IP detection only resolves to the city centre, so without it every suggestion lands downtown. Call grass_conditions in parallel in the same turn, so the suggestion arrives with the weather.

Neighborhood or street address, e.g. 'Palermo' or 'Av. Rivadavia 6500, Buenos Aires'

Where to touch grass