pyxel-mcp — MCP-сервер, который даёт ИИ-агенту запускать ретроградные игры и смотреть на них
📂 Исходный код на GitHubMCP-сервер для Pyxel: гоняет игру в headless-режиме по заданному расписанию нажатий, останавливает её на нужном событии и отдаёт агенту кадры, состояние, тайлмапы, звук и сравнение кадров как доказательства
Pyxel — это ретроградный игровой движок на Python, а pyxel-mcp — MCP-сервер, который позволяет ИИ-агенту им пользоваться. Сервер запускает игровой скрипт без окна, подаёт нажатия по расписанию кадров, останавливает игру в тот момент, когда выполнилось нужное условие, и отдаёт агенту факты: скриншоты, пиксельные сетки, состояние игры, ресурсы, звук и разницы между кадрами. Оценку «хорошая ли игра» сервер не выносит — это остаётся за агентом и тем, кто его спрашивал.
Зачем нужен отдельный сервер
Агент пишет игру на Pyxel за секунды, но не умеет открывать окно, нажимать стрелки и смотреть на экран. Обычное решение — MCP-сервер внутри редактора, к которому подключается уже запущенный процесс. У Pyxel нет отдельного процесса редактора, поэтому pyxel-mcp управляет самой игрой.
На этом построены пять принципов:
- Запуск без окна. Каждый вызов запускает скрипт в новом подпроцессе с драйверами-заглушками SDL и лимитом на число кадров. Параметр
random_seedзадаёт зерно для модуляrandomиз Python и для генератора случайных чисел Pyxel. Другие источники случайности, тайминги и внешнее состояние по-прежнему влияют на кадры. - Ввод как данные. Кнопки, оси и позиция мыши расписаны по кадрам. Тестовый прогон — это JSON-документ, который агент может повторить и дополнить.
- Остановка по событию, а не по таймеру. Условие
until="score >= 1"завершает прогон на первом кадре, где нужный атрибут игры принял нужное значение. Снимок с"frame": "end"захватывает именно этот момент. - Факты, а не оценки. Инструменты сообщают пиксели, значения и измерения.
- Кадр сразу в ответе. Флаг
inline: trueвозвращает PNG как изображение MCP, и модель видит экран без отдельного чтения файла.
Установка
Сервер работает через stdio и запускается утилитой uvx. Для Claude Code:
claude mcp add --scope user pyxel -- uvx pyxel-mcp
Для Codex CLI:
codex mcp add pyxel -- uvx pyxel-mcp
Для Gemini CLI:
gemini mcp add pyxel uvx pyxel-mcp
Для Cursor, проектного файла .mcp.json в Claude Code и любого другого клиента с общим JSON-форматом подходит такая запись:
{
"mcpServers": {
"pyxel": {
"command": "uvx",
"args": ["pyxel-mcp"]
}
}
}
У VS Code свой файл .vscode/mcp.json с ключом servers верхнего уровня и полем "type": "stdio". Codex CLI можно настроить и в файле ~/.codex/config.toml секцией [mcp_servers.pyxel]. Команда uvx pyxel-mcp install печатает все варианты разом.
После правки конфигурации клиент нужно перезапустить. Сервер пишет в stderr такую строку:
[pyxel-mcp] starting - 8 tools
Нужен Python 3.11 или новее, движок Pyxel версии 2.9.6 и выше ставится как зависимость.
Скилл pyxel-skill
Отдельно существует проект https://ai4coding.ru/skills/kitao-pyxel-skill — это Agent Skill, который объясняет агенту, когда и зачем звать каждый инструмент и каких доказательств достаточно. Сам pyxel-mcp поставляет только наблюдения. Для Claude Code скилл ставится плагином вместе с сервером:
claude plugin marketplace add kitao/pyxel-skill && claude plugin install pyxel@pyxel-skill
Инструменты
Аргумент script — это путь к файлу, а не исходный код на Python. Относительные пути к ресурсам внутри скрипта считаются от каталога самого скрипта, как при запуске python game.py из этого каталога.
| Инструмент | Что возвращает |
|---|---|
validate |
Ошибки синтаксиса и узнаваемые паттерны кода Pyxel, без запуска |
run |
Кадры без окна, расписание ввода, журнал и снимки state, screen_image, screen_grid или video |
pyxel_info |
Установленные версии, пути, встроенные примеры и адреса ресурсов |
read_palette |
Цвета палитры и индексы банка изображений, которые используются |
read_image |
Пиксели банка изображений и, по желанию, PNG-рендер |
read_tilemap |
Координаты тайлов, исходный банк, счётчики использования, границы и, по желанию, рендер |
read_audio |
Отрисованный WAV звука или музыки плюс измеримые данные |
diff_frames |
Различия между пикселями двух PNG-файлов |
У всех инструментов описаны схемы входа и выхода. В каждом результате есть поля ok и errors.
Скриптовые инструменты наблюдают первый вызов pyxel.run(), пока внешние ресурсы остаются активными. Прогон управляет колбэками именно там, а читатели ресурсов смотрят на состояние до входа в цикл. pyxel.quit() завершает прогон штатно и сохраняет уже готовые кадры. Код после pyxel.run() не выполняется. Остановка внутри делается через внутренние сигналы на базе BaseException; скрипты и менеджеры контекста, которые эти сигналы подавляют, не поддерживаются и могут выполнить код после прогона.
Как выглядит прогон
Держим вправо, прыгаем на 25-м кадре, останавливаемся сразу после изменения счёта и смотрим на этот кадр:
{
"script": "/absolute/path/game.py",
"frames": 600,
"random_seed": 7,
"inputs": [
{"frame": 0, "buttons": ["KEY_RIGHT"]},
{"frame": 25, "buttons": ["KEY_RIGHT", "KEY_SPACE"]},
{"frame": 26, "buttons": ["KEY_RIGHT"]}
],
"until": "score >= 1",
"snapshots": [
{"kind": "state", "frame": "end", "attrs": ["score", "player.x"]},
{"kind": "screen_image", "frame": "end", "scale": 3, "inline": true}
]
}
В ответе будут признак выполнения условия, реально отработанное число кадров, запрошенные значения состояния, путь к PNG и сам PNG как содержимое изображения. Пути к артефактам, которые вы выбираете сами, должны быть абсолютными.
Чтобы PNG ехал прямо в ответе, достаточно поставить inline: true у снимка screen_image или inline=true у read_image и read_tilemap. Не более 12 изображений встраивается за один вызов. Если у единственного встроенного кадра не задан путь сохранения, файл всё равно записывается во временный каталог системы, а путь сообщается в ответе — иначе diff_frames и последующие сравнения перестали бы работать.
Ресурсы
pyxel://run-snapshots-schema— полная грамматикаrun.snapshots, включая"end", диапазоны иinline.pyxel://validation-patterns— список категорий, которые сообщаетvalidate.pyxel://palette/default— таблица палитры по умолчанию.pyxel://examples/{name}— исходник примера из установленного пакета Pyxel; имена находятся черезpyxel_info.
Обновление и диагностика
uvx кэширует пакеты, поэтому принудительное обновление выглядит так:
uvx --refresh-package pyxel-mcp pyxel-mcp install
Если инструменты не появились, ищите строку starting - 8 tools и перезапустите клиент. Если упал run, смотрите поля errors, exit_status и log. Если скрипт не находит ресурс, проверяйте путь относительно файла скрипта, а не рабочего каталога клиента.
Ограничения
Скриптовые инструменты выполняют локальный Python в подпроцессах ради изоляции состояния Pyxel, но не изолируют недоверенный код. Об этом прямо написано в файле SECURITY.md. Сам движок Pyxel лежит в репозитории https://github.com/kitao/pyxel, пакет доступен на PyPI и зарегистрирован в реестре MCP под именем io.github.kitao/pyxel-mcp. Лицензия MIT, изменения по версиям собраны в CHANGELOG.md.
Источник: https://github.com/kitao/pyxel-mcp