Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Working with AI agents (MCP)

ponko-mcp is a Model Context Protocol server. It lets an AI coding agent (Claude Code, Cursor, Codex, …) start your game, look inside it while it runs, change it, and step the simulation, instead of guessing from source code and print statements.

It builds on the control endpoint and reflection.

Setup

In this repository it’s already configured: .mcp.json registers the server for any MCP client that reads project config (Claude Code asks once to approve it).

For your own game, install the binary and add it to your agent:

cargo install ponko_mcp
claude mcp add ponko -- ponko-mcp            # Claude Code
claude mcp add ponko-view -- ponko-mcp --read-only

Other clients take the same command (ponko-mcp, stdio transport) in their MCP config.

Then make the components you want agents to see reflected:

#![allow(unused)]
fn main() {
ctx.world.reflect::<Health>();
}

What an agent can do

ToolChanges the gameWhat it’s for
list_instancesfind running games
launch✓build and start a game or example; headless and paused by default
instance_infotick, sim time, paused, speed, fps, entity count, render CPU/GPU ms
world_summaryarchetypes, entity counts, reflected components
list_entitiesentities with component values; filter by component names and where conditions (Health.current < 5), paged
get_entityone entity’s components
entity_statsmin, max, mean and value counts over every matching entity: “how many units per team, how hurt are they?” (details)
list_resources / get_resourcegame-wide state: scores, rules, the current round
system_timingswhich systems are slow
perf_reportwhy frames are slow: fps and frame-time percentiles, CPU vs GPU, GPU time per pass, triangles drawn for the camera and the shadows, the heaviest meshes (details)
record_tracethe next N frames as a Perfetto trace, to find hitches
perf_hudshow the performance overlay, e.g. before a screenshot
list_eventsevent types the game shows tools, and how many are in flight
subscribe_events / take_eventscollect events while ticks run, then see what happened (each with its tick)
read_logsrecent stdout/stderr of any running game, also one you started yourself (logs); pass next back as since for only new lines
screenshotthe next frame of a windowed game, as an image the agent can look at
set_cameramove or zoom the camera (look_at, zoom, position), e.g. before a screenshot
run_ticks✓run exactly N ticks of a paused sim and wait
watch✓run N ticks and sample every K: tick, resources, entity counts, time per tick
pause / resume / set_speed✓sim clock
spawn_entity / despawn_entity✓
set_component / remove_component✓change values; object fields merge
set_resource✓change game-wide state; object fields merge
save_worldcheckpoint the world to a file
load_world✓roll back to a checkpoint (same entity ids)
save_scene / load_scene✓write or read a scene in the game’s assets/
spawn_prefab / save_prefab✓place a prefab at a point, or save an entity as one
quit_instance✓

--read-only hides every tool marked ✓, for agents that should only watch.

Several agents, several games

Every game with the endpoint on registers itself in ~/.crabby/instances/, and every ponko-mcp reads that registry. So:

  • each agent runs its own ponko-mcp, and all of them see all games;
  • every tool takes instance (the id, pid, port or name from list_instances), optional when only one game runs;
  • the game applies requests between ticks, one at a time, so agents never race the simulation or each other.

A typical split: one agent edits gameplay code and relaunches the game, while a read-only agent watches system timings in a long-running instance.

A session

An agent asked to “find out why boids clump at the edges” might do:

  1. launch with example: "boids". The game builds, starts headless and paused, and returns {"id": "6257-57560", "name": "boids", ...}.
  2. world_summary: one archetype [Transform2D, Sprite, Vel] with 20,000 entities.
  3. run_ticks with ticks: 600 (10 simulated seconds), then entity_stats with fields: ["Transform2D.position"] and where: ["Transform2D.position.0 < 20"] to count how many are near the left border (and list_entities with the same where to look at some).
  4. Edit the steering code, quit_instance, launch again, and compare.
  5. system_timings to check the fix didn’t make the tick slower.

Or, without relaunching: save_world at tick 0, set_component to try a different value on some boids, run_ticks 600, look, then load_world to roll back and try the next value.

Because the sim only advances on run_ticks, every run is repeatable and the agent can check results at exact ticks.

To see how a run develops, watch does it in one call. In the battle example, with its Battle stats reflected:

{"ticks": 3600, "every": 600, "resources": ["Score"], "count": ["Unit"]}

returns seven samples (before, then every 10 simulated seconds), each with the tick, the Score resource, how many entities have a Unit, and the wall-clock time per tick, so “who’s winning, and does it get slower as the armies meet?” is one answer.

To check how things look, launch with headless: false and call screenshot. It works while the window is hidden behind others, and is scaled down to 1024 pixels by default (max_size) to keep it cheap to look at. A screenshot of the ants example after 40 seconds showed food carriers looping instead of going home; giving them a sense of where home is raised deliveries in that time from 328 to 1,747.

Making your game agent-friendly

  • Reflect the components that matter for gameplay, the resources that hold game-wide state (world.reflect_resource::<Score>()), so an agent can ask “who’s winning?” without counting entities, and the events (ctx.events.reflect::<Died>()), so it can see what happened, not only the state afterwards.
  • Read sizes and seeds from environment variables (like the examples’ BOIDS and HEADLESS), so launch can set them through env.
  • Support headless: put game logic in sim systems; it then runs without a window or GPU, which is faster for agents and works on CI.

A reproducible development loop

Start with a small reproduction and record the engine revision, example or game command, features, seed, input sequence and expected state. Use launch with a controlled environment, inspect the reflected state, then run_ticks to a fixed tick and record get_entity/get_resource results. Save a checkpoint when useful; only reflected/serialized game data can be inspected or restored through these tools.

After editing code, compile, run the relevant tests and relaunch the same reproduction. Compare the same state and ticks. For a visual change, use a windowed instance and record camera, resolution, quality and screenshots; headless success does not verify rendering. Collect a perf_report or record_trace when the change affects performance, and preserve comparable before/after workloads. A screenshot alone does not establish simulation correctness or a performance improvement.

These tools exist today. A general gameplay-input recording/replay service, structured scheduler inspection, richer shader diagnostics and broader GPU/asset memory reports are not implied by their presence. Follow #145, #153 and #157 for the relevant planned extensions. Reliable autonomous creation of complete games remains a long-term objective, with human review and reproducible evidence still required. See the project vision.