pyxel-mcp
MCP server for Pyxel, a retro game engine for Python. It gives AI agents a compact set of verbs to run and observe Pyxel programs without a window: headless, deterministic, and scriptable.
The server is deliberately an observation adapter. It does not judge whether a game is good. Agents use the returned state, pixels, audio, docs, and diffs to make task-specific decisions.
Tools that accept script execute trusted local Python. pyxel-mcp isolates Pyxel state per subprocess, but it is not a sandbox for untrusted code.
Why this exists
LLM agents writing Pyxel code without feedback often stop at "the script runs". pyxel-mcp closes that loop:
- Headless runs. Drive frame counts and scheduled inputs without opening a window.
- Subprocess isolation. Each tool call starts fresh; Pyxel state cannot leak between calls.
- Structured output. Tools return JSON with uniform
ok/errorsfields. - Pyxel footguns.
validateand resource readers expose common mistakes such as missingcls, missingcolkey, tilemap(0, 0)traps, and ragged image rows. - No universal quality score. The agent writes the predicates that matter for the current game and visually inspects captured PNGs.
Install
Register the published package as an MCP server in your client. The CLI prints the snippet:
uvx pyxel-mcp install
The printed snippet uses uvx pyxel-mcp, so it resolves the published package. For an unreleased checkout, point your MCP config at that checkout's console script or local Python environment instead.
Paste the printed JSON into your client's MCP config:
- Claude Code:
~/.claude/.mcp.jsonor per-project.mcp.json - Cursor:
~/.cursor/mcp.json - Codex CLI:
~/.codex/mcp.json
Snippet:
{
"mcpServers": {
"pyxel": {
"command": "uvx",
"args": ["pyxel-mcp"]
}
}
}
Restart your client. The server logs a startup line to stderr so you can confirm it loaded:
[pyxel-mcp] starting - 9 tools
Pyxel >= 2.9.6 is installed as a dependency.
For workflow guidance, install the separate pyxel-skill repository. pyxel-mcp does not ship or publish skills.
Tools
| Tool | Purpose |
|---|---|
run | Drive N frames headlessly. Supports inputs plus screen_image, screen_grid, state, layout, and video snapshots. |
validate | Syntax and common Pyxel anti-pattern checks. |
pyxel_info | Version, path, example, and resource discovery. |
read_palette | Palette state, used indices, hierarchy hints, and contrast warnings. |
read_image | Image-bank region pixels and optional rendered PNG. |
read_animation | Adjacent sprite-frame consistency and per-pair diffs. |
read_tilemap | Tile usage, non-empty region, and (0, 0) trap warning. |
read_audio | Render a sound or music target to WAV and return duration, peak, notes, warnings. |
diff_frames | Pixel-wise diff between two PNG files. |
Minimal loop
- Run
validatebefore the first dynamic run. - Use
runwith astatesnapshot and ascreen_imageat the frame being verified. - Inspect the captured PNG yourself; pixels are the player-facing truth.
- Add
read_*ordiff_framesonly when the task needs that specific observation. - Keep proof bundles and long reports for release/audit requests, not for every small game.
Resources
pyxel://run-snapshots-schema- full grammar forrun.snapshots.pyxel://anti-patterns-validateissue catalog.pyxel://api-reference,pyxel://user-guide,pyxel://mml-commands,pyxel://pyxres-format- Pyxel docs.pyxel://palette/default- default palette table.pyxel://examples/<name>- bundled Pyxel examples.
Update
uvx caches packages. Force a refresh with:
uvx --refresh-package pyxel-mcp pyxel-mcp install
Troubleshooting
Tools do not appear. Look for [pyxel-mcp] starting - 9 tools in client logs, then restart the client if the config changed.
A script crashes on pyxel.init(). User scripts should call pyxel.init() once. Tool calls are isolated subprocesses, so repeated runs should go through pyxel-mcp rather than re-importing a script in the same process.
A validation issue is unfamiliar. Read pyxel://anti-patterns.
MCP Registry
mcp-name: io.github.kitao/pyxel-mcp
License
MIT