|
| 1 | +# mcp-retroarch |
| 2 | + |
| 3 | +[](https://www.npmjs.com/package/mcp-retroarch) |
| 4 | +[](https://www.npmjs.com/package/mcp-retroarch) |
| 5 | +[](https://github.com/dmang-dev/mcp-retroarch/actions/workflows/ci.yml) |
| 6 | +[](LICENSE) |
| 7 | + |
| 8 | +An [MCP](https://modelcontextprotocol.io) server that bridges Claude (and any other MCP client) to [RetroArch](https://www.retroarch.com/) via its built-in **Network Control Interface** (UDP, port 55355). |
| 9 | + |
| 10 | +Works against any libretro core (NES, SNES, Genesis, GB/GBC/GBA, PSX, N64, etc.) — give the model memory r/w, save-state automation, screenshot, pause / frame-advance / reset, and on-screen messages. |
| 11 | + |
| 12 | +## What it can do |
| 13 | + |
| 14 | +| Capability | Available? | Notes | |
| 15 | +|---|---|---| |
| 16 | +| Memory read / write | ✅ | Two paths: `READ_CORE_MEMORY` (system memory map, preferred) and `READ_CORE_RAM` (CHEEVOS, fallback) | |
| 17 | +| Save / load state | ✅ | Current slot or explicit slot for load; save is current-slot-only (NCI limitation) | |
| 18 | +| Screenshot | ✅ | Saved to RetroArch's configured screenshot directory | |
| 19 | +| Pause / frame advance | ✅ | `PAUSE_TOGGLE` flips state; `FRAMEADVANCE` steps one frame | |
| 20 | +| Reset | ✅ | Hard-reset the running game | |
| 21 | +| On-screen message | ✅ | Useful for "look here" cues during scripted runs | |
| 22 | +| Game info | ✅ | Title, system, CRC32 | |
| 23 | +| **Game-pad input** | ❌ | **NCI doesn't expose this.** RetroArch has a separate "Remote RetroPad" core on UDP port 55400 that does, but it requires loading that specific core (you can't drive an existing emulation core through it). Not in scope for v0.1.0. | |
| 24 | + |
| 25 | +If you need game-pad input on Game Boy Advance specifically, see [mcp-mgba](https://github.com/dmang-dev/mcp-mgba). For PCSX2 (memory + savestate only, no input/screenshot), see [mcp-pine](https://github.com/dmang-dev/mcp-pine). |
| 26 | + |
| 27 | +## How it works |
| 28 | + |
| 29 | +``` |
| 30 | ++----------------+ stdio +-----------------+ UDP :55355 +-----------------+ |
| 31 | +| MCP client | JSON-RPC | mcp-retroarch | text proto | RetroArch | |
| 32 | +| (Claude etc) | -----------> | (Node.js) | ------------> | (NCI enabled) | |
| 33 | ++----------------+ +-----------------+ +-----------------+ |
| 34 | +``` |
| 35 | + |
| 36 | +## Requirements |
| 37 | + |
| 38 | +- **RetroArch** (any recent version) with Network Commands enabled |
| 39 | +- **Node.js 18+** |
| 40 | + |
| 41 | +## Install |
| 42 | + |
| 43 | +### Option A — install from npm (recommended) |
| 44 | + |
| 45 | +```bash |
| 46 | +npm install -g mcp-retroarch |
| 47 | +``` |
| 48 | + |
| 49 | +### Option B — `npx` (no install) |
| 50 | + |
| 51 | +```bash |
| 52 | +npx -y mcp-retroarch |
| 53 | +``` |
| 54 | + |
| 55 | +### Option C — clone and develop |
| 56 | + |
| 57 | +```bash |
| 58 | +git clone https://github.com/dmang-dev/mcp-retroarch |
| 59 | +cd mcp-retroarch |
| 60 | +npm install |
| 61 | +``` |
| 62 | + |
| 63 | +## Enable RetroArch's Network Control Interface |
| 64 | + |
| 65 | +Either: |
| 66 | +- **GUI:** Settings → Network → Network Commands → **ON**, then confirm `Network Cmd Port` is `55355` (the default) |
| 67 | +- **Or via `retroarch.cfg`:** |
| 68 | + ```ini |
| 69 | + network_cmd_enable = "true" |
| 70 | + network_cmd_port = "55355" |
| 71 | + ``` |
| 72 | + |
| 73 | +Then launch any libretro core + game. The NCI is always-on once enabled — no script to load. |
| 74 | + |
| 75 | +## Register with your MCP client |
| 76 | + |
| 77 | +### Claude Code |
| 78 | + |
| 79 | +```bash |
| 80 | +claude mcp add retroarch --scope user mcp-retroarch |
| 81 | +``` |
| 82 | + |
| 83 | +Verify: |
| 84 | +```bash |
| 85 | +claude mcp list |
| 86 | +# retroarch: mcp-retroarch - ✓ Connected |
| 87 | +``` |
| 88 | + |
| 89 | +### Claude Desktop |
| 90 | + |
| 91 | +Edit `claude_desktop_config.json`: |
| 92 | + |
| 93 | +| Platform | Path | |
| 94 | +|---|---| |
| 95 | +| macOS | `~/Library/Application Support/Claude/claude_desktop_config.json` | |
| 96 | +| Windows | `%APPDATA%\Claude\claude_desktop_config.json` | |
| 97 | +| Linux | `~/.config/Claude/claude_desktop_config.json` | |
| 98 | + |
| 99 | +```json |
| 100 | +{ |
| 101 | + "mcpServers": { |
| 102 | + "retroarch": { |
| 103 | + "command": "mcp-retroarch" |
| 104 | + } |
| 105 | + } |
| 106 | +} |
| 107 | +``` |
| 108 | + |
| 109 | +Restart Claude Desktop after editing. |
| 110 | + |
| 111 | +## Configuration |
| 112 | + |
| 113 | +| Env var | Default | Purpose | |
| 114 | +|---------------------|---------------|---------| |
| 115 | +| `RETROARCH_HOST` | `127.0.0.1` | UDP destination host | |
| 116 | +| `RETROARCH_PORT` | `55355` | UDP port (must match `network_cmd_port` in `retroarch.cfg`) | |
| 117 | + |
| 118 | +## Tools |
| 119 | + |
| 120 | +| Tool | Description | |
| 121 | +|------|-------------| |
| 122 | +| `retroarch_ping` | Verify reachability — returns RetroArch version | |
| 123 | +| `retroarch_get_status` | State (playing/paused), system, game, CRC32 | |
| 124 | +| `retroarch_get_config` | Read named RetroArch config values (e.g. `savestate_directory`) | |
| 125 | +| `retroarch_read_memory` / `retroarch_write_memory` | Memory r/w via system memory map | |
| 126 | +| `retroarch_read_ram` / `retroarch_write_ram` | Memory r/w via CHEEVOS address space (fallback when no memory map) | |
| 127 | +| `retroarch_pause_toggle` | Toggle pause state | |
| 128 | +| `retroarch_frame_advance` | Step one frame (only effective while paused) | |
| 129 | +| `retroarch_reset` | Hardware-reset the running game | |
| 130 | +| `retroarch_screenshot` | Save a screenshot to RetroArch's screenshot directory | |
| 131 | +| `retroarch_show_message` | Display a notification on the RetroArch window | |
| 132 | +| `retroarch_save_state_current` | Save to currently-selected slot | |
| 133 | +| `retroarch_load_state_current` | Load from currently-selected slot | |
| 134 | +| `retroarch_load_state_slot` | Load from explicit slot number | |
| 135 | +| `retroarch_state_slot_plus` / `retroarch_state_slot_minus` | Change current slot pointer (NCI has no "set slot to N") | |
| 136 | + |
| 137 | +See [`docs/RECIPES.md`](docs/RECIPES.md) for end-to-end examples. |
| 138 | + |
| 139 | +## Troubleshooting |
| 140 | + |
| 141 | +| Symptom | Cause / Fix | |
| 142 | +|---|---| |
| 143 | +| `RetroArch query timed out` | Network Commands aren't enabled in RetroArch, or the port doesn't match `RETROARCH_PORT`. Confirm `network_cmd_enable = "true"` in `retroarch.cfg`. | |
| 144 | +| `READ_CORE_MEMORY failed: no memory map defined` | The loaded libretro core doesn't advertise a system memory map. Try `retroarch_read_ram` (CHEEVOS path) — many cores expose CHEEVOS even without a memory map. | |
| 145 | +| `READ_CORE_MEMORY failed: no descriptor for address` | The address isn't covered by the core's memory map. Either a different core would expose it, or the address you want is outside the system bus (e.g. video memory in some cores). | |
| 146 | +| Screenshots don't appear where I expect | RetroArch saves to its configured screenshot directory. The NCI doesn't expose `screenshot_directory` via `GET_CONFIG_PARAM`, so check the value via RetroArch's GUI: Settings → Directory → Screenshot. | |
| 147 | +| Can't save to a specific state slot directly | NCI limitation, not a bug. The protocol only exposes "save to current slot" — you have to walk the slot pointer to your target with `state_slot_plus`/`state_slot_minus`, then save. | |
| 148 | + |
| 149 | +## Development |
| 150 | + |
| 151 | +```bash |
| 152 | +npm install |
| 153 | +npm run dev # tsc --watch |
| 154 | +``` |
| 155 | + |
| 156 | +Smoke test against a running RetroArch: |
| 157 | +```bash |
| 158 | +node .scratch/smoke.cjs |
| 159 | +``` |
| 160 | + |
| 161 | +## License |
| 162 | + |
| 163 | +[MIT](LICENSE) |
| 164 | + |
| 165 | +## Related |
| 166 | + |
| 167 | +- [mcp-mgba](https://github.com/dmang-dev/mcp-mgba) — Game Boy Advance via mGBA's Lua bridge (includes button input + screenshot) |
| 168 | +- [mcp-pine](https://github.com/dmang-dev/mcp-pine) — PINE-speaking emulators (PCSX2 et al.) — memory + savestate only |
| 169 | +- [RetroArch NCI documentation](https://docs.libretro.com/development/retroarch/network-control-interface/) |
0 commit comments