- Update README.md with accurate lokkju fork info and troubleshooting - Add CLAUDE.md development guide for future sessions - Document GDB protocol details, architecture, and common issues
11 KiB
DOSBox-X MCP Server - Development Guide
Project Overview
An MCP server that enables Claude to programmatically debug DOS binaries running in DOSBox-X via the GDB Remote Serial Protocol. The primary use case is reverse engineering the unpublished Bezier algorithm in RIPTERM.EXE.
Quick Reference
Running the System
# Start DOSBox-X container (must be running for MCP tools to work)
xhost +local:docker && docker compose up -d
# Verify GDB server is listening
nc -zv localhost 1234
# Check container logs
docker compose logs -f
# Stop container
docker compose down
Testing the MCP Server
# Run full test suite
uv run pytest
# Run specific test file
uv run pytest tests/test_gdb_client.py -v
# Test MCP tools via CLI (important: --allowedTools required!)
claude -p "attach to localhost:1234 and read registers" \
--allowedTools "mcp__dosbox-mcp__*"
Common Development Commands
# Run server directly (for debugging)
uv run dosbox-mcp
# Lint and format
uv run ruff check src/ && uv run ruff format src/
# Rebuild container after Dockerfile changes
docker compose build --no-cache
Architecture
Component Overview
┌─────────────────────────────────────────────────────────────────┐
│ Claude Code │
└─────────────────────────────────────────────────────────────────┘
│ MCP (stdio)
▼
┌─────────────────────────────────────────────────────────────────┐
│ FastMCP Server │
│ server.py - registers tools from tools/ modules │
└─────────────────────────────────────────────────────────────────┘
│
┌───────────────┼───────────────┐
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ state.py │ │ types.py │ │ utils.py │
│ Global client │ │ Registers │ │ parse_address │
│ and manager │ │ Breakpoint │ │ format helpers │
└─────────────────┘ │ MemoryRegion │ └─────────────────┘
│ └─────────────────┘
▼
┌─────────────────────────────────────────────────────────────────┐
│ gdb_client.py │
│ GDB Remote Serial Protocol client │
│ - Packet formatting ($cmd#checksum) │
│ - Register/memory read/write │
│ - Breakpoint management │
│ - Execution control (step, continue) │
└─────────────────────────────────────────────────────────────────┘
│ TCP :1234
▼
┌─────────────────────────────────────────────────────────────────┐
│ DOSBox-X Container (lokkju fork) │
│ Built with --enable-remotedebug │
│ Ports: 1234 (GDB), 4444 (QMP) │
└─────────────────────────────────────────────────────────────────┘
Key Files
| File | Purpose |
|---|---|
server.py |
FastMCP entry point, registers all tools |
gdb_client.py |
GDB protocol implementation (~400 lines) |
state.py |
Singleton instances (manager, client) to avoid circular imports |
types.py |
Dataclasses: Registers, Breakpoint, MemoryRegion, etc. |
utils.py |
Address parsing, format conversion |
tools/execution.py |
launch, attach, continue, step, quit |
tools/breakpoints.py |
breakpoint_set, list, delete |
tools/inspection.py |
registers, memory_read/write, disassemble, stack |
tools/peripheral.py |
screenshot, serial_send (stubs) |
GDB Remote Serial Protocol
Packet Format
Send: $<command>#<2-digit-hex-checksum>
Receive: +$<response>#<checksum>
(+ is ACK, - is NAK)
Key Commands
| Command | Description | Example |
|---|---|---|
qSupported |
Handshake/capabilities | First packet after connect |
g |
Read all registers | Returns hex blob |
G<hex> |
Write all registers | |
m<addr>,<len> |
Read memory | m7c00,10 |
M<addr>,<len>:<hex> |
Write memory | |
Z0,<addr>,1 |
Set software breakpoint | Z0,7c00,1 |
z0,<addr>,1 |
Clear breakpoint | |
c |
Continue execution | |
s |
Step one instruction | |
? |
Query stop reason | Returns S05 (SIGTRAP) etc. |
Register Order (x86)
GDB returns registers in this order:
EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI, EIP, EFLAGS,
CS, SS, DS, ES, FS, GS
Each is 32-bit little-endian hex (8 chars), except segments (16-bit, 4 chars).
DOSBox-X Configuration
Critical: Use [dosbox] Section
The lokkju fork's GDB/QMP config goes in [dosbox], NOT [debug]:
[dosbox]
gdbserver=true
gdbserver port=1234
qmpserver=true
qmpserver port=4444
Why lokkju/dosbox-x-remotedebug?
Mainline DOSBox-X has no GDB stub. The lokkju fork (branch: remotedebug) adds:
--enable-remotedebugconfigure flaggdbserver.cppandqmp.cppsources- Runtime config options
Build command in Dockerfile:
./configure --enable-remotedebug --enable-debug --enable-sdl2
Real Mode Addressing
DOS uses segment:offset addressing (20-bit physical):
physical = (segment << 4) + offset
# Example: 1234:5678
# physical = 0x12340 + 0x5678 = 0x179B8
The utils.py module handles parsing:
"1234:5678"→ segment:offset"0x12345"or"12345"→ flat hex"#65536"→ decimal"CS:IP"→ register-based (requires current register state)
Adding New Tools
1. Create the Tool Function
In appropriate tools/*.py file:
from ..state import client, manager
from ..types import SomeType
from ..utils import parse_address
def my_new_tool(address: str, count: int = 10) -> dict:
"""Short description for MCP.
Args:
address: Memory address (segment:offset or flat hex)
count: Number of items (default: 10)
Returns:
Result dictionary with data
"""
if not client.connected:
return {"error": "Not connected. Use attach() first."}
addr = parse_address(address, client.read_registers())
# ... implementation ...
return {"address": f"{addr:05x}", "data": result}
2. Export from tools/__init__.py
from .mymodule import my_new_tool
__all__ = [
# ...existing...
"my_new_tool",
]
3. Register in server.py
mcp.tool()(tools.my_new_tool)
4. Add Tests
In tests/test_tools.py or new file:
def test_my_new_tool():
# Mock the GDB client
...
Unfinished Work
Peripheral Tools (Stubs)
screenshot and serial_send are placeholders. Implementation ideas:
screenshot: DOSBox-X can capture via:
- Built-in screenshot command (key binding)
- QMP protocol on port 4444
- X11 window capture
serial_send: Configure serial port in dosbox.conf:
[serial]
serial1=nullmodem server:1234
Then send data via socket.
Step Over Implementation
step_over in execution.py is basic — it steps but doesn't properly handle CALL instructions. A full implementation would:
- Disassemble current instruction
- If CALL, set temp breakpoint after it
- Continue instead of step
- Remove temp breakpoint
Disassembler
Current disassembler in inspection.py is minimal (pattern matching common opcodes). For full disassembly, consider:
- Capstone library (
pip install capstone) - External tool like
ndisasm
Testing Against Real DOSBox-X
Manual GDB Connection
# From host, after container is running
nc localhost 1234
# Type (no newline):
$qSupported#37
# Should receive capabilities response
Python Quick Test
from dosbox_mcp.gdb_client import GDBClient
client = GDBClient()
client.connect("localhost", 1234)
regs = client.read_registers()
print(f"CS:IP = {regs.cs:04x}:{regs.ip:04x}")
client.disconnect()
Debugging Tips
Enable Verbose Logging
import logging
logging.getLogger("dosbox_mcp").setLevel(logging.DEBUG)
Check Container Logs
# GDB connections show up here
docker logs dosbox-mcp 2>&1 | grep -i gdb
Common Issues
| Symptom | Cause | Fix |
|---|---|---|
| "Connection refused" | Container not running | docker compose up -d |
| "Connection closed" | Wrong DOSBox build | Verify lokkju fork |
| Config not applied | Wrong section | Use [dosbox] not [debug] |
| Timeout on continue | No breakpoint hit | Set breakpoint first |
| "unhealthy" status | Missing nc in image |
Cosmetic, ignore or add netcat |
Future Enhancements
-
RIPTERM Bezier Tracing
- Set breakpoints at drawing routines
- Capture coordinate pairs from registers/stack
- Build coordinate trace for algorithm analysis
-
QMP Integration
- Port 4444 provides machine control
- Could enable screenshots, state snapshots
-
Memory Watchpoints
- GDB protocol supports
Z2/z2for write watchpoints - Useful for tracking variable changes
- GDB protocol supports
-
Scripted Automation
- Create
examples/ripterm_bezier.pyfor the target use case - Document coordinate capture methodology
- Create
Related Resources
- GDB Remote Protocol Docs
- lokkju fork PR/issue — Original discussion
- FastMCP Docs — MCP server framework
- RIPscrip Spec