mcp-ppsspp
The mcp-ppsspp server bridges an AI client to PPSSPP's WebSocket debugger, enabling game automation, reverse engineering, cheat development, and visual inspection.
Connectivity & Game Info
Ping PPSSPP to verify the connection and retrieve its version
Get the loaded game's title, disc ID, version, and run state
Memory Access
Read/write 8-, 16-, or 32-bit values at any valid PSP memory address
Read/write arbitrary byte ranges (up to 64 KiB) as hex data
Read null-terminated UTF-8 strings from PSP memory
Supported regions include user RAM, kernel RAM, VRAM, scratchpad, and hardware registers
Input Simulation
Press/release PSP buttons (cross, circle, triangle, square, d-pad, start, select, L/R triggers, home) persistently or for a specified number of frames
Set the analog stick X/Y position
Emulation Control
Pause, resume, step one MIPS instruction, or soft-reset the loaded game
Visual Capture
Capture the PSP framebuffer as a PNG screenshot (from the raw render target or composited output)
CPU Debugging & Reverse Engineering
Read all MIPS Allegrex CPU registers (GPR, FPU, special registers like PC, SP, RA)
Add, remove, and list CPU execution breakpoints
Provides tools to interact with the PPSSPP emulator for PlayStation Portable, enabling memory read/write, button input, screenshot capture, breakpoints, and register inspection via WebSocket debugger.
mcp-ppsspp
An MCP server that exposes PPSSPP — the PlayStation Portable emulator — to any MCP-compatible client (Claude Desktop, Claude Code, etc.) via PPSSPP's built-in WebSocket debugger interface.
Read and write PSP memory, drive games with button input, capture screenshots, set CPU breakpoints, inspect MIPS Allegrex registers — all through a clean tool interface. No bridge plugin needed; PPSSPP's debugger is built into the emulator.
How it works
+------------------+ stdio +------------------+ WebSocket +------------------+
| MCP client | JSON-RPC | mcp-ppsspp | JSON-RPC | PPSSPP |
| (Claude / etc.) | ===========> | (Node.js) | =============> | (debugger) |
+------------------+ +------------------+ +------------------+Unlike the mcp-bizhawk / mcp-mgba bridges (which need a Lua plugin loaded into the emulator), PPSSPP ships with its own debugger WebSocket interface — we just speak JSON to it. No plugin to install.
The connection uses subprotocol debugger.ppsspp.org on PPSSPP's debugger port.
Related MCP server: chrome-devtools-mcp
Requirements
PPSSPP (recent version with WebSocket debugger — 1.7+)
Node.js 22+
"Allow remote debugger" enabled in PPSSPP
Install
npm install -g mcp-ppssppOr npx -y mcp-ppsspp.
Set up PPSSPP's debugger
Launch PPSSPP, load any PSP ISO/EBOOT
Settings → Tools → Developer Tools → Allow remote debugger (check the box)
PPSSPP will show the active host:port (e.g.
ws://192.168.1.10:12345/debugger)Note the port number — you'll set it as an environment variable for the MCP server
Register with your MCP client
Claude Code (CLI)
claude mcp add ppsspp --scope user --env PPSSPP_PORT=12345 mcp-ppssppReplace 12345 with your actual port. Verify:
claude mcp list
# ppsspp: mcp-ppsspp - ✓ ConnectedClaude Desktop
Edit claude_desktop_config.json:
{
"mcpServers": {
"ppsspp": {
"command": "mcp-ppsspp",
"env": { "PPSSPP_PORT": "12345" }
}
}
}Restart Claude Desktop after editing.
Configuration
Env var | Default | Purpose |
|
| WebSocket host to dial |
| (required) | WebSocket port — see PPSSPP's debugger settings |
Tools
Tool | Description |
| Verify connectivity (returns version) |
| Title, disc ID, version, run state |
| Read u8 / u16-LE / u32-LE from PSP memory |
| Write to PSP memory |
| Read up to 64 KiB as a byte array |
| Write byte array to memory |
| Read null-terminated UTF-8 string |
| Set persistent PSP button state |
| Press a button for N frames + auto-release |
| Set analog stick position |
| Pause / resume emulation |
| Step one MIPS instruction |
| Soft-reset the loaded game |
| Capture framebuffer as inline PNG |
| Read all MIPS Allegrex registers |
| CPU execution breakpoints |
PSP memory map (cheat sheet)
Range | Region |
| Scratchpad (fast 16 KiB SRAM) |
| VRAM (2 MiB GE video memory) |
| Kernel RAM (8 MiB, low half) |
| User RAM (24 MiB, where most game state lives) |
| Hardware registers |
PSP is little-endian (MIPS Allegrex). Kernel-mode mirrors at 0x88xxxxxx map to the same physical RAM as 0x08xxxxxx.
PSP buttons
cross, circle, triangle, square, up, down, left, right, start, select, ltrigger, rtrigger, home.
Troubleshooting
Symptom | Cause / Fix |
| Set the env var to the port shown in PPSSPP's Developer Tools dialog |
| PPSSPP isn't running, "Allow remote debugger" isn't checked, or you have the wrong port |
Tool calls hang / time out | Check the PPSSPP UI is responding; the WebSocket request requires PPSSPP's main loop to dispatch |
| Address is outside the PSP's mapped regions (user RAM is |
Screenshot returns no data | No game loaded — boot an ISO/EBOOT first |
Buttons don't seem to do anything | PPSSPP's input has the buttons but they may not "feel" right via remote input if the game polls fast; try |
Limitations
No savestate API — PPSSPP's WebSocket debugger doesn't expose
savestate.save/load. Use PPSSPP's keybinds (F1-F8 for slots) via the UI for now. Could be hacked by usinginput.buttons.pressto trigger the keybind, but not native.Frame-advance is instruction-level only (
cpu.stepInto). To advance a whole frame, set a breakpoint at the vblank handler andresume.Analog stick is shared state —
ppsspp_send_analogupdates the persistent stick position; not auto-released.
Development
npm install
npm run dev # tsc --watch — autobuilds on src/ changesDebugging with the MCP Inspector
Browse and call this server's tools interactively with the MCP Inspector:
PPSSPP_PORT=<port> npm run inspectorBuild first if you've edited src/ since your last npm install (npm run build, or keep npm run dev running). mcp-ppsspp has no default port — read the active one off PPSSPP's Developer Tools → Allow remote debugger dialog and pass it as PPSSPP_PORT. tools/list works even without PPSSPP connected; calling a tool needs PPSSPP running with the remote debugger enabled.
License
Available Tools
23 toolsppsspp_breakpoint_addA
PURPOSE: Add a CPU execution breakpoint at the given PSP physical address. Emulation halts when PC reaches that address. USAGE: For RE work and HLE intercepts. Combine with ppsspp_resume + (later) ppsspp_get_registers to inspect state at the breakpoint. BEHAVIOR: Modifies PPSSPP's breakpoint table. The breakpoint persists until removed via ppsspp_breakpoint_remove or PPSSPP restarts. Returns an error if the address isn't executable memory. RETURNS: Single line 'Breakpoint added at ADDR_HEX'.
Inputs: address (required).
ppsspp_breakpoint_listA
PURPOSE: List all currently-set CPU execution breakpoints. USAGE: Inventory before bulk-removing, or sanity-check what's set. BEHAVIOR: No side effects — pure read. RETURNS: Multi-line text, one line per breakpoint with its address and any conditions.
Inputs: no named parameters.
ppsspp_breakpoint_removeA
PURPOSE: Remove a previously-added CPU execution breakpoint. USAGE: Clean up breakpoints when done debugging. To remove all, query ppsspp_breakpoint_list first. BEHAVIOR: Modifies PPSSPP's breakpoint table. Idempotent for non-existent breakpoints (no error). RETURNS: Single line 'Breakpoint removed at ADDR_HEX'.
Inputs: address (required).
ppsspp_get_infoA
PURPOSE: Get the loaded game's title, disc ID, and version, plus PPSSPP's run state. USAGE: Call after ppsspp_ping to learn what game is loaded and whether emulation is currently running or stepping. BEHAVIOR: No side effects — pure read. Returns 'no game loaded' fields if PPSSPP is at the home menu / not currently emulating. RETURNS: Multi-line text with Title, Disc ID, Version, and run state (running / paused / stepping).
Inputs: no named parameters.
ppsspp_get_registersA
PURPOSE: Read all MIPS Allegrex CPU registers (general-purpose + FPU + special). USAGE: For reverse engineering and debugging — inspect function arguments, return values, PC, stack pointer. PSP's calling convention puts args in $a0-$a3, return in $v0, stack in $sp, return address in $ra. BEHAVIOR: No side effects — pure read. Most informative when called while emulation is paused (ppsspp_pause first); on a running CPU the snapshot is from whenever PPSSPP samples it. RETURNS: Multi-line text with all register names + hex values, grouped by class (GPR, FPU, special).
Inputs: no named parameters.
ppsspp_pauseA
PURPOSE: Pause PSP emulation (the debugger calls this 'stepping mode'). USAGE: Use before a sequence of memory inspects when you need a stable game state across calls. Memory r/w tool calls still work while paused. Use ppsspp_resume to continue. BEHAVIOR: Modifies emulator run state. Pauses the MIPS CPU; rendering may continue at last frame. Idempotent — pausing already-paused is a no-op. RETURNS: Single line 'Emulation paused'.
Inputs: no named parameters.
ppsspp_pingA
PURPOSE: Verify that the PPSSPP WebSocket debugger is reachable and responding. USAGE: Call once at start-of-session before any other tool calls; if it succeeds, the WebSocket handshake worked and PPSSPP's debugger is available. BEHAVIOR: No side effects — calls the 'version' event to learn PPSSPP's release version. Times out after ~10 seconds if PPSSPP isn't running, doesn't have 'Allow remote debugger' enabled (Settings → Tools → Developer Tools), or the host:port isn't reachable. RETURNS: Single line 'pong (PPSSPP VERSION)'.
Inputs: no named parameters.
ppsspp_press_buttonA
PURPOSE: Press a PSP button for a fixed number of frames, then auto-release. USAGE: Use for discrete actions like pressing Start to skip a cutscene, or Cross to confirm a menu. For longer holds across many frames use ppsspp_press_buttons (persistent state) instead. BEHAVIOR: Modifies emulator input state. PPSSPP queues the press internally and releases the button after duration frames; the tool call returns immediately. Returns an error if the button name isn't recognized. RETURNS: Single line 'Pressed BUTTON for N frames (auto-released)'.
Inputs: button (required), duration.
ppsspp_press_buttonsA
PURPOSE: Set the PSP joypad button state — the buttons in the map are 'held' until you send another buttons command. USAGE: Drive games with input. Unlike one-frame-only schemes on other emulators, PPSSPP's input.buttons.send updates the persistent button state — the buttons stay held until you call ppsspp_press_buttons again with them set false (or use ppsspp_press_button for a timed one-shot). To release all buttons, call with all keys set to false. BEHAVIOR: Modifies emulator input state until changed. PSP buttons (case-sensitive): cross, circle, triangle, square, up, down, left, right, start, select, ltrigger, rtrigger, home. Unrecognized button names return an error. RETURNS: Single line 'Set buttons: BUTTON+BUTTON+...' or '... (all released)' if nothing was pressed.
Inputs: buttons (required).
ppsspp_read16A
PURPOSE: Read an unsigned 16-bit little-endian value from PSP memory at the given physical address. USAGE: Use for 16-bit game-state fields (most counters, IDs, small numerics). For single bytes use ppsspp_read8; for 32-bit use ppsspp_read32; for arbitrary byte spans use ppsspp_read_range. BEHAVIOR: No side effects — pure read. PSP is little-endian (MIPS Allegrex). Returns an error if address+2 exceeds the valid memory region. RETURNS: Single line 'ADDR_HEX: VAL_DEC (0xVAL_HEX)'.
Inputs: address (required).
ppsspp_read32A
PURPOSE: Read an unsigned 32-bit little-endian value from PSP memory at the given physical address. USAGE: Use for 32-bit fields — timestamps, large counters, pointers, RGBA colors. For 8/16-bit use ppsspp_read8/read16; for spans use ppsspp_read_range. BEHAVIOR: No side effects — pure read. PSP is little-endian. Returns an error if address+4 exceeds the valid memory region. RETURNS: Single line 'ADDR_HEX: VAL_DEC (0xVAL_HEX)'.
Inputs: address (required).
ppsspp_read8A
PURPOSE: Read an unsigned 8-bit byte from PSP memory at the given physical address. USAGE: Use for single-byte status flags, counters, and 8-bit fields. For 16/32-bit values use ppsspp_read16/read32 (one call instead of multi-byte assembly); for spans use ppsspp_read_range. BEHAVIOR: No side effects — pure read. Returns an error if the address isn't a valid PSP memory address (PPSSPP validates against the PSP's mapped regions). RETURNS: Single line 'ADDR_HEX: VAL_DEC (0xVAL_HEX)'.
Inputs: address (required).
ppsspp_read_rangeA
PURPOSE: Read a contiguous range of bytes from PSP memory and return as a hex dump. USAGE: Use whenever you need more than ~4 bytes — one round-trip vs N typed reads. PPSSPP returns the data base64-encoded over the wire; this tool decodes and formats as space-separated hex bytes. No hard size limit from the WebSocket but stay reasonable (≤16 KiB per call) for response sizes. BEHAVIOR: No side effects — pure read. Reads size consecutive bytes starting at address. Returns an error if any byte in the range is outside the valid PSP memory map. RETURNS: 'ADDR_HEX [N bytes]:' header + space-separated 2-digit uppercase hex bytes.
Inputs: address (required), size (required).
ppsspp_read_stringA
PURPOSE: Read a null-terminated UTF-8 string from PSP memory at the given address. USAGE: Use for in-game text, character names, dialogue, file names — anywhere the PSP stores a C-style null-terminated string. Stops at the first 0x00 byte. BEHAVIOR: No side effects — pure read. Reads bytes until null terminator, decodes as UTF-8. Returns an error if the address is outside valid memory, or if the string runs past valid memory before hitting a null. RETURNS: Single line 'ADDR_HEX: "STRING"'.
Inputs: address (required).
ppsspp_resetA
PURPOSE: Reset the loaded PSP game — equivalent to soft-resetting the console. USAGE: Use to start fresh from the game's intro. To return to a specific point, set up a savestate via PPSSPP's UI and load it (savestate API is not in the WebSocket interface, so this must be done via PPSSPP's keybinds — typically F1-F8 for slots). BEHAVIOR: DESTRUCTIVE: RAM contents cleared, CPU returns to game entry point, framecount/game-state lost. The ISO/EBOOT stays loaded. RETURNS: Single line 'Game reset'.
Inputs: no named parameters.
ppsspp_resumeA
PURPOSE: Resume PSP emulation from a paused/stepping state. USAGE: Counterpart to ppsspp_pause. Use after a paused inspection sequence. To step a single frame instead, use ppsspp_step. BEHAVIOR: Modifies emulator run state. Idempotent — resuming already-running is a no-op. RETURNS: Single line 'Emulation resumed'.
Inputs: no named parameters.
ppsspp_screenshotA
PURPOSE: Capture the current PSP framebuffer as a PNG-encoded screenshot. USAGE: For visual inspection or sequence documentation. Default 'render' source reads the active GPU render target — safer, native 480x272, what the PSP CPU asked the GPU to draw. Opt-in 'output' source reads PPSSPP's final composited output (post scaling/shaders) but can crash PPSSPP on games whose output framebuffer state confuses GPU_GetOutputFramebuffer (a real upstream bug — an assert that should be a graceful failure). Prefer 'render' unless you specifically need the post-processed image. BEHAVIOR: Transparently pauses the CPU (cpu.stepping), captures, then resumes — both PPSSPP buffer events require stepping. If the emulator was already paused, leaves it paused. Returns an error if no game is loaded. The 'output' source CAN crash PPSSPP on certain games; if it does, MCP auto-reconnects to the relaunched PPSSPP cleanly. RETURNS: Text confirmation + inline PNG image block.
Inputs: source.
ppsspp_send_analogA
PURPOSE: Set the PSP analog stick state (one of left/right; the PSP only has one stick natively but PPSSPP exposes both for forward-compat). USAGE: Drive games that need analog input — character movement, camera control. X and Y are signed in [-1.0, 1.0]; (0, 0) = neutral, (1, 0) = full right, (0, -1) = full up. BEHAVIOR: Modifies emulator analog input. State persists until updated. RETURNS: Single line 'Set analog stick STICK to (X, Y)'.
Inputs: stick (required), x (required), y (required).
ppsspp_stepA
PURPOSE: Step the MIPS CPU forward by ONE instruction (cpu.stepInto). USAGE: For instruction-level debugging — set a breakpoint, hit it, then step. NOT a frame-advance — one MIPS instruction is much smaller than one frame. To advance a frame's worth of execution, set a breakpoint at the start of the next frame's render and use ppsspp_resume. BEHAVIOR: Modifies emulator run state. Executes one MIPS instruction, then returns to stepping mode. Returns an error if emulation isn't currently in stepping mode (call ppsspp_pause first). RETURNS: Single line 'Stepped one instruction. PC: 0xADDR'.
Inputs: no named parameters.
ppsspp_write16A
PURPOSE: Write an unsigned 16-bit little-endian value to PSP memory. USAGE: Use for 16-bit cheats and pokes (HP, score, coordinates). For single bytes use ppsspp_write8; for 32/larger use ppsspp_write32/write_range. BEHAVIOR: DESTRUCTIVE: overwrites two bytes with no undo. PSP is little-endian (low byte at address, high at address+1). Returns an error if address+2 exceeds valid memory or value > 65535. RETURNS: Single line 'Wrote VAL → ADDR_HEX'.
Inputs: address (required), value (required).
ppsspp_write32A
PURPOSE: Write an unsigned 32-bit little-endian value to PSP memory. USAGE: Use for 32-bit cheats and pokes — timestamps, large counters, pointers. For 8/16-bit use ppsspp_write8/write16; for spans use ppsspp_write_range. BEHAVIOR: DESTRUCTIVE: overwrites four bytes with no undo. PSP is little-endian. Returns an error if address+4 exceeds valid memory or value > 4294967295. RETURNS: Single line 'Wrote VAL → ADDR_HEX'.
Inputs: address (required), value (required).
ppsspp_write8A
PURPOSE: Write an unsigned byte (0-255) to PSP memory at the given physical address. USAGE: Use for single-byte cheats, debug pokes, game-state mutations. For 16/32-bit use ppsspp_write16/write32; for spans use ppsspp_write_range. BEHAVIOR: DESTRUCTIVE: overwrites whatever was at address with no undo. Direct memory write — no hardware mediation. Returns an error if the address is outside valid memory or value > 255. RETURNS: Single line 'Wrote VAL → ADDR_HEX'.
Inputs: address (required), value (required).
ppsspp_write_rangeA
PURPOSE: Write a contiguous byte sequence to PSP memory starting at the given address. USAGE: Use for installing cheat tables, patching code blocks, or seeding regions. Bytes are sent base64-encoded over the wire. BEHAVIOR: DESTRUCTIVE: overwrites N bytes with no undo. Direct memory write. Returns an error if address+N exceeds valid memory or any byte value is outside 0-255. RETURNS: Single line 'Wrote N bytes → ADDR_HEX'.
Inputs: address (required), bytes (required).
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 23 tools
Each tool targets a distinct operation: memory reads/writes are separated by size (8/16/32, range, string), breakpoints have add/list/remove, input has button press/hold and analog, and emulation control has pause/resume/step/reset. No two tools overlap in purpose.
All tools follow a consistent `ppsspp_verb_noun` pattern with snake_case. Verbs like read, write, breakpoint, press, send, pause, resume, step, reset, ping, get, and screenshot are used uniformly. The specificity (e.g., read8, read16) follows a clear convention.
23 tools is slightly above the typical 3-15 range, but for an emulator debugger, each tool serves a distinct purpose (memory access sizes, breakpoints, input, state control). The count is justified and not excessive for the domain.
The tool set covers the core debugging workflow: memory inspection/modification, breakpoints, input simulation, and emulation control. Missing features like savestate management (not available via WebSocket) and direct register writes are minor gaps, but the set is well-scoped for RE and HLE tasks.
Maintenance
Related MCP Connectors
Shared memory MCP server for Claude Code, Codex, Gemini CLI and Cursor. Cited, editable.
Augments MCP Server - A comprehensive framework documentation provider for Claude Code
Talk to your public-facing AI from any MCP client — Claude, ChatGPT, Cursor, Cline, Windsurf.
Hosted X (Twitter) MCP server for Claude, ChatGPT and Cursor, with each action switched per account.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceA minimal WebSocket-based MCP server implementation that enables modern tool integrations with VSCode, Claude, and other applications.6 npmISC
- AlicenseNot gradedqualityDmaintenanceAn MCP Server for Chrome DevTools, following the Chrome DevTools Protocol. Integrates with Claude Desktop and Claude Code.313MIT
- AlicenseAqualityBmaintenanceMCP 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.1438 npm3MIT
- AlicenseAqualityBmaintenanceMCP 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.1736 npm6MIT