Skip to content

Latest commit

 

History

History
180 lines (116 loc) · 6.8 KB

File metadata and controls

180 lines (116 loc) · 6.8 KB

mcp-retroarch recipes

Practical examples of driving RetroArch from Claude or any MCP client. Each recipe is self-contained.

Prerequisites: RetroArch running, Network Commands enabled, any libretro core + game loaded. Test with retroarch_ping first.


1. Identify what's loaded

retroarch_ping
retroarch_get_status

Returns the RetroArch version and the loaded game's system, basename, and CRC32. CRC32 is the canonical fingerprint — useful for cross-referencing community cheat databases or speedrun tables.


2. RAM hunting — find the address of a counter you can see on screen

"I'm playing Pokémon Red. The badges count is currently 2. Walk into the gym, beat the leader, see badges count rise to 3, find the address."

1. retroarch_read_memory(address=0xC000, length=4096)   # snapshot A (badges=2)
2. <user fights and wins, badges=3>
3. retroarch_read_memory(address=0xC000, length=4096)   # snapshot B (badges=3)
4. <Claude diffs A vs B for any byte/u16/u32 that changed 2 → 3>

Memory map varies wildly by core — check retroarch_get_status first to know the system, then look up its memory map online. Common landmarks:

System WRAM start Cart RAM start
NES 0x0000 0x6000
SNES 0x7E0000 0x700000
GB/GBC 0xC000 0xA000
GBA 0x02000000 (EWRAM), 0x03000000 (IWRAM) 0x0E000000 (SRAM)
Genesis 0xFF0000 0x200000
PSX 0x80000000 (saves on memcard, not addressable)

If retroarch_read_memory returns "no memory map defined", the loaded core doesn't advertise one. Try retroarch_read_ram (CHEEVOS path) — many cores expose CHEEVOS even without a memory map.


3. Write a value into memory

"The lives counter for this Mega Man game is at 0xC0FE. Set it to 99."

retroarch_write_memory(address=0xC0FE, bytes=[99])

Note: this disables hardcore achievement mode for the session — RetroArch won't let you cheat your way to achievements. If you want to keep hardcore on, save state first, write, do the test, then load state to restore.


4. Take a screenshot

retroarch_screenshot

RetroArch saves it to its configured screenshot directory. The NCI doesn't expose screenshot_directory via GET_CONFIG_PARAM, so check it via the GUI: Settings → Directory → Screenshot. (savestate_directory, savefile_directory, etc. ARE queryable via retroarch_get_config.)


5. Pause, inspect, resume

"Pause the game, dump the current player position from RAM, then resume."

retroarch_pause_toggle                                        # pause
retroarch_read_memory(address=0xD361, length=2)               # X coord
retroarch_read_memory(address=0xD362, length=2)               # Y coord
retroarch_pause_toggle                                        # resume

PAUSE_TOGGLE flips current state — there's no separate PAUSE and UNPAUSE. If you need to know the current state, call retroarch_get_status first.


6. Frame-perfect inspection

"Pause, then advance 1 frame at a time and read RNG state to see how it changes per frame."

retroarch_pause_toggle
for i in 1..10:
  rng_before = retroarch_read_memory(address=<rng_addr>, length=4)
  retroarch_frame_advance
  rng_after  = retroarch_read_memory(address=<rng_addr>, length=4)
  print(f"frame {i}: {rng_before} → {rng_after}")
retroarch_pause_toggle  # resume

FRAMEADVANCE only works when paused.


7. Snapshot, experiment, restore

"Save current state, write some experimental values to RAM, see what happens, then restore."

retroarch_save_state_current
# ... experiment freely ...
retroarch_load_state_current

To target a specific slot:

# Walk slot pointer to slot 5
retroarch_state_slot_minus  # repeat as needed to reach slot 0 first if unsure
retroarch_state_slot_plus   # ×5
retroarch_save_state_current

(NCI has no "current slot is N" query and no "save to slot N" command — only walk + save_current.)


8. Show the user a status message

"Tell the user 'starting RAM dump in 3 seconds' before doing the work."

retroarch_show_message(message="starting RAM dump in 3 seconds")

Renders on the RetroArch overlay for a few seconds. Useful for narrating long-running scripted sessions, or for letting the user know an automation has reached a checkpoint.


9. Sanity-check the bridge before doing anything

retroarch_ping        # returns "OK — RetroArch <version>"
retroarch_get_status  # confirms a game is loaded

If ping fails: RetroArch isn't running, Network Commands aren't enabled in retroarch.cfg, or the port doesn't match RETROARCH_PORT.


10. PSX (SwanStation) — when read_memory says "no memory map"

"I'm playing a PS1 game in RetroArch with the SwanStation core. read_memory keeps failing — what gives?"

SwanStation doesn't define a system memory map (verified — see README's Tested cores table). Use read_ram instead, which goes through the CHEEVOS address space. PSX main RAM starts mapping around CHEEVOS offset 0x010000:

retroarch_read_ram(address=0x010000, length=64)

You should see real game code (MIPS R3000 instructions) and data, not zeros. Lower addresses (0x00000x010000) are typically the PSX kernel area and read as zeros for most games.

A reasonable RAM-hunting starting point for PSX: scan from 0x010000 upward in 4096-byte chunks until you find the structure you're after.


What this server can NOT do (clearly)

  • Send game-pad input. NCI exposes RetroArch hotkeys (pause, reset, state slots, etc.) but NOT controller buttons. There's a separate "Remote RetroPad" libretro core on UDP port 55400+ that does, but it requires loading that specific core (you can't drive a normal emulation core through it).

    If you need input automation, see mcp-mgba (Game Boy Advance, full input + screenshot via Lua bridge).

  • Save to a specific slot in one call. NCI has no SAVE_STATE_SLOT N command — only the slot-pointer + save-current dance shown in recipe #7.

  • Discover where screenshots land. screenshot_directory isn't in the NCI's GET_CONFIG_PARAM allowlist. Check via RetroArch's GUI.


Tips for using these tools

  • Always start with retroarch_get_status. It tells you the system identifier (so you know the memory map) and the CRC32 (so you can cross-reference cheats databases).
  • Try read_memory first, fall back to read_ram. Most cores have a memory map; the CHEEVOS path is for cores that don't.
  • Pause before bulk reads if memory layout depends on game state — pausing freezes everything including DMA, ensuring consistent snapshots.
  • show_message is your friend when the model is doing something visually invisible (like RAM scanning) and the user wants confirmation.