dolphin-decomp-mcp
by MarkMcCaskey
README.md
# dolphin-decomp-mcp
A small, general-purpose tool surface over Dolphin's [DAP debug
server](https://github.com/Jacoby6000/dolphin/tree/feature/dap-server) for
decompilation / reverse-engineering work on GameCube & Wii titles.
It exposes **12 primitives** — read/write memory and registers, disassemble,
run control, code & data breakpoints, a PPC assembler, code injection, detours,
and non-halting realtime watches/freezes — as an **MCP server** (for an agent
like Claude Code) and an equivalent **CLI** (for manual poking). Both front-ends
share the exact same handlers, so behaviour can't drift.
The tools are deliberately low-level. The agent composes them into whatever it
needs; the three headline workflows fall out of composition (see
[Recipes](#recipes)).
## Requirements
- A Dolphin built from the `feature/dap-server` branch with the DAP server
enabled (see below). This project talks to that server; it does not embed one.
- Python ≥ 3.10.
- For the assembler tools: the `powerpc-eabi-*` binutils. This project reuses
the ones the melee decomp tree ships under `build/binutils`. Point it
elsewhere with `DOLPHIN_DECOMP_BINUTILS=/path/to/dir`.
- For the MCP server front-end only: `pip install mcp`. The CLI and the Python
API need no third-party packages.
## Install
```bash
cd dolphin-decomp-mcp
python3 -m venv .venv && . .venv/bin/activate
pip install -e '.[mcp,dev]' # drop [mcp] if you only want the CLI
pytest -q # 21 tests, no emulator needed
```
## Connecting to Dolphin
Start Dolphin's DAP server. **Prefer a Unix socket** — it is gated by
filesystem permissions. The TCP transport in this fork binds an *unauthenticated*
debug server that grants full guest-memory read/write and code injection; if you
use TCP, keep it on loopback (`127.0.0.1`) and off any untrusted network. (A
local patch pinning the fork's TCP bind to loopback lives in the sibling
`dolphin-dap` checkout.)
```bash
dolphin-emu-nogui \
-C Dolphin.General.DAPSocket=/tmp/dolphin-decomp.sock \
-C Dolphin.General.DAPStopOnEntry=true \
-v Null --platform headless \
--exec /path/to/GALE01.iso
# optional: --debug-elf /path/to/main.elf (adds DWARF 1.1 line info / symbols)
```
Then point either front-end at it via `--socket` / `--port` or the env vars
`DOLPHIN_DAP_SOCKET`, `DOLPHIN_DAP_HOST`, `DOLPHIN_DAP_PORT`.
## MCP server (agent-facing)
Run it over stdio:
```bash
dolphin-decomp-mcp --socket /tmp/dolphin-decomp.sock
```
Register it with Claude Code (`.mcp.json` in your project, or `claude mcp add`):
```json
{
"mcpServers": {
"dolphin": {
"command": "/abs/path/dolphin-decomp-mcp/.venv/bin/dolphin-decomp-mcp",
"args": ["--socket", "/tmp/dolphin-decomp.sock"],
"env": { "DOLPHIN_DECOMP_BINUTILS": "/abs/path/melee/build/binutils" }
}
}
}
```
The connection to Dolphin is lazy and reconnects if the socket drops, so the MCP
server can start before the emulator is up.
## CLI (manual)
Every tool is a subcommand (the `dolphin_` prefix is dropped). Scalars are
positional/flag args; object/array args are JSON strings. Output is JSON.
```bash
dolphin-decomp --socket /tmp/dolphin-decomp.sock session status
dolphin-decomp --socket /tmp/dolphin-decomp.sock read 0x80000000 16
dolphin-decomp --socket /tmp/dolphin-decomp.sock read 0x80003100 4 --format disasm
dolphin-decomp --socket /tmp/dolphin-decomp.sock control pause
dolphin-decomp --socket /tmp/dolphin-decomp.sock inject $'li r3, 1\nblr'
dolphin-decomp --socket /tmp/dolphin-decomp.sock breakpoints --data '[{"address":2147483648,"access":"write"}]'
dolphin-decomp assemble $'li r3, 0x1234\nblr' # no emulator needed
```
## The tools
| Tool | What it does |
|------|--------------|
| `dolphin_session` | status / attach / launch / restart / terminate / disconnect |
| `dolphin_read` | read memory as hex, or as disassembly |
| `dolphin_write` | write raw bytes (hex) to memory |
| `dolphin_registers` | read all registers of the top frame; optionally write some first |
| `dolphin_eval` | evaluate a PPC debugger expression (`r3 + r4`, memory derefs) |
| `dolphin_stack` | backtrace the stopped core (who called / who wrote) |
| `dolphin_control` | continue / pause / step_over / step_in / step_out / goto — returns the resulting stop |
| `dolphin_breakpoints` | set code breakpoints and/or data watchpoints (authoritative) |
| `dolphin_assemble` | PPC (Gekko) asm text → machine-code bytes, no emulator needed |
| `dolphin_inject` | assemble (or take hex) and write code into a cave or a fixed address |
| `dolphin_detour` | install a transparent detour at an instruction (observe or replace) |
| `dolphin_watch` | non-halting realtime memory watch/poll, and freeze/unfreeze |
## Recipes
**Find the writer (reverse breakpoint).** What corrupts `0x8045C000`?
1. `dolphin_breakpoints data=[{address: 0x8045C000, access: "write"}]`
2. `dolphin_control continue` → stops with `reason: "data breakpoint"`, PC = the
writing instruction.
3. `dolphin_stack` + `dolphin_registers` → the culprit and its inputs.
**Call a function / test a hypothesis.** Does `fn(0x10)` return what you think?
1. `dolphin_control goto <fn_entry>`; `dolphin_registers writes={r3: 0x10}`.
2. `dolphin_breakpoints code=[{address: <caller_return_or_blr>}]`.
3. `dolphin_control continue`; read the result from `r3` via `dolphin_registers`.
**Test a code change live.** Try a fix without rebuilding the game.
- Patch in place: `dolphin_inject code="<asm>" address=<site>` (read it first if
you want to restore), or
- Observe/replace non-destructively: `dolphin_detour target=<fn> body="<asm>"`
(end with `b`-to-trampoline to observe, or `blr` after setting `r3` to
replace), then `dolphin_control continue` and verify.
## Layout
```
dolphin_decomp/
dap.py # framed DAP client core + event pump (no deps)
session.py # high-level Dolphin operations (no deps)
ppc.py # powerpc-eabi assembler wrapper (no deps)
tools.py # the 12-tool registry + Backend (shared by both front-ends)
server.py # MCP server (needs `mcp`)
cli.py # CLI (no deps)
tests/ # framing, session-over-mock, assembler; mcp_smoke.py is live-only
```
## License
MIT.
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues