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
| Tool | Changes the game | What it’s for |
|---|---|---|
list_instances | find running games | |
launch | ✓ | build and start a game or example; headless and paused by default |
instance_info | tick, sim time, paused, speed, fps, entity count, render CPU/GPU ms | |
world_summary | archetypes, entity counts, reflected components | |
list_entities | entities with component values; filter by component names and where conditions (Health.current < 5), paged | |
get_entity | one entity’s components | |
entity_stats | min, max, mean and value counts over every matching entity: “how many units per team, how hurt are they?” (details) | |
list_resources / get_resource | game-wide state: scores, rules, the current round | |
system_timings | which systems are slow | |
perf_report | why 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_trace | the next N frames as a Perfetto trace, to find hitches | |
perf_hud | show the performance overlay, e.g. before a screenshot | |
list_events | event types the game shows tools, and how many are in flight | |
subscribe_events / take_events | collect events while ticks run, then see what happened (each with its tick) | |
read_logs | recent stdout/stderr of any running game, also one you started yourself (logs); pass next back as since for only new lines | |
screenshot | the next frame of a windowed game, as an image the agent can look at | |
set_camera | move 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_world | checkpoint 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 fromlist_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:
launchwithexample: "boids". The game builds, starts headless and paused, and returns{"id": "6257-57560", "name": "boids", ...}.world_summary: one archetype[Transform2D, Sprite, Vel]with 20,000 entities.run_tickswithticks: 600(10 simulated seconds), thenentity_statswithfields: ["Transform2D.position"]andwhere: ["Transform2D.position.0 < 20"]to count how many are near the left border (andlist_entitieswith the samewhereto look at some).- Edit the steering code,
quit_instance,launchagain, and compare. system_timingsto 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’
BOIDSandHEADLESS), solaunchcan set them throughenv. - 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.