@chessceo/mcp 0.15.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +63 -24
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -185,11 +185,15 @@ const TOOLS = [
|
|
|
185
185
|
},
|
|
186
186
|
line: {
|
|
187
187
|
type: "string",
|
|
188
|
-
description: "Move sequence in SAN, space-separated, no move numbers required. Example: 'e4 e5 Nf3'. Leave empty for
|
|
188
|
+
description: "Move sequence in SAN from the starting position, space-separated, no move numbers required. Example: 'e4 e5 Nf3'. Leave empty for startpos. Alias: `moves` (same thing).",
|
|
189
|
+
},
|
|
190
|
+
moves: {
|
|
191
|
+
type: "string",
|
|
192
|
+
description: "SAN moves to apply on top of `fen` (or on top of startpos if no fen). Same shape as `line`. Use this when you have a starting FEN and want to walk from there — e.g. fen='<game tabiya>', moves='b4 a5 c3' to explore that continuation. Wins over `line` if both are set.",
|
|
189
193
|
},
|
|
190
194
|
fen: {
|
|
191
195
|
type: "string",
|
|
192
|
-
description: "
|
|
196
|
+
description: "Starting position as FEN. Combine with `moves` to walk from there, or use alone.",
|
|
193
197
|
},
|
|
194
198
|
limit: {
|
|
195
199
|
type: "integer",
|
|
@@ -210,7 +214,15 @@ const TOOLS = [
|
|
|
210
214
|
properties: {
|
|
211
215
|
fen: {
|
|
212
216
|
type: "string",
|
|
213
|
-
description: "FEN
|
|
217
|
+
description: "Starting position as FEN. Combine with `moves` to walk from there.",
|
|
218
|
+
},
|
|
219
|
+
moves: {
|
|
220
|
+
type: "string",
|
|
221
|
+
description: "Optional SAN moves to apply on top of `fen` (or on top of startpos). Example: fen='<tabiya>', moves='b4 a5'.",
|
|
222
|
+
},
|
|
223
|
+
line: {
|
|
224
|
+
type: "string",
|
|
225
|
+
description: "Synonym for `moves` from the starting position; kept for compatibility.",
|
|
214
226
|
},
|
|
215
227
|
limit: {
|
|
216
228
|
type: "integer",
|
|
@@ -219,18 +231,22 @@ const TOOLS = [
|
|
|
219
231
|
description: "Number of top continuations to return.",
|
|
220
232
|
},
|
|
221
233
|
},
|
|
222
|
-
required: ["fen"],
|
|
223
234
|
},
|
|
224
235
|
},
|
|
225
236
|
{
|
|
226
237
|
name: "analyse",
|
|
227
238
|
description: "Short Stockfish evaluation at a position. Returns the top-N candidate moves with score (centipawns from side-to-move POV, positive = advantage; or mate distance) and the principal variation for each. Defaults: 2s think time, top-3 lines. PV moves come back in SAN (e4, Nf3, Bxc4 — not UCI). Free (no cloud instance needed) — use liberally.\n\n" +
|
|
228
239
|
"GROUNDING: cite this tool's actual output when you claim things about positions. Don't invent evaluations from general principles or training data — if you don't have engine output for a FEN, call this. Compute is cheap.\n\n" +
|
|
240
|
+
"Position input is flexible — pass `fen`, or `moves` from startpos, or `fen + moves` to walk from an arbitrary position. Also see the flip-side-to-move threat check in read_engine_usage_guide.\n\n" +
|
|
229
241
|
"Use this to sanity-check candidate lines from get_position_stats or get_player_preparation — human game frequency tells you what people play, engine evaluation tells you what's actually good.",
|
|
230
242
|
inputSchema: {
|
|
231
243
|
type: "object",
|
|
232
244
|
properties: {
|
|
233
|
-
fen: { type: "string", description: "
|
|
245
|
+
fen: { type: "string", description: "Starting position as FEN (defaults to startpos)." },
|
|
246
|
+
moves: {
|
|
247
|
+
type: "string",
|
|
248
|
+
description: "Optional SAN moves to apply on top of `fen` (or on top of startpos). Example: fen='<tabiya>', moves='b4 a5'.",
|
|
249
|
+
},
|
|
234
250
|
movetime_ms: {
|
|
235
251
|
type: "integer",
|
|
236
252
|
minimum: 100,
|
|
@@ -244,7 +260,6 @@ const TOOLS = [
|
|
|
244
260
|
description: "Number of candidate lines to return (default 3).",
|
|
245
261
|
},
|
|
246
262
|
},
|
|
247
|
-
required: ["fen"],
|
|
248
263
|
},
|
|
249
264
|
},
|
|
250
265
|
{
|
|
@@ -338,12 +353,17 @@ const TOOLS = [
|
|
|
338
353
|
"• 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.\n" +
|
|
339
354
|
"• 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.\n\n" +
|
|
340
355
|
"Contempt (`contempt`) skews Lc0 (only Lc0 — Stockfish always stays objective) toward White (positive) or Black (negative). Practical range -20..+20. Use it to find non-objective 'practical' ideas or when the user needs to steer toward fighting/solid lines with a specific colour. Do NOT quote a contempt-biased eval as objective — cross-check with Stockfish.\n\n" +
|
|
356
|
+
"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.\n\n" +
|
|
341
357
|
"For the full guide including worked examples, call the `read_engine_usage_guide` tool.\n\n" +
|
|
342
358
|
"Not for casual questions — this costs real money per second. Use the free `analyse` tool (single Stockfish, 2s) or `get_position_stats` for anything that doesn't require deep prep.",
|
|
343
359
|
inputSchema: {
|
|
344
360
|
type: "object",
|
|
345
361
|
properties: {
|
|
346
|
-
fen: { type: "string", description: "FEN
|
|
362
|
+
fen: { type: "string", description: "Starting position as FEN. Combine with `moves` to walk from there." },
|
|
363
|
+
moves: {
|
|
364
|
+
type: "string",
|
|
365
|
+
description: "Optional SAN moves to apply on top of `fen` (or on top of startpos). Example: fen='<tabiya>', moves='b4 a5 c3' analyses the position after those three moves.",
|
|
366
|
+
},
|
|
347
367
|
movetime_ms: {
|
|
348
368
|
type: "integer",
|
|
349
369
|
minimum: 100,
|
|
@@ -500,18 +520,26 @@ function convertAvailableMovesToSAN(raw, fen) {
|
|
|
500
520
|
}
|
|
501
521
|
return raw;
|
|
502
522
|
}
|
|
503
|
-
// Resolve a starting FEN from
|
|
504
|
-
//
|
|
505
|
-
//
|
|
506
|
-
//
|
|
523
|
+
// Resolve a starting FEN from any combination of `fen`, `line`, and
|
|
524
|
+
// `moves` the tool received. Three modes, all valid:
|
|
525
|
+
//
|
|
526
|
+
// fen alone → use as-is
|
|
527
|
+
// line/moves alone → walk from startpos
|
|
528
|
+
// fen + moves (or line) → walk from that fen
|
|
529
|
+
//
|
|
530
|
+
// `line` is the historical field name from the backend's prep endpoint;
|
|
531
|
+
// `moves` is the flexible-input name we now surface for LLM ergonomics
|
|
532
|
+
// ("start from this FEN and play these moves next"). They're synonyms
|
|
533
|
+
// here — same SAN sequence, same chess.js walker. `moves` wins if both
|
|
534
|
+
// happen to be provided.
|
|
507
535
|
function resolveFenFromArgs(args) {
|
|
508
536
|
const fenArg = typeof args.fen === "string" ? args.fen.trim() : "";
|
|
509
|
-
|
|
510
|
-
return fenArg;
|
|
537
|
+
const movesArg = typeof args.moves === "string" ? args.moves.trim() : "";
|
|
511
538
|
const lineArg = typeof args.line === "string" ? args.line.trim() : "";
|
|
512
|
-
const
|
|
513
|
-
|
|
514
|
-
|
|
539
|
+
const sequence = movesArg || lineArg;
|
|
540
|
+
const board = fenArg ? new Chess(fenArg) : new Chess();
|
|
541
|
+
if (sequence) {
|
|
542
|
+
for (const raw of sequence.split(/\s+/)) {
|
|
515
543
|
const san = raw.replace(/^\d+\.+/, "");
|
|
516
544
|
if (!san)
|
|
517
545
|
continue;
|
|
@@ -519,7 +547,7 @@ function resolveFenFromArgs(args) {
|
|
|
519
547
|
board.move(san);
|
|
520
548
|
}
|
|
521
549
|
catch {
|
|
522
|
-
throw new Error(`bad SAN token '${raw}' in
|
|
550
|
+
throw new Error(`bad SAN token '${raw}' in moves`);
|
|
523
551
|
}
|
|
524
552
|
}
|
|
525
553
|
}
|
|
@@ -561,15 +589,26 @@ async function callToolInner(name, args) {
|
|
|
561
589
|
case "get_player_profile":
|
|
562
590
|
return get("/api/chess/players/profile", { fideId: Number(args.fide_id) });
|
|
563
591
|
case "get_player_preparation": {
|
|
592
|
+
// Prefer sending `line` to the backend when the caller specified moves
|
|
593
|
+
// WITHOUT a fen — the backend then attaches the cumulative-line SAN to
|
|
594
|
+
// each move in the response (nicer for the LLM's follow-up walks).
|
|
595
|
+
// When a fen was supplied, always resolve the effective position
|
|
596
|
+
// client-side and send just fen — the backend can't reconstruct
|
|
597
|
+
// history it didn't see anyway.
|
|
598
|
+
const fenArg = typeof args.fen === "string" ? args.fen.trim() : "";
|
|
599
|
+
const movesArg = typeof args.moves === "string" ? args.moves.trim() : "";
|
|
600
|
+
const lineArg = typeof args.line === "string" ? args.line.trim() : "";
|
|
564
601
|
const params = {
|
|
565
602
|
fideId: Number(args.fide_id),
|
|
566
603
|
color: String(args.color),
|
|
567
604
|
compact: "true",
|
|
568
605
|
};
|
|
569
|
-
if (
|
|
570
|
-
params.
|
|
571
|
-
|
|
572
|
-
|
|
606
|
+
if (fenArg || movesArg) {
|
|
607
|
+
params.fen = resolveFenFromArgs(args);
|
|
608
|
+
}
|
|
609
|
+
else if (lineArg) {
|
|
610
|
+
params.line = lineArg;
|
|
611
|
+
}
|
|
573
612
|
if (typeof args.limit === "number")
|
|
574
613
|
params.limit = args.limit;
|
|
575
614
|
if (typeof args.offset === "number")
|
|
@@ -578,7 +617,7 @@ async function callToolInner(name, args) {
|
|
|
578
617
|
return convertAvailableMovesToSAN(raw, resolveFenFromArgs(args));
|
|
579
618
|
}
|
|
580
619
|
case "get_position_stats": {
|
|
581
|
-
const fen =
|
|
620
|
+
const fen = resolveFenFromArgs(args);
|
|
582
621
|
const raw = await get("/api/chess/database/main", {
|
|
583
622
|
fen,
|
|
584
623
|
limit: typeof args.limit === "number" ? args.limit : 20,
|
|
@@ -587,7 +626,7 @@ async function callToolInner(name, args) {
|
|
|
587
626
|
return convertAvailableMovesToSAN(raw, fen);
|
|
588
627
|
}
|
|
589
628
|
case "analyse": {
|
|
590
|
-
const fen =
|
|
629
|
+
const fen = resolveFenFromArgs(args);
|
|
591
630
|
const params = { fen };
|
|
592
631
|
if (typeof args.movetime_ms === "number")
|
|
593
632
|
params.movetime_ms = args.movetime_ms;
|
|
@@ -621,7 +660,7 @@ async function callToolInner(name, args) {
|
|
|
621
660
|
case "stop_cloud_engine":
|
|
622
661
|
return authedRequest("DELETE", `/api/agent/cloud-engines/${encodeURIComponent(String(args.contract_id))}`);
|
|
623
662
|
case "cloud_analyse": {
|
|
624
|
-
const fen =
|
|
663
|
+
const fen = resolveFenFromArgs(args);
|
|
625
664
|
const body = { fen };
|
|
626
665
|
if (typeof args.movetime_ms === "number")
|
|
627
666
|
body.movetime_ms = args.movetime_ms;
|
package/package.json
CHANGED