mcdosbox-x/README.md
Ryan Malloy 79c646cf87 Add comprehensive documentation for MCP server
- 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
2026-01-27 15:26:22 -07:00

309 lines
8.9 KiB
Markdown

# DOSBox-X MCP Server
AI-assisted debugging of DOS binaries via the Model Context Protocol (MCP).
This MCP server enables Claude to programmatically debug DOS programs running in DOSBox-X by providing tools for:
- Setting breakpoints and tracing execution
- Reading/writing CPU registers and memory
- Disassembling code at any address
- Step-by-step instruction execution
## Primary Use Case
**Reverse engineering classic DOS programs** — specifically, tracing the unpublished Bezier curve algorithm in RIPTERM.EXE for the [RIPscrip graphics protocol research project](https://github.com/ryanmalloy/rpmesh).
## Quick Start
### Prerequisites
- Python 3.11+
- [uv](https://github.com/astral-sh/uv) package manager
- Docker and Docker Compose
- X11 display (for DOSBox GUI) or use headless mode
### Installation
```bash
# Clone the repository
git clone https://github.com/ryanmalloy/dosbox-mcp.git
cd dosbox-mcp
# Install dependencies
uv sync
# Build Docker image (uses lokkju/dosbox-x-remotedebug fork)
docker compose build
# Create DOS directory for your binaries
mkdir -p dos
```
### Running
```bash
# Allow X11 access for Docker (Linux)
xhost +local:docker
# Start DOSBox-X with GDB server
docker compose up -d
# Verify GDB port is listening
nc -zv localhost 1234
# View logs
docker compose logs -f
```
### Register with Claude Code
```bash
# Add to Claude Code
claude mcp add dosbox-mcp -- uv run --directory /path/to/dosbox-mcp dosbox-mcp
# Verify registration
claude mcp list
```
## Usage with Claude
Once registered, Claude can debug DOS binaries:
```python
# Connect to the running DOSBox-X GDB server
attach("localhost", 1234)
# Read current CPU state
registers()
# Read memory at CS:IP
memory_read("CS:IP", 64)
# Set a breakpoint at the boot sector
breakpoint_set("0x7C00")
# Continue execution until breakpoint
continue_execution()
# Step through instructions
step(10)
# Disassemble at current location
disassemble("CS:IP", 20)
# Disconnect when done
quit()
```
## Tools Reference
### Execution Control (6 tools)
| Tool | Description |
|------|-------------|
| `launch` | Start DOSBox-X container with optional binary |
| `attach` | Connect to GDB stub at host:port |
| `continue_execution` | Run until breakpoint or signal |
| `step` | Step N instructions (default: 1) |
| `step_over` | Step over CALL instructions |
| `quit` | Disconnect and optionally stop DOSBox-X |
### Breakpoints (3 tools)
| Tool | Description |
|------|-------------|
| `breakpoint_set` | Set software breakpoint at address |
| `breakpoint_list` | List all active breakpoints |
| `breakpoint_delete` | Remove breakpoint by ID or all |
### Inspection (6 tools)
| Tool | Description |
|------|-------------|
| `registers` | Read all CPU registers (32-bit, 16-bit, segments, flags) |
| `memory_read` | Read memory in hex/ASCII/dump format |
| `memory_write` | Write hex or ASCII data to memory |
| `disassemble` | Disassemble instructions with opcode hints |
| `stack` | Dump stack contents from SS:SP |
| `status` | Get debugger connection status |
### Peripheral (2 tools — stubs)
| Tool | Description |
|------|-------------|
| `screenshot` | Capture DOSBox-X display (not yet implemented) |
| `serial_send` | Send data to serial port (not yet implemented) |
## Address Formats
The server supports multiple address formats:
| Format | Example | Description |
|--------|---------|-------------|
| Segment:offset | `1234:5678` | Standard DOS format |
| Flat hex | `0x12345` or `12345` | Physical address |
| Decimal | `#65536` | Decimal address |
| Register-based | `CS:IP`, `DS:SI` | Uses current register values |
## Architecture
```
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Claude Code │────▶│ DOSBox-X MCP │────▶│ DOSBox-X │
│ │ MCP │ Server │ GDB │ Container │
└─────────────────┘ └──────────────────┘ └─────────────────┘
│ │ │
│ │ │
▼ ▼ ▼
FastMCP 2.x GDB Remote lokkju/dosbox-x-
(stdio) Protocol remotedebug fork
(TCP :1234) (--enable-remotedebug)
```
### Why lokkju/dosbox-x-remotedebug?
Mainline DOSBox-X doesn't include a GDB server. The [lokkju/dosbox-x-remotedebug](https://github.com/lokkju/dosbox-x-remotedebug) fork adds:
- `--enable-remotedebug` build flag
- GDB server on port 1234 (configurable)
- QMP server on port 4444 for machine control
- Config options in `[dosbox]` section: `gdbserver=true`, `gdbserver port=1234`
## Project Structure
```
dosbox-mcp/
├── src/dosbox_mcp/
│ ├── server.py # FastMCP server entry point
│ ├── gdb_client.py # GDB Remote Serial Protocol client
│ ├── dosbox.py # DOSBox-X process/container management
│ ├── state.py # Shared global state (manager, client)
│ ├── types.py # Type definitions (Registers, Breakpoint, etc.)
│ ├── utils.py # Address parsing, format conversion
│ └── tools/ # MCP tool implementations
│ ├── execution.py # launch, attach, continue, step, quit
│ ├── breakpoints.py # breakpoint_set, list, delete
│ ├── inspection.py # registers, memory, disassemble, stack
│ └── peripheral.py # screenshot, serial (stubs)
├── tests/ # pytest test suite
├── config/
│ └── dosbox.conf # DOSBox-X configuration with GDB enabled
├── dos/ # Mount point for DOS binaries
├── Dockerfile # Multi-stage build for dosbox-x-remotedebug
├── docker-compose.yml # Container orchestration
└── pyproject.toml # Python package definition
```
## Configuration
### DOSBox-X Config (`config/dosbox.conf`)
```ini
[dosbox]
memsize=16
gdbserver=true
gdbserver port=1234
qmpserver=true
qmpserver port=4444
[autoexec]
MOUNT C /dos
C:
```
### Environment Variables (`.env`)
```bash
GDB_PORT=1234 # Host port for GDB
QMP_PORT=4444 # Host port for QMP
DOS_DIR=./dos # DOS files mount point
CONFIG_FILE=./config/dosbox.conf
```
## Development
```bash
# Run tests
uv run pytest
# Run tests with coverage
uv run pytest --cov=dosbox_mcp
# Lint code
uv run ruff check src/
# Format code
uv run ruff format src/
# Type check (optional)
uv run mypy src/
```
## Technical Details
### GDB Remote Serial Protocol
The server implements a client for the [GDB Remote Serial Protocol](https://sourceware.org/gdb/current/onlinedocs/gdb.html/Remote-Protocol.html):
| Command | Description |
|---------|-------------|
| `qSupported` | Capability negotiation (handshake) |
| `g` / `G` | Read/write all registers |
| `p` / `P` | Read/write single register |
| `m` / `M` | Read/write memory |
| `Z0` / `z0` | Set/clear software breakpoint |
| `c` / `s` | Continue / step |
| `?` | Query stop reason |
Packets use format: `$<command>#<2-digit checksum>`
### Real Mode Addressing
DOS uses real mode with segment:offset addressing:
```
Physical Address = (Segment << 4) + Offset
```
This gives a 20-bit address space (1MB). The `Registers` class provides helpers like `cs_ip` and `ss_sp` for common address calculations.
## Troubleshooting
### GDB Connection Refused
```bash
# Check if container is running
docker ps | grep dosbox
# Check if GDB port is listening
docker exec dosbox-mcp nc -zv localhost 1234
# Check logs for GDB server startup
docker logs dosbox-mcp 2>&1 | grep -i gdb
```
### Container Shows "unhealthy"
The healthcheck uses `nc` which may not be installed in the slim image. This is cosmetic — the GDB server still works. To fix, add `netcat-openbsd` to the Dockerfile runtime stage.
### X11 Display Issues
```bash
# Allow Docker X11 access
xhost +local:docker
# Or use headless mode
docker compose --profile headless up -d dosbox-headless
```
## Related Projects
- [RIPscrip Research (rpmesh)](https://github.com/ryanmalloy/rpmesh) — Parent project
- [lokkju/dosbox-x-remotedebug](https://github.com/lokkju/dosbox-x-remotedebug) — DOSBox-X fork with GDB
- [FastMCP](https://gofastmcp.com/) — MCP server framework
- [GDB Remote Protocol](https://sourceware.org/gdb/current/onlinedocs/gdb.html/Remote-Protocol.html)
## License
MIT License