From 91ef4bc21fbb5187e9bc2ac9a6639671f20374bb Mon Sep 17 00:00:00 2001 From: Natalie Date: Sun, 17 May 2026 03:43:32 -0700 Subject: [PATCH] =?UTF-8?q?feat(@projects/@magic-civilization):=20?= =?UTF-8?q?=E2=9C=A8=20rename=20claude-player=20to=20player-api=20refactor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Lilith Autocommit --- scripts/claude-demo-25turn.sh | 4 +- scripts/claude-smoke-5endturn.sh | 4 +- scripts/player-api-example.py | 147 ++++++++++++++++++ ...-player-server.sh => player-api-server.sh} | 22 ++- .../{CLAUDE_PLAYER_API.md => PLAYER_API.md} | 2 +- .../scenes/headless/claude_player_main.tscn | 6 - ...aude_player_main.gd => player_api_main.gd} | 12 +- .../scenes/headless/player_api_main.tscn | 6 + src/simulator/api-gdext/src/lib.rs | 4 +- src/simulator/api-gdext/src/player_api.rs | 4 +- .../tests/full_game_transcript.rs | 2 +- .../tests/smoke_5_endturn_mock.rs | 2 +- tooling/claude-player-mcp/README.md | 25 ++- tooling/claude-player-mcp/src/harness.ts | 4 +- tooling/claude-player-mcp/src/index.ts | 16 +- tooling/claude-player-mcp/src/types.ts | 2 +- 16 files changed, 219 insertions(+), 43 deletions(-) create mode 100755 scripts/player-api-example.py rename scripts/{claude-player-server.sh => player-api-server.sh} (70%) rename src/game/engine/docs/{CLAUDE_PLAYER_API.md => PLAYER_API.md} (99%) delete mode 100644 src/game/engine/scenes/headless/claude_player_main.tscn rename src/game/engine/scenes/headless/{claude_player_main.gd => player_api_main.gd} (97%) create mode 100644 src/game/engine/scenes/headless/player_api_main.tscn diff --git a/scripts/claude-demo-25turn.sh b/scripts/claude-demo-25turn.sh index 8a3a9aae..3c0ef6db 100755 --- a/scripts/claude-demo-25turn.sh +++ b/scripts/claude-demo-25turn.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # p2-67 Phase 5 — Real-apricot Claude-vs-AI demo driver, 25 EndTurns. # -# Spawns claude-player-server.sh, sends CP_TURNS act:end_turn requests +# Spawns player-api-server.sh, sends CP_TURNS act:end_turn requests # (default 25) followed by shutdown. Streams the FULL JSON-Lines # response stream verbatim to stdout — caller redirects to transcript.jsonl. # @@ -47,7 +47,7 @@ trap "rm -rf '$TMP'" EXIT # Drive the harness. timeout is a hard ceiling; CP_TIMEOUT_SEC drives the # in-process Godot watchdog inside the harness. HARNESS_RC=0 -timeout "$((CP_TIMEOUT_SEC + 60))" "$SCRIPT_DIR/claude-player-server.sh" \ +timeout "$((CP_TIMEOUT_SEC + 60))" "$SCRIPT_DIR/player-api-server.sh" \ < "$TMP/in.jsonl" > "$TMP/out.jsonl" 2>"$TMP/err.log" \ || HARNESS_RC=$? diff --git a/scripts/claude-smoke-5endturn.sh b/scripts/claude-smoke-5endturn.sh index cae03fd4..e84b1bf4 100755 --- a/scripts/claude-smoke-5endturn.sh +++ b/scripts/claude-smoke-5endturn.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # p2-71 — 5-EndTurn smoke driver for the Claude Player API harness. # -# Spawns claude-player-server.sh, sends 5 act:end_turn requests over stdin, +# Spawns player-api-server.sh, sends 5 act:end_turn requests over stdin, # parses JSON-Lines responses, and prints a one-line verdict: # # {"turns": 5, "ai_turn_completed_events": N, @@ -37,7 +37,7 @@ trap "rm -rf '$TMP'" EXIT } > "$TMP/in.jsonl" # Run harness with timeout safety. 60s should be plenty for 5 turns on duel. -timeout 90 "$SCRIPT_DIR/claude-player-server.sh" < "$TMP/in.jsonl" > "$TMP/out.jsonl" 2>"$TMP/err.log" || true +timeout 90 "$SCRIPT_DIR/player-api-server.sh" < "$TMP/in.jsonl" > "$TMP/out.jsonl" 2>"$TMP/err.log" || true # Parse — for each turn-response, count `ai_turn_completed` events and # sum actions_applied across slots. Output one verdict line. diff --git a/scripts/player-api-example.py b/scripts/player-api-example.py new file mode 100755 index 00000000..993f14c7 --- /dev/null +++ b/scripts/player-api-example.py @@ -0,0 +1,147 @@ +#!/usr/bin/env python3 +"""Example: drive the Magic Civilization Player API from a plain Python client. + +This script exists as documentation-by-example: it shows that +`scripts/player-api-server.sh` is not Claude-specific. Any client that +speaks JSON-Lines over stdin/stdout can take a player slot — Claude Code +via `tooling/claude-player-mcp/`, an OpenSpiel adapter pumping the same +pipe, or this 80-line random/greedy bot. + +Wire-protocol contract: `src/game/engine/docs/PLAYER_API.md`. + +Usage: + python3 scripts/player-api-example.py [TURNS] + +Spawns the harness, plays `TURNS` turns (default 10) with a trivial +greedy policy, prints a one-line summary per turn, then shuts the +harness down cleanly. +""" +from __future__ import annotations + +import json +import os +import subprocess +import sys +from pathlib import Path +from typing import Any + +REPO_ROOT = Path(__file__).resolve().parent.parent +HARNESS = REPO_ROOT / "scripts" / "player-api-server.sh" + + +class PlayerApiClient: + """Thin JSON-Lines client over a child process. Stateless except for + the request-id counter — the harness holds the simulator state.""" + + def __init__(self, env_overrides: dict[str, str] | None = None) -> None: + env = {**os.environ, **(env_overrides or {})} + self._proc = subprocess.Popen( + ["bash", str(HARNESS)], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.DEVNULL, + cwd=str(REPO_ROOT), + text=True, + bufsize=1, + env=env, + ) + self._next_id = 1 + + def _send(self, msg: dict[str, Any]) -> dict[str, Any] | None: + msg["id"] = self._next_id + self._next_id += 1 + assert self._proc.stdin is not None and self._proc.stdout is not None + self._proc.stdin.write(json.dumps(msg) + "\n") + self._proc.stdin.flush() + # Read until correlated response (skip async notifications). + for _ in range(5000): + line = self._proc.stdout.readline() + if not line: + return None + try: + obj = json.loads(line) + except json.JSONDecodeError: + continue + if obj.get("id") == msg["id"]: + return obj + return None + + def view(self) -> dict[str, Any] | None: + r = self._send({"type": "view"}) + return r["view"] if r and r.get("ok") else None + + def act(self, action: dict[str, Any]) -> bool: + r = self._send({"type": "act", "action": action}) + return bool(r and r.get("ok")) + + def end_turn(self) -> bool: + return self.act({"type": "end_turn"}) + + def shutdown(self) -> None: + self._send({"type": "shutdown"}) + try: + self._proc.wait(timeout=5) + except subprocess.TimeoutExpired: + self._proc.kill() + + +def greedy_policy(view: dict[str, Any]) -> list[dict[str, Any]]: + """Pick a small bundle of actions for this turn — found if possible, + fortify warriors, queue a worker in any city with an empty queue.""" + actions: list[dict[str, Any]] = [] + for u in view.get("units", []): + if u.get("owner") != view.get("player"): + continue + legal = [a["action"] for a in u.get("legal_actions", [])] + # 1. Found city if available (founder) + for a in legal: + if a["type"] == "found_city": + actions.append(a) + break + else: + # 2. Fortify warriors + for a in legal: + if a["type"] == "fortify" and not u.get("fortified"): + actions.append(a) + break + for c in view.get("cities", []): + if c.get("production_queue"): + continue + for a in (a["action"] for a in c.get("legal_actions", [])): + if a.get("type") == "queue_production" and a.get("item") == "worker": + actions.append(a) + break + return actions + + +def main() -> int: + turns = int(sys.argv[1]) if len(sys.argv) > 1 else 10 + client = PlayerApiClient(env_overrides={"CP_SEED": "42", "CP_PLAYERS": "2"}) + try: + for t in range(1, turns + 1): + view = client.view() + if view is None: + print(f"[t{t}] view failed; aborting") + return 1 + score = view.get("score", {}) + res = view.get("resources", {}) + print( + f"[t{t:3d}] cities={int(score.get('city_count', 0))} " + f"units={int(score.get('unit_count', 0))} " + f"gold={int(res.get('gold', 0))} " + f"score={int(score.get('score_estimate', 0))}" + ) + for action in greedy_policy(view): + if not client.act(action): + print(f" action failed: {action}") + break + if not client.end_turn(): + print(f"[t{t}] end_turn failed; aborting") + return 1 + finally: + client.shutdown() + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/scripts/claude-player-server.sh b/scripts/player-api-server.sh similarity index 70% rename from scripts/claude-player-server.sh rename to scripts/player-api-server.sh index 52e23ae4..92d5db4d 100755 --- a/scripts/claude-player-server.sh +++ b/scripts/player-api-server.sh @@ -1,14 +1,22 @@ #!/usr/bin/env bash -# p2-67 Phase 3 — Launch wrapper for the Claude Player headless harness. +# Generic external-player JSON-Lines harness for Magic Civilization. # -# Spawns flatpak Godot headless with the claude_player_main scene and -# stdin/stdout piped through. Designed to be exec'd by the Claude SDK -# adapter as a child process — `child = spawn("scripts/claude-player-server.sh")`. +# Spawns Godot headless with the `player_api_main` scene and pipes +# stdin/stdout. Any client that speaks the JSON-Lines wire protocol +# documented in `src/game/engine/docs/PLAYER_API.md` can drive a game +# slot through this harness — Claude Code via the +# `tooling/claude-player-mcp/` adapter, an OpenSpiel/RL trainer via a +# Python `subprocess.Popen`, a smoke-test shell script, etc. # -# Env vars are forwarded into the sandbox (see CLAUDE_PLAYER_API.md): +# Env vars (see PLAYER_API.md for the full schema): # CP_SEED, CP_PLAYERS, CP_CLAUDE_SLOT, CP_MAP_SIZE, CP_MAP_TYPE, # CP_OMNISCIENT, CP_TIMEOUT_SEC, CP_LOG_FILE. # +# `CP_CLAUDE_SLOT` is the env-var name the harness has used since p2-67; +# it identifies the externally-controlled player slot — kept as-is for +# backward compatibility with existing clients. Despite the name it is +# not Claude-specific. +# # Exit code 0 on clean shutdown (shutdown request received or stdin EOF). # Non-zero on protocol error or harness crash. @@ -47,7 +55,7 @@ case "$(uname -s)" in --path "$PROJECT_DIR/src/game" \ --headless \ --rendering-method gl_compatibility \ - res://engine/scenes/headless/claude_player_main.tscn + res://engine/scenes/headless/player_api_main.tscn ;; *) exec flatpak run --user \ @@ -63,6 +71,6 @@ case "$(uname -s)" in --path "$PROJECT_DIR/src/game" \ --headless \ --rendering-method gl_compatibility \ - res://engine/scenes/headless/claude_player_main.tscn + res://engine/scenes/headless/player_api_main.tscn ;; esac diff --git a/src/game/engine/docs/CLAUDE_PLAYER_API.md b/src/game/engine/docs/PLAYER_API.md similarity index 99% rename from src/game/engine/docs/CLAUDE_PLAYER_API.md rename to src/game/engine/docs/PLAYER_API.md index 17597c22..dd79ed6d 100644 --- a/src/game/engine/docs/CLAUDE_PLAYER_API.md +++ b/src/game/engine/docs/PLAYER_API.md @@ -353,7 +353,7 @@ Three MCP tools become available after reload: | `magic_civ_act` | `{action: PlayerAction}` | `{events, view}` | | `magic_civ_end_turn` | none | Sugar for `act({type:"end_turn"})` | -The MCP server spawns `scripts/claude-player-server.sh` as a child on +The MCP server spawns `scripts/player-api-server.sh` as a child on first tool invocation and reuses the harness across the session. Any MCP-stdio client works the same way — `claude-player-mcp` is just a thin pump. diff --git a/src/game/engine/scenes/headless/claude_player_main.tscn b/src/game/engine/scenes/headless/claude_player_main.tscn deleted file mode 100644 index 8ae3c768..00000000 --- a/src/game/engine/scenes/headless/claude_player_main.tscn +++ /dev/null @@ -1,6 +0,0 @@ -[gd_scene load_steps=2 format=3] - -[ext_resource type="Script" path="res://engine/scenes/headless/claude_player_main.gd" id="1"] - -[node name="ClaudePlayerMain" type="Node"] -script = ExtResource("1") diff --git a/src/game/engine/scenes/headless/claude_player_main.gd b/src/game/engine/scenes/headless/player_api_main.gd similarity index 97% rename from src/game/engine/scenes/headless/claude_player_main.gd rename to src/game/engine/scenes/headless/player_api_main.gd index 6128ce55..cdb27115 100644 --- a/src/game/engine/scenes/headless/claude_player_main.gd +++ b/src/game/engine/scenes/headless/player_api_main.gd @@ -1,11 +1,17 @@ extends Node -## p2-67 Phase 3 — Headless harness for the Claude Player API. +## Headless harness for the generic external-player JSON-Lines API. ## ## Boots a seeded GameState, instantiates a `GdPlayerApi`, then enters ## a JSON-Lines pump on stdin/stdout. Each line in is one `Request` ## (`view` / `act` / `shutdown`); each line out is one `Response` or ## one `Notification`. The protocol contract lives in -## `src/game/engine/docs/CLAUDE_PLAYER_API.md`. +## `src/game/engine/docs/PLAYER_API.md`. +## +## Originally introduced in p2-67 as `claude_player_main.gd`. Renamed +## 2026-05-17 to drop the Claude-flavored naming — the wire protocol is +## client-agnostic; Claude Code is one consumer via +## `tooling/claude-player-mcp/`, an OpenSpiel/RL trainer can plug in via +## `subprocess.Popen`, and shell smoke tests use raw JSON-Lines. ## ## Env vars consumed: ## - `CP_SEED` — RNG seed (default 42) @@ -272,7 +278,7 @@ func _apply_combat_balance(gs: RefCounted) -> void: ## JSON-serialise the Array and hand it to the new Rust setters ## (`set_units_catalog_json` / `set_buildings_catalog_json`). func _apply_ai_catalogs() -> void: - # Project root is mounted at `src/game/` (see claude-player-server.sh + # Project root is mounted at `src/game/` (see player-api-server.sh # `--path` arg), so the bridge module's `res://` form drops the # `src/game/` prefix. var AiTurnBridgeState: Script = load("res://engine/src/modules/ai/ai_turn_bridge_state.gd") diff --git a/src/game/engine/scenes/headless/player_api_main.tscn b/src/game/engine/scenes/headless/player_api_main.tscn new file mode 100644 index 00000000..6f42bc5f --- /dev/null +++ b/src/game/engine/scenes/headless/player_api_main.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3] + +[ext_resource type="Script" path="res://engine/scenes/headless/player_api_main.gd" id="1"] + +[node name="PlayerApiMain" type="Node"] +script = ExtResource("1") diff --git a/src/simulator/api-gdext/src/lib.rs b/src/simulator/api-gdext/src/lib.rs index 452d39e2..43c8ff4c 100644 --- a/src/simulator/api-gdext/src/lib.rs +++ b/src/simulator/api-gdext/src/lib.rs @@ -3393,7 +3393,7 @@ impl GdGameState { /// `JSON.stringify`s the result before handing it to this setter — same /// disk-IO discipline as `_apply_ai_personalities`. /// - /// **Order contract (claude_player_main.gd)**: this setter MUST run + /// **Order contract (player_api_main.gd)**: this setter MUST run /// BEFORE `add_player_militarist`. Without a populated catalog, /// `MapUnit::new` reads `base_moves = 0` (the `UnitsCatalog::get` miss /// path), every AI `MoveUnit` is rejected at @@ -3435,7 +3435,7 @@ impl GdGameState { /// `public/games/age-of-dwarves/data/combat_balance.json`. /// /// Caller (GDScript boot, autoload `GameState.initialize_game` or the - /// headless `claude_player_main`) reads the file bytes and forwards the + /// headless `player_api_main`) reads the file bytes and forwards the /// raw string. The Rust parser /// (`mc_turn::combat_balance::load_combat_balance`) is the single source /// of truth for the wire shape; unknown keys are ignored and missing diff --git a/src/simulator/api-gdext/src/player_api.rs b/src/simulator/api-gdext/src/player_api.rs index 74609592..11e5acde 100644 --- a/src/simulator/api-gdext/src/player_api.rs +++ b/src/simulator/api-gdext/src/player_api.rs @@ -20,7 +20,7 @@ //! Error path: on any failure (parse, illegal action, internal) the //! returned JSON is an error-shaped envelope with `ok: false` and a //! typed `error` payload — adapters branch on the `ok` field exactly -//! as documented in `CLAUDE_PLAYER_API.md`. +//! as documented in `PLAYER_API.md`. use godot::prelude::*; use mc_ai::tactical::state::{TacticalBuildingSpec, TacticalUnitSpec}; @@ -71,7 +71,7 @@ impl GdPlayerApi { /// choices MUST follow up with `set_units_catalog_json` / /// `set_buildings_catalog_json` / `set_difficulty_threshold_mult` /// after every `load_state_json`. The harness - /// (`claude_player_main.gd::_apply_ai_catalogs`) does this once at + /// (`player_api_main.gd::_apply_ai_catalogs`) does this once at /// boot — if a future flow re-loads state mid-game, repeat the /// catalog setters. #[func] diff --git a/src/simulator/crates/mc-player-api/tests/full_game_transcript.rs b/src/simulator/crates/mc-player-api/tests/full_game_transcript.rs index b20a0d33..08fe4d4d 100644 --- a/src/simulator/crates/mc-player-api/tests/full_game_transcript.rs +++ b/src/simulator/crates/mc-player-api/tests/full_game_transcript.rs @@ -2,7 +2,7 @@ //! //! Drives a 3-player `GameState` (Claude=slot 0, AI=slots 1+2) for up to //! 25 turns and emits the canonical JSON-Lines wire transcript that -//! `claude_player_main.gd` would produce if it ran headlessly against the +//! `player_api_main.gd` would produce if it ran headlessly against the //! same construction. Output lands under //! `.local/demo-runs/2026-05-12-claude-vs-ai-mock/`: //! diff --git a/src/simulator/crates/mc-player-api/tests/smoke_5_endturn_mock.rs b/src/simulator/crates/mc-player-api/tests/smoke_5_endturn_mock.rs index 9aa0e11a..36f93626 100644 --- a/src/simulator/crates/mc-player-api/tests/smoke_5_endturn_mock.rs +++ b/src/simulator/crates/mc-player-api/tests/smoke_5_endturn_mock.rs @@ -2,7 +2,7 @@ //! //! Exercises `mc_player_api::apply_action(PlayerAction::EndTurn)` through 5 //! consecutive end-turn cycles against a hand-built `GameState` that mirrors -//! the GDScript headless harness (`claude_player_main.gd`) one-for-one. +//! the GDScript headless harness (`player_api_main.gd`) one-for-one. //! Construction lives in `tests/common/mod.rs` so it can be reused by the //! 25-turn `full_game_transcript` test (and any future scripted-game test). //! diff --git a/tooling/claude-player-mcp/README.md b/tooling/claude-player-mcp/README.md index edb63f1d..0911c95c 100644 --- a/tooling/claude-player-mcp/README.md +++ b/tooling/claude-player-mcp/README.md @@ -1,12 +1,23 @@ # @magic-civ/claude-player-mcp -MCP server exposing the Magic Civilization **Claude Player API** as tools -that Claude Code (or any MCP-compatible client) can call. Lets Claude Code -play the game directly — no API key, no second SDK layer. +Claude-Code-specific MCP transport for the generic **Magic Civilization +Player API**. Lets Claude Code play one slot of the game directly — no +API key, no second SDK layer. + +This package is *one* client of the player API; the underlying Godot +harness (`scripts/player-api-server.sh` + `src/game/engine/scenes/headless/player_api_main.tscn`) +is client-agnostic and speaks JSON-Lines. Other consumers: + +- **Plain Python / RL trainer / OpenSpiel adapter** — `subprocess.Popen` + the harness directly; example in `scripts/player-api-example.py`. +- **Shell smoke / regression tests** — `scripts/claude-smoke-5endturn.sh`, + `scripts/claude-demo-25turn.sh` write JSON-Lines to stdin. + +Wire-protocol contract: `src/game/engine/docs/PLAYER_API.md`. ## What it does -Stdio-transport MCP server. Spawns `scripts/claude-player-server.sh` as a +Stdio-transport MCP server. Spawns `scripts/player-api-server.sh` as a child process on first tool invocation; reuses the harness across calls. Translates each MCP tool call into one JSON-Lines request to the harness and returns the response as MCP `text` content. @@ -20,7 +31,7 @@ and returns the response as MCP `text` content. | `magic_civ_end_turn` | none | Sugar for `magic_civ_act({type:"end_turn"})` | Full `PlayerAction` taxonomy and view shape live in -`src/game/engine/docs/CLAUDE_PLAYER_API.md`. +`src/game/engine/docs/PLAYER_API.md`. ## Install + build @@ -72,11 +83,11 @@ Claude Code (stdio MCP client) @magic-civ/claude-player-mcp (this package) │ ▼ spawn() + JSON-Lines pipe -scripts/claude-player-server.sh +scripts/player-api-server.sh │ ▼ flatpak Godot --headless GdPlayerApi (api-gdext) → mc-player-api → mc-turn handlers ``` Owning objective: `.project/objectives/p2-67-claude-player-api.md`. -Wire-protocol contract: `src/game/engine/docs/CLAUDE_PLAYER_API.md`. +Wire-protocol contract: `src/game/engine/docs/PLAYER_API.md`. diff --git a/tooling/claude-player-mcp/src/harness.ts b/tooling/claude-player-mcp/src/harness.ts index dcbd3caa..88f56545 100644 --- a/tooling/claude-player-mcp/src/harness.ts +++ b/tooling/claude-player-mcp/src/harness.ts @@ -1,4 +1,4 @@ -// JSON-Lines pump over a child process running `scripts/claude-player-server.sh`. +// JSON-Lines pump over a child process running `scripts/player-api-server.sh`. // // Each request/response/notification is one JSON value per line. Responses are // correlated by an optional `id` field; the MCP server assigns monotonically @@ -17,7 +17,7 @@ import type { } from "./types.js"; export interface HarnessOptions { - /** Absolute path to claude-player-server.sh. */ + /** Absolute path to player-api-server.sh. */ serverScript: string; /** Env-var overrides for the harness (CP_SEED, CP_PLAYERS, ...). */ env?: Record; diff --git a/tooling/claude-player-mcp/src/index.ts b/tooling/claude-player-mcp/src/index.ts index 6dc4533a..faa5806c 100644 --- a/tooling/claude-player-mcp/src/index.ts +++ b/tooling/claude-player-mcp/src/index.ts @@ -1,11 +1,15 @@ #!/usr/bin/env node -// MCP server entry — exposes the Magic Civilization Claude Player API as +// MCP server entry — exposes the Magic Civilization external-player API as // stdio-transport MCP tools. Claude Code (or any MCP client) lists three // tools and calls them: `magic_civ_view`, `magic_civ_act`, `magic_civ_end_turn`. // -// Spawns `scripts/claude-player-server.sh` as a child process on first -// tool invocation and reuses it across calls in the same session. The -// child speaks JSON-Lines per `CLAUDE_PLAYER_API.md`. +// This package is the Claude-Code-specific transport for the *generic* +// player API; the underlying Godot harness is not Claude-flavored — it +// speaks JSON-Lines per `src/game/engine/docs/PLAYER_API.md` and is +// drivable by any client (OpenSpiel adapter, RL trainer, shell smoke). +// +// Spawns `scripts/player-api-server.sh` as a child process on first +// tool invocation and reuses it across calls in the same session. import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; @@ -24,7 +28,7 @@ const PROJECT_ROOT = resolvePath(HERE, "..", "..", ".."); const DEFAULT_SERVER_SCRIPT = resolvePath( PROJECT_ROOT, "scripts", - "claude-player-server.sh", + "player-api-server.sh", ); function emitStderr(line: string): void { @@ -73,7 +77,7 @@ const ACT_TOOL = { "Take one player action in Magic Civilization. The `action` argument is a " + "PlayerAction JSON object (e.g. {type:'move',unit_id:'42',to:[5,7]} or " + "{type:'found_city',unit_id:'42'} or {type:'end_turn'}). The full action " + - "taxonomy is documented in src/game/engine/docs/CLAUDE_PLAYER_API.md. " + + "taxonomy is documented in src/game/engine/docs/PLAYER_API.md. " + "Returns the events that fired plus the post-action view.", inputSchema: { type: "object" as const, diff --git a/tooling/claude-player-mcp/src/types.ts b/tooling/claude-player-mcp/src/types.ts index cd5eb51c..f0f16641 100644 --- a/tooling/claude-player-mcp/src/types.ts +++ b/tooling/claude-player-mcp/src/types.ts @@ -1,5 +1,5 @@ // Wire-protocol types — mirror the Rust enums in `mc-player-api`. -// Source of truth: src/game/engine/docs/CLAUDE_PLAYER_API.md. +// Source of truth: src/game/engine/docs/PLAYER_API.md. export type WireHex = [number, number]; export type PlayerId = number;