mcp-retroarch
MCP server for RetroArch via its Network Control Interface. Drive any libretro core — read/write memory, save/load state, screenshot, pause/frame-advance/reset — across NES, SNES, Genesis, N64, GBA, PS1 and more.
README
mcp-retroarch
An MCP server that bridges Claude (and any other MCP client) to RetroArch via its built-in Network Control Interface (UDP, port 55355).
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.
What it can do
| Capability | Available? | Notes |
|---|---|---|
| Memory read / write | ✅ | Two paths: READ_CORE_MEMORY (system memory map, preferred) and READ_CORE_RAM (CHEEVOS, fallback) |
| Save / load state | ✅ | Current slot or explicit slot for load; save is current-slot-only (NCI limitation) |
| Screenshot | ✅ | Saved to RetroArch's configured screenshot directory |
| Pause / frame advance | ✅ | PAUSE_TOGGLE flips state; FRAMEADVANCE steps one frame |
| Reset | ✅ | Hard-reset the running game |
| On-screen message | ✅ | Useful for "look here" cues during scripted runs |
| Game info | ✅ | Title, system, CRC32 |
| 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. |
If you need game-pad input on Game Boy Advance specifically, see mcp-mgba. For PCSX2 (memory + savestate only, no input/screenshot), see mcp-pine.
How it works
+----------------+ stdio +-----------------+ UDP :55355 +-----------------+
| MCP client | JSON-RPC | mcp-retroarch | text proto | RetroArch |
| (Claude etc) | -----------> | (Node.js) | ------------> | (NCI enabled) |
+----------------+ +-----------------+ +-----------------+
Requirements
- RetroArch (any recent version) with Network Commands enabled
- Node.js 18+
Install
Option A — install from npm (recommended)
npm install -g mcp-retroarch
Option B — npx (no install)
npx -y mcp-retroarch
Option C — clone and develop
git clone https://github.com/dmang-dev/mcp-retroarch
cd mcp-retroarch
npm install
Enable RetroArch's Network Control Interface
Either:
- GUI: Settings → Network → Network Commands → ON, then confirm
Network Cmd Portis55355(the default) - Or via
retroarch.cfg:network_cmd_enable = "true" network_cmd_port = "55355"
Then launch any libretro core + game. The NCI is always-on once enabled — no script to load.
Register with your MCP client
Claude Code
claude mcp add retroarch --scope user mcp-retroarch
Verify:
claude mcp list
# retroarch: mcp-retroarch - ✓ Connected
Claude Desktop
Edit claude_desktop_config.json:
| Platform | Path |
|---|---|
| macOS | ~/Library/Application Support/Claude/claude_desktop_config.json |
| Windows | %APPDATA%\Claude\claude_desktop_config.json |
| Linux | ~/.config/Claude/claude_desktop_config.json |
{
"mcpServers": {
"retroarch": {
"command": "mcp-retroarch"
}
}
}
Restart Claude Desktop after editing.
Configuration
| Env var | Default | Purpose |
|---|---|---|
RETROARCH_HOST |
127.0.0.1 |
UDP destination host |
RETROARCH_PORT |
55355 |
UDP port (must match network_cmd_port in retroarch.cfg) |
Tools
| Tool | Description |
|---|---|
retroarch_ping |
Verify reachability — returns RetroArch version |
retroarch_get_status |
State (playing/paused), system, game, CRC32 |
retroarch_get_config |
Read named RetroArch config values (e.g. savestate_directory) |
retroarch_read_memory / retroarch_write_memory |
Memory r/w via system memory map |
retroarch_read_ram / retroarch_write_ram |
Memory r/w via CHEEVOS address space (fallback when no memory map) |
retroarch_pause_toggle |
Toggle pause state |
retroarch_frame_advance |
Step one frame (only effective while paused) |
retroarch_reset |
Hardware-reset the running game |
retroarch_screenshot |
Save a screenshot to RetroArch's screenshot directory |
retroarch_show_message |
Display a notification on the RetroArch window |
retroarch_save_state_current |
Save to currently-selected slot |
retroarch_load_state_current |
Load from currently-selected slot |
retroarch_load_state_slot |
Load from explicit slot number |
retroarch_state_slot_plus / retroarch_state_slot_minus |
Change current slot pointer (NCI has no "set slot to N") |
See docs/RECIPES.md for end-to-end examples.
Tested cores
Verified end-to-end against mcp-retroarch:
| System | Core | read_memory |
read_ram |
Notes |
|---|---|---|---|---|
| Game Boy Advance | mgba_libretro |
✅ | ✅ | GBA interrupt vector table visible at 0x0000 (d3 00 00 ea ...) |
| NES | mesen_libretro |
✅ (only NES core tested that does) | ✅ | Full 16-bit NES address space exposed. WRAM at 0x0000-0x07FF, mirrored to 0x1FFF. CHEEVOS bounded to first 64 KB. |
| NES | nestopia_libretro |
❌ no memory map | ✅ | CHEEVOS only. 64 KB bound. For NES + memory map, prefer Mesen. |
| SNES | snes9x_libretro |
❌ no memory map | ✅ | CHEEVOS bounded to ~128 KB (matches SNES WRAM size). 65C816 RTS opcodes (60) visible in code regions. |
| Sega Mega Drive / Genesis | genesis_plus_gx_libretro |
❌ no memory map | ⚠️ sparse | CHEEVOS exposes some 68K WRAM addresses but fails at others ("no error message"). Usable if you know specific addresses; blanket sweep doesn't work. |
| Nintendo 64 | mupen64plus_next_libretro |
✅ | ✅ | Full N64 RAM exposed. KSEG0 mirror is faithful — read_memory(0x80000000) returns the same bytes as read_memory(0x0). Bound is the connected RAM size (4 MB without Expansion Pak, 8 MB with). |
| PlayStation 1 | swanstation_libretro |
❌ no memory map | ✅ | CHEEVOS only. PSX main RAM begins around CHEEVOS offset 0x010000 (lower addresses are typically zero). |
Patterns observed
- Most libretro cores don't advertise a system memory map to NCI — they implement only the CHEEVOS read API. Of those tested, only Mesen (NES) and Mupen64Plus-Next (N64) expose a system memory map. Both also expose CHEEVOS, so they're strictly better.
- System memory maps are faithful to real hardware — Mupen64Plus-Next preserves the N64's KSEG0 mirror (
0x80000000reads as0x0); Mesen preserves the NES's WRAM mirroring (0x1000reads as0x0). This is great for anyone using the bridge alongside disassembly. - CHEEVOS bounds match the system's main RAM size — NES exposes 64 KB, SNES 128 KB, etc. Reads past the bound fail with "no error message".
- When choosing a core for memory work, prefer the one with a system memory map if available.
If you've tested another core, please open a PR adding it to this table.
Troubleshooting
| Symptom | Cause / Fix |
|---|---|
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. Also: UDP datagrams can be dropped under load even on loopback — if a single call times out but a retry succeeds, that's the cause. The bridge doesn't auto-retry; just call again. |
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. Confirmed for SwanStation (PSX); use read_ram for that core. |
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). |
| 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. |
| 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. |
Development
npm install
npm run dev # tsc --watch
Smoke test against a running RetroArch:
node .scratch/smoke.cjs
License
Related
- mcp-mgba — Game Boy Advance via mGBA's Lua bridge (includes button input + screenshot)
- mcp-pine — PINE-speaking emulators (PCSX2 et al.) — memory + savestate only
- RetroArch NCI documentation
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。