Add asynchronous machine over and out to shared MCP

This commit is contained in:
Александр Петров
2026-09-17 23:32:46 +03:00
parent 259c782190
commit a8d0692eb2
15 changed files with 241 additions and 19 deletions
+74
View File
@@ -0,0 +1,74 @@
#!/usr/bin/env python3
"""Живой MCP: долгий машинный out из tail-jump можно прервать Pause."""
from __future__ import annotations
import asyncio
import json
import os
from pathlib import Path
import sys
import tempfile
import time
from mcp import Client, StdioServerParameters
ROOT = Path(__file__).resolve().parents[2]
async def probe() -> None:
home = Path(os.environ['MAME_HOME']).resolve()
binary = Path(os.environ.get('MAME_BIN', home / 'sprinter')).resolve()
package = ROOT / 'tests/hello/.sprinter-cc-hello'
with tempfile.TemporaryDirectory(prefix='sprinter-out-mcp-', dir='/tmp') as temp:
parameters = StdioServerParameters(
command=sys.executable,
args=[str(ROOT / 'toolchain/sdbg_mcp.py'), '--build', str(package),
'--socket', str(Path(temp) / 'session.sock'),
'--mame-home', str(home), '--mame-bin', str(binary)])
async with Client(parameters) as client:
async def call(name: str, arguments: dict | None = None) -> dict:
result = await client.call_tool(name, arguments or {})
if result.is_error or result.structured_content is None:
raise RuntimeError(name + ': ' + repr(result.content))
return result.structured_content
await call('start_session')
try:
deadline = time.monotonic() + 90
while time.monotonic() < deadline:
status = await call('session_status')
if status['phase'] == 'ready':
break
if status['phase'] in ('failed', 'stopped'):
raise RuntimeError('Launcher: ' + repr(status))
await asyncio.sleep(.2)
else:
raise TimeoutError('hello не дошёл до main')
entered = await call('step_instruction', {'count': 1})
code = await call('disassemble_logical',
{'address': hex(entered['pc']), 'length': 12})
if 'jp (hl)' not in code['text']:
raise RuntimeError('Ожидался tail-jump helper: ' + code['text'])
started_at = time.monotonic()
accepted = await call('step_out_instruction')
response_ms = round((time.monotonic() - started_at) * 1000, 1)
if not accepted['accepted'] or response_ms > 3000:
raise RuntimeError('Машинный out не ответил быстро: ' + repr(accepted))
await asyncio.sleep(.3)
if not (await call('session_status'))['running']:
raise RuntimeError('Tail-jump out неожиданно завершился')
paused = await call('pause_execution')
if (await call('session_status'))['running']:
raise RuntimeError('Pause не остановил долгий out')
print(json.dumps({'event': 'machine_out_pause_verified',
'response_ms': response_ms,
'entered_pc': entered['pc'],
'paused_pc': paused['pc']}, ensure_ascii=False),
flush=True)
finally:
await call('stop_session')
if __name__ == '__main__':
asyncio.run(probe())