# MCP-доступ к source debugger Sprinter `toolchain/sdbg_mcp.py` подключается к уже работающей `sdbg_server.py` через Unix socket. Это отдельный stdio MCP-сервер для C-уровня: он использует ту же сессию, карту сборки и журнал, что VS Code/DAP. В MAME.HT отдельно работает raw MCP-мост `-plugin mamebridge` с `src/mame_mcp.py`. Старый `src/mame_bridge.lua` удалён после проверки: он обрабатывал лишь 16 из 30 публикуемых Python-команд и не отвечал при остановленном CPU. Plugin использует `register_periodic` и отвечает даже при остановленном CPU. Ни один из этих вариантов не должен одновременно управлять тем же MAME в обход sdbg-сессии. `src/mame_mcp.py` всё ещё импортирует `FastMCP` по пути SDK 1.x; наш адаптер использует `MCPServer` SDK 2.x. Пока raw frontend не мигрирован, держите для него отдельное Python-окружение. Это различие касается Python обвязки, а не Lua-плагина `mamebridge`. Для MCP нужен Python 3.12 с официальным SDK 2.x. Он не требуется для сборки приложений и работы VS Code. Например, создайте отдельное окружение и установите `toolchain/requirements-mcp.txt`: ```sh pyenv exec python -m venv /tmp/sprinter-sdbg-mcp-venv /tmp/sprinter-sdbg-mcp-venv/bin/python -m pip install -r toolchain/requirements-mcp.txt ``` В профиле `launch` типа `sprinter-mame` укажите фиксированный socket, например `"socket": "/tmp/sprinter-sdbg-hello.sock"`. После F5 дождитесь остановки в `main`. Путь должен быть коротким (у Unix socket есть лимит длины), уникальным для этого сеанса и доступным только вашему пользователю. Затем зарегистрируйте в MCP-клиенте stdio-сервер: ```json { "command": "/tmp/sprinter-sdbg-mcp-venv/bin/python", "args": [ "/ABS/PATH/C-Compiler/toolchain/sdbg_mcp.py", "--socket", "/tmp/sprinter-sdbg-hello.sock" ] } ``` Замените `/ABS/PATH/C-Compiler` абсолютным путём к toolkit. MCP-сервер при старте проверяет подключение; если VS Code ещё не запустил MAME или socket устарел, он завершается с понятной ошибкой в stderr. После Stop запустите MCP-сервер заново вместе с новой DAP-сессией. Альтернатива параметру `--socket` — переменная окружения `SDBG_SOCKET`. Для работы **без VS Code** запустите тот же MCP-сервер с `--build`: ```json { "command": "/tmp/sprinter-sdbg-mcp-venv/bin/python", "args": [ "/ABS/PATH/C-Compiler/toolchain/sdbg_mcp.py", "--build", "/ABS/PATH/C-Compiler/tests/hello/.sprinter-cc-hello", "--socket", "/tmp/sprinter-hello-agent.sock", "--mame-home", "/ABS/PATH/MAME/runtime", "--mame-bin", "/ABS/PATH/MAME.HT/sprinter" ] } ``` MCP handshake не ждёт загрузку DSS. `start_session` сразу возвращает socket и launch ID; `session_status` показывает `starting`, затем `ready` с build ID, session ID, PC и PID MAME либо `failed` с диагностикой. Повторный start при активном сеансе отклоняется. `stop_session` завершает только запущенный этим MCP-сервером MAME и удаляет socket. Закрытие самого MCP-процесса также завершает его автономный MAME. DAP/VS Code может подключиться к тому же socket; его disconnect не завершает MCP-owned MAME. Конфигурация VS Code — в [разделе attach](vscode-sprinter-debug.md#ручной-attach). Доступны также `--mame-rompath`, `--mame-dss-image`, `--mame-system-hdd-image`, `--mame-bios`, `--app-hdd`, `--launch-path`, `--data`, `--debugger`, `--launch-at`, `--dss-timeout`. Живой stdio MCP→DSS→`main`→DAP attach→stop прогон прошёл; подключение непосредственно из UI Codex и Claude ещё не проверено. Доступные инструменты: `session_status`, `where`, `read_registers`, `read_memory`, `read_program_memory`, `disassemble_logical`, `list_ports`, `list_shares`, `read_share`, `read_vram`, `read_screen_pixels`, `screenshot`, `press_key`, `type_string`, `list_variables`, `read_variable`, `recent_events`, `mame_console_tail`, `set_line_breakpoint`, `set_function_breakpoint`, `list_breakpoints`, `clear_breakpoint`, `clear_my_breakpoints`, `claim_control`, `release_control`, `continue_execution`, `pause_execution`, `step_instruction`, `step_source`. Ответы SDK 2.x содержат `structuredContent`. `step_source` принимает `into`, `over` или `out` и возвращает принятие команды; итоговую остановку получите через `recent_events` с курсором `last`. Логи `SDBG_LOG` приходят как события `output`; журнал ограничен 1024 событиями и сообщает `first`/`last`/`lost`. Число 29 в attach-режиме (31 в автономном с `start_session`/`stop_session`) не означает полного переноса более нового `-plugin mamebridge`: его `src/mame_mcp.py` публикует 30 raw-инструментов, ориентированных на машину MAME. Здесь инструменты сгруппированы по операциям C-сессии, а некоторые функции (C-позиция, typed global/static, журнал с generation) в raw MCP вообще отсутствуют. Оставшиеся группы требуют отдельного контракта: | Группа raw MCP | Что требуется перед переносом в общую сессию | |---|---| | `setmem`, raw `debugger_command`, watchpoints | Проверка прав, банка, диапазона и согласование с DAP-точками; произвольная debugger-команда может нарушить состояние сессии | | VRAM/shares, screen pixels, screenshot | Реализованы через общий `sdbgbridge`; точный tag, пределы 4096 байт/8192 пикселя, PNG только в каталоге сессии | | Клавиатура, мышь, type/press | `list_ports`, одиночный `press_key` и ограниченный `type_string` перенесены; natural keyboard, произвольные поля портов и мышь требуют отдельного контракта | | Raw disassembly | Текущее окно logical Z80 уже доступно через `disassemble_logical`; адреса raw program вне отображённого банка требуют отдельного безопасного режима | Это следующий этап, а не запрет на функции raw MCP. Нельзя просто загрузить `mamebridge` рядом с `sdbgbridge`: два независимых обработчика начнут менять CPU и точки без общего owner ID и журнала. Результаты удаления legacy Lua и этапы переноса всех 30 возможностей в общую C-сессию описаны в [плане сведения MCP](mcp-convergence-plan.md) и [матрице покрытия](mcp-capability-matrix.md). Чтение памяти принимает десятичный адрес или `0xHEX`, 1–256 байт logical Z80 memory, только при остановленном CPU, без side effects. Ответ содержит generation и страницы банков; диапазон не может пересекать границу 64 КБ. `read_variable` работает с проверенными global/static размером 1, 2 или 4 байта, только когда нужный банк отображён. Локальные, стек, watchpoints, запись переменных и произвольные выражения пока не поддерживаются. `read_program_memory` читает raw program space 0..0x3ffff без side effects, до 4096 байт при остановленном CPU; это отдельный инструмент, так как `read_memory` означает logical Z80. `list_ports` возвращает tag, имена полей и битовые маски; результат ограничен 512 портами/4096 полями и помечает `truncated`, если достигнут предел. Оба инструмента проверены в живом MAME. `disassemble_logical` принимает числовой адрес (десятичный или `0xHEX`) и длину 1..256 байт в текущем logical Z80 пространстве, только при stop. Сервер сверяет загруженный C-код, вызывает фиксированную читающую команду MAME `dasm` во временном каталоге сессии и возвращает текст, generation и текущие `bank_pages`. Это не произвольная debugger-команда. Дизассемблирование raw program вне текущих банков пока не поддержано. На живом `hello` ответ начинался с адреса `8224`, соответствующего `main`. `list_shares` возвращает точные tag/размеры. `read_share` требует полный tag, `read_vram` выбирает единственный VRAM share; обе операции читают до 4096 байт без Z80 bank mapping. `read_screen_pixels` возвращает pen16-значения в hex и номер кадра. `screenshot` сохраняет PNG до 8 МиБ в каталоге текущей сессии и возвращает путь; после завершения сессии временный каталог удаляется. Для обоих экранных инструментов `stale_frame=true` означает, что CPU остановлен и показан последний нарисованный кадр. Экран работающего `hello` во время `getchar()` проверен живым MCP-прогоном. `press_key` принимает один символ раскладки PC или `enter`, `space`, `tab` и `frames` от 1 до 60 (по умолчанию 3). Он доступен только при работающем CPU, после запуска EXE; применяется общий control lease. Shift удерживается автоматически для заглавных букв и соответствующих символов. После нужного числа кадров или остановки CPU клавиша отпускается, включая путь ошибки. Ответ сообщает число прошедших кадров и признак остановки. Живой автономный MCP-прогон ввёл `x` в ожидающий `getchar()` и дошёл до следующей C-строки. Прямой физический ввод в MAME проверялся отдельно; `press_key` не заменяет его. `type_string` принимает 1..64 символа той же PC-раскладки, включая реальный перевод строки `\n` для Enter. Все символы проверяются до первого нажатия. Каждый символ удерживается три кадра и затем отпускается; между символами выдерживаются четыре кадра. При stop дальнейший набор прекращается, ответ сообщает `requested`, `typed`, `complete` и `stopped`. Живой тест `gets()` получил `Ab9` и Enter; снимок показал введённую и напечатанную строку, а CPU остановился на следующей C-строке. Ввод через natural keyboard, произвольные имена физических клавиш, мышь и поля портов пока не публичны в C-MCP. `list_breakpoints` возвращает логические ID, владельца (`mcp:…`, `dap` или `build`), вид C-точки, адреса и фактические условия банковской страницы для всех точек общей сессии. Это чтение доступно и при running CPU. Точки, поставленные вручную в родном debugger MAME, в этот список не входят. Инструмент не даёт удалить чужую точку: `clear_breakpoint` по-прежнему проверяет owner. После первого `session_status` клиент отправляет session ID и build ID в каждом RPC; команды, меняющие CPU/точки, дополнительно сверяют generation. При замене сеанса или устаревшей generation команда отклоняется без автоматического повтора. У каждой MCP-копии свой owner ID. Она может удалить только свои точки; при обычном закрытии stdio они очищаются. Живой тест подтвердил, что попытка удалить DAP-точку отвергается и точка VS Code срабатывает после выхода MCP. Удаление личной точки при running CPU отдельно проверено на ожидании `getchar()`; оно не ставит CPU на паузу и не блокирует ввод. Общий session server выдаёт 30-секундный control lease первому владельцу команды CPU; MCP может захватить/освободить его явно. Фоновый heartbeat MCP и опрос событий DAP продлевают активный lease. Конкурентная команда получает отказ с именем владельца, после штатного закрытия lease освобождается. При аварийном завершении MCP lease истекает за 30 секунд; сервер удаляет личные точки и отпускает удерживаемые клавиши. Живой прогон с владельцем без heartbeat подтвердил удаление его точки и отсутствие ложной остановки после возобновления DAP; освобождение удерживаемой клавиши пока проверено только unit-тестом. Несколько одновременно запущенных MAME с MCP не проверялись и не гарантируются; это отложенный тест. Проверка совместной работы с DAP и реальным MAME: ```sh MAME_HOME=/path/to/MAME/runtime pyenv exec python tests/sdbg/run_vscode_dap_probe.py \ --socket /tmp/sprinter-sdbg-mcp-probe.sock \ --mcp-python /tmp/sprinter-sdbg-mcp-venv/bin/python ```