# https://github.com/dmang-dev/mcp-retroarch 项目说明书

生成时间：2026-05-16 06:53:34 UTC

## 目录

- [Introduction](#page-introduction)
- [Quick Start Guide](#page-quick-start)
- [System Architecture](#page-architecture)
- [Data Flow](#page-data-flow)
- [Memory Read/Write Operations](#page-memory-operations)
- [Savestate Management](#page-savestate-management)
- [Emulation Control](#page-emulation-control)
- [MCP Tools Reference](#page-tools-reference)
- [Core Compatibility](#page-core-compatibility)
- [Configuration](#page-configuration)

<a id='page-introduction'></a>

## Introduction

### 相关页面

相关主题：[System Architecture](#page-architecture), [Quick Start Guide](#page-quick-start)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [README.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)
- [src/index.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/index.ts)
- [src/retroarch.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/retroarch.ts)
- [src/tools.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/tools.ts)
- [package.json](https://github.com/dmang-dev/mcp-retroarch/blob/main/package.json)
- [CHANGELOG.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/CHANGELOG.md)
</details>

# Introduction

mcp-retroarch is a Model Context Protocol (MCP) server that bridges AI assistants (such as Claude) with the RetroArch emulator through its Network Control Interface (NCI). This enables AI-driven automation of retro gaming tasks including memory inspection, save state management, screenshot capture, and frame-by-frame execution control.

## Project Overview

mcp-retroarch exposes RetroArch's NCI functionality as MCP tools, allowing AI agents to:

- Read and write emulated system memory
- Manage save states across slots
- Capture screenshots
- Control emulation execution (pause, frame advance, reset)
- Display notifications within the emulator

**Key characteristics:**

| Attribute | Value |
|-----------|-------|
| Protocol | MCP over stdio |
| Transport to RetroArch | UDP (IPv4) |
| Default UDP port | 55355 |
| Language | TypeScript |
| MCP SDK version | ^1.12.0 |
| License | MIT |

资料来源：[README.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)

## Architecture

The system consists of three primary components communicating across two protocols:

```mermaid
graph TD
    subgraph "MCP Client Layer"
        A[Claude / AI Assistant]
    end
    
    subgraph "mcp-retroarch Process"
        B[MCP SDK<br/>stdio JSON-RPC]
        C[RetroArch Client<br/>src/retroarch.ts]
    end
    
    subgraph "RetroArch"
        D[Network Control<br/>Interface]
    end
    
    A -->|"MCP JSON-RPC"| B
    B -->|Tool calls| C
    C -->|"UDP Datagrams"| D
    D -->|"UDP Response"| C
    C -->|"MCP Response"| B
    B -->|"JSON-RPC"| A
```

### Component Responsibilities

| Component | Role |
|-----------|------|
| MCP Client | Sends JSON-RPC requests via stdio |
| mcp-retroarch | Translates MCP tools to NCI UDP commands |
| RetroArch NCI | Executes commands on the running emulator |

资料来源：[src/index.ts:1-20](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/index.ts)

## How It Works

### Connection Flow

1. **Startup**: The MCP server initializes and creates a UDP socket for RetroArch communication
2. **Background probe**: An asynchronous connection attempt retrieves RetroArch version
3. **Ready signal**: Server signals readiness via stderr and stdout

```mermaid
sequenceDiagram
    participant RA as RetroArch
    participant MCP as mcp-retroarch
    participant Client as MCP Client
    
    MCP->>MCP: Create UDP socket
    Note over MCP: Background probe<br/>(fire-and-forget)
    MCP->>RA: VERSION command
    RA-->>MCP: RetroArch version
    MCP->>Client: Ready notification (stdout)
    Client->>MCP: Tool call (e.g., read_memory)
    MCP->>RA: READ_CORE_MEMORY
    RA-->>MCP: Memory bytes
    MCP-->>Client: Hex dump response
```

资料来源：[src/index.ts:8-17](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/index.ts)

### UDP Communication Model

The RetroArch client implements two communication patterns:

| Pattern | Method | Use Case |
|---------|--------|----------|
| Fire-and-forget | `send(command)` | Pause toggle, reset, screenshot |
| Query/Response | `query(command)` | Memory reads, status queries |

```typescript
// Fire-and-forget: no response expected
async send(command: string): Promise<void>

// Query with timeout: awaits UDP response
async query(command: string): Promise<Buffer>
```

**Timeout behavior**: Queries default to a configurable timeout. If no response arrives, the promise rejects with a timeout error. The client maintains serial request semantics—one query must complete before the next begins.

资料来源：[src/retroarch.ts:43-64](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/retroarch.ts)

### Serial Request Constraint

The implementation enforces serial requests at the protocol level:

```typescript
if (this.pending) {
  throw new Error("retroarch query already in flight (client is serial)");
}
```

This means concurrent tool calls from the MCP client will queue or fail if queries overlap.

资料来源：[src/retroarch.ts:56-59](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/retroarch.ts)

## Prerequisites

Before using mcp-retroarch, ensure:

1. **RetroArch is installed** with Network Commands enabled
2. **A libretro core and game are loaded**
3. **Network Command Interface is active**

### Enabling Network Commands in RetroArch

**GUI Method**:
- Navigate to Settings → Network → Network Commands → ON
- Confirm Network Cmd Port is `55355` (default)

**Configuration File Method** (retroarch.cfg):
```ini
network_cmd_enable = "true"
network_cmd_port   = "55355"
```

资料来源：[README.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)

## Configuration

mcp-retroarch is configured via environment variables:

| Environment Variable | Default | Purpose |
|---------------------|---------|---------|
| `RETROARCH_HOST` | `127.0.0.1` | UDP destination host |
| `RETROARCH_PORT` | `55355` | UDP port (must match `network_cmd_port` in retroarch.cfg) |

资料来源：[README.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)

## Available Tools

The following MCP tools are exposed by mcp-retroarch:

### Memory Operations

| Tool | Purpose | Address Space |
|------|---------|---------------|
| `retroarch_read_memory` | Read bytes from system memory map | libretro memory map |
| `retroarch_write_memory` | Write bytes to system memory | libretro memory map |
| `retroarch_read_ram` | Read bytes via CHEEVOS address space | Achievement space |
| `retroarch_write_ram` | Write bytes via CHEEVOS space | Achievement space |

**Memory read/write limitations**:
- Maximum 4096 bytes per call (NCI single-datagram limit)
- `write_memory` returns byte count; `write_ram` does not acknowledge

### Emulation Control

| Tool | Purpose |
|------|---------|
| `retroarch_pause_toggle` | Toggle pause state |
| `retroarch_frame_advance` | Step exactly one frame |
| `retroarch_reset` | Hard reset the game |

### State Management

| Tool | Purpose |
|------|---------|
| `retroarch_save_state_current` | Save to current slot |
| `retroarch_load_state_current` | Load from current slot |
| `retroarch_load_state_slot` | Load from explicit slot number |
| `retroarch_state_slot_plus` | Increment slot pointer |
| `retroarch_state_slot_minus` | Decrement slot pointer |

### Utility

| Tool | Purpose |
|------|---------|
| `retroarch_screenshot` | Save screenshot to RetroArch's configured directory |
| `retroarch_show_message` | Display notification overlay |
| `retroarch_get_status` | Query current emulation state |
| `retroarch_get_config` | Read a config parameter value |

资料来源：[src/tools.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/tools.ts)

## Address Space Differences

Understanding the distinction between address spaces is critical:

| Address Space | Used By | Notes |
|---------------|---------|-------|
| libretro memory map | `read_memory` / `write_memory` | System-specific layout (e.g., GBA EWRAM at 0x02000000) |
| CHEEVOS space | `read_ram` / `write_ram` | RetroAchievements address conventions |

**Example address mappings**:
- GBA ROM: `0x08000000` (memory map) vs `0x000000` (CHEEVOS)
- SNES WRAM: `0x7E0000-0x7FFFFF` (memory map) vs `0x000000` (CHEEVOS)

资料来源：[src/tools.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/tools.ts)

## Verified Core Support

The following cores have been tested with mcp-retroarch:

| System | Core | read_memory | read_ram | Notes |
|--------|------|-------------|----------|-------|
| Game Boy Advance | `mgba_libretro` | ✅ | ✅ | GBA interrupt vector at 0x0000 visible |
| NES | `mesen_libretro` | ✅ | ✅ | Full 16-bit address space; WRAM at 0x0000-0x07FF |
| NES | `nestopia_libretro` | ❌ | ✅ | No memory map; use read_ram |
| SNES | `snes9x_libretro` | ❌ | — | Memory map not exposed |

资料来源：[README.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)

## Fire-and-Forget Semantics

Most NCI commands do not receive acknowledgments from RetroArch. The response message from mcp-retroarch indicates only that the UDP datagram was sent, not that RetroArch received or acted on it.

**Exceptions**:
- `retroarch_write_memory` — returns byte count (NCI acknowledges this command)
- `retroarch_read_memory` / `retroarch_read_ram` — returns actual data read

**Verification strategy**: For fire-and-forget commands, verify success by:
1. Reading memory back with `read_ram`
2. Checking state with `retroarch_get_status`

资料来源：[CHANGELOG.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/CHANGELOG.md)

## Error Handling

### Common Errors

| Error | Cause | Resolution |
|-------|-------|------------|
| `RetroArch query timed out` | Network Commands disabled or port mismatch | Verify `network_cmd_enable = "true"` and matching ports |
| `READ_CORE_MEMORY failed: no memory map defined` | Core doesn't expose memory map | Use `retroarch_read_ram` instead |
| `READ_CORE_MEMORY failed: no descriptor for address` | Address outside core's memory regions | Try different core or address |

**UDP reliability note**: UDP datagrams may be dropped under load even on loopback. If a single call times out, retry the operation.

资料来源：[README.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)

## Development

### Project Structure

```
mcp-retroarch/
├── src/
│   ├── index.ts        # MCP server entry point
│   ├── retroarch.ts    # UDP client for NCI protocol
│   └── tools.ts        # Tool definitions and handlers
├── package.json
├── tsconfig.json
└── .scratch/
    └── smoke.cjs       # Manual smoke test
```

### Running Locally

```bash
npm install
npm run dev      # tsc --watch (development mode)
node .scratch/smoke.cjs  # Smoke test against running RetroArch
```

资料来源：[README.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md), [package.json](https://github.com/dmang-dev/mcp-retroarch/blob/main/package.json)

## Limitations

| Feature | Status | Notes |
|---------|--------|-------|
| Game-pad input | ❌ Not available | NCI doesn't expose input; see [mcp-mgba](https://github.com/dmang-dev/mcp-mgba) for GBA input |
| Save to specific slot | Limited | Only current slot save; must walk to target slot |
| Screenshot directory | Not configurable via NCI | Check RetroArch GUI for configured path |

资料来源：[README.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)

## Related Projects

| Project | Purpose |
|---------|---------|
| [mcp-mgba](https://github.com/dmang-dev/mcp-mgba) | GBA via mGBA's Lua bridge (includes input + screenshot) |
| [mcp-pine](https://github.com/dmang-dev/mcp-pine) | PCSX2 and PINE-speaking emulators |

资料来源：[README.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)

---

<a id='page-quick-start'></a>

## Quick Start Guide

### 相关页面

相关主题：[Introduction](#page-introduction), [Configuration](#page-configuration)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [README.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)
- [package.json](https://github.com/dmang-dev/mcp-retroarch/blob/main/package.json)
- [src/index.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/index.ts)
- [src/retroarch.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/retroarch.ts)
- [src/tools.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/tools.ts)
- [CHANGELOG.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/CHANGELOG.md)
</details>

# Quick Start Guide

This guide walks you through setting up **mcp-retroarch**, a Model Context Protocol (MCP) server that enables AI assistants to interact with RetroArch through its Network Command Interface (NCI). The integration provides memory inspection, savestate management, screenshot capture, and emulator control via JSON-RPC over stdio.

## Overview

mcp-retroarch bridges AI coding assistants (like Claude Code or Claude Desktop) with RetroArch by:

- Exposing RetroArch NCI commands as MCP tools
- Communicating over stdio (JSON-RPC) with the MCP client
- Sending UDP datagrams to RetroArch's Network Command Interface on port 55355

资料来源：[README.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)

## System Architecture

```mermaid
graph TD
    A["MCP Client<br/>(Claude Code/Desktop)"] -->|stdio JSON-RPC| B["mcp-retroarch<br/>MCP Server"]
    B -->|UDP :55355| C["RetroArch<br/>Network Commands"]
    C -->|Emulator Control| D["Libretro Core<br/>+ Game ROM"]
    
    B -->|Background Probe| C
    
    style A fill:#e1f5fe
    style B fill:#fff3e0
    style C fill:#e8f5e9
```

资料来源：[src/index.ts:8-14](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/index.ts), [src/retroarch.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/retroarch.ts)

## Prerequisites

| Requirement | Details |
|-------------|---------|
| Node.js | Version compatible with `@modelcontextprotocol/sdk` ^1.12.0 |
| RetroArch | Version with Network Command Interface enabled |
| MCP Client | Claude Code or Claude Desktop |
| Network Commands | Must be enabled in RetroArch |

资料来源：[package.json:20-26](https://github.com/dmang-dev/mcp-retroarch/blob/main/package.json), [README.md:40-50](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)

## Step 1: Install mcp-retroarch

### From npm (Recommended)

```bash
npm install -g mcp-retroarch
```

This makes the `mcp-retroarch` command globally available.

资料来源：[package.json:1-10](https://github.com/dmang-dev/mcp-retroarch/blob/main/package.json)

### From Source

```bash
git clone https://github.com/dmang-dev/mcp-retroarch.git
cd mcp-retroarch
npm install
npm run build   # or use npm run dev for watch mode
```

资料来源：[README.md:100-105](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)

## Step 2: Enable Network Commands in RetroArch

RetroArch's NCI must be enabled before mcp-retroarch can communicate with it.

### Via GUI

1. Open RetroArch
2. Navigate to **Settings → Network → Network Commands**
3. Set **Network Commands** to **ON**
4. Confirm **Network Cmd Port** is `55355` (default)

### Via Configuration File

Edit `retroarch.cfg` (located in RetroArch's config directory):

```ini
network_cmd_enable = "true"
network_cmd_port   = "55355"
```

资料来源：[README.md:40-50](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)

### Verify Setup

Launch RetroArch and load any libretro core with a game. The Network Command Interface is always-on once enabled—no additional scripts required.

## Step 3: Register with Your MCP Client

### Claude Code

Register retroarch as a user-level MCP server:

```bash
claude mcp add retroarch --scope user mcp-retroarch
```

Verify registration:

```bash
claude mcp list
# retroarch: mcp-retroarch - ✓ Connected
```

资料来源：[README.md:52-60](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)

### Claude Desktop

Edit the platform-specific configuration file:

| Platform | Config Path |
|----------|-------------|
| macOS | `~/Library/Application Support/Claude/claude_desktop_config.json` |
| Windows | `%APPDATA%\Claude\claude_desktop_config.json` |
| Linux | `~/.config/Claude/claude_desktop_config.json` |

Add the `mcpServers` section:

```json
{
  "mcpServers": {
    "retroarch": {
      "command": "mcp-retroarch"
    }
  }
}
```

Restart Claude Desktop after editing.

资料来源：[README.md:62-78](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)

## Step 4: Configure Environment Variables

| Environment Variable | Default | Purpose |
|---------------------|---------|---------|
| `RETROARCH_HOST` | `127.0.0.1` | UDP destination host |
| `RETROARCH_PORT` | `55355` | UDP port (must match `network_cmd_port` in retroarch.cfg) |

Set these before running Claude Code or Claude Desktop if your RetroArch runs on a different host or port:

```bash
export RETROARCH_HOST=192.168.1.100
export RETROARCH_PORT=55355
```

资料来源：[README.md:80-85](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)

## Available Tools

Once connected, the following MCP tools become available:

### Emulator Control

| Tool | Purpose |
|------|---------|
| `retroarch_get_status` | Query running state, system, game, CRC32 |
| `retroarch_pause_toggle` | Toggle pause on/off |
| `retroarch_frame_advance` | Advance exactly one frame |
| `retroarch_reset` | Hard reset the running game |
| `retroarch_show_message` | Display notification on RetroArch window |

### Memory Operations

| Tool | Description |
|------|-------------|
| `retroarch_read_memory` | Read from libretro system memory map |
| `retroarch_write_memory` | Write to libretro system memory map |
| `retroarch_read_ram` | Read from CHEEVOS (achievements) address space |
| `retroarch_write_ram` | Write to CHEEVOS address space |

### Savestate Management

| Tool | Purpose |
|------|---------|
| `retroarch_save_state_current` | Save to currently-selected slot |
| `retroarch_load_state_current` | Load from currently-selected slot |
| `retroarch_load_state_slot` | Load from explicit slot number |
| `retroarch_state_slot_plus` | Increment slot pointer |
| `retroarch_state_slot_minus` | Decrement slot pointer |

### Media & Config

| Tool | Purpose |
|------|---------|
| `retroarch_screenshot` | Save screenshot to RetroArch's screenshot directory |
| `retroarch_get_config` | Read a RetroArch configuration parameter |

资料来源：[README.md:1-30](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md), [src/tools.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/tools.ts)

## Step 5: Smoke Test

Verify connectivity by running the smoke test script against a running RetroArch:

```bash
node .scratch/smoke.cjs
```

资料来源：[README.md:107-110](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)

## First Commands to Try

### Check Emulator Status

```
retroarch_get_status
```

Expected output:
```
State:  playing
System: snes9x_libretro
Game:   Super Metroid (USA).sfc
CRC32:  12345678
```

### Pause and Read Memory

```
retroarch_pause_toggle
retroarch_read_memory(address=0x7E0000, length=16)
```

### Take a Screenshot

```
retroarch_screenshot
```

资料来源：[src/tools.ts:1-50](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/tools.ts)

## Verified Core Compatibility

| System | Core | `read_memory` | `read_ram` | Notes |
|--------|------|---------------|------------|-------|
| Game Boy Advance | `mgba_libretro` | ✅ | ✅ | GBA interrupt vector at `0x0000` |
| NES | `mesen_libretro` | ✅ | ✅ | Full 16-bit NES address space |
| NES | `nestopia_libretro` | ❌ | ✅ | CHEEVOS only |
| SNES | `snes9x_libretro` | ❌ | — | Memory map not exposed |

资料来源：[README.md:30-45](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)

## Troubleshooting

| Symptom | Cause / Fix |
|---------|-------------|
| `RetroArch query timed out` | Network Commands not enabled, or port mismatch. Confirm `network_cmd_enable = "true"` in retroarch.cfg |
| `READ_CORE_MEMORY failed: no memory map defined` | Core doesn't advertise memory map. Try `retroarch_read_ram` as fallback |
| `READ_CORE_MEMORY failed: no descriptor for address` | Address outside core's memory regions |
| Screenshots don't appear | Check RetroArch's screenshot directory via Settings → Directory → Screenshot |
| Can't save to specific slot | NCI limitation—use `state_slot_plus`/`state_slot_minus` to walk to target slot |

资料来源：[README.md:115-130](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)

## Next Steps

- Review [docs/RECIPES.md](docs/RECIPES.md) for end-to-end examples
- Explore related MCP servers: [mcp-mgba](https://github.com/dmang-dev/mcp-mgba) for GBA input + screenshots
- Consult [RetroArch NCI documentation](https://docs.libretro.com/development/retroarch/network-control-interface/) for protocol details

资料来源：[README.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)

---

<a id='page-architecture'></a>

## System Architecture

### 相关页面

相关主题：[Introduction](#page-introduction), [Data Flow](#page-data-flow)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [src/index.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/index.ts)
- [src/retroarch.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/retroarch.ts)
- [src/tools.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/tools.ts)
- [README.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)
- [package.json](https://github.com/dmang-dev/mcp-retroarch/blob/main/package.json)
</details>

# System Architecture

## Overview

mcp-retroarch is a Model Context Protocol (MCP) server that bridges MCP clients (such as Claude Code or Claude Desktop) with RetroArch's Network Control Interface (NCI). It enables AI assistants to interact with running emulated games through memory inspection, state manipulation, and emulator control.

**资料来源：** [README.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)

## High-Level Architecture

The system follows a three-layer architecture:

```mermaid
graph TD
    subgraph "Client Layer"
        MCP[MCP Client<br/>Claude Code / Claude Desktop]
    end

    subgraph "Bridge Layer"
        MCP_Server[MCP Server<br/>mcp-retroarch]
        Tools[MCP Tools<br/>tools.ts]
        Transport[UDP Transport<br/>retroarch.ts]
    end

    subgraph "Target Layer"
        RA[RetroArch<br/>Running Emulator]
        NCI[Network Control<br/>Interface]
    end

    MCP -->|"stdio JSON-RPC"| MCP_Server
    MCP_Server --> Tools
    Tools --> Transport
    Transport -->|"UDP :55355"| NCI
    NCI --> RA
```

**资料来源：** [README.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)

## Component Architecture

### Layer 1: MCP Server Entry Point

**File:** `src/index.ts`

The main entry point initializes the MCP server using the `@modelcontextprotocol/sdk` package and establishes a background connectivity probe to RetroArch.

```typescript
// Simplified initialization flow
const server = new Server(
  { name: "mcp-retroarch", version: "0.1.2" },
  { capabilities: { tools: {} } }
);

ra.connect()
  .then(() => ra.getVersion())
  .then((v) => process.stderr.write(`[mcp-retroarch] connected to ${ra.describeTarget()} — RetroArch ${v}\n`))
  .catch((err) => process.stderr.write(`[mcp-retroarch] note: RetroArch not reachable yet...\n`));
```

**Key responsibilities:**
- Initialize MCP server with stdio transport
- Register all tool handlers
- Perform fire-and-forget connectivity probe on startup
- Handle fatal errors gracefully

**资料来源：** [src/index.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/index.ts)

### Layer 2: UDP Transport Module

**File:** `src/retroarch.ts`

The transport layer manages UDP socket communication with RetroArch's NCI. It implements a serial query pattern where only one request can be in-flight at a time.

```mermaid
sequenceDiagram
    participant Client as MCP Client
    participant Server as MCP Server
    participant Transport as UDP Transport
    participant RA as RetroArch NCI

    Client->>Server: tool_call
    Server->>Transport: send(query)
    Transport->>RA: UDP datagram
    RA-->>Transport: UDP response
    Transport-->>Server: Buffer
    Server-->>Client: JSON response
```

**Connection Management:**

| Method | Purpose |
|--------|---------|
| `connect()` | Initialize UDP socket, bind to random port |
| `disconnect()` | Close socket and cleanup |
| `send(command)` | Fire-and-forget send (for hotkey commands) |
| `query(command)` | Send and await response with timeout |

**Configuration parameters:**

| Parameter | Default | Description |
|-----------|---------|-------------|
| `RETROARCH_HOST` | `127.0.0.1` | UDP destination host |
| `RETROARCH_PORT` | `55355` | UDP port (must match `network_cmd_port` in `retroarch.cfg`) |

**资料来源：** [src/retroarch.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/retroarch.ts), [README.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)

### Layer 3: MCP Tools Definition

**File:** `src/tools.ts`

All available MCP tools are defined with JSON Schema input validation and descriptive documentation following a PURPOSE / USAGE / BEHAVIOR / RETURNS template.

**Tool Categories:**

| Category | Tools |
|----------|-------|
| **Memory** | `retroarch_read_memory`, `retroarch_write_memory`, `retroarch_read_ram`, `retroarch_write_ram` |
| **State** | `retroarch_save_state_current`, `retroarch_load_state_current`, `retroarch_load_state_slot`, `retroarch_state_slot_plus`, `retroarch_state_slot_minus` |
| **Control** | `retroarch_pause_toggle`, `retroarch_frame_advance`, `retroarch_reset` |
| **Media** | `retroarch_screenshot`, `retroarch_show_message` |
| **Info** | `retroarch_get_status`, `retroarch_get_config`, `retroarch_get_version` |

**资料来源：** [src/tools.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/tools.ts)

## Data Flow

### Memory Read Flow

```mermaid
graph LR
    A[Client:<br/>retroarch_read_memory] --> B[Validate:<br/>address, length]
    B --> C[Send:<br/>READ_CORE_MEMORY]
    C --> D{RetroArch<br/>Memory Map<br/>Available?}
    D -->|Yes| E[Return bytes<br/>via UDP]
    D -->|No| F[Error:<br/>no memory map defined]
    E --> G[Format hex dump<br/>ADDR [N bytes]: XX XX...]
```

### Memory Write Flow

```mermaid
graph LR
    A[Client:<br/>retroarch_write_memory] --> B[Validate:<br/>address, bytes array]
    B --> C[Send:<br/>WRITE_CORE_MEMORY]
    C --> D[RetroArch<br/>Acknowledges<br/>byte count]
    D --> E[Return count<br/>to client]
```

**资料来源：** [src/retroarch.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/retroarch.ts), [src/tools.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/tools.ts)

## Command Mapping

The following table shows how MCP tools map to RetroArch NCI commands:

| MCP Tool | NCI Command | Response Pattern |
|----------|-------------|-------------------|
| `retroarch_get_version` | `VERSION` | String version |
| `retroarch_get_status` | `GET_STATUS` | `GET_STATUS {state} {system},{game},crc32={crc}` |
| `retroarch_read_memory` | `READ_CORE_MEMORY {addr} {len}` | Binary bytes |
| `retroarch_write_memory` | `WRITE_CORE_MEMORY {addr} {bytes}` | `{count} bytes written` |
| `retroarch_read_ram` | `READ_CORE_RAM {addr} {len}` | Binary bytes (CHEEVOS) |
| `retroarch_write_ram` | `WRITE_CORE_RAM {addr} {bytes}` | Fire-and-forget |
| `retroarch_pause_toggle` | `PAUSE_TOGGLE` | Fire-and-forget |
| `retroarch_frame_advance` | `FRAMEADVANCE` | Fire-and-forget |
| `retroarch_reset` | `RESET` | Fire-and-forget |
| `retroarch_save_state_current` | `SAVE_STATE` | Fire-and-forget |
| `retroarch_load_state_current` | `LOAD_STATE` | Fire-and-forget |
| `retroarch_load_state_slot` | `LOAD_STATE {slot}` | Fire-and-forget |
| `retroarch_screenshot` | `SCREENSHOT` | Fire-and-forget |
| `retroarch_show_message` | `SET_MESSAGE "{msg}"` | Fire-and-forget |

**资料来源：** [src/retroarch.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/retroarch.ts)

## Transport Characteristics

### Serial Query Pattern

The transport layer enforces serial query execution:

```typescript
async query(command: string): Promise<Buffer> {
  if (!this.socket) await this.connect();
  if (this.pending) {
    throw new Error("retroarch query already in flight (client is serial)");
  }
  // ... timeout handling
}
```

**Key behaviors:**
- Only one UDP query can be in-flight at a time
- Queries timeout after configurable `timeoutMs` (default: 5000ms)
- Fire-and-forget commands (`send()`) bypass the serial queue

### Error Handling

| Error Condition | Cause | Resolution |
|-----------------|-------|------------|
| `RetroArch query timed out` | Network Commands not enabled or port mismatch | Verify `network_cmd_enable = "true"` in `retroarch.cfg` |
| `no memory map defined` | Core doesn't advertise system memory map | Use `retroarch_read_ram` as fallback |
| `no descriptor for address` | Address outside core's memory regions | Use different core or address |
| `query already in flight` | Multiple concurrent queries attempted | Wait for previous query to complete |

**资料来源：** [README.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md), [src/retroarch.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/retroarch.ts)

## Memory Address Spaces

mcp-retroarch exposes two distinct memory address spaces:

### System Memory Map (`read_memory` / `write_memory`)

Uses `READ_CORE_MEMORY` / `WRITE_CORE_MEMORY` via the libretro core's system memory map.

| System | Typical Address Range |
|--------|------------------------|
| GBA EWRAM | `0x02000000` - `0x0203FFFF` |
| SNES WRAM | `0x7E0000` - `0x7FFFFF` |
| Genesis 68K RAM | `0xFF0000` - `0xFFFFFF` |

### CHEEVOS Address Space (`read_ram` / `write_ram`)

Uses `READ_CORE_RAM` / `WRITE_CORE_RAM` via the RetroAchievements address space. Uses different conventions per system (e.g., SNES WRAM starts at `0x000000` in CHEEVOS space).

**资料来源：** [src/tools.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/tools.ts)

## Dependencies

**File:** `package.json`

| Dependency | Version | Purpose |
|------------|---------|---------|
| `@modelcontextprotocol/sdk` | `^1.12.0` | MCP server implementation |
| `@types/node` | `^22.0.0` | TypeScript definitions |
| `typescript` | `^5.5.0` | Build tooling |

**资料来源：** [package.json](https://github.com/dmang-dev/mcp-retroarch/blob/main/package.json)

## Tested Cores Compatibility

| System | Core | `read_memory` | `read_ram` | Notes |
|--------|------|:------------:|:----------:|-------|
| Game Boy Advance | `mgba_libretro` | ✅ | ✅ | GBA interrupt vector at `0x0000` |
| NES | `mesen_libretro` | ✅ | ✅ | Full 16-bit address space |
| NES | `nestopia_libretro` | ❌ | ✅ | CHEEVOS only |
| SNES | `snes9x_libretro` | ❌ | N/A | Memory map not exposed |
| PSX | `swanstation_libretro` | ❌ | ✅ | Use `read_ram` |

**资料来源：** [README.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)

## Project Structure

```
mcp-retroarch/
├── src/
│   ├── index.ts       # MCP server entry point
│   ├── retroarch.ts  # UDP transport & NCI protocol
│   └── tools.ts      # Tool definitions & handlers
├── docs/
│   └── RECIPES.md     # End-to-end usage examples
├── package.json
├── tsconfig.json
└── README.md
```

**资料来源：** [README.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)

---

<a id='page-data-flow'></a>

## Data Flow

### 相关页面

相关主题：[System Architecture](#page-architecture), [Memory Read/Write Operations](#page-memory-operations)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [src/retroarch.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/retroarch.ts)
- [src/tools.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/tools.ts)
- [src/index.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/index.ts)
- [README.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)
- [CHANGELOG.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/CHANGELOG.md)
</details>

# Data Flow

This page documents the complete data flow through the mcp-retroarch system, from MCP client requests through UDP communication with RetroArch's Network Control Interface (NCI).

## Architecture Overview

mcp-retroarch bridges two distinct communication protocols:

| Layer | Transport | Protocol |
|-------|-----------|----------|
| MCP Client ↔ mcp-retroarch | stdio | JSON-RPC 2.0 |
| mcp-retroarch ↔ RetroArch | UDP (port 55355) | NCI text commands |

资料来源：[README.md:1-50]()

```mermaid
graph TB
    subgraph "MCP Layer (stdio)"
        A["MCP Client<br/>(Claude Code, Claude Desktop)"]
    end
    
    subgraph "mcp-retroarch Process"
        B["src/index.ts<br/>MCP Server Entry"]
        C["src/tools.ts<br/>Tool Handlers"]
        D["src/retroarch.ts<br/>RetroArch Client"]
    end
    
    subgraph "RetroArch"
        E["Network Control Interface<br/>(UDP :55355)"]
    end
    
    A -->|"JSON-RPC 2.0<br/>stdio"| B
    B -->|routes| C
    C -->|invokes methods| D
    D -->|"NCI commands<br/>UDP"| E
    E -->|"UDP response"| D
```

## Request-Response Flow

### 1. MCP Request Ingress

When an MCP client invokes a tool (e.g., `retroarch_read_memory`), the following occurs:

1. The client sends a JSON-RPC 2.0 request via stdio
2. `src/index.ts` receives the request and routes it to the appropriate tool handler in `src/tools.ts`
3. The tool handler validates parameters against the input schema

资料来源：[src/tools.ts:1-100]()

### 2. Tool Handler Processing

Each tool maps to a specific case in the tool handler switch statement:

```typescript
case "retroarch_read_memory": {
  const bytes = await ra.readMemory(p.address as number, p.length as number);
  const hex = Array.from(bytes).map((b) => b.toString(16).padStart(2, "0").toUpperCase()).join(" ");
  return ok(`${addrHex(p.address as number)} [${bytes.length} bytes]:\n${hex}`);
}
```

资料来源：[src/tools.ts:20-30]()

### 3. UDP Query Execution

The `RetroArch` class in `src/retroarch.ts` handles all network communication:

```mermaid
sequenceDiagram
    participant MCP as MCP Client
    participant Server as mcp-retroarch
    participant Socket as UDP Socket
    participant RA as RetroArch NCI
    
    MCP->>Server: retroarch_read_memory(0x02000000, 256)
    Server->>Socket: send("READ_CORE_MEMORY 02000000 0100")
    Socket->>RA: UDP datagram to 127.0.0.1:55355
    RA-->>Socket: "READ_CORE_MEMORY OK [256 bytes]"
    Socket-->>Server: Buffer response
    Server-->>MCP: JSON-RPC response with hex dump
```

资料来源：[src/retroarch.ts:1-50]()

## Socket Communication Model

### Connection Lifecycle

```mermaid
graph LR
    A[Lazy Connect] -->|on first query| B[Socket Created]
    B --> C[bind to random port]
    C --> D[ready for send/receive]
    D --> E[on tool call]
    E --> F[send UDP datagram]
    F --> G[wait for response]
    G -->|timeout| H[Error: query timed out]
    G -->|response| I[resolve promise]
```

资料来源：[src/retroarch.ts:20-45]()

### Query Serialization

The `query()` method enforces serial execution:

```typescript
async query(command: string): Promise<Buffer> {
  if (!this.socket) await this.connect();
  if (this.pending) {
    throw new Error("retroarch query already in flight (client is serial)");
  }
  return new Promise<Buffer>((resolve, reject) => {
    let timer: NodeJS.Timeout | null = setTimeout(() => {
      this.pending = null;
      reject(new Error(
        `RetroArch query "${command.split(" ")[0]}" timed out after ${this.timeoutMs}ms ` +
        `— is RetroArch running with Network Commands enabled?`,
      ));
    }, this.timeoutMs);

    this.pending = (data) => {
      if (timer) { clearTimeout(timer); timer = null; }
      resolve(data);
    };
    // ...
  });
}
```

资料来源：[src/retroarch.ts:70-95]()

**Key characteristics:**
- **Lazy connection**: Socket is created on first query, not at startup
- **Serial queries**: Only one pending query at a time (prevents response ambiguity)
- **Timeout handling**: Configurable timeout with cleanup on resolution or expiry

### Fire-and-Forget Commands

Some commands use `send()` instead of `query()` for hotkey-style operations:

```typescript
async send(command: string): Promise<void> {
  if (!this.socket) await this.connect();
  return new Promise((resolve, reject) => {
    this.socket!.send(command, this.port, this.host, (err) =>
      err ? reject(err) : resolve(),
    );
  });
}
```

These commands do not wait for or expect a response from RetroArch:
- `retroarch_pause_toggle`
- `retroarch_reset`
- `retroarch_screenshot`

资料来源：[src/retroarch.ts:55-68]()

## Memory Read/Write Data Paths

### Two Memory APIs

mcp-retroarch exposes two distinct memory access paths:

```mermaid
graph TD
    A[Memory Request] --> B{Which API?}
    B -->|System Memory Map| C[READ_CORE_MEMORY<br/>WRITE_CORE_MEMORY]
    B -->|CHEEVOS Space| D[READ_CORE_RAM<br/>WRITE_CORE_RAM]
    
    C --> E[Full system bus access]
    C --> F[Precise memory regions]
    C --> G[Returns byte count on write]
    
    D --> H[Achievement address space]
    D --> I[Limited to 64KB on some cores]
    D --> J[No acknowledgment on write<br/>"fire-and-forget"]
```

资料来源：[src/tools.ts:100-200]()

### Memory Read Flow

1. **Tool handler** receives `address` and `length` parameters
2. **RetroArch client** calls `readMemory()` or `readRam()`
3. **UDP query** sent with address and byte count
4. **Response parsing** extracts bytes from NCI response
5. **Hex encoding** converts bytes to space-separated hex string

```typescript
case "retroarch_read_memory": {
  const bytes = await ra.readMemory(p.address as number, p.length as number);
  const hex = Array.from(bytes).map((b) => b.toString(16).padStart(2, "0").toUpperCase()).join(" ");
  return ok(`${addrHex(p.address as number)} [${bytes.length} bytes]:\n${hex}`);
}
```

### Memory Write Flow

| API | Acknowledgment | Use Case |
|-----|----------------|----------|
| `write_memory` | Yes (returns byte count) | Precise writes, cheats |
| `write_ram` | No (fire-and-forget) | Fallback when memory map unavailable |

资料来源：[src/tools.ts:30-50]()

## Error Flow

```mermaid
graph TD
    A[Tool Call] --> B{UDP Send Success?}
    B -->|No| C[Reject with send error]
    B -->|Yes| D{Ack received?}
    D -->|No| E[Timeout after 5000ms]
    E --> F["Error: RetroArch query timed out"]
    D -->|Yes| G{NCI Response OK?}
    G -->|No| H[Parse error or invalid response]
    G -->|Yes| I[Return success data]
    
    C --> J[Error logged to stderr]
    F --> J
    H --> J
    I --> K[JSON-RPC response to MCP client]
```

资料来源：[src/retroarch.ts:80-90]()

### Error Scenarios

| Error | Cause | User Message |
|-------|-------|--------------|
| `RetroArch query timed out` | Network Commands disabled or wrong port | Check `network_cmd_enable` in retroarch.cfg |
| `no memory map defined` | Core doesn't expose system memory map | Use `read_ram` / `write_ram` instead |
| `no descriptor for address` | Address outside core's memory regions | Use different address or core |
| `retroarch query already in flight` | Concurrent query attempted | Tool calls are serialized |

资料来源：[README.md:troubleshooting]()

## Response Data Format

### Memory Read Response

```
ADDR_HEX [N bytes]:
AB CD EF 12 34 56 78 9A ...
```

Example:
```
0x02000000 [16 bytes]:
03 D0 00 EA 03 D1 00 EA 03 D2 00 EA 03 D3 00 EA
```

### Status Response

```
State:  playing
System: snes
Game:   Super Mario World (USA).sfc
CRC32:  1A9FBD77
```

### Config Response

```
KEY = VALUE
```

## Configuration and Environment

```mermaid
graph LR
    subgraph "Environment Variables"
        RA_HOST["RETROARCH_HOST<br/>default: 127.0.0.1"]
        RA_PORT["RETROARCH_PORT<br/>default: 55355"]
    end
    
    subgraph "Runtime Config"
        SOCKET["UDP Socket"]
        TIMEOUT["timeoutMs<br/>default: 5000"]
    end
    
    RA_HOST -->|target IP| SOCKET
    RA_PORT -->|target port| SOCKET
```

| Variable | Default | Purpose |
|----------|---------|---------|
| `RETROARCH_HOST` | `127.0.0.1` | UDP destination host |
| `RETROARCH_PORT` | `55355` | UDP port (must match `network_cmd_port` in retroarch.cfg) |

资料来源：[README.md:configuration]()

## Startup Connectivity Probe

The server performs a non-blocking connectivity check on startup:

```typescript
ra.connect()
  .then(() => ra.getVersion())
  .then((v) => process.stderr.write(`[mcp-retroarch] connected to ${ra.describeTarget()} — RetroArch ${v}\n`))
  .catch((err) => process.stderr.write(
    `[mcp-retroarch] note: RetroArch not reachable yet (${ra.describeTarget()}): ${err}\n` +
    `             Enable Network Commands in retroarch.cfg...\n`,
  ));
```

This probe is **fire-and-forget** and never blocks MCP server readiness.

资料来源：[src/index.ts:1-20]()

## Tool-to-Command Mapping

| MCP Tool | NCI Command | Transport | Expects Response |
|----------|-------------|-----------|------------------|
| `retroarch_get_status` | `GET_STATUS` | query | Yes |
| `retroarch_get_config` | `GET_CONFIG_PARAM` | query | Yes |
| `retroarch_read_memory` | `READ_CORE_MEMORY` | query | Yes |
| `retroarch_read_ram` | `READ_CORE_RAM` | query | Yes |
| `retroarch_write_memory` | `WRITE_CORE_MEMORY` | query | Yes |
| `retroarch_write_ram` | `WRITE_CORE_RAM` | send | No |
| `retroarch_save_state_current` | `SAVE_STATE` | send | No |
| `retroarch_load_state_current` | `LOAD_STATE` | send | No |
| `retroarch_load_state_slot` | `LOAD_STATE` | send | No |
| `retroarch_pause_toggle` | `PAUSE_TOGGLE` | send | No |
| `retroarch_frame_advance` | `FRAMEADVANCE` | send | No |
| `retroarch_reset` | `RESET` | send | No |
| `retroarch_screenshot` | `SCREENSHOT` | send | No |
| `retroarch_show_message` | `SHOW_MSG` | send | No |

资料来源：[src/tools.ts:1-300]()
资料来源：[src/retroarch.ts:100-200]()

## Non-Blocking Architecture

Unlike earlier versions, the MCP server does not wait for RetroArch connectivity on startup:

| Version | Behavior |
|---------|----------|
| ≤ 0.1.0 | Blocked ~5s on VERSION query timeout |
| ≥ 0.1.1 | Non-blocking, probe runs in background |

This change eliminated startup delay when RetroArch is not running.

资料来源：[CHANGELOG.md:0.1.1]()

## Summary

The data flow through mcp-retroarch follows a clear layered architecture:

1. **MCP Layer**: JSON-RPC over stdio for client-server communication
2. **Tool Layer**: Parameter validation and response formatting in `src/tools.ts`
3. **Transport Layer**: UDP socket management in `src/retroarch.ts`
4. **NCI Layer**: RetroArch Network Control Interface commands

The design prioritizes:
- **Serial query execution** to prevent response ambiguity
- **Lazy connection** to avoid startup blocking
- **Fire-and-forget** for hotkey commands to reduce latency
- **Explicit acknowledgment** for writes that support it

---

<a id='page-memory-operations'></a>

## Memory Read/Write Operations

### 相关页面

相关主题：[Core Compatibility](#page-core-compatibility), [MCP Tools Reference](#page-tools-reference)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [src/tools.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/tools.ts)
- [src/retroarch.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/retroarch.ts)
</details>

# Memory Read/Write Operations

## Overview

The mcp-retroarch project exposes memory read/write functionality through RetroArch's Network Control Interface (NCI), enabling MCP clients to inspect and mutate the emulated system's memory in real-time. This capability is fundamental for debugging, cheat implementation, game state inspection, and scripted automation.

Memory operations target the emulated system—not the host machine—and support two distinct address space APIs depending on the loaded libretro core's capabilities.

## Architecture

Memory operations flow through a serial UDP transport layer to RetroArch's NCI endpoint:

```mermaid
graph TD
    A["MCP Client<br/>retroarch_read_memory<br/>retroarch_write_ram"] --> B["mcp-retroarch<br/>src/tools.ts"]
    B --> C["RetroArchClient<br/>src/retroarch.ts"]
    C -->|UDP :55355| D["RetroArch NCI"]
    D -->|Memory Map API<br/>or CHEEVOS API| E["libretro Core<br/>Emulated System Memory"]
    
    F["READ_CORE_MEMORY<br/>WRITE_CORE_MEMORY"] -.->|system bus| E
    G["READ_CORE_RAM<br/>WRITE_CORE_RAM"] -.->|CHEEVOS space| E
```

### Transport Layer

The UDP socket is initialized in `src/retroarch.ts` with the following behavior:

- **Query mode** (`query()`): Sends a command and awaits exactly one UDP response, throwing on timeout (~5 seconds)
- **Fire-and-forget mode** (`send()`): Sends a command without waiting for acknowledgment

```typescript
// Serial query with timeout: src/retroarch.ts:60-85
async query(command: string): Promise<Buffer> {
  if (!this.socket) await this.connect();
  if (this.pending) {
    throw new Error("retroarch query already in flight (client is serial)");
  }
  return new Promise<Buffer>((resolve, reject) => {
    let timer: NodeJS.Timeout | null = setTimeout(() => {
      this.pending = null;
      reject(new Error(
        `RetroArch query "${command.split(" ")[0]}" timed out after ${this.timeoutMs}ms ` +
        `— is RetroArch running with Network Commands enabled?`,
      ));
    }, this.timeoutMs);
    // ...
  });
}
```

资料来源：[src/retroarch.ts:60-75]()

## Two Memory APIs

mcp-retroarch exposes two independent memory interfaces, each mapping to a different RetroArch NCI command family:

| Aspect | `_memory` API | `_ram` API |
|--------|--------------|------------|
| NCI Command | `READ_CORE_MEMORY` / `WRITE_CORE_MEMORY` | `READ_CORE_RAM` / `WRITE_CORE_RAM` |
| Address Space | System memory bus | CHEEVOS (achievement) space |
| Core Requirement | Core must expose memory map | Works via CHEEVOS interface |
| Fallback | Falls back to `_ram` if "no memory map defined" | N/A (always available on supported cores) |
| Write Acknowledgment | ✅ Returns byte count | ❌ Fire-and-forget |

资料来源：[src/tools.ts:1-150]()

### System Memory Map (`_memory`)

The `_memory` tools use the libretro core's exposed system memory descriptors. This is the **preferred** approach when available because:

- Addresses follow the native system bus layout (e.g., SNES WRAM at `0x7E0000-0x7FFFFF`)
- `WRITE_CORE_MEMORY` returns acknowledgment with actual byte count written
- Read-only descriptors are honored (write stops at boundary)

### CHEEVOS Address Space (`_ram`)

The `_ram` tools use RetroAchievements' address space conventions, which differ from the native system bus:

| System | CHEEVOS WRAM Start | System Bus WRAM Start |
|--------|-------------------|----------------------|
| SNES | `0x000000` | `0x7E0000` |
| GBA | `0x03000000` | `0x02000000` (EWRAM) |

资料来源：[src/tools.ts:95-120]()

## Tool Reference

### `retroarch_read_memory`

Read bytes via the libretro core's system memory map.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `address` | integer | ✅ | Starting address in system memory map |
| `length` | integer | ✅ | Bytes to read (1-4096) |

**Returns:** Header line `ADDR_HEX [N bytes]:` followed by space-separated uppercase hex bytes.

**Error Conditions:**
- `"no memory map defined"` — Core doesn't expose system memory map
- `"no descriptor for address"` — Address outside core's memory regions
- Timeout after ~5 seconds

资料来源：[src/tools.ts:70-100]()

### `retroarch_write_memory`

Write bytes via the libretro core's system memory map. **This is the only write tool that returns acknowledgment.**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `address` | integer | ✅ | Starting address in system memory map |
| `bytes` | integer[] | ✅ | Byte values 0-255 (max 4096) |

**Returns:** `Wrote N bytes → ADDR_HEX` where N is confirmed written count.

**Side Effects:**
- Disables RetroArch's hardcore mode for the session
- Destructive (no undo; use `retroarch_save_state_current` first)

资料来源：[src/tools.ts:100-140]()

### `retroarch_read_ram`

Read bytes via the CHEEVOS (achievement) address space.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `address` | integer | ✅ | Starting address in CHEEVOS space |
| `length` | integer | ✅ | Bytes to read (1-4096) |

**Returns:** Header line `ADDR_HEX [N bytes, CHEEVOS]:` followed by hex dump.

**Note:** RetroArch may return fewer bytes than requested at memory-region boundaries.

资料来源：[src/tools.ts:120-150]()

### `retroarch_write_ram`

Write bytes via the CHEEVOS address space. **Fire-and-forget—no acknowledgment.**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `address` | integer | ✅ | Starting address in CHEEVOS space |
| `bytes` | integer[] | ✅ | Byte values 0-255 (max 4096) |

**Returns:** `Wrote N bytes → ADDR_HEX (CHEEVOS, no ack)`

**Verification:** Follow up with `retroarch_read_ram` at the same address to confirm the write landed.

资料来源：[src/tools.ts:150-180]()

## Usage Workflow

### Read Memory (System Bus)

```typescript
// Read 16 bytes from SNES WRAM at 0x7E0000
const result = await tools.retroarch_read_memory({
  address: 0x7E0000,
  length: 16
});
// Returns: "7E0000 [16 bytes]:
//          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"
```

### Write Memory with Verification

```mermaid
graph LR
    A["Save State<br/>retroarch_save_state_current"] --> B["Write Memory<br/>retroarch_write_memory"]
    B --> C{"Response received?"}
    C -->|Yes| D["Read Back<br/>retroarch_read_memory"]
    D --> E["Verify bytes match"]
    C -->|No/Timeout| F["Load State<br/>retroarch_load_state_current"]
    E -->|Mismatch| F
```

### Write RAM (CHEEVOS) with Verification

```mermaid
graph LR
    A["Save State"] --> B["Write RAM<br/>retroarch_write_ram"]
    B --> C["Read Back<br/>retroarch_read_ram"]
    C --> D{"Bytes match?"}
    D -->|Yes| E["Continue"]
    D -->|No| F["Load State<br/>retroarch_load_state_current"]
```

## Common Address Ranges

| System | Region | System Bus | CHEEVOS Space |
|--------|--------|------------|---------------|
| GBA | EWRAM | `0x02000000-0x0203FFFF` | `0x03000000` offset |
| GBA | IWRAM | `0x03000000-0x03007FFF` | Direct |
| SNES | WRAM | `0x7E0000-0x7FFFFF` | `0x000000` |
| Genesis | 68K RAM | `0xFF0000-0xFFFFFF` | N/A |

资料来源：[src/tools.ts:85-95]()

## Limitations

| Limitation | Cause | Workaround |
|------------|-------|------------|
| Max 4096 bytes/call | NCI single-datagram size | Batch larger reads in 4 KiB chunks |
| No "save to slot N" | NCI protocol limitation | Walk slot pointer with `state_slot_plus`/`state_slot_minus` |
| `write_ram` has no ack | RetroArch doesn't respond to `WRITE_CORE_RAM` | Follow up with `retroarch_read_ram` |
| Address spaces differ | CHEEVOS vs system bus | Use `_memory` tools when possible |
| Memory regions bounded | Core exposes specific regions | Some addresses (VRAM, etc.) may be inaccessible |

资料来源：[src/tools.ts:145-155]()

## Configuration

| Environment Variable | Default | Purpose |
|---------------------|---------|---------|
| `RETROARCH_HOST` | `127.0.0.1` | UDP destination host |
| `RETROARCH_PORT` | `55355` | UDP port (must match `network_cmd_port` in retroarch.cfg) |

资料来源：[README.md:configuration]()

---

<a id='page-savestate-management'></a>

## Savestate Management

### 相关页面

相关主题：[MCP Tools Reference](#page-tools-reference)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [src/tools.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/tools.ts)
- [src/retroarch.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/retroarch.ts)
- [README.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)
- [CHANGELOG.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/CHANGELOG.md)
</details>

# Savestate Management

## Overview

Savestate Management in mcp-retroarch enables programmatic save and load operations for game sessions running under RetroArch's Network Command Interface (NCI). The system provides five MCP tools that wrap RetroArch's savestate NCI commands, allowing MCP clients to snapshot emulator state, restore from previous snapshots, and navigate between save slots.

The savestate system operates over UDP to RetroArch's NCI on port 55355, with mcp-retroarch acting as a bridge between the MCP JSON-RPC protocol and RetroArch's line-based command protocol.

## Architecture

```
┌─────────────────┐    stdio JSON-RPC    ┌──────────────────────┐   UDP :55355   ┌─────────────────┐
│   MCP Client    │◄───────────────────►│    mcp-retroarch     │◄──────────────►│   RetroArch     │
│ (Claude Code,   │                      │  (src/retroarch.ts)  │                │   (NCI Server)  │
│  Claude Desktop)│                      └──────────────────────┘                └─────────────────┘
└─────────────────┘
```

The savestate subsystem relies on:

| Layer | Technology | Role |
|-------|------------|------|
| MCP Protocol | stdio + JSON-RPC | Client-facing tool interface |
| Bridge | TypeScript (src/retroarch.ts) | Protocol translation and UDP communication |
| Transport | UDP datagrams | Direct communication with RetroArch NCI |
| Target | RetroArch NCI | Savestate command execution |

## Savestate Tools

| Tool | NCI Command | Purpose |
|------|-------------|---------|
| `retroarch_save_state_current` | `SAVE_STATE` | Save to currently-selected slot |
| `retroarch_load_state_current` | `LOAD_STATE` | Load from currently-selected slot |
| `retroarch_load_state_slot` | `LOAD_STATE_SLOT N` | Load from explicit slot N (1-9) |
| `retroarch_state_slot_plus` | `STATE_SLOT_PLUS` | Increment slot pointer |
| `retroarch_state_slot_minus` | `STATE_SLOT_MINUS` | Decrement slot pointer |

### Tool Parameters

#### retroarch_load_state_slot

```json
{
  "name": "retroarch_load_state_slot",
  "arguments": {
    "slot": {
      "type": "integer",
      "minimum": 1,
      "maximum": 9,
      "description": "Savestate slot number (1-9). RetroArch uses 1-based slot indexing."
    }
  }
}
```

### Return Values

All savestate tools return single-line confirmation messages:

| Tool | Return Message |
|------|----------------|
| `retroarch_save_state_current` | `"Saved to current slot"` |
| `retroarch_load_state_current` | `"Loaded from current slot"` |
| `retroarch_load_state_slot` | `"Loaded from slot N"` |
| `retroarch_state_slot_plus` | `"Slot: N"` |
| `retroarch_state_slot_minus` | `"Slot: N"` |

> **Note**: These return messages are UDP-send confirmations only. The NCI does not acknowledge command receipt. If a savestate operation fails silently, the tool will still return a success-like message.

资料来源：[src/tools.ts:77-79]()

## Slot-Based Savestate Model

### How Slots Work

RetroArch's savestate system uses a **slot-based model** with 10 slots (0-9 by default, though some builds use 1-9). Each slot holds one savestate file. When you save to an occupied slot, the existing savestate is overwritten.

### The Slot Pointer

RetroArch maintains an internal **current slot pointer** that determines which slot `SAVE_STATE` and `LOAD_STATE` (without a slot argument) target. The slot pointer can be changed using:

```mermaid
graph LR
    A["Current Slot = N"] --> B["state_slot_plus"]
    B --> C["Current Slot = N+1"]
    C --> D["state_slot_plus"]
    D --> E["Current Slot = N+2"]
    
    A --> F["state_slot_minus"]
    F --> G["Current Slot = N-1"]
    G --> H["state_slot_minus"]
    H --> I["Current Slot = N-2"]
```

### Slot Navigation Workflow

To save to slot 5 when currently at slot 1:

```mermaid
graph TD
    A["Start: Slot = 1"] --> B["retroarch_state_slot_plus"]
    B --> C["Slot = 2"]
    C --> D["retroarch_state_slot_plus"]
    D --> E["Slot = 3"]
    E --> F["retroarch_state_slot_plus"]
    F --> G["Slot = 4"]
    G --> H["retroarch_state_slot_plus"]
    H --> I["Slot = 5"]
    I --> J["retroarch_save_state_current"]
    J --> K["Saved to slot 5"]
```

## Critical Limitations

### No Direct Save-to-Slot

The NCI protocol does not expose a `SAVE_STATE_SLOT N` command. This is a protocol limitation, not a bug in mcp-retroarch.

| Operation | Available? | Workaround |
|-----------|------------|------------|
| Save to current slot | ✅ Yes | `retroarch_save_state_current` |
| Save to specific slot | ❌ No | Walk slot pointer with plus/minus, then save |
| Load from current slot | ✅ Yes | `retroarch_load_state_current` |
| Load from specific slot | ✅ Yes | `retroarch_load_state_slot` |

资料来源：[README.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)

### Current Slot Not Queryable

The NCI does not expose a command to query the current slot pointer. Clients must track slot position client-side or use `retroarch_show_message` to echo the slot number.

```mermaid
graph TD
    A["Track slot client-side"] --> B["State variable: currentSlot"]
    A --> C["Use show_message for confirmation"]
    C --> D["retroarch_show_message with 'Slot: N'"]
```

## UDP Communication Details

### Query/Response Pattern

The savestate bridge uses serial UDP communication with timeout handling:

```typescript
async query(command: string): Promise<Buffer> {
  if (!this.socket) await this.connect();
  if (this.pending) {
    throw new Error("retroarch query already in flight (client is serial)");
  }
  return new Promise<Buffer>((resolve, reject) => {
    let timer: NodeJS.Timeout | null = setTimeout(() => {
      this.pending = null;
      reject(new Error(
        `RetroArch query "${command.split(" ")[0]}" timed out after ${this.timeoutMs}ms`,
      ));
    }, this.timeoutMs);
    // ...
  });
}
```

资料来源：[src/retroarch.ts:72-89]()

### Fire-and-Forget Commands

`retroarch_save_state_current`, `retroarch_state_slot_plus`, and `retroarch_state_slot_minus` use fire-and-forget semantics:

```typescript
async send(command: string): Promise<void> {
  if (!this.socket) await this.connect();
  return new Promise((resolve, reject) => {
    this.socket!.send(command, this.port, this.host, (err) =>
      err ? reject(err) : resolve(),
    );
  });
}
```

资料来源：[src/retroarch.ts:63-69]()

## Best Practices

### Establish Rollback Points

Before memory writes or destructive operations, always save the current state:

```typescript
// 1. Save rollback point
retroarch_save_state_current();

// 2. Perform memory operations
retroarch_write_memory(address, bytes);

// 3. If something goes wrong, restore
retroarch_load_state_current();
```

### Pre-Check Before Toggle Operations

Since the NCI only exposes toggle commands, verify state before operations that depend on pause state:

```mermaid
graph TD
    A["retroarch_get_status"] --> B{"state: playing?"}
    B -->|Yes, want to pause| C["retroarch_pause_toggle"]
    B -->|No, want to unpause| C
    C --> D["retroarch_save_state_current"]
    D --> E["Continue workflow"]
```

### Slot Tracking Pattern

```typescript
let currentSlot = 1; // Track client-side

async function saveToSlot(targetSlot) {
  while (currentSlot < targetSlot) {
    await retroarch_state_slot_plus();
    currentSlot++;
  }
  while (currentSlot > targetSlot) {
    await retroarch_state_slot_minus();
    currentSlot--;
  }
  await retroarch_save_state_current();
}
```

## Error Handling

### Timeout Errors

If RetroArch doesn't respond within the timeout period (default: 5000ms), the tool returns an error:

```
RetroArch query "SAVE_STATE" timed out after 5000ms — is RetroArch running with Network Commands enabled?
```

**Troubleshooting**:
1. Confirm `network_cmd_enable = "true"` in retroarch.cfg
2. Verify `network_cmd_port = "55355"` matches `RETROARCH_PORT` environment variable
3. Check that a game/ROM is currently loaded (NCI requires active content)

### UDP Drop Handling

UDP datagrams can be dropped under load even on loopback. If a call times out but a retry succeeds, that's normal UDP behavior—the bridge does not auto-retry.

## Configuration

| Environment Variable | Default | Purpose |
|---------------------|---------|---------|
| `RETROARCH_HOST` | `127.0.0.1` | UDP destination host |
| `RETROARCH_PORT` | `55355` | UDP port (must match `network_cmd_port` in retroarch.cfg) |

## Related Tools

| Tool | Relationship |
|------|--------------|
| `retroarch_get_status` | Use to verify game state before loading states |
| `retroarch_frame_advance` | Step frames after loading state to verify restore |
| `retroarch_show_message` | Echo current slot number for tracking |

## Changelog

### v0.1.2 (2026-05-15)

Documentation improvements:
- Slot-based savestate model now explicitly documented
- Fire-and-forget UDP semantics surfaced in tool descriptions
- NCI limitation ("no save to slot N") clearly documented with workaround

资料来源：[CHANGELOG.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/CHANGELOG.md)

---

<a id='page-emulation-control'></a>

## Emulation Control

### 相关页面

相关主题：[MCP Tools Reference](#page-tools-reference)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [src/tools.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/tools.ts)
- [src/retroarch.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/retroarch.ts)
- [src/index.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/index.ts)
- [README.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)
- [package.json](https://github.com/dmang-dev/mcp-retroarch/blob/main/package.json)
</details>

# Emulation Control

## Overview

Emulation Control in mcp-retroarch encompasses the set of tools and mechanisms that allow MCP clients to interact with a running RetroArch emulator. This includes pausing/resuming execution, advancing frames one at a time, resetting games, capturing screenshots, displaying on-screen notifications, and managing save states.

The control layer operates exclusively through RetroArch's Network Control Interface (NCI), a UDP-based command protocol that listens on port 55355 by default. All control commands are sent as plain-text line-terminated strings over UDP, and the majority are fire-and-forget — RetroArch does not acknowledge receipt or execution status.

## Architecture

### Transport Layer

The UDP transport is implemented in `src/retroarch.ts`. The communication model uses a single persistent UDP socket with promise-based request/response handling.

```mermaid
graph TD
    A["MCP Client<br/>stdio JSON-RPC"] --> B["mcp-retroarch<br/>src/index.ts"]
    B --> C["RetroArchClient<br/>src/retroarch.ts"]
    C --> D["UDP Socket<br/>127.0.0.1:55355"]
    D --> E["RetroArch NCI"]
    
    F["NCI Response"] --> D
    D --> G["Pending Promise<br/>resolve/reject"]
    G --> B
```

**Key transport behaviors:**

| Method | Behavior | Use Case |
|--------|----------|----------|
| `send(command)` | Fire-and-forget, no response expected | Pause, reset, frame advance |
| `query(command)` | Sends command, awaits one UDP response | Memory reads, status queries |
| `connect()` | Binds UDP socket to ephemeral port | Transport initialization |
| `disconnect()` | Closes socket | Cleanup |

资料来源：[src/retroarch.ts:1-50]()

### Serialization Constraint

UDP datagrams are line-terminated. This has a critical implication for the `retroarch_show_message` tool: any message containing `\n` or `\r` will be truncated at the first newline, with the remainder silently dropped on the wire.

## Core Control Tools

### Pause Toggle

**Tool:** `retroarch_pause_toggle`

Toggles RetroArch's pause state between paused and running.

```mermaid
graph LR
    A["Call retroarch_get_status"] --> B{"state?"}
    B -->|"playing"| C["retroarch_pause_toggle<br/>→ paused"]
    B -->|"paused"| D["retroarch_pause_toggle<br/>→ playing"]
    C --> E["Verify with retroarch_get_status"]
    D --> E
```

**Important:** The NCI exposes only a toggle, not separate pause and unpause commands. Without first checking the current state via `retroarch_get_status`, the result is indeterminate.

| Property | Value |
|----------|-------|
| Input Schema | `{}` (no parameters) |
| Return | `"Pause toggled"` (UDP-send confirmation only) |
| Fire-and-forget | Yes — no verification of actual pause state |

资料来源：[src/tools.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/tools.ts)

### Frame Advance

**Tool:** `retroarch_frame_advance`

Advances the emulation by exactly one frame. This is only effective while emulation is paused — calling FRAMEADVANCE while running is a no-op.

| Property | Value |
|----------|-------|
| Input Schema | `{}` (no parameters) |
| Prerequisite | Emulation must be paused |
| Use Case | Frame-precise input automation, animation inspection |
| Alternative | For large frame jumps, use `retroarch_save_state_current` / `retroarch_load_state_current` |

资料来源：[src/tools.ts]()

### Reset

**Tool:** `retroarch_reset`

Performs a hard reset of the currently loaded game. The core is reloaded and execution begins from the game's reset vector.

| Property | Value |
|----------|-------|
| Input Schema | `{}` (no parameters) |
| Return | `"Game reset"` (UDP-send confirmation only) |
| Behavior | Immediately interrupts current execution |

资料来源：[src/tools.ts]()

## Save State Management

### State Slots

RetroArch maintains a current state slot pointer (0-9 typically). The save state tools operate on this pointer.

```mermaid
graph TD
    A["Current Slot = N"] --> B["retroarch_save_state_current"]
    B --> C["Save → Slot N"]
    
    A --> D["retroarch_load_state_slot<br/>slot: M"]
    D --> E{"M == N?"}
    E -->|Yes| F["Load from Slot N"]
    E -->|No| G["retroarch_state_slot_plus/minus<br/>to walk to M"]
    G --> H["Load from Slot M"]
```

### Available Tools

| Tool | Purpose | Input | Return |
|------|---------|-------|--------|
| `retroarch_save_state_current` | Save to current slot | None | `"Saved to current slot"` |
| `retroarch_load_state_current` | Load from current slot | None | `"Loaded from current slot"` |
| `retroarch_load_state_slot` | Load from explicit slot | `{ slot: number }` | `"Loaded from slot N"` |
| `retroarch_state_slot_plus` | Increment slot pointer | None | Slot number confirmation |
| `retroarch_state_slot_minus` | Decrement slot pointer | None | Slot number confirmation |

### Known Limitation

The NCI protocol does not expose a command to save directly to a specific slot. To save to slot 5 when the pointer is on slot 0:

1. Call `retroarch_state_slot_plus` five times
2. Call `retroarch_save_state_current`

This is an NCI limitation, not a bug in mcp-retroarch.

资料来源：[README.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)

## Visual Feedback Tools

### Screenshot

**Tool:** `retroarch_screenshot`

Captures the current frame and saves it to RetroArch's configured screenshot directory.

| Property | Value |
|----------|-------|
| Input Schema | `{}` (no parameters) |
| Return | `"Screenshot saved to RetroArch's configured screenshot directory"` |
| Directory | Must be verified in RetroArch GUI: Settings → Directory → Screenshot |
| NCI Limitation | The `screenshot_directory` param is NOT exposed via `GET_CONFIG_PARAM` |

资料来源：[src/tools.ts](), [README.md]()

### On-Screen Message

**Tool:** `retroarch_show_message`

Displays a single-line notification overlay on the RetroArch window for approximately 3 seconds (RetroArch's default notification timeout).

| Property | Value |
|----------|-------|
| Input Schema | `{ message: string }` | Required, minLength: 1 |
| Message Limits | ≤80 characters recommended; newlines (`\n`, `\r`) truncate |
| Duration | ~3 seconds (configurable via `input_overlay_show_inputs_port` settings) |
| Queueing | Messages are NOT queued — rapid calls replace the previous message |
| Use Case | Debug output, progress markers, "look here" cues |

```mermaid
graph LR
    A["Message: 'Frame 1234'"] --> B["NCI OSD Overlay"]
    C["Rapid Second Call"] --> D["Message: 'Frame 1235'"]
    D -->|Before A renders| B
```

**Important:** Because messages replace each other, do not issue multiple `show_message` calls in rapid succession without verification that the previous message was read.

资料来源：[src/tools.ts]()

## UDP Transport Deep Dive

### Socket Lifecycle

From `src/retroarch.ts`:

```typescript
async connect(): Promise<void> {
  return new Promise((resolve, reject) => {
    const sock = dgram.createSocket("udp4");
    sock.once("error", (err) => reject(err));
    sock.bind(0, () => {
      sock.on("message", (msg) => {
        const cb = this.pending;
        if (!cb) return;  // unsolicited or late reply — drop
        this.pending = null;
        cb(msg);
      });
      // ...
    });
  });
}
```

### Query Timeout

The `query()` method enforces a configurable timeout (default ~5 seconds):

```typescript
async query(command: string): Promise<Buffer> {
  if (!this.socket) await this.connect();
  if (this.pending) {
    throw new Error("retroarch query already in flight (client is serial)");
  }
  return new Promise<Buffer>((resolve, reject) => {
    let timer: NodeJS.Timeout | null = setTimeout(() => {
      this.pending = null;
      reject(new Error("RetroArch query timed out"));
    }, this.timeoutMs);
    // ...
  });
}
```

**Serial constraint:** Only one query can be in-flight at a time. The client enforces this with a `pending` callback reference.

资料来源：[src/retroarch.ts]()

## Error Handling

### Common Errors

| Symptom | Cause | Fix |
|---------|-------|-----|
| `RetroArch query timed out` | Network Commands disabled or port mismatch | Verify `network_cmd_enable = "true"` in `retroarch.cfg` |
| `retroarch query already in flight` | Code bug or concurrent tool calls | Ensure serial tool execution |
| Screenshot not where expected | Screenshot saved to RetroArch's configured directory | Check Settings → Directory → Screenshot |

### Background Connectivity

The MCP server performs a fire-and-find connectivity probe on startup in `src/index.ts`:

```typescript
ra.connect()
  .then(() => ra.getVersion())
  .then((v) => process.stderr.write(`[mcp-retroarch] connected to ${ra.describeTarget()} — RetroArch ${v}\n`))
  .catch((err) => process.stderr.write(
    `[mcp-retroarch] note: RetroArch not reachable yet (${ra.describeTarget()}): ${err}\n` +
    `             Enable Network Commands in retroarch.cfg (network_cmd_enable / network_cmd_port)\n`
  ));
```

This does not block server startup — tool calls will connect on demand.

资料来源：[src/index.ts]()

## Configuration

| Environment Variable | Default | Purpose |
|---------------------|---------|---------|
| `RETROARCH_HOST` | `127.0.0.1` | UDP destination host |
| `RETROARCH_PORT` | `55355` | UDP port (must match `network_cmd_port` in `retroarch.cfg`) |

RetroArch-side configuration (via GUI or `retroarch.cfg`):

```ini
network_cmd_enable = "true"
network_cmd_port   = "55355"
```

Or via RetroArch GUI: **Settings → Network → Network Commands → ON**

资料来源：[README.md]()

## Tool Summary Table

| Tool | Type | Input | Fire-and-Forget | Verified Return |
|------|------|-------|-----------------|-----------------|
| `retroarch_pause_toggle` | Control | None | Yes | Confirmation only |
| `retroarch_frame_advance` | Control | None | Yes | Confirmation only |
| `retroarch_reset` | Control | None | Yes | Confirmation only |
| `retroarch_save_state_current` | State | None | Yes | Confirmation only |
| `retroarch_load_state_current` | State | None | Yes | Confirmation only |
| `retroarch_load_state_slot` | State | `{ slot }` | Yes | Confirmation only |
| `retroarch_state_slot_plus` | State | None | Yes | Confirmation only |
| `retroarch_state_slot_minus` | State | None | Yes | Confirmation only |
| `retroarch_screenshot` | Visual | None | Yes | Confirmation only |
| `retroarch_show_message` | Visual | `{ message }` | Yes | Confirmation only |

## Dependencies

The emulation control functionality depends on:

```json
{
  "@modelcontextprotocol/sdk": "^1.12.0"
}
```

The `@modelcontextprotocol/sdk` provides the MCP server framework that exposes these tools via stdio JSON-RPC to compatible MCP clients (Claude Code, Claude Desktop, etc.).

资料来源：[package.json](https://github.com/dmang-dev/mcp-retroarch/blob/main/package.json)

---

<a id='page-tools-reference'></a>

## MCP Tools Reference

### 相关页面

相关主题：[Memory Read/Write Operations](#page-memory-operations), [Savestate Management](#page-savestate-management), [Emulation Control](#page-emulation-control)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [src/tools.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/tools.ts)
- [README.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)
- [src/retroarch.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/retroarch.ts)
- [src/index.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/index.ts)
- [CHANGELOG.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/CHANGELOG.md)
</details>

# MCP Tools Reference

The mcp-retroarch project exposes a collection of Model Context Protocol (MCP) tools that enable programmatic control of RetroArch through its Network Control Interface (NCI). These tools provide capabilities ranging from memory inspection and modification to savestate management and emulator control.

## Overview

mcp-retroarch acts as a bridge between MCP clients and RetroArch's UDP-based NCI protocol. The MCP server communicates with RetroArch over UDP port 55355 (default), translating JSON-RPC requests from MCP clients into NCI commands. 资料来源：[README.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)

```
+----------------+    stdio     +-----------------+   UDP :55355  +-----------------+
|   MCP client   |   JSON-RPC   |  mcp-retroarch  |   NCI commands |  RetroArch      |
+----------------+              +-----------------+                +-----------------+
```

## Tool Categories

The tools are organized into five functional categories:

| Category | Tools | Purpose |
|----------|-------|---------|
| **Status & Config** | `retroarch_get_status`, `retroarch_get_config` | Query emulator state and configuration |
| **Memory Access** | `retroarch_read_memory`, `retroarch_write_memory`, `retroarch_read_ram`, `retroarch_write_ram` | Read/write emulated system memory |
| **Savestate** | `retroarch_save_state_current`, `retroarch_load_state_current`, `retroarch_load_state_slot`, `retroarch_state_slot_plus`, `retroarch_state_slot_minus` | Manage game save states |
| **Emulator Control** | `retroarch_pause_toggle`, `retroarch_frame_advance`, `retroarch_reset` | Control emulation execution |
| **Media & UI** | `retroarch_screenshot`, `retroarch_show_message` | Capture screenshots and display notifications |

## Memory Architecture

mcp-retroarch provides two distinct memory access APIs, each using different address spaces and underlying NCI commands. 资料来源：[src/tools.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/tools.ts)

### System Memory Map (`_memory` tools)

The `_memory` tools use `READ_CORE_MEMORY` and `WRITE_CORE_MEMORY` commands, accessing the libretro core's system memory map. This is the preferred method when the loaded core advertises a memory map. 资料来源：[src/tools.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/tools.ts)

**Address conventions vary by system:**

| System | Memory Region | Address Range |
|--------|---------------|---------------|
| GBA EWRAM | External Work RAM | `0x02000000-0x0203FFFF` |
| SNES WRAM | Work RAM | `0x7E0000-0x7FFFFF` |
| Genesis 68K RAM | Main RAM | `0xFF0000-0xFFFFFF` |

### CHEEVOS Address Space (`_ram` tools)

The `_ram` tools use `READ_CORE_RAM` and `WRITE_CORE_RAM` commands (the older CHEEVOS/achievements API). These tools access the RetroAchievements address space, which follows per-system conventions distinct from the system memory map. 资料来源：[src/tools.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/tools.ts)

**When to use which:**

- Use `_memory` tools as the starting point for memory access
- Fall back to `_ram` tools when `_memory` returns `'no memory map defined'` (older cores)
- `read_ram` confirmed working for SwanStation (PSX), Mesen (NES) 资料来源：[README.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)

### Memory Tool Comparison

| Feature | `_memory` tools | `_ram` tools |
|---------|-----------------|--------------|
| NCI Command | `READ_CORE_MEMORY` / `WRITE_CORE_MEMORY` | `READ_CORE_RAM` / `WRITE_CORE_RAM` |
| Address Space | System memory map | CHEEVOS (achievements) |
| Write Acknowledgment | Returns byte count | No acknowledgment (fire-and-forget) |
| Core Support | Requires memory map | Broader (achievements-compatible cores) |
| Max Bytes/Call | 4096 | 4096 |

## Status & Configuration Tools

### retroarch_get_status

Queries the current emulator state including run-state, loaded ROM, and CRC32.

**Input:** No parameters

**Returns:**
- `State: playing|paused`
- `System: SYSTEM_ID`
- `Game: BASENAME`
- `CRC32: HEX or (none reported)`

Returns `'No content loaded'` when RetroArch is at the menu with no ROM. 资料来源：[src/tools.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/tools.ts)

### retroarch_get_config

Reads a single RetroArch configuration parameter by name.

**Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `name` | string | Yes | Configuration parameter name |

**Notes:**
- Uses `GET_CONFIG_PARAM` command
- RetroArch whitelists exposed parameters; non-whitelisted names error
- `screenshot_directory` is NOT exposed via this API 资料来源：[src/tools.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/tools.ts)

## Memory Access Tools

### retroarch_read_memory

Reads bytes from the system memory map via `READ_CORE_MEMORY`.

**Parameters:**

| Parameter | Type | Required | Constraints | Description |
|-----------|------|----------|-------------|-------------|
| `address` | integer | Yes | ≥ 0 | Starting address in system memory map |
| `length` | integer | Yes | 1-4096 | Number of bytes to read |

**Returns:** `ADDR_HEX [N bytes]:` followed by space-separated uppercase hex bytes

**Notes:**
- Returns fewer bytes if read crosses a memory-region boundary
- Works whether emulation is paused or running
- Throws error if core doesn't expose a memory map 资料来源：[src/tools.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/tools.ts)

### retroarch_write_memory

Writes bytes to the system memory map via `WRITE_CORE_MEMORY`.

**Parameters:**

| Parameter | Type | Required | Constraints | Description |
|-----------|------|----------|-------------|-------------|
| `address` | integer | Yes | ≥ 0 | Starting address in system memory map |
| `bytes` | array | Yes | 1-4096 elements, each 0-255 | Byte values to write |

**Returns:** `Wrote N bytes → ADDR_HEX`

**Notes:**
- **DESTRUCTIVE** - overwrites existing data with no undo
- Disables RetroArch's hardcore mode for the session
- Returns byte count (the only NCI write command that acknowledges) 资料来源：[src/tools.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/tools.ts)

### retroarch_read_ram

Reads bytes from the CHEEVOS address space via `READ_CORE_RAM`.

**Parameters:**

| Parameter | Type | Required | Constraints | Description |
|-----------|------|----------|-------------|-------------|
| `address` | integer | Yes | ≥ 0 | Starting address in CHEEVOS space |
| `length` | integer | Yes | 1-4096 | Number of bytes to read |

**Returns:** `ADDR_HEX [N bytes, CHEEVOS]:` followed by space-separated uppercase hex bytes

**Notes:**
- Fallback when `_memory` returns `'no memory map defined'`
- CHEEVOS addresses follow RetroAchievements conventions (e.g., SNES WRAM starts at `0x000000`, not `0x7E0000`) 资料来源：[src/tools.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/tools.ts)

### retroarch_write_ram

Writes bytes to the CHEEVOS address space via `WRITE_CORE_RAM`.

**Parameters:**

| Parameter | Type | Required | Constraints | Description |
|-----------|------|----------|-------------|-------------|
| `address` | integer | Yes | ≥ 0 | Starting address in CHEEVOS space |
| `bytes` | array | Yes | 1-4096 elements, each 0-255 | Byte values to write |

**Returns:** `Wrote N bytes → ADDR_HEX (CHEEVOS, no ack)`

**Notes:**
- **DESTRUCTIVE** and **fire-and-forget** - no verification
- Disables RetroArch's hardcore mode
- RetroArch does not acknowledge this command
- Verify writes with `retroarch_read_ram` after writing 资料来源：[src/tools.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/tools.ts)

## Savestate Management

The NCI protocol has limitations for savestate operations:

1. **No direct "save to slot N"** - only save to the currently-selected slot
2. **No query for current slot** - client must track the slot pointer
3. **Load** supports both current slot and explicit slot numbers 资料来源：[README.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)

### retroarch_save_state_current

Saves the current game state to the currently-selected slot.

**Input:** No parameters

**Returns:** `Saved to current slot`

### retroarch_load_state_current

Loads game state from the currently-selected slot.

**Input:** No parameters

**Returns:** `Loaded from current slot`

### retroarch_load_state_slot

Loads game state from an explicit slot number.

**Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `slot` | integer | Yes | Savestate slot number (0-9 typically) |

**Returns:** `Loaded from slot N`

### retroarch_state_slot_plus / retroarch_state_slot_minus

Moves the current slot pointer up or down. Used to navigate to a target slot before saving.

**Input:** No parameters

**Returns:** Slot navigation confirmation

**Workflow for saving to a specific slot:**

```mermaid
graph TD
    A[Start] --> B{Current slot = target?}
    B -->|Yes| E[retroarch_save_state_current]
    B -->|No| C{Increment or decrement?}
    C -->|Plus| D[retroarch_state_slot_plus]
    C -->|Minus| F[retroarch_state_slot_minus]
    D --> B
    F --> B
    E --> G[Done]
```

## Emulator Control Tools

### retroarch_pause_toggle

Toggles RetroArch's pause state.

**Input:** No parameters

**Returns:** `Pause toggled`

**Notes:**
- NCI exposes only a toggle, not separate pause/unpause
- Call `retroarch_get_status` first to check current state if needed 资料来源：[src/tools.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/tools.ts)

### retroarch_frame_advance

Advances emulation by exactly one frame.

**Input:** No parameters

**Returns:** `Advanced one frame`

### retroarch_reset

Hard-resets the running game.

**Input:** No parameters

**Returns:** `Game reset`

## Media & UI Tools

### retroarch_screenshot

Captures a screenshot and saves it to RetroArch's configured screenshot directory.

**Input:** No parameters

**Returns:** `Screenshot saved to RetroArch's configured screenshot directory`

**Notes:**
- The NCI doesn't expose `screenshot_directory` via `GET_CONFIG_PARAM`
- Check the configured path via RetroArch GUI: Settings → Directory → Screenshot 资料来源：[README.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)

### retroarch_show_message

Displays a notification on the RetroArch window.

**Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `message` | string | Yes | Message text to display |

**Returns:** `Showed: {message}`

## Transport Semantics

All tools communicate with RetroArch via UDP datagrams. This has important implications:

### Fire-and-Forget Commands

Most NCI commands (write_ram, pause_toggle, frame_advance, reset, save_state, etc.) do **not** receive acknowledgment from RetroArch. The success message returned by the tool confirms only that the UDP packet was sent, not that RetroArch received or acted on it. 资料来源：[src/tools.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/tools.ts)

### Query Commands with Acknowledgment

The `retroarch_write_memory` command is the **only** write command that returns a byte count from RetroArch, providing limited verification of the write operation.

### Timeout Handling

UDP queries timeout after a configurable interval (default: 5000ms). Common timeout causes:

1. Network Commands not enabled in RetroArch
2. Port mismatch between `RETROARCH_PORT` and RetroArch's `network_cmd_port`
3. UDP datagrams dropped under load (even on loopback) 资料来源：[README.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)

The bridge does **not** auto-retry on timeout; retry the call if needed. 资料来源：[README.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)

## Configuration

| Environment Variable | Default | Purpose |
|---------------------|---------|---------|
| `RETROARCH_HOST` | `127.0.0.1` | UDP destination host |
| `RETROARCH_PORT` | `55355` | UDP port (must match `network_cmd_port` in `retroarch.cfg`) |

## Error Handling

| Error Message | Cause / Fix |
|---------------|-------------|
| `RetroArch query timed out` | Network Commands not enabled; port mismatch; UDP dropped |
| `READ_CORE_MEMORY failed: no memory map defined` | Core doesn't advertise memory map; use `read_ram` instead |
| `READ_CORE_MEMORY failed: no descriptor for address` | Address outside core's memory regions |
| Screenshots don't appear | Check RetroArch's screenshot directory setting via GUI |
| Can't save to specific slot directly | NCI limitation; walk slot pointer with `state_slot_plus/minus` first |

## Tool Summary Table

| Tool | Input Parameters | Returns | Side Effects |
|------|------------------|---------|--------------|
| `retroarch_get_status` | — | Emulator state info | None |
| `retroarch_get_config` | `name` | Config value | None |
| `retroarch_read_memory` | `address`, `length` | Hex dump | None |
| `retroarch_write_memory` | `address`, `bytes` | Byte count | Disables hardcore mode |
| `retroarch_read_ram` | `address`, `length` | Hex dump (CHEEVOS) | None |
| `retroarch_write_ram` | `address`, `bytes` | Confirmation | Disables hardcore mode |
| `retroarch_pause_toggle` | — | Pause toggled | Changes run-state |
| `retroarch_frame_advance` | — | Frame advanced | Advances emulation |
| `retroarch_reset` | — | Game reset | Hard-resets game |
| `retroarch_screenshot` | — | Screenshot path | Creates file |
| `retroarch_show_message` | `message` | Message displayed | Shows notification |
| `retroarch_save_state_current` | — | Save confirmed | Overwrites current slot |
| `retroarch_load_state_current` | — | Load confirmed | Loads current slot |
| `retroarch_load_state_slot` | `slot` | Load confirmed | Loads specified slot |
| `retroarch_state_slot_plus` | — | Slot moved | Increments slot pointer |
| `retroarch_state_slot_minus` | — | Slot moved | Decrements slot pointer |

---

<a id='page-core-compatibility'></a>

## Core Compatibility

### 相关页面

相关主题：[Memory Read/Write Operations](#page-memory-operations)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [README.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)
- [src/tools.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/tools.ts)
- [src/retroarch.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/retroarch.ts)
- [src/index.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/index.ts)
- [package.json](https://github.com/dmang-dev/mcp-retroarch/blob/main/package.json)
- [CHANGELOG.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/CHANGELOG.md)
</details>

# Core Compatibility

## Overview

Core Compatibility refers to the ability of mcp-retroarch to interact with different libretro cores running inside RetroArch. Since each emulator core implements the libretro API differently—and exposes different memory maps and features—mcp-retroarch provides multiple memory access pathways and behavioral workarounds to maximize cross-core support.

The MCP server communicates with RetroArch via the Network Control Interface (NCI), a UDP-based command protocol. The level of functionality available depends on:

1. Whether the loaded core exposes a system memory map via libretro
2. Whether the core responds to the CHEEVOS (RetroAchievements) read API
3. Which specific NCI commands the core supports

资料来源：[README.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)

## Memory Access Architecture

mcp-retroarch provides two distinct memory reading pathways, each targeting a different address space:

### Memory Access Methods Comparison

| Aspect | `retroarch_read_memory` | `retroarch_read_ram` |
|--------|------------------------|---------------------|
| **NCI Command** | `READ_CORE_MEMORY` | `READ_CORE_RAM` |
| **Address Space** | Libretro system memory map | CHEEVOS achievement address space |
| **Core Requirement** | Core must expose memory map descriptors | Works if core exposes CHEEVOS |
| **Fallback** | Auto-fall back to `read_ram` on "no memory map" | No further fallback |
| **Use Case** | Primary tool for memory inspection | Fallback / older cores |

资料来源：[src/tools.ts:1-200](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/tools.ts)

### Address Space Differences

The two memory paths use fundamentally different addressing schemes:

```mermaid
graph TB
    subgraph "Libretro Memory Map read_memory"
        A["SNES WRAM: 0x7E0000-0x7FFFFF"] 
        B["GBA EWRAM: 0x02000000-0x0203FFFF"]
        C["Genesis 68K RAM: 0xFF0000-0xFFFFFF"]
    end
    
    subgraph "CHEEVOS Address Space read_ram"
        D["SNES WRAM: 0x000000"]
        E["GBA WRAM: 0x02000000"]
        F["NES WRAM: 0x0000"]
    end
```

For example, SNES WRAM appears at:
- **System memory map**: `0x7E0000`
- **CHEEVOS space**: `0x000000`

资料来源：[src/tools.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/tools.ts)

## Tested Cores Matrix

The following cores have been verified end-to-end with mcp-retroarch:

| System | Core | `read_memory` | `read_ram` | Notes |
|--------|------|---------------|------------|-------|
| Game Boy Advance | `mgba_libretro` | ✅ | ✅ | GBA interrupt vector table visible at `0x0000` (`d3 00 00 ea ...`) |
| NES | `mesen_libretro` | ✅ (only NES core tested that does) | ✅ | Full 16-bit NES address space exposed. WRAM at `0x0000-0x07FF`, mirrored to `0x1FFF`. CHEEVOS bounded to first 64 KB. |
| NES | `nestopia_libretro` | ❌ (no memory map) | ✅ | CHEEVOS only. 64 KB bound. **For NES + memory map, prefer Mesen.** |
| SNES | `snes9x_libretro` | ❌ | — | Status not fully documented in current release |

资料来源：[README.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)

## Feature Support by Core

### Memory Read/Write

| Capability | Supported | Details |
|------------|-----------|---------|
| System memory map | Core-dependent | Only cores that expose descriptors via `READ_CORE_MEMORY` |
| CHEEVOS RAM read | Most cores | Achievement API is widely supported |
| Memory write | Same pathways as reads | `WRITE_CORE_MEMORY` or `WRITE_CORE_RAM` |

**Important limitation**: Memory writes via NCI automatically disable RetroArch's hardcore mode for the rest of the session.

资料来源：[src/tools.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/tools.ts)

### Savestate Operations

Savestate functionality is supported universally across cores since it uses RetroArch's internal state management:

| Operation | Support | Notes |
|-----------|---------|-------|
| Save to current slot | ✅ | Uses `SAVE_STATE_CURRENT` |
| Load from current slot | ✅ | Uses `LOAD_STATE_CURRENT` |
| Load from explicit slot | ✅ | Uses `LOAD_STATE` with slot number |
| Save to explicit slot | ❌ | NCI limitation—must walk slot pointer |

资料来源：[README.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)

### Game-Pad Input

**Not supported** via mcp-retroarch. The NCI protocol does not expose game-pad input. RetroArch has a separate "Remote RetroPad" mechanism on UDP port 55400, but it requires loading a specific core and cannot drive an existing emulation session.

资料来源：[README.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)

## Choosing the Right Memory Tool

```mermaid
graph TD
    A["Need to read/write memory?"] --> B{"Does read_memory return<br/>'no memory map defined'?"}
    B -->|Yes| C["Use retroarch_read_ram"]
    B -->|No| D["Use retroarch_read_memory"]
    C --> E["Address in CHEEVOS space"]
    D --> F["Address in libretro memory map"]
    
    style B fill:#ffcccc
    style C fill:#ccffcc
    style D fill:#ccffcc
```

### Decision Criteria

1. **Start with `retroarch_read_memory`** — It provides access to the native system memory map and is the preferred pathway.

2. **Fall back to `retroarch_read_ram`** when:
   - The core returns "no memory map defined"
   - You need CHEEVOS achievement-style addresses
   - Working with cores like `nestopia_libretro` that don't expose system memory maps

3. **Write operations follow the same path** as your read operations.

资料来源：[src/tools.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/tools.ts)

## Core-Specific Behaviors

### NES Cores Comparison

| Feature | Mesen | Nestopia |
|---------|-------|----------|
| System memory map | ✅ Exposed | ❌ Not exposed |
| CHEEVOS RAM | ✅ 64 KB bound | ✅ 64 KB bound |
| Full address space | ✅ 0x0000-0xFFFF | Limited to first 64 KB |
| **Recommendation** | **Preferred for memory work** | Use for achievements only |

### GBA with mgba_libretro

The `mgba_libretro` core provides full memory map access:
- Interrupt vector table at `0x0000` is readable via `read_memory`
- Example data visible: `d3 00 00 ea ...`

资料来源：[README.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)

## UDP Communication Model

mcp-retroarch uses a serial query model with timeout handling:

```mermaid
sequenceDiagram
    participant MCP as MCP Client
    participant Bridge as mcp-retroarch
    participant RA as RetroArch NCI
    
    MCP->>Bridge: Tool call (e.g., retroarch_read_memory)
    Bridge->>Bridge: Check if socket connected
    Bridge->>RA: UDP query (e.g., READ_CORE_MEMORY ...)
    Note over Bridge: Set timeout timer
    RA-->>Bridge: UDP response
    Bridge->>Bridge: Clear timeout, resolve promise
    Bridge-->>MCP: JSON-RPC response
    
    Note over Bridge,RA: Timeout → Error "RetroArch query timed out"
```

Key behaviors:
- Only one query can be in flight at a time
- Default timeout: configurable, typically 5000ms
- UDP is unreliable—retries may be needed under load

资料来源：[src/retroarch.ts:1-100](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/retroarch.ts)

## Troubleshooting Core Compatibility

| Symptom | Cause | Solution |
|---------|-------|----------|
| `READ_CORE_MEMORY failed: no memory map defined` | Core doesn't expose system memory map | Use `retroarch_read_ram` instead |
| `READ_CORE_MEMORY failed: no descriptor for address` | Address outside core's memory regions | Use a different core or check valid address range |
| `RetroArch query timed out` | Network Commands not enabled or port mismatch | Enable in RetroArch GUI or set `network_cmd_enable = "true"` |
| Inconsistent responses | UDP datagram drops under load | Retry the query—mcp-retroarch doesn't auto-retry |

资料来源：[README.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)

## Configuration for Core Support

| Environment Variable | Default | Purpose |
|---------------------|---------|---------|
| `RETROARCH_HOST` | `127.0.0.1` | UDP destination host |
| `RETROARCH_PORT` | `55355` | UDP port (must match `network_cmd_port` in RetroArch config) |

RetroArch must have Network Commands enabled:
- **GUI**: Settings → Network → Network Commands → ON
- **Config file**: `network_cmd_enable = "true"`

资料来源：[README.md](https://github://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)

## Architecture Summary

```
+----------------+    stdio     +-----------------+   UDP :55355  +-----------------+
|   MCP client   |   JSON-RPC   |  mcp-retroarch  |   commands   |   RetroArch     |
|                |◄────────────►|                 |◄────────────►|   (any core)    |
+----------------+              +-----------------+              +-----------------+
```

The bridge translates MCP tool calls into NCI UDP commands. Core compatibility depends entirely on what the underlying libretro core exposes via the NCI protocol.

资料来源：[README.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)

## Related Tools for Other Platforms

| Platform | Tool | Features |
|----------|------|----------|
| Game Boy Advance | [mcp-mgba](https://github.com/dmang-dev/mcp-mgba) | Memory + button input + screenshot via mGBA Lua bridge |
| PCSX2, etc. | [mcp-pine](https://github.com/dmang-dev/mcp-pine) | Memory + savestate only via PINE protocol |

These alternatives may provide better compatibility for their respective platforms when NCI-based cores lack features.

---

<a id='page-configuration'></a>

## Configuration

### 相关页面

相关主题：[Quick Start Guide](#page-quick-start)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [README.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/README.md)
- [src/tools.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/tools.ts)
- [src/retroarch.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/retroarch.ts)
- [src/index.ts](https://github.com/dmang-dev/mcp-retroarch/blob/main/src/index.ts)
- [package.json](https://github.com/dmang-dev/mcp-retroarch/blob/main/package.json)
- [CHANGELOG.md](https://github.com/dmang-dev/mcp-retroarch/blob/main/CHANGELOG.md)
</details>

# Configuration

The Configuration system in mcp-retroarch encompasses two distinct layers: the MCP server's runtime settings (controlled via environment variables) and RetroArch's Network Control Interface (NCI) parameters (queried at runtime). Together, they establish the communication bridge between an MCP client and a running RetroArch instance.

## Overview

mcp-retroarch acts as a bridge server that translates MCP tool calls into UDP datagrams sent to RetroArch's Network Command Interface. Configuration determines:

1. **Network transport** — where to send UDP commands (host and port)
2. **RetroArch NCI readiness** — ensuring RetroArch has Network Commands enabled
3. **Runtime parameter discovery** — querying RetroArch's internal configuration state

资料来源：[README.md:30-35]()

```mermaid
graph TD
    A[MCP Client] -->|JSON-RPC over stdio| B[mcp-retroarch Server]
    B -->|UDP datagrams| C{RetroArch Network Commands}
    C -->|Enabled?| D[Commands processed]
    C -->|Disabled| E[Timeout / Error]
    
    F[Environment Variables] --> B
    G[retroarch.cfg] --> C
```

## Environment Variables

The MCP server reads the following environment variables at startup to configure its UDP transport layer.

| Environment Variable | Default | Purpose |
|---------------------|---------|---------|
| `RETROARCH_HOST` | `127.0.0.1` | UDP destination host — the machine running RetroArch |
| `RETROARCH_PORT` | `55355` | UDP port — must match `network_cmd_port` in RetroArch's config |

资料来源：[README.md:52-54]()

### Configuration Precedence

Environment variables are read directly without any configuration file parsing. If not set, the defaults apply. The server does not support configuration via `~/.retroarch.cfg` or similar — only environment variables control mcp-retroarch itself.

### Non-blocking Startup

The MCP server starts immediately without blocking on RetroArch connectivity. A background probe attempts to connect and log the RetroArch version, but tool calls connect on demand if RetroArch is unreachable at startup.

资料来源：[src/index.ts:20-27]()

```typescript
// Background connectivity probe — fire-and-forget, never blocks the server.
ra.connect()
  .then(() => ra.getVersion())
  .then((v) => process.stderr.write(`[mcp-retroarch] connected to ${ra.describeTarget()} — RetroArch ${v}\n`))
  .catch((err) => process.stderr.write(
    `[mcp-retroarch] note: RetroArch not reachable yet (${ra.describeTarget()}): ${err}\n` +
    `             Enable Network Commands in retroarch.cfg (network_cmd_enable / network_cmd_port)\n` +
    `             or Settings > Network > Network Commands. Tool calls will connect on demand.\n`,
  ));
```

资料来源：[src/index.ts:20-27]()

## RetroArch Network Commands Setup

For mcp-retroarch to communicate with RetroArch, Network Commands must be enabled in RetroArch itself. This is a one-time setup per RetroArch installation.

### Method 1: GUI Configuration

1. Navigate to **Settings → Network → Network Commands**
2. Set **Network Commands** to **ON**
3. Confirm **Network Cmd Port** is `55355` (the default)

资料来源：[README.md:25-28]()

### Method 2: Configuration File

Add or edit the following in `retroarch.cfg`:

```ini
network_cmd_enable = "true"
network_cmd_port   = "55355"
```

资料来源：[README.md:30-35]()

### Port Matching Requirement

The port configured in RetroArch (`network_cmd_port`) **must match** the `RETROARCH_PORT` environment variable used by mcp-retroarch. Mismatches result in `RetroArch query timed out` errors.

资料来源：[README.md:52-54]()

```mermaid
graph LR
    A[mcp-retroarch<br/>RETROARCH_PORT] -->|55355| B{Port Match?}
    C[RetroArch<br/>network_cmd_port] -->|55355| B
    B -->|Yes| D[Communication OK]
    B -->|No| E[Timeout Error]
```

## Reading RetroArch Configuration

The `retroarch_get_config` tool queries RetroArch's internal configuration state via the NCI `GET_CONFIG_PARAM` command. This reads **runtime values** from RetroArch, not the static config file.

资料来源：[src/tools.ts:80-96]()

### Available Configuration Parameters

RetroArch's NCI exposes a **whitelist** of configuration keys. The following parameters are confirmed available:

| Category | Parameter | Returns | Notes |
|----------|-----------|---------|-------|
| Directories | `savefile_directory` | Absolute path | User's save file directory |
| Directories | `savestate_directory` | Absolute path | Savestate directory |
| Directories | `system_directory` | Absolute path | System/BIOS directory |
| Directories | `cache_directory` | Absolute path | Cache location |
| Directories | `log_dir` | Absolute path | Log file directory |
| Directories | `runtime_log_directory` | Absolute path | Runtime log directory |
| Directories | `core_assets_directory` | Absolute path | Downloaded core assets |
| User Data | `netplay_nickname` | String | Netplay display name |
| Video | `video_fullscreen` | `true` / `false` | Fullscreen toggle |
| Video | `video_vsync` | `true` / `false` | Vertical sync toggle |
| Audio | `audio_mute_enable` | `true` / `false` | Audio mute toggle |

资料来源：[src/tools.ts:80-96]()

### Notable Exclusions

The `screenshot_directory` parameter is **intentionally NOT exposed** by RetroArch's NCI whitelist. Screenshots are saved to RetroArch's configured screenshot directory, but this cannot be queried programmatically. Check the value manually via **Settings → Directory → Screenshot**.

资料来源：[src/tools.ts:80-96]()

### Current Savestate Slot

There is **no NCI key** for querying the currently-selected savestate slot. Track the slot number client-side by using `retroarch_state_slot_plus` and `retroarch_state_slot_minus`, or start from slot 1 (NCI default).

资料来源：[src/tools.ts:80-96]()

### Tool Definition

```json
{
  "name": "retroarch_get_config",
  "description": "PURPOSE: Read a single RetroArch configuration parameter by name via the NCI GET_CONFIG_PARAM command.",
  "inputSchema": {
    "type": "object",
    "required": ["name"],
    "properties": {
      "name": {
        "type": "string",
        "minLength": 1,
        "description": "Config key — same snake_case ASCII identifier RetroArch uses in retroarch.cfg"
      }
    }
  }
}
```

资料来源：[src/tools.ts:80-96]()

### Return Format

```
NAME = VALUE
```

Where `VALUE` is the raw string as stored in `retroarch.cfg`:
- Paths are **unquoted**
- Booleans are `'true'` / `'false'`
- Integers are **decimal**

资料来源：[src/tools.ts:80-96]()

### Error Conditions

The tool returns an error if:
1. The parameter name is not in RetroArch's NCI whitelist
2. The value contains characters that break the line-based reply parser (rare — embedded newlines or null bytes)
3. The UDP query times out (RetroArch not reachable)

资料来源：[src/tools.ts:80-96]()

## UDP Transport Layer

The communication between mcp-retroarch and RetroArch uses UDP datagrams on a serial-by-default basis.

### Connection Behavior

```typescript
async connect(): Promise<void> {
  return new Promise((resolve, reject) => {
    const sock = dgram.createSocket("udp4");
    sock.once("error", (err) => reject(err));
    sock.bind(0, () => {
      sock.on("message", (msg) => {
        const cb = this.pending;
        if (!cb) return;       // unsolicited or late reply — drop
        this.pending = null;
        cb(msg);
      });
      sock.on("error", () => { /* swallow late errors */ });
      this.socket = sock;
      resolve();
    });
  });
}
```

资料来源：[src/retroarch.ts:10-27]()

### Serial Query Model

The NCI protocol carries no request IDs, so matching responses by command-name echo is fragile. mcp-retroarch uses a **serial-by-default** approach: only one query can be in flight at a time.

```typescript
async query(command: string): Promise<Buffer> {
  if (!this.socket) await this.connect();
  if (this.pending) {
    throw new Error("retroarch query already in flight (client is serial)");
  }
  // ... send and wait for response
}
```

资料来源：[src/retroarch.ts:50-58]()

### Fire-and-Forget Commands

Hotkey-style commands (pause toggle, frame advance, etc.) use fire-and-forget semantics since the NCI doesn't acknowledge them:

```typescript
async send(command: string): Promise<void> {
  if (!this.socket) await this.connect();
  return new Promise((resolve, reject) => {
    this.socket!.send(command, this.port, this.host, (err) =>
      err ? reject(err) : resolve(),
    );
  });
}
```

资料来源：[src/retroarch.ts:35-43]()

## MCP Client Registration

After configuring mcp-retroarch, register it with your MCP client.

### Claude Code

```bash
claude mcp add retroarch --scope user mcp-retroarch
```

Verify registration:

```bash
claude mcp list
# retroarch: mcp-retroarch - ✓ Connected
```

资料来源：[README.md:38-43]()

### Claude Desktop

Edit `claude_desktop_config.json`:

| Platform | Path |
|----------|------|
| macOS | `~/Library/Application Support/Claude/claude_desktop_config.json` |
| Windows | `%APPDATA%\Claude\claude_desktop_config.json` |
| Linux | `~/.config/Claude/claude_desktop_config.json` |

```json
{
  "mcpServers": {
    "retroarch": {
      "command": "mcp-retroarch"
    }
  }
}
```

资料来源：[README.md:45-57]()

After editing, **restart Claude Desktop** for changes to take effect.

## Troubleshooting

| Symptom | Cause / Fix |
|---------|-------------|
| `RetroArch query timed out` | Network Commands aren't enabled in RetroArch, or the port doesn't match `RETROARCH_PORT`. Confirm `network_cmd_enable = "true"` in `retroarch.cfg`. UDP datagrams can be dropped under load even on loopback — if a single call times out but a retry succeeds, that's the cause. |
| `READ_CORE_MEMORY failed: no memory map defined` | The loaded libretro core doesn't advertise a system memory map. Try `retroarch_read_ram` (CHEEVOS path). |
| `READ_CORE_MEMORY failed: no descriptor for address` | The address isn't covered by the core's memory map. Either a different core would expose it, or the address is outside the system bus. |
| Screenshots don't appear where I expect | RetroArch saves to its configured screenshot directory. This cannot be queried via NCI — check via **Settings → Directory → Screenshot**. |

资料来源：[README.md:85-98]()

## Development Configuration

For local development:

```bash
npm install
npm run dev      # tsc --watch
```

资料来源：[README.md:75-79]()

Smoke test against a running RetroArch:

```bash
node .scratch/smoke.cjs
```

资料来源：[README.md:80-82]()

## Configuration Summary

| Layer | Setting | Default | Source |
|-------|---------|---------|--------|
| MCP Server | `RETROARCH_HOST` | `127.0.0.1` | Environment variable |
| MCP Server | `RETROARCH_PORT` | `55355` | Environment variable |
| RetroArch | `network_cmd_enable` | `"true"` | `retroarch.cfg` or GUI |
| RetroArch | `network_cmd_port` | `55355` | `retroarch.cfg` or GUI |

资料来源：[README.md:30-35, 52-54]()

## Version History

| Version | Date | Configuration Changes |
|---------|------|----------------------|
| 0.1.2 | 2026-05-15 | Tool description quality pass — improved configuration parameter documentation |
| 0.1.1 | 2026-05-11 | Non-blocking startup — server no longer waits for RetroArch connectivity |
| 0.1.0 | 2026-05-10 | Initial release with UDP client and MCP server |

资料来源：[CHANGELOG.md:1-30]()

---

---

## Doramagic Pitfall Log

Project: dmang-dev/mcp-retroarch

Summary: Found 7 potential pitfall items; 0 are high/blocking. Highest priority: configuration - 可能修改宿主 AI 配置.

## 1. configuration · 可能修改宿主 AI 配置

- Severity: medium
- Evidence strength: source_linked
- Finding: 项目面向 Claude/Cursor/Codex/Gemini/OpenCode 等宿主，或安装命令涉及用户配置目录。
- User impact: 安装可能改变本机 AI 工具行为，用户需要知道写入位置和回滚方法。
- Suggested check: 列出会写入的配置文件、目录和卸载/回滚步骤。
- Guardrail action: 涉及宿主配置目录时必须给回滚路径，不能只给安装命令。
- Evidence: capability.host_targets | github_repo:1234498337 | https://github.com/dmang-dev/mcp-retroarch | host_targets=mcp_host, claude

## 2. capability · 能力判断依赖假设

- Severity: medium
- Evidence strength: source_linked
- Finding: README/documentation is current enough for a first validation pass.
- User impact: 假设不成立时，用户拿不到承诺的能力。
- Suggested check: 将假设转成下游验证清单。
- Guardrail action: 假设必须转成验证项；没有验证结果前不能写成事实。
- Evidence: capability.assumptions | github_repo:1234498337 | https://github.com/dmang-dev/mcp-retroarch | README/documentation is current enough for a first validation pass.

## 3. maintenance · 维护活跃度未知

- Severity: medium
- Evidence strength: source_linked
- Finding: 未记录 last_activity_observed。
- User impact: 新项目、停更项目和活跃项目会被混在一起，推荐信任度下降。
- Suggested check: 补 GitHub 最近 commit、release、issue/PR 响应信号。
- Guardrail action: 维护活跃度未知时，推荐强度不能标为高信任。
- Evidence: evidence.maintainer_signals | github_repo:1234498337 | https://github.com/dmang-dev/mcp-retroarch | last_activity_observed missing

## 4. security_permissions · 下游验证发现风险项

- Severity: medium
- Evidence strength: source_linked
- Finding: no_demo
- User impact: 下游已经要求复核，不能在页面中弱化。
- Suggested check: 进入安全/权限治理复核队列。
- Guardrail action: 下游风险存在时必须保持 review/recommendation 降级。
- Evidence: downstream_validation.risk_items | github_repo:1234498337 | https://github.com/dmang-dev/mcp-retroarch | no_demo; severity=medium

## 5. security_permissions · 存在评分风险

- Severity: medium
- Evidence strength: source_linked
- Finding: no_demo
- User impact: 风险会影响是否适合普通用户安装。
- Suggested check: 把风险写入边界卡，并确认是否需要人工复核。
- Guardrail action: 评分风险必须进入边界卡，不能只作为内部分数。
- Evidence: risks.scoring_risks | github_repo:1234498337 | https://github.com/dmang-dev/mcp-retroarch | no_demo; severity=medium

## 6. maintenance · issue/PR 响应质量未知

- Severity: low
- Evidence strength: source_linked
- Finding: issue_or_pr_quality=unknown。
- User impact: 用户无法判断遇到问题后是否有人维护。
- Suggested check: 抽样最近 issue/PR，判断是否长期无人处理。
- Guardrail action: issue/PR 响应未知时，必须提示维护风险。
- Evidence: evidence.maintainer_signals | github_repo:1234498337 | https://github.com/dmang-dev/mcp-retroarch | issue_or_pr_quality=unknown

## 7. maintenance · 发布节奏不明确

- Severity: low
- Evidence strength: source_linked
- Finding: release_recency=unknown。
- User impact: 安装命令和文档可能落后于代码，用户踩坑概率升高。
- Suggested check: 确认最近 release/tag 和 README 安装命令是否一致。
- Guardrail action: 发布节奏未知或过期时，安装说明必须标注可能漂移。
- Evidence: evidence.maintainer_signals | github_repo:1234498337 | https://github.com/dmang-dev/mcp-retroarch | release_recency=unknown

<!-- canonical_name: dmang-dev/mcp-retroarch; human_manual_source: deepwiki_human_wiki -->
