mcp-retroarch
Provides tools for interacting with RetroArch through its Network Control Interface and Network RetroPad protocols, enabling memory read/write, save state management, screenshots, emulator control (pause, frame advance, reset), and gamepad input.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@mcp-retroarch Save the current state to slot 2"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
mcp-retroarch
MCP server for RetroArch — memory r/w, save states, screenshots, pause/frame-advance/reset via the Network Control Interface, plus gamepad input via the Network RetroPad protocol (UDP).
Inspired by dmang-dev/mcp-retroarch. Rewritten in Python to eliminate the npm git-install reliability issues that plagued the original TypeScript distribution (
npm install -g github:...produces a broken symlink on most systems due to a known npm bug).uv tool install git+...handles this correctly.
Requirements
Python 3.13
RetroArch with:
Network Commands enabled for NCI tools (memory, save states, screenshots, emulator control)
Network Gamepad enabled for input tools
Related MCP server: mcp-retroarch
Install
# Install as a persistent uv tool (recommended)
uv tool install git+https://github.com/pythoninthegrass/mcp-retroarch
# Or run ephemerally without installing
uvx --from git+https://github.com/pythoninthegrass/mcp-retroarch mcp-retroarchRetroArch configuration
Network Commands (NCI — required for most tools)
In retroarch.cfg or via Settings → Network → Network Commands:
network_cmd_enable = "true"
network_cmd_port = "55355"Network Gamepad (required for input tools)
In retroarch.cfg or via Settings → Input → Network Gamepad:
network_remote_enable = "true"
network_remote_base_port = "55400"
# Enable per player (p1 through p16)
network_remote_enable_user_p1 = "true"Client configuration
Claude Desktop
~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or
%APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"retroarch": {
"command": "uvx",
"args": [
"--from",
"git+https://github.com/pythoninthegrass/mcp-retroarch",
"mcp-retroarch"
]
}
}
}If installed via uv tool install:
{
"mcpServers": {
"retroarch": {
"command": "mcp-retroarch"
}
}
}Environment variables
Variable | Default | Description |
|
| RetroArch host for both NCI and RetroPad |
|
| NCI UDP port ( |
|
| Override host for Network Gamepad only |
|
| Base port for Network Gamepad ( |
Tools
Connectivity & introspection
Tool | Description |
| Verify NCI connectivity; returns RetroArch version |
| Report playing/paused state, loaded system, game, and CRC32 |
| Read a single RetroArch config parameter by name |
Memory
Tool | Description |
| Read bytes via the libretro system memory map (preferred) |
| Read bytes via the CHEEVOS address space (fallback) |
| Write bytes via the libretro system memory map |
| Write bytes via the CHEEVOS address space (no acknowledgement) |
Emulator control
Tool | Description |
| Toggle pause/unpause |
| Step one frame (paused only) |
| Soft-reset the loaded game |
| Save a screenshot to RetroArch's screenshot directory |
| Display an OSD notification on the RetroArch window |
Save states
Tool | Description |
| Save to currently-selected slot |
| Load from currently-selected slot |
| Load from an explicit slot number (does not change the current-slot pointer) |
| Increment current slot pointer |
| Decrement current slot pointer |
Gamepad input (Network RetroPad)
Tool | Description |
| Latch one or more buttons down |
| Release one or more buttons |
| Zero all buttons and analog sticks in one packet |
| Set an analog stick's X/Y position |
| Press, hold for a duration, then release (everyday input) |
Valid RetroPad button names: b, y, select, start, up, down, left, right, a, x, l, r, l2, r2, l3, r3.
On PlayStation cores: b=Cross, a=Circle, y=Square, x=Triangle.
See docs/RECIPES.md for usage patterns.
Development
# Install with dev dependencies
uv sync
# Run tests
uv run pytest
# Format and lint
uv run ruff format .
uv run ruff check .Docker
docker build -t mcp-retroarch .
docker run --rm -i \
-e RETROARCH_HOST=host.docker.internal \
mcp-retroarchLicense
MIT — see LICENSE.
Available Tools
22 toolsretroarch_frame_advanceA
PURPOSE: Step emulation forward by exactly one frame. USAGE: Use for frame-precise input automation, animation inspection, or letting the system initialize after a reset. ONLY effective while emulation is paused — RetroArch's FRAMEADVANCE is a no-op when running, so call retroarch_pause_toggle first (after checking retroarch_get_status to confirm you'll end up paused, not unpaused). For long jumps (thousands of frames) prefer retroarch_save_state_current / retroarch_load_state_current of a pre-prepared state — frame-by-frame stepping costs ~1 UDP round-trip per frame. BEHAVIOR: When paused, advances the emulator by exactly one frame and remains paused. When NOT paused, the command is silently ignored by RetroArch. FIRE-AND-FORGET: the NCI does NOT acknowledge this command — the call returns as soon as the UDP datagram is sent, with no confirmation that RetroArch received or applied it. To verify the effect, follow up with an observable tool (retroarch_get_status for run state, retroarch_read_memory / retroarch_read_ram for memory mutations, retroarch_screenshot for visual state). UDP packets to a not-listening RetroArch are silently dropped. The new frame count is not reported — to verify progress, take screenshots before/after with retroarch_screenshot or read a known-changing memory value. Transport: RetroArch's Network Control Interface (NCI) over UDP (default 127.0.0.1:55355, requires network_cmd_enable = true in retroarch.cfg). RETURNS: Single line 'Advanced one frame' (UDP-send confirmation only — does NOT confirm that RetroArch was paused or that the frame actually advanced).
Inputs: no named parameters.
retroarch_get_configA
PURPOSE: Read a single RetroArch configuration parameter by name via the NCI GET_CONFIG_PARAM command. USAGE: Discover RetroArch's filesystem paths and selected settings without parsing retroarch.cfg yourself. For run-state (playing/paused, loaded ROM) use retroarch_get_status instead — this tool only reads static config. RetroArch whitelists which params are exposed; non-whitelisted names error even if they exist in retroarch.cfg. screenshot_directory is NOT exposed — see retroarch_screenshot. BEHAVIOR: No side effects — pure read. Transport: RetroArch's Network Control Interface (NCI) over UDP (default 127.0.0.1:55355, requires network_cmd_enable = true in retroarch.cfg). Errors if the param isn't in RetroArch's NCI whitelist, the value contains characters that break the line-based reply parser (rare — embedded newlines or null bytes), or the UDP query times out. RETURNS: 'NAME = VALUE' where VALUE is the raw string as stored in retroarch.cfg (paths unquoted, booleans as 'true'/'false', integers as decimal).
Inputs: name (required).
retroarch_get_statusA
PURPOSE: Report whether RetroArch is currently playing or paused, plus the loaded system, game basename, and CRC32. USAGE: Call after retroarch_ping to learn what (if anything) is loaded; before retroarch_pause_toggle to decide whether the toggle will pause or unpause; before retroarch_frame_advance (which only steps when paused); whenever you need to confirm the previous fire-and-forget control command (pause/reset/load_state) actually took effect. For RetroArch settings (paths, flags) use retroarch_get_config instead — this tool only reports run-state and the loaded ROM identity. BEHAVIOR: No side effects — pure read of emulator status via the NCI's GET_STATUS command. Returns 'No content loaded' (state=contentless) when RetroArch is sitting at the menu with no ROM. Returns an error on UDP timeout (RetroArch not reachable). RETURNS: When content is loaded: four lines 'State: playing|paused', 'System: SYSTEM_ID', 'Game: BASENAME', 'CRC32: HEX or (none reported)'. When no content: literal 'No content loaded'.
Inputs: no named parameters.
retroarch_input_pressA
PURPOSE: Press (latch down) one or more RetroPad buttons for a player via RetroArch's Network Gamepad channel. USAGE: The low-level press primitive. For a simple press-and-release use retroarch_input_tap instead. For frame-accurate scripting: pause first, call this for ONE button, then retroarch_frame_advance, repeating per change (see BEHAVIOR for why). Buttons follow libretro RetroPad naming — on PlayStation cores: b=Cross, a=Circle, y=Square, x=Triangle. BEHAVIOR: Two receiver behaviors govern all input tools: (1) LATCHING — a pressed button stays down until an explicit release; there is no auto-release or keepalive. Always pair presses with releases or use retroarch_input_release_all. (2) ONE DATAGRAM PER FRAME — RetroArch consumes at most one queued input message per emulated frame per player, so N changes sent back-to-back land over N consecutive frames. For frame-accurate scripting: pause, then alternate one input tool call with one retroarch_frame_advance per change. FIRE-AND-FORGET: the Network Gamepad channel does NOT acknowledge this command — the call returns as soon as the UDP datagram is sent, with no confirmation that RetroArch received or applied it. To verify the effect, follow up with an observable tool (retroarch_get_status for run state, retroarch_read_memory / retroarch_read_ram for memory mutations, retroarch_screenshot for visual state). UDP packets to a not-listening RetroArch are silently dropped. Transport: RetroArch's Network Gamepad receiver over UDP (default 127.0.0.1:55400 + player index; requires network_remote_enable = true AND network_remote_enable_user_p1 = true (per player) in retroarch.cfg, or Settings > Input > Network Gamepad). This is a SEPARATE channel from the NCI command port — enabling Network Commands alone is not enough. RETURNS: 'Pressed BUTTONS (player N)' — UDP-send confirmation only; verify effect via memory read or screenshot.
Inputs: buttons (required), player.
retroarch_input_releaseA
PURPOSE: Release (latch up) one or more previously pressed RetroPad buttons for a player. USAGE: Counterpart to retroarch_input_press — every press must eventually be paired with a release (or retroarch_input_release_all), because the receiver latches state indefinitely. Releasing a button that is not pressed is harmless. BEHAVIOR: Two receiver behaviors govern all input tools: (1) LATCHING — a pressed button stays down until an explicit release; there is no auto-release or keepalive. Always pair presses with releases or use retroarch_input_release_all. (2) ONE DATAGRAM PER FRAME — RetroArch consumes at most one queued input message per emulated frame per player, so N changes sent back-to-back land over N consecutive frames. For frame-accurate scripting: pause, then alternate one input tool call with one retroarch_frame_advance per change. Transport: RetroArch's Network Gamepad receiver over UDP (default 127.0.0.1:55400 + player index; requires network_remote_enable = true AND network_remote_enable_user_p1 = true (per player) in retroarch.cfg, or Settings > Input > Network Gamepad). This is a SEPARATE channel from the NCI command port — enabling Network Commands alone is not enough. Fire-and-forget UDP — no acknowledgement. RETURNS: 'Released BUTTONS (player N)' — UDP-send confirmation only.
Inputs: buttons (required), player.
retroarch_input_release_allA
PURPOSE: Zero every button and both analog sticks for a player in a single packet — the input panic button. USAGE: Use at the start and end of every scripted input session so no latched button leaks into or out of your script; also as recovery when you have lost track of which buttons are down. Unlike releasing 16 buttons individually (16 datagrams = 16 frames to drain), this clears everything in ONE datagram, applied on the next frame. BEHAVIOR: Implementation detail worth knowing: RetroArch's receiver zeroes all input state for a player whenever it reads a datagram whose size is not exactly sizeof(struct remote_message) — this tool sends a deliberately undersized datagram to trigger exactly that. Documented receiver behavior in input/input_driver.c, stable across RetroArch releases, but it is technically a protocol edge case rather than a designed command. Transport: RetroArch's Network Gamepad receiver over UDP (default 127.0.0.1:55400 + player index; requires network_remote_enable = true AND network_remote_enable_user_p1 = true (per player) in retroarch.cfg, or Settings > Input > Network Gamepad). This is a SEPARATE channel from the NCI command port — enabling Network Commands alone is not enough. Fire-and-forget UDP — no acknowledgement. RETURNS: 'Released all input (player N)' — UDP-send confirmation only.
Inputs: player.
retroarch_input_set_analogA
PURPOSE: Set one analog stick's X/Y position for a player (values persist until changed). USAGE: For analog movement, camera control, or pressure-sensitive navigation on cores that read analog sticks. Values are int16: -32768 (full left/up) to 32767 (full right/down), 0,0 = centered. Like buttons, analog state LATCHES — recenter with (0, 0) or retroarch_input_release_all when done. Sends two datagrams (X then Y), which the receiver applies over two consecutive frames. BEHAVIOR: Two receiver behaviors govern all input tools: (1) LATCHING — a pressed button stays down until an explicit release; there is no auto-release or keepalive. Always pair presses with releases or use retroarch_input_release_all. (2) ONE DATAGRAM PER FRAME — RetroArch consumes at most one queued input message per emulated frame per player, so N changes sent back-to-back land over N consecutive frames. For frame-accurate scripting: pause, then alternate one input tool call with one retroarch_frame_advance per change. Transport: RetroArch's Network Gamepad receiver over UDP (default 127.0.0.1:55400 + player index; requires network_remote_enable = true AND network_remote_enable_user_p1 = true (per player) in retroarch.cfg, or Settings > Input > Network Gamepad). This is a SEPARATE channel from the NCI command port — enabling Network Commands alone is not enough. Fire-and-forget UDP — no acknowledgement. RETURNS: 'Analog STICK set to (X, Y) (player N)' — UDP-send confirmation only.
Inputs: x (required), y (required), stick (required), player.
retroarch_input_tapA
PURPOSE: Press buttons, hold them for a duration, then release them — the everyday building block for menu navigation and normal play. USAGE: Use while emulation is RUNNING (state 'playing' per retroarch_get_status). The default 100 ms hold spans ~6 frames at 60 fps, comfortably above the one-datagram-per-frame application delay and typical game input-polling windows. For frame-accurate work while PAUSED, do not use this tool — compose retroarch_input_press / retroarch_frame_advance / retroarch_input_release manually, because this tool's hold is wall-clock and a paused emulator never samples it. BEHAVIOR: Sends press datagrams, waits hold_ms of wall-clock time, sends release datagrams. Multiple buttons latch over consecutive frames (see below), so a two-button chord is fully held only from the second frame of the hold window onward. Two receiver behaviors govern all input tools: (1) LATCHING — a pressed button stays down until an explicit release; there is no auto-release or keepalive. Always pair presses with releases or use retroarch_input_release_all. (2) ONE DATAGRAM PER FRAME — RetroArch consumes at most one queued input message per emulated frame per player, so N changes sent back-to-back land over N consecutive frames. For frame-accurate scripting: pause, then alternate one input tool call with one retroarch_frame_advance per change. Transport: RetroArch's Network Gamepad receiver over UDP (default 127.0.0.1:55400 + player index; requires network_remote_enable = true AND network_remote_enable_user_p1 = true (per player) in retroarch.cfg, or Settings > Input > Network Gamepad). This is a SEPARATE channel from the NCI command port — enabling Network Commands alone is not enough. RETURNS: 'Tapped BUTTONS for HOLDms (player N)' after the release datagrams are sent.
Inputs: buttons (required), player, hold_ms.
retroarch_load_state_currentA
PURPOSE: Restore the emulator from RetroArch's currently-selected save slot (one of slots 0-9). USAGE: Counterpart to retroarch_save_state_current. Use to undo a sequence of writes/inputs (the snapshot/experiment/restore workflow) or to start each tool-call sequence from a known baseline. Loads from whichever slot is currently selected (the same slot save_state_current would target). To load from a specific slot WITHOUT changing the current-slot pointer, use retroarch_load_state_slot instead — that's important if you're alternating between bookmarks. To start fresh from boot, use retroarch_reset. BEHAVIOR: DESTRUCTIVE TO LIVE STATE: replaces ALL current emulator state (RAM, registers, mapper, audio, framecount) with the slot file's contents. Anything not previously snapshotted is lost. The state file MUST come from the same ROM and same core version that produced it — loading mismatched files typically fails or destabilizes the core. FIRE-AND-FORGET: the NCI does NOT acknowledge this command — the call returns as soon as the UDP datagram is sent, with no confirmation that RetroArch received or applied it. To verify the effect, follow up with an observable tool (retroarch_get_status for run state, retroarch_read_memory / retroarch_read_ram for memory mutations, retroarch_screenshot for visual state). UDP packets to a not-listening RetroArch are silently dropped. If the currently-selected slot has no saved state, RetroArch silently ignores the command — no error is raised. To verify the load happened, follow up with a memory-read or screenshot. Transport: RetroArch's Network Control Interface (NCI) over UDP (default 127.0.0.1:55355, requires network_cmd_enable = true in retroarch.cfg). RETURNS: Single line 'Loaded from current slot' (UDP-send confirmation only — does NOT verify the slot existed or the load succeeded).
Inputs: no named parameters.
retroarch_load_state_slotA
PURPOSE: Load state from an explicitly-named save slot number, without modifying RetroArch's currently-selected slot pointer. USAGE: Use to load from a specific slot when you don't want to disturb the current-slot pointer (e.g. you're alternating between two bookmarks while keeping the 'live' slot for ongoing saves). For loading from the currently-selected slot, use retroarch_load_state_current — semantically distinct: this tool ignores the current-slot pointer entirely and addresses by number. Slots are numbered 0-9 by RetroArch convention. There is no retroarch_save_state_slot counterpart in the NCI — saving to a specific slot still requires walking the pointer with state_slot_plus/minus and then calling save_state_current. BEHAVIOR: DESTRUCTIVE TO LIVE STATE: replaces ALL current emulator state with the named slot's contents. Anything not previously snapshotted is lost. The state file MUST come from the same ROM and core version that produced it. Transport: RetroArch's Network Control Interface (NCI) over UDP (default 127.0.0.1:55355, requires network_cmd_enable = true in retroarch.cfg). UNLIKE most NCI control commands, LOAD_STATE_SLOT does send a reply (this client awaits it), so a UDP timeout will surface as an error here even though sibling load/save calls are fire-and-forget. If the named slot has no saved state, RetroArch's reply still indicates the command was processed — verify with a memory-read or screenshot. The current-slot pointer is unchanged after this call. RETURNS: Single line 'Loaded from slot N' echoing the requested slot number.
Inputs: slot (required).
retroarch_pause_toggleA
PURPOSE: Toggle RetroArch's pause state — pause if running, unpause if paused. USAGE: RetroArch's NCI exposes ONLY a toggle, not separate pause/unpause commands. To reach a known state, call retroarch_get_status first to check state: playing|paused, then toggle if and only if you need to flip it. Use before a sequence of memory-inspect / write / screenshot calls when you need a stable game state across calls; pair with retroarch_frame_advance to step single frames without leaving pause. BEHAVIOR: Modifies emulator run state by flipping it. FIRE-AND-FORGET: the NCI does NOT acknowledge this command — the call returns as soon as the UDP datagram is sent, with no confirmation that RetroArch received or applied it. To verify the effect, follow up with an observable tool (retroarch_get_status for run state, retroarch_read_memory / retroarch_read_ram for memory mutations, retroarch_screenshot for visual state). UDP packets to a not-listening RetroArch are silently dropped. Calling toggle when you don't know the current state will flip it to whichever state it ISN'T — confirm with retroarch_get_status before and after if it matters. Transport: RetroArch's Network Control Interface (NCI) over UDP (default 127.0.0.1:55355, requires network_cmd_enable = true in retroarch.cfg). RETURNS: Single line 'Pause toggled' (this is a confirmation that the UDP datagram was sent, NOT that RetroArch received or acted on it).
Inputs: no named parameters.
retroarch_pingA
PURPOSE: Verify connectivity to RetroArch's Network Control Interface and return the running RetroArch version string. USAGE: Call once at start-of-session before issuing other tool calls — if it succeeds, the UDP transport is up and other tools should reach RetroArch. Use retroarch_get_status afterwards to confirm a game is loaded (ping succeeds even when RetroArch is sitting at the menu with no content). BEHAVIOR: No side effects — pure liveness probe. Transport: RetroArch's Network Control Interface (NCI) over UDP (default 127.0.0.1:55355, requires network_cmd_enable = true in retroarch.cfg). Times out after ~5 seconds with a clear error if RetroArch isn't running, has Network Commands disabled, is bound to a different host/port, or a firewall is blocking UDP 55355. RETURNS: Single line 'OK — RetroArch VERSION', e.g. 'OK — RetroArch 1.20.0'.
Inputs: no named parameters.
retroarch_read_memoryA
PURPOSE: Read up to 4096 bytes from emulated memory via the libretro core's system memory map (READ_CORE_MEMORY) and return them as a hex dump. USAGE: Preferred memory-read tool when the loaded core advertises a memory map (most modern cores do). If it returns 'no memory map defined', fall back to retroarch_read_ram which uses the CHEEVOS address space. To poke a value back, pair with retroarch_write_memory at the same address. The classic two-snapshot RAM-hunt workflow uses this: snapshot before a known change, snapshot after, diff for matching deltas. Maximum 4096 bytes per call (NCI line-length limit); for larger reads, batch in 4 KiB chunks. BEHAVIOR: No side effects — pure read. Transport: RetroArch's Network Control Interface (NCI) over UDP (default 127.0.0.1:55355, requires network_cmd_enable = true in retroarch.cfg). Reads work whether emulation is paused or running. Returns an error if the loaded core doesn't expose a memory map ('no memory map defined'), the address is outside any core descriptor, length < 1, length > 4096, or the UDP query times out. RetroArch may return FEWER bytes than requested if the read crosses a memory-region boundary — the response reports the actual count.
RetroArch exposes TWO distinct memory APIs with different address spaces: • READ_CORE_MEMORY / WRITE_CORE_MEMORY (used by retroarch_read_memory / retroarch_write_memory): goes through the libretro core's system memory map. Preferred when the loaded core advertises a memory map (most modern cores do). Errors with 'no memory map defined' if the loaded core doesn't. • READ_CORE_RAM / WRITE_CORE_RAM (used by retroarch_read_ram / retroarch_write_ram): uses the achievement (CHEEVOS) address space. Works even when no core memory map is defined, but addresses follow CHEEVOS conventions, not the system bus. Use as a fallback when read_memory returns 'no memory map defined'. Both APIs depend on the loaded core's exposed mapping — addresses you used on a different core / system will NOT carry over.
RETURNS: Header line 'ADDR_HEX [N bytes]:' followed by space-separated 2-digit uppercase hex bytes.
Inputs: length (required), address (required).
retroarch_read_ramA
PURPOSE: Read up to 4096 bytes from emulated memory via the achievement (CHEEVOS) address space (READ_CORE_RAM) and return them as a hex dump. USAGE: Fallback memory-read tool — use when retroarch_read_memory returns 'no memory map defined' (older cores or those without an exposed system memory map can still respond to the older CHEEVOS read API). To poke back, pair with retroarch_write_ram at the same CHEEVOS address. Maximum 4096 bytes per call (NCI line-length limit). BEHAVIOR: No side effects — pure read. Transport: RetroArch's Network Control Interface (NCI) over UDP (default 127.0.0.1:55355, requires network_cmd_enable = true in retroarch.cfg). Reads work whether emulation is paused or running. Returns an error if the address is invalid for the CHEEVOS space, length < 1, length > 4096, or the UDP query times out. Like read_memory, RetroArch may return fewer bytes than requested at memory-region boundaries.
RetroArch exposes TWO distinct memory APIs with different address spaces: • READ_CORE_MEMORY / WRITE_CORE_MEMORY (used by retroarch_read_memory / retroarch_write_memory): goes through the libretro core's system memory map. Preferred when the loaded core advertises a memory map (most modern cores do). Errors with 'no memory map defined' if the loaded core doesn't. • READ_CORE_RAM / WRITE_CORE_RAM (used by retroarch_read_ram / retroarch_write_ram): uses the achievement (CHEEVOS) address space. Works even when no core memory map is defined, but addresses follow CHEEVOS conventions, not the system bus. Use as a fallback when read_memory returns 'no memory map defined'. Both APIs depend on the loaded core's exposed mapping — addresses you used on a different core / system will NOT carry over.
RETURNS: Header line 'ADDR_HEX [N bytes, CHEEVOS]:' followed by space-separated 2-digit uppercase hex bytes.
Inputs: length (required), address (required).
retroarch_resetA
PURPOSE: Soft-reset the running game — equivalent to pressing the console's reset button (NOT a power cycle). USAGE: Use to start fresh from the game's reset vector. To return to a specific known-good point instead of boot, use retroarch_load_state_current or retroarch_load_state_slot with a previously saved state. Note this is a SOFT reset (button reset): RAM contents and any cart-internal state may persist depending on the system, unlike a true power cycle. BEHAVIOR: DESTRUCTIVE: triggers the loaded core's reset routine, which on most systems clears registers, resets the PC to the reset vector, and starts the boot sequence over. Unsaved game progress is lost. The loaded ROM stays loaded — only volatile state is affected. FIRE-AND-FORGET: the NCI does NOT acknowledge this command — the call returns as soon as the UDP datagram is sent, with no confirmation that RetroArch received or applied it. To verify the effect, follow up with an observable tool (retroarch_get_status for run state, retroarch_read_memory / retroarch_read_ram for memory mutations, retroarch_screenshot for visual state). UDP packets to a not-listening RetroArch are silently dropped. To confirm the reset took, follow up with retroarch_get_status (state should still be 'playing') and/or a screenshot. Transport: RetroArch's Network Control Interface (NCI) over UDP (default 127.0.0.1:55355, requires network_cmd_enable = true in retroarch.cfg). RETURNS: Single line 'Game reset' (UDP-send confirmation only — does NOT verify the reset executed).
Inputs: no named parameters.
retroarch_save_state_currentA
PURPOSE: Save the entire emulator state to RetroArch's currently-selected save slot (one of slots 0-9). USAGE: Use as a rollback point before risky writes, to bookmark interesting game states, or to share repro states. RetroArch's NCI has NO 'save to slot N' command — to target a specific slot, you must first walk the slot pointer there with retroarch_state_slot_plus / retroarch_state_slot_minus, then call this. The current slot is RetroArch's internal state and is NOT reported back by the NCI, so if you don't track it yourself, observe the on-screen slot indicator after each plus/minus or use retroarch_show_message as a confirmation echo. The companion retroarch_load_state_current restores from the same slot. For path-based savestate I/O (no slots), there is no NCI equivalent — use the BizHawk or mGBA MCP servers instead. BEHAVIOR: DESTRUCTIVE TO TARGET SLOT FILE: overwrites whatever was previously in the currently-selected slot with no prompt or backup. The state file lands in RetroArch's configured savestate_directory (queryable via retroarch_get_config). State files are bound to the EXACT ROM and core version that produced them — loading on a different ROM or core typically fails. FIRE-AND-FORGET: the NCI does NOT acknowledge this command — the call returns as soon as the UDP datagram is sent, with no confirmation that RetroArch received or applied it. To verify the effect, follow up with an observable tool (retroarch_get_status for run state, retroarch_read_memory / retroarch_read_ram for memory mutations, retroarch_screenshot for visual state). UDP packets to a not-listening RetroArch are silently dropped. To verify the save happened, retroarch_load_state_current it back and observe via memory-read or screenshot. Transport: RetroArch's Network Control Interface (NCI) over UDP (default 127.0.0.1:55355, requires network_cmd_enable = true in retroarch.cfg). RETURNS: Single line 'Saved to current slot' (UDP-send confirmation only — does NOT verify the file was written, nor report which slot number it landed in).
Inputs: no named parameters.
retroarch_screenshotA
PURPOSE: Capture a PNG screenshot of the current emulator display and save it to RetroArch's configured screenshot directory. USAGE: Use to capture visible game state for inspection, sequence documentation, or to verify that a fire-and-forget control command (pause / reset / load_state / write) had a visible effect. To capture a specific game state, pause / advance frames / load state first to get the frame you want, then call this. IMPORTANT: unlike most screenshot tools, this one DOES NOT take a path argument — RetroArch saves to its own configured screenshot_directory, which the NCI does not expose (it is NOT readable via retroarch_get_config). To find the file, check RetroArch's settings UI (Settings → Directory → Screenshots) or look at where screenshots normally land for your install. BEHAVIOR: Writes a new timestamped PNG to RetroArch's screenshot directory — no existing files are overwritten (RetroArch generates a fresh filename per shot). FIRE-AND-FORGET: the NCI does NOT acknowledge this command — the call returns as soon as the UDP datagram is sent, with no confirmation that RetroArch received or applied it. To verify the effect, follow up with an observable tool (retroarch_get_status for run state, retroarch_read_memory / retroarch_read_ram for memory mutations, retroarch_screenshot for visual state). UDP packets to a not-listening RetroArch are silently dropped. The returned message confirms only that the SCREENSHOT command was sent, not that the file was actually written (disk full, permission denied, etc. would fail silently from the tool's perspective). Transport: RetroArch's Network Control Interface (NCI) over UDP (default 127.0.0.1:55355, requires network_cmd_enable = true in retroarch.cfg). RETURNS: Single line 'Screenshot saved to RetroArch's configured screenshot directory' (UDP-send confirmation only).
Inputs: no named parameters.
retroarch_show_messageA
PURPOSE: Display a single-line notification message overlaid on the RetroArch window (OSD). USAGE: For in-emulator debug output, progress markers during long scripts, or text to a human watching the RetroArch window. Purely cosmetic — no effect on game state. The ONLY way to push agent-generated text onto the RetroArch display; there is no read-the-screen counterpart. BEHAVIOR: Renders the message in RetroArch's notification area for ~3 seconds (RetroArch's default notification timeout, configurable via the input_overlay_show_inputs_port setting family). Messages are NOT queued — rapid calls replace the previous message before users can read it. FIRE-AND-FORGET: the NCI does NOT acknowledge this command — the call returns as soon as the UDP datagram is sent, with no confirmation that RetroArch received or applied it. To verify the effect, follow up with an observable tool (retroarch_get_status for run state, retroarch_read_memory / retroarch_read_ram for memory mutations, retroarch_screenshot for visual state). UDP packets to a not-listening RetroArch are silently dropped. Transport: RetroArch's Network Control Interface (NCI) over UDP (default 127.0.0.1:55355, requires network_cmd_enable = true in retroarch.cfg). RETURNS: 'Showed: MESSAGE' echoing what was sent (UDP-send confirmation only — does NOT verify the overlay rendered).
Inputs: message (required).
retroarch_state_slot_minusA
PURPOSE: Decrement RetroArch's currently-selected save slot pointer by 1 (e.g. slot 3 → slot 2). USAGE: Counterpart to retroarch_state_slot_plus. Combine with retroarch_save_state_current or retroarch_load_state_current to target a lower-numbered slot — these tools always operate on the current slot. RetroArch's NCI exposes NO way to set the slot directly to N or to query the current slot number, so track it client-side or walk from a known position. For loading a specific slot WITHOUT changing the pointer, use retroarch_load_state_slot. BEHAVIOR: Mutates RetroArch's internal current-slot pointer (-1). Wraps or clamps per RetroArch's slot-cycling configuration (typically wraps at 0 → 9). FIRE-AND-FORGET: the NCI does NOT acknowledge this command — the call returns as soon as the UDP datagram is sent, with no confirmation that RetroArch received or applied it. To verify the effect, follow up with an observable tool (retroarch_get_status for run state, retroarch_read_memory / retroarch_read_ram for memory mutations, retroarch_screenshot for visual state). UDP packets to a not-listening RetroArch are silently dropped. The new slot number is NOT reported back — track it client-side or watch the on-screen slot indicator. No effect on emulator memory / run state — only the slot pointer used by future save_state_current / load_state_current calls changes. Transport: RetroArch's Network Control Interface (NCI) over UDP (default 127.0.0.1:55355, requires network_cmd_enable = true in retroarch.cfg). RETURNS: Single line 'Decremented current slot' (UDP-send confirmation only — does NOT report the new slot number).
Inputs: no named parameters.
retroarch_state_slot_plusA
PURPOSE: Increment RetroArch's currently-selected save slot pointer by 1 (e.g. slot 3 → slot 4). USAGE: Combine with retroarch_save_state_current or retroarch_load_state_current to target a specific slot — these tools always operate on the current slot, so to save TO slot 5 you must walk the pointer there first. Pair with retroarch_state_slot_minus to walk backwards. RetroArch's NCI exposes NO way to set the slot directly to N or to query the current slot number, so if you don't track it yourself you must walk from a known position (e.g. slot 0) or observe the on-screen indicator. For loading a specific slot WITHOUT changing the pointer, use retroarch_load_state_slot instead. BEHAVIOR: Mutates RetroArch's internal current-slot pointer (+1). Wraps or clamps per RetroArch's slot-cycling configuration (typically wraps at 9 → 0). FIRE-AND-FORGET: the NCI does NOT acknowledge this command — the call returns as soon as the UDP datagram is sent, with no confirmation that RetroArch received or applied it. To verify the effect, follow up with an observable tool (retroarch_get_status for run state, retroarch_read_memory / retroarch_read_ram for memory mutations, retroarch_screenshot for visual state). UDP packets to a not-listening RetroArch are silently dropped. The new slot number is NOT reported back — track it client-side or watch the on-screen slot indicator. No effect on emulator memory / run state — only the slot pointer used by future save_state_current / load_state_current calls changes. Transport: RetroArch's Network Control Interface (NCI) over UDP (default 127.0.0.1:55355, requires network_cmd_enable = true in retroarch.cfg). RETURNS: Single line 'Incremented current slot' (UDP-send confirmation only — does NOT report the new slot number).
Inputs: no named parameters.
retroarch_write_memoryA
PURPOSE: Write a byte sequence to emulated memory via the libretro core's system memory map (WRITE_CORE_MEMORY). USAGE: Preferred memory-write tool when the loaded core advertises a memory map. Use for cheats, debug pokes, and game-state mutations (give a player N lives, unlock a flag, install a cheat table). If it returns 'no memory map defined', fall back to retroarch_write_ram. Maximum 4096 bytes per call (NCI line-length limit); for larger writes, batch in 4 KiB chunks. To establish a rollback point first, use retroarch_save_state_current. BEHAVIOR: DESTRUCTIVE: overwrites N bytes starting at address with no undo (snapshot via retroarch_save_state_current first if you need rollback). Disables RetroArch's hardcore mode for the rest of the session (RetroArch silently flips this flag when any memory-write NCI command is used). UNLIKE most NCI commands, this one DOES return a count — RetroArch replies with the number of bytes actually written, which may be less than requested if a read-only descriptor is hit mid-write (writes still apply up to that boundary). Direct memory write — bypasses MBC/mapper/DMA semantics. Transport: RetroArch's Network Control Interface (NCI) over UDP (default 127.0.0.1:55355, requires network_cmd_enable = true in retroarch.cfg). Returns an error if the loaded core doesn't expose a memory map, the address is invalid, the byte array is empty or > 4096, or the UDP query times out.
RetroArch exposes TWO distinct memory APIs with different address spaces: • READ_CORE_MEMORY / WRITE_CORE_MEMORY (used by retroarch_read_memory / retroarch_write_memory): goes through the libretro core's system memory map. Preferred when the loaded core advertises a memory map (most modern cores do). Errors with 'no memory map defined' if the loaded core doesn't. • READ_CORE_RAM / WRITE_CORE_RAM (used by retroarch_read_ram / retroarch_write_ram): uses the achievement (CHEEVOS) address space. Works even when no core memory map is defined, but addresses follow CHEEVOS conventions, not the system bus. Use as a fallback when read_memory returns 'no memory map defined'. Both APIs depend on the loaded core's exposed mapping — addresses you used on a different core / system will NOT carry over.
RETURNS: Single line 'Wrote N bytes → ADDR_HEX' where N is RetroArch's reported actual byte count.
Inputs: bytes (required), address (required).
retroarch_write_ramA
PURPOSE: Write a byte sequence to emulated memory via the achievement (CHEEVOS) address space (WRITE_CORE_RAM). USAGE: Fallback memory-write tool — use when retroarch_write_memory returns 'no memory map defined' or the core only supports the older CHEEVOS write API. Maximum 4096 bytes per call (NCI line-length limit). To verify the write landed (this command does NOT acknowledge — see BEHAVIOR), follow up with retroarch_read_ram at the same address. To establish a rollback point first, use retroarch_save_state_current. BEHAVIOR: DESTRUCTIVE: overwrites bytes starting at address with no undo (snapshot via retroarch_save_state_current first if you need rollback). Disables RetroArch's hardcore mode for the rest of the session. FIRE-AND-FORGET: the NCI does NOT acknowledge this command — the call returns as soon as the UDP datagram is sent, with no confirmation that RetroArch received or applied it. To verify the effect, follow up with an observable tool (retroarch_get_status for run state, retroarch_read_memory / retroarch_read_ram for memory mutations, retroarch_screenshot for visual state). UDP packets to a not-listening RetroArch are silently dropped. This is the key behavioral difference vs retroarch_write_memory, which DOES return a count: write_ram has no way to report a partial write or a rejected address — the only way to verify is a follow-up retroarch_read_ram. Direct memory write — bypasses MBC/mapper/DMA semantics. Transport: RetroArch's Network Control Interface (NCI) over UDP (default 127.0.0.1:55355, requires network_cmd_enable = true in retroarch.cfg). Local input validation rejects empty arrays, > 4096 bytes, or values outside 0-255 before the UDP send.
RetroArch exposes TWO distinct memory APIs with different address spaces: • READ_CORE_MEMORY / WRITE_CORE_MEMORY (used by retroarch_read_memory / retroarch_write_memory): goes through the libretro core's system memory map. Preferred when the loaded core advertises a memory map (most modern cores do). Errors with 'no memory map defined' if the loaded core doesn't. • READ_CORE_RAM / WRITE_CORE_RAM (used by retroarch_read_ram / retroarch_write_ram): uses the achievement (CHEEVOS) address space. Works even when no core memory map is defined, but addresses follow CHEEVOS conventions, not the system bus. Use as a fallback when read_memory returns 'no memory map defined'. Both APIs depend on the loaded core's exposed mapping — addresses you used on a different core / system will NOT carry over.
RETURNS: Single line 'Wrote N bytes → ADDR_HEX (CHEEVOS, no ack)' where N is the array length you sent. The 'no ack' in the message is a reminder that RetroArch did not confirm the write.
Inputs: bytes (required), address (required).
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 22 tools
Every tool has a distinct purpose: input press/release/tap/analog, save/load state, memory read/write via two clearly documented APIs, status/config probes, and emulator controls. The only potential overlap between read_memory and read_ram is thoroughly disambiguated by explicit descriptions of the two address spaces and fallback guidance.
All tools follow the 'retroarch_' prefix with a consistent verb-noun or noun-verb structure (e.g., input_press, load_state_current, read_memory, get_status). Even compound names like state_slot_plus and input_release_all follow a logical pattern. No mixing of camelCase or inconsistent verb styles.
22 tools is on the higher side but appropriate for RetroArch's broad feature set, covering input, memory, save states, status, config, and screenshots. Each tool serves a distinct function with no obvious redundancy, justifying the count as slightly over the ideal range but reasonable for the domain.
The tool surface covers the core workflows of emulator control: connectivity, status, input, memory manipulation, save/load states, and screenshots. Minor gaps exist such as no direct ROM loading or slot-number query, but these can be worked around given the NCI protocol's limitations and the server's assumptions of a loaded game.
Maintenance
Related MCP Connectors
An MCP server that provides read access to your cloud storage providers, bank accounts and more.
The official MCP Server for the Mux API
Related MCP Servers
- AlicenseAqualityCmaintenanceMCP server for PCSX2 and other emulators that speak the PINE protocol. Read and write 8/16/32/64-bit emulator memory and control save states for PlayStation-family emulation.1445 npm3MIT
- AlicenseAqualityCmaintenanceMCP 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.1726 npm6MIT
- AlicenseAqualityBmaintenanceMCP server for the mGBA Game Boy Advance emulator. Read and write GBA memory, inject button presses, take screenshots, save/load state, and step the emulator through a Lua bridge.1835 npm7MIT
- AlicenseAqualityCmaintenanceMCP server for BizHawk, the multi-system emulator. Drive NES, SNES, GB/GBC/GBA, Genesis, N64, PSX, Saturn and more through one Lua bridge — memory r/w across named domains, joypad input, frame-advance, screenshot, save/load state.2068 npm6MIT