Skip to main content
Glama

mcp-ppsspp

npm version npm downloads CI License: MIT Snyk Socket Bundlephobia npmgraph

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-ppsspp

Or npx -y mcp-ppsspp.

Set up PPSSPP's debugger

  1. Launch PPSSPP, load any PSP ISO/EBOOT

  2. Settings → Tools → Developer Tools → Allow remote debugger (check the box)

  3. PPSSPP will show the active host:port (e.g. ws://192.168.1.10:12345/debugger)

  4. 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-ppsspp

Replace 12345 with your actual port. Verify:

claude mcp list
# ppsspp: mcp-ppsspp - ✓ Connected

Claude 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

PPSSPP_HOST

127.0.0.1

WebSocket host to dial

PPSSPP_PORT

(required)

WebSocket port — see PPSSPP's debugger settings

Tools

Tool

Description

ppsspp_ping

Verify connectivity (returns version)

ppsspp_get_info

Title, disc ID, version, run state

ppsspp_read8 / ppsspp_read16 / ppsspp_read32

Read u8 / u16-LE / u32-LE from PSP memory

ppsspp_write8 / ppsspp_write16 / ppsspp_write32

Write to PSP memory

ppsspp_read_range

Read up to 64 KiB as a byte array

ppsspp_write_range

Write byte array to memory

ppsspp_read_string

Read null-terminated UTF-8 string

ppsspp_press_buttons

Set persistent PSP button state

ppsspp_press_button

Press a button for N frames + auto-release

ppsspp_send_analog

Set analog stick position

ppsspp_pause / ppsspp_resume

Pause / resume emulation

ppsspp_step

Step one MIPS instruction

ppsspp_reset

Soft-reset the loaded game

ppsspp_screenshot

Capture framebuffer as inline PNG

ppsspp_get_registers

Read all MIPS Allegrex registers

ppsspp_breakpoint_add / _remove / _list

CPU execution breakpoints

PSP memory map (cheat sheet)

Range

Region

0x00010000 - 0x00013FFF

Scratchpad (fast 16 KiB SRAM)

0x04000000 - 0x041FFFFF

VRAM (2 MiB GE video memory)

0x08000000 - 0x087FFFFF

Kernel RAM (8 MiB, low half)

0x08800000 - 0x09FFFFFF

User RAM (24 MiB, where most game state lives)

0xBC000000+

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

PPSSPP_PORT must be set on startup

Set the env var to the port shown in PPSSPP's Developer Tools dialog

WebSocket connection failed

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

Invalid address on memory ops

Address is outside the PSP's mapped regions (user RAM is 0x08800000+, not 0x00000000+)

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 ppsspp_press_button with a longer duration

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 using input.buttons.press to 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 and resume.

  • Analog stick is shared state — ppsspp_send_analog updates the persistent stick position; not auto-released.

Development

npm install
npm run dev      # tsc --watch — autobuilds on src/ changes

Debugging with the MCP Inspector

Browse and call this server's tools interactively with the MCP Inspector:

PPSSPP_PORT=<port> npm run inspector

Build 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

MIT

Available Tools

23 tools
ppsspp_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

    A4.6/5.0

    Scored across 23 tools

    Disambiguation5/5

    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.

    Naming Consistency5/5

    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.

    Tool Count4/5

    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.

    Completeness4/5

    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

    ActivityActive
    ResponsivenessLimited data

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      A
      quality
      B
      maintenance
      MCP 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.
      14
      38 npm
      3
      MIT
    • A
      license
      A
      quality
      B
      maintenance
      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.
      17
      36 npm
      6
      MIT