Doramagic 项目包 · 项目说明书
mcp-pine 项目
生成时间:2026-05-17 20:22:23 UTC
项目介绍
mcp-pine 是一个基于 Model Context Protocol (MCP) 的桥接服务器,用于连接 MCP 客户端(如 Claude Code、Claude Desktop)与支持 PINE 协议的 PlayStation 系列模拟器。该项目使 AI 助手能够直接读取和写入模拟器内存、保存/加载游戏状态,从而实现自动化游戏调试、内存修改、状态快照实验等高级功能。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
概述
mcp-pine 是一个基于 Model Context Protocol (MCP) 的桥接服务器,用于连接 MCP 客户端(如 Claude Code、Claude Desktop)与支持 PINE 协议的 PlayStation 系列模拟器。该项目使 AI 助手能够直接读取和写入模拟器内存、保存/加载游戏状态,从而实现自动化游戏调试、内存修改、状态快照实验等高级功能。
资料来源:README.md
项目背景与目标
在传统的游戏逆向工程和模拟器调试中,开发者需要手动使用调试工具或编写专用脚本与模拟器交互。mcp-pine 的出现旨在将这种能力抽象为一个标准化的 MCP 工具集,使 AI 助手能够:
- 实时查询游戏运行状态和元数据
- 读取任意内存地址的数据
- 写入内存实现游戏修改(Cheats/Pokes)
- 保存和恢复游戏快照
- 执行自动化的内存搜索和结构解码
资料来源:README.md
支持的模拟器
mcp-pine 目前支持以下模拟器:
| 模拟器 | 状态 | 说明 |
|---|---|---|
| PCSX2 | 完整支持 | 1.7.x Qt 或更高版本,支持 PINE Server |
| RPCS3 | 实验性支持 | 有独立的 IPC 实现,与 PINE 操作码兼容 |
| Duckstation | 部分支持 | 取决于具体编译版本是否包含 PINE Server |
对于 PCSX2,用户只需在 Settings → Advanced → Enable PINE Server 中启用即可,无需脚本或控制台命令。默认插槽为 28011。
资料来源:README.md
系统架构
mcp-pine 采用典型的客户端-服务器分层架构:
graph TD
A[MCP 客户端<br/>Claude Code/Desktop] -->|MCP 协议<br/>stdio| B[mcp-pine<br/>桥接服务器]
B -->|PINE 协议<br/>Unix Socket| C[PCSX2 PINE Server]
B -->|PINE 协议<br/>TCP| D[RPCS3 IPC Server]
B -->|PINE 协议<br/>Socket| E[Duckstation PINE]
F[EE 主内存<br/>0x00100000-0x01FFFFFF] --> C
G[IOP RAM<br/>0x1C000000+] --> C核心组件
| 组件 | 文件位置 | 职责 |
|---|---|---|
| PineClient | src/pine.ts | 底层 PINE 协议通信,处理 socket 连接、请求队列、响应解析 |
| 工具注册器 | src/tools.ts | 定义 MCP 工具 schema,实现工具调用路由 |
| 入口点 | src/index.ts | MCP 服务器初始化,工具注册,stdin/stdout 管道 |
资料来源:src/pine.ts,src/tools.ts
MCP 工具集详解
mcp-pine 提供以下 MCP 工具,分为四个类别:
连接与自检
| 工具名 | 功能描述 | 返回值 |
|---|---|---|
pine_ping | 检查与模拟器的连接,获取模拟器版本 | OK — emulator: VERSION |
游戏信息查询
| 工具名 | 功能描述 | 返回值示例 |
|---|---|---|
pine_get_info | 批量获取游戏元数据(标题、序列号、CRC、版本、状态) | Title: GameName\nSerial: SLUS-12345\nDisc CRC: ABCD1234\nGame version: 1.00\nStatus: running |
pine_get_status | 获取模拟器运行状态 | Status: running/paused/shutdown/unknown |
内存读取
| 工具名 | 数据宽度 | 对齐要求 | 说明 |
|---|---|---|---|
pine_read8 | 8 位 | 无 | 单字节读取 |
pine_read16 | 16 位 | 2 字节 | 小端序 |
pine_read32 | 32 位 | 4 字节 | 小端序,用于指针、RGBA 颜色 |
pine_read64 | 64 位 | 8 字节 | 完整 PS2 EE 指针、大 ID |
pine_read_range | 可变 | 自动选择最大对齐 | 批量读取最多 4096 字节 |
内存写入
| 工具名 | 数据宽度 | 对齐要求 | 风险提示 |
|---|---|---|---|
pine_write8 | 8 位 | 无 | 直接写入,无撤销 |
pine_write16 | 16 位 | 2 字节 | 覆盖式写入,绕过 TLB |
pine_write32 | 32 位 | 4 字节 | 32 位 Cheats/Pokes |
pine_write64 | 64 位 | 8 字节 | 完整 64 位值写入 |
状态管理
| 工具名 | 功能描述 | 参数 |
|---|---|---|
pine_save_state | 保存当前游戏状态到指定槽位 | slot: 0-255 |
pine_load_state | 从指定槽位加载游戏状态 | slot: 0-255 |
资料来源:src/tools.ts
配置与环境变量
mcp-pine 通过环境变量进行配置:
| 环境变量 | 默认值 | 用途 |
|---|---|---|
PINE_TARGET | pcsx2 | 指定模拟器名称,用于 Unix socket 文件路径前缀 |
PINE_SLOT | 28011 | PINE 插槽号(Linux/macOS)或 TCP 端口(Windows) |
PINE_PIPELINE_BATCH | 1 | 批量请求数,控制性能与稳定性权衡 |
连接路径说明
| 操作系统 | 连接方式 | 路径格式 |
|---|---|---|
| Linux | Unix Socket | $XDG_RUNTIME_DIR/<target>.sock.<slot> 或 $TMPDIR/<target>.sock.<slot> 或 /tmp/<target>.sock.<slot> |
| macOS | Unix Socket | 同 Linux |
| Windows | TCP | 127.0.0.1:<slot> |
资料来源:README.md
安装与部署
方式一:npm 全局安装
npm install -g mcp-pine
方式二:npx 临时运行
npx -y mcp-pine
方式三:源码开发
git clone https://github.com/dmang-dev/mcp-pine
cd mcp-pine
npm install
Claude Code 集成
claude mcp add pine --scope user mcp-pine
验证连接:
claude mcp list
# pine: mcp-pine - ✓ Connected
Claude Desktop 集成
编辑配置文件:
| 平台 | 路径 |
|---|---|
| macOS | ~/Library/Application Support/Claude/claude_desktop_config.json |
| Windows | %APPDATA%\Claude\claude_desktop_config.json |
| Linux | ~/.config/Claude/claude_desktop_config.json |
{
"mcpServers": {
"pine": {
"command": "mcp-pine"
}
}
}
资料来源:README.md
已知问题与限制
PCSX2 PINE 服务器脆弱性
PCSX2 的 PINE 服务器请求队列非常脆弱。当有超过约 6-7 个飞行中的请求时,服务器会静默丢弃请求,导致回复管道失同步。一旦失同步,即使新的 pine_ping 也会超时,只能通过完全重启 PCSX2 恢复。
缓解措施:
pine_read_range默认以完全串行方式发出请求- 使用
PINE_PIPELINE_BATCH=2或更高值可提升延迟,但有失同步风险
内存访问注意事项
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 读取返回零值 | 地址在未分配区域 | 尝试 0x00100000(几乎总在 EE RAM 内) |
| 数值看起来损坏 | 对齐问题 | PINE 不强制对齐,未对齐的多字节读取会返回对齐地址下的数据 |
| 字符串解析错误 | 字节序问题 | PINE 返回小端序,字符串应使用 read_range 按字节读取 |
| 写入 IOP ROM 无效 | 只读区域 | BIOS、IOP ROM 区域写入会被静默丢弃 |
性能基准
| 操作 | 耗时 | 说明 |
|---|---|---|
| 完整 4096 字节读取 | ~52 ms | PCSX2 v2.6.3,loopback TCP |
单次 pine_ping | <1 ms | 本地连接 |
| 状态保存/加载 | 取决于存档大小 | 通过 PINE 协议传输 |
资料来源:README.md,CHANGELOG.md
项目依赖
mcp-pine 的核心依赖非常精简:
| 依赖 | 版本 | 用途 |
|---|---|---|
@modelcontextprotocol/sdk | ^1.12.0 | MCP 协议 SDK,处理 stdio 通信 |
开发依赖:
| 依赖 | 版本 | 用途 |
|---|---|---|
typescript | ^5.5.0 | TypeScript 编译 |
@types/node | ^22.0.0 | Node.js 类型定义 |
资料来源:package.json
开发指南
本地开发
npm install
npm run dev # tsc --watch 监听文件变化
冒烟测试
在运行 PCSX2 并启用 PINE Server 后:
node .scratch/smoke.cjs
相关项目
资料来源:README.md
版本历史
| 版本 | 日期 | 主要变更 |
|---|---|---|
| 0.2.1 | 2026-05-15 | 工具描述质量改进,完整 TDQS 模板重构 |
| 0.2.0 | 2026-05-10 | 新增 pine_read_range,10 秒超时,文档化 PCSX2 管道问题 |
| 0.1.0 | 初始版本 | 基础 MCP 工具集 |
资料来源:CHANGELOG.md
许可证
本项目采用 MIT 许可证开源。
资料来源:README.md
快速开始
本页面介绍如何快速部署和配置 mcp-pine,这是一个基于 Model Context Protocol (MCP) 的桥接工具,用于连接 Claude 等 AI 助手与支持 PINE 协议的模拟器(PCSX2、RPCS3、Duckstation),从而实现对 PlayStation 模拟器的内存读取、写入和状态管理。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
架构概览
graph TD
subgraph "MCP 客户端"
A[Claude Desktop / Claude Code]
end
subgraph "mcp-pine 桥接层"
B[MCP Server<br/>stdio 传输]
C[PINE Client<br/>网络通信]
end
subgraph "目标模拟器"
D[PCSX2 / RPCS3 / Duckstation]
E[PINE Server]
end
A -->|"MCP 协议"| B
B -->|"工具调用"| C
C -->|"PINE 协议"| E
E -->|"游戏状态"| D
style B fill:#e1f5fe
style C fill:#fff3e0
style E fill:#f3e5f5工作原理:mcp-pine 作为 MCP 服务器运行在 stdio 管道上,将 MCP 工具调用转换为 PINE 协议的二进制请求,通过 TCP 或 Unix 域套接字发送至模拟器的 PINE Server,从而实现对模拟器内存空间的读写操作。
前置要求
| 组件 | 要求 | 说明 |
|---|---|---|
| Node.js | ≥ 18.0.0 | 用于运行 mcp-pine |
| 包管理器 | npm / npx | 安装和执行 |
| 模拟器 | PCSX2 1.7.x+ / RPCS3 / Duckstation | 支持 PINE 协议 |
| MCP 客户端 | Claude Desktop / Claude Code | AI 助手界面 |
| 操作系统 | Windows / macOS / Linux | 跨平台支持 |
安装方式
方式一:全局安装(推荐)
npm install -g mcp-pine
安装完成后,mcp-pine 命令将全局可用:
mcp-pine
资料来源:README.md:安装方法
方式二:npx 免安装运行
如仅需临时使用,无需安装依赖:
npx -y mcp-pine
资料来源:README.md:npx 安装
方式三:源码本地开发
git clone https://github.com/dmang-dev/mcp-pine
cd mcp-pine
npm install # 同时运行 build(prepare hook 触发)
开发模式热重载:
npm run dev # tsc --watch 监听源码变更
资料来源:README.md:clone and develop
环境变量配置
mcp-pine 通过环境变量控制连接目标和通信方式:
| 环境变量 | 默认值 | 说明 |
|---|---|---|
PINE_TARGET | pcsx2 | 模拟器标识名称,用于生成 Unix 套接字路径 <target>.sock.<slot> |
PINE_SLOT | 28011 | PINE 槽位/端口号 |
PINE_HOST | 127.0.0.1 | TCP 目标主机(Windows 默认使用 TCP) |
PINE_SOCKET_PATH | 自动解析 | Unix 套接字路径,优先级最高 |
PINE_PIPELINE_BATCH | 1 | 请求流水线批次大小,建议保持默认 |
跨平台套接字路径解析规则:
graph TD
A[启动 mcp-pine] --> B{操作系统?}
B -->|Windows| C[使用 TCP<br/>127.0.0.1:PINE_SLOT]
B -->|Linux/macOS| D[检查 XDG_RUNTIME_DIR]
D -->|存在| E[使用 $XDG_RUNTIME_DIR/<target>.sock.<slot>]
D -->|不存在| F[检查 TMPDIR]
F -->|存在| G[使用 $TMPDIR/<target>.sock.<slot>]
F -->|不存在| H[使用 /tmp/<target>.sock.<slot>]
style C fill:#ffcdd2
style E fill:#c8e6c9
style G fill:#c8e6c9
style H fill:#c8e6c9资料来源:README.md:Configuration
模拟器配置
PCSX2 设置
- 启动 PCSX2(1.7.x Qt 或更高版本)
- 进入 Settings → Advanced
- 启用 Enable PINE Server 选项
- 默认槽位为 28011(如需修改,记下新端口并设置
PINE_SLOT) - 加载任意游戏
注意:无需脚本或控制台命令,PINE 启用后始终保持运行状态。
资料来源:README.md:PCSX2
RPCS3 设置
RPCS3 实现了兼容 PINE 操作码的 IPC 机制,但线级兼容性尚未完全测试:
- 进入 Configuration → Advanced
- 启用 IPC Server(具体菜单位置请参考当前 RPCS3 文档)
- 记录配置的端口号
- 运行命令:
PINE_TARGET=rpcs3 PINE_SLOT=<端口号> mcp-pine
资料来源:README.md:RPCS3
Duckstation 设置
- 确认 Duckstation 构建版本包含 PINE 服务器(并非所有版本都包含)
- 如包含,设置环境变量后运行:
PINE_TARGET=duckstation PINE_SLOT=<端口号> mcp-pine
资料来源:README.md:Duckstation
MCP 客户端配置
Claude Code (CLI)
添加 MCP 服务器:
claude mcp add pine --scope user mcp-pine
验证连接状态:
claude mcp list
# 输出示例:pine: mcp-pine - ✓ Connected
资料来源:README.md:Claude Code (CLI)
Claude Desktop
编辑配置文件 claude_desktop_config.json:
| 平台 | 配置文件路径 |
|---|---|
| macOS | ~/Library/Application Support/Claude/claude_desktop_config.json |
| Windows | %APPDATA%\Claude\claude_desktop_config.json |
| Linux | ~/.config/Claude/claude_desktop_config.json |
添加服务器配置:
{
"mcpServers": {
"pine": {
"command": "mcp-pine"
}
}
}
修改配置后需重启 Claude Desktop。
资料来源:README.md:Claude Desktop
其他 MCP 客户端
mcp-pine 使用标准 MCP over stdio 协议,可连接任何兼容 MCP 的客户端:
mcp-pine # 直接运行,stdin/stdout 用于通信
可用工具一览
连接成功后,以下 MCP 工具将自动注册到客户端:
| 工具名称 | 功能描述 | 用途场景 |
|---|---|---|
pine_ping | 获取模拟器版本 | 连接验证 |
pine_get_info | 获取游戏完整信息 | 游戏识别 |
pine_get_status | 获取运行状态 | 状态监控 |
pine_read8/16/32/64 | 读取指定地址内存 | 内存读取 |
pine_write8/16/32/64 | 写入指定地址内存 | 内存修改 |
pine_read_range | 批量读取内存区域 | 大块数据读取 |
pine_save_state | 保存游戏状态 | 存档快照 |
pine_load_state | 加载游戏状态 | 恢复存档 |
地址参数说明:
- 地址为 EE 主地址空间的绝对字节地址
- 多字节访问需对齐(
read16需 2 字节对齐,read32需 4 字节对齐) - 推荐范围:
0x00100000-0x01FFFFFF(EE 主内存,99% 游戏状态所在区域) - 未映射地址返回 PINE FAIL 响应
资料来源:src/tools.ts:工具定义
快速验证流程
sequenceDiagram
participant User as 用户
participant MCP as Claude MCP
participant Bridge as mcp-pine
participant Emu as PCSX2
User->>Emu: 1. 启动 PCSX2 并启用 PINE Server
User->>MCP: 2. 配置并启动 Claude Desktop
MCP->>Bridge: 3. 启动 mcp-pine 进程
Bridge->>Emu: 4. 建立 PINE 连接
User->>MCP: 5. 调用 pine_ping
MCP->>Bridge: 6. 转发工具调用
Bridge->>Emu: 7. PINE Version 请求
Emu-->>Bridge: 8. 返回版本信息
Bridge-->>MCP: 9. 返回工具结果
MCP-->>User: 10. 显示 "OK — emulator: PCSX2 v2.x.x"首次使用建议按以下顺序测试:
# 步骤 1:验证连接
pine_ping
# 预期输出:OK — emulator: PCSX2 v2.x.x
# 步骤 2:获取游戏信息
pine_get_info
# 预期输出:Title, Serial, Disc CRC, Game version, Status
# 步骤 3:读取内存(示例:EE RAM 起始地址)
pine_read32 address=0x00100000
# 预期输出:ADDR_HEX: VAL_DEC (0xVAL_HEX)
# 步骤 4:保存当前状态
pine_save_state slot=0
# 预期输出:State saved to slot 0
# 步骤 5:测试写入(谨慎操作)
pine_write32 address=0x00100000 value=0
# 预期输出:Wrote 0 → ADDR_HEX
常见问题排查
| 问题现象 | 原因 | 解决方案 |
|---|---|---|
Cannot reach PINE server | 模拟器未运行、PINE 未启用、端口不匹配 | 检查 PINE_SLOT 配置 |
PINE FAIL response (0xFF) | 模拟器拒绝请求(无游戏加载、地址未映射) | 加载游戏、检查地址有效性 |
| 读取返回全零 | 地址在未分配区域 | 尝试 0x00100000(几乎总是 EE RAM 内部) |
| 数值看起来损坏 | 字节序错误 | PINE 返回小端序,字符串读取使用 read_range |
pine_ping 超时(10s) | PCSX2 PINE 服务器卡死 | 完全重启 PCSX2(重新连接无效) |
重要提示:PCSX2 的 PINE 服务器请求队列较脆弱,当超过约 6 个飞行中请求时会静默丢弃请求,导致回复错位与错误客户端配对。一旦卡死,只能通过完全重启模拟器恢复。mcp-pine 默认使用完全串行请求(PINE_PIPELINE_BATCH=1),实测 Loopback TCP 速度足够(4096 字节读取约 52ms)。
资料来源:README.md:Troubleshooting
下一步
资料来源:README.md:安装方法
系统架构
mcp-pine 是一个基于 Model Context Protocol (MCP) 的桥接服务器,用于连接 MCP 客户端(如 Claude Desktop、Claude Code)与游戏模拟器的 PINE (Programmable Interface for Networked Emulation) 协议接口。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
1. 概述
mcp-pine 是一个基于 Model Context Protocol (MCP) 的桥接服务器,用于连接 MCP 客户端(如 Claude Desktop、Claude Code)与游戏模拟器的 PINE (Programmable Interface for Networked Emulation) 协议接口。
该项目的核心功能是让 AI 助手能够通过标准化的 MCP 工具接口与运行中的模拟器进行交互,实现内存读取、内存写入、保存/加载游戏状态等操作。资料来源:README.md
2. 整体架构
mcp-pine 采用分层架构设计,从上到下依次为:
| 层级 | 组件 | 职责 |
|---|---|---|
| MCP 协议层 | MCP Server | 暴露标准化的工具接口,处理 MCP 协议的请求/响应 |
| 工具定义层 | Tools Registry | 定义所有可用的 MCP 工具及其参数模式 |
| PINE 客户端层 | PineClient | 实现 PINE 协议的二进制通信 |
| 传输层 | Socket/TCP | 处理底层网络通信,支持 Unix Domain Socket 和 TCP |
graph TD
A[MCP 客户端<br/>Claude Desktop/Code] -->|stdio| B[MCP Server]
B -->|工具调用| C[Tools Registry]
C -->|调用方法| D[PineClient]
D -->|TCP/Unix Socket| E[模拟器 PINE Server]
F[src/index.ts] --> B
G[src/tools.ts] --> C
H[src/pine.ts] --> D资料来源:src/index.ts:1-50
3. 核心组件
3.1 MCP Server
MCP Server 是整个应用的入口点,负责初始化和启动服务。
// src/index.ts 核心逻辑
const server = new Server(
{ name: "mcp-pine", version: "0.2.0" },
{ capabilities: { tools: {} } },
);
registerTools(server, pine);
const transport = new StdioServerTransport();
await server.connect(transport);
关键特性:
- 使用
@modelcontextprotocol/sdk包提供的 Server 类 - 通过 StdioServerTransport 实现标准输入/输出通信
- 仅声明
tools能力,不使用资源或提示功能 - 工具注册由
registerTools()函数完成
资料来源:src/index.ts:20-35
3.2 PineClient
PineClient 是 PINE 协议的客户端实现,负责与模拟器的 PINE 服务器进行二进制通信。
主要职责:
| 功能 | 方法 | 说明 |
|---|---|---|
| 连接管理 | connect() | 建立 TCP 或 Unix Socket 连接 |
| 内存读取 | read8/16/32/64() | 按指定宽度读取内存 |
| 内存写入 | write8/16/32/64() | 按指定宽度写入内存 |
| 批量读取 | readRange() | 连续内存区域读取 |
| 状态管理 | saveState() / loadState() | 保存/加载游戏状态 |
| 信息查询 | getVersion/Tittle/Id/Uuid/Status() | 获取模拟器和游戏信息 |
连接传输方式:
// Unix Domain Socket (Linux/macOS)
net.createConnection({ path: this.descriptor.path })
// TCP 连接 (Windows)
net.createConnection({ host: this.descriptor.host, port: this.descriptor.port })
资料来源:src/pine.ts:60-90
3.3 工具注册系统
工具系统基于 TOOLS 数组定义,每个工具包含完整的元数据:
const TOOLS: Tool[] = [
{
name: "pine_ping",
description: "PURPOSE: ...",
inputSchema: { ... }
},
// ... 其他工具
];
工具处理流程:
graph LR
A[MCP 请求] -->|name + arguments| B[switch 语句]
B -->|匹配工具名| C[执行 PineClient 方法]
C -->|结果| D[ok() 格式化]
D -->|{content: [...]} | E[MCP 响应]资料来源:src/tools.ts:80-150
4. 通信协议
4.1 PINE Wire Protocol
PINE 协议采用二进制格式,消息结构如下:
| 字段 | 大小 | 字节序 | 说明 |
|---|---|---|---|
| Size | 4 字节 | Little-endian | 消息总长度 |
| Opcode | 1 字节 | - | 操作码 |
| Payload | N 字节 | Little-endian | 操作数据 |
Opcode 定义:
| 操作码 | 值 | 功能 |
|---|---|---|
| Version | 0x01 | 获取模拟器版本 |
| Title | 0x02 | 获取游戏标题 |
| ID | 0x03 | 获取游戏序列号 |
| UUID | 0x04 | 获取光盘 CRC |
| GameVersion | 0x05 | 获取游戏版本 |
| Status | 0x06 | 获取运行状态 |
| Read8 | 0x10 | 读取 8 位 |
| Read16 | 0x11 | 读取 16 位 |
| Read32 | 0x12 | 读取 32 位 |
| Read64 | 0x13 | 读取 64 位 |
| Write8 | 0x14 | 写入 8 位 |
| Write16 | 0x15 | 写入 16 位 |
| Write32 | 0x16 | 写入 32 位 |
| Write64 | 0x17 | 写入 64 位 |
| SaveState | 0x30 | 保存状态 |
| LoadState | 0x31 | 加载状态 |
资料来源:src/pine.ts:1-50
4.2 请求队列管理
PineClient 内部维护一个待处理请求队列:
interface Pending {
size: number;
resolve: (data: Buffer) => void;
reject: (err: Error) => void;
}
接收数据时的帧解析流程:
graph TD
A[接收数据 chunk] --> B[追加到 buffer]
B --> C{buffer.length >= 4?}
C -->|否| F[等待更多数据]
C -->|是| D[读取 size 字段]
D --> E{buffer.length >= size?}
E -->|否| F
E -->|是| G[提取完整帧]
G --> H[出队匹配]
H --> I[resolve/reject]
I --> B每个请求包含 10 秒超时,超时后自动 reject,防止请求永久挂起。资料来源:src/pine.ts:100-140
4.3 批量读取实现
readRange() 方法实现了高效的批量内存读取:
graph TD
A[输入: 地址 + 长度] --> B[计算最优分块]
B --> C{地址对齐检查}
C -->|8字节对齐| D[使用 read64]
C -->|4字节对齐| E[使用 read32]
C -->|2字节对齐| F[使用 read16]
C -->|其他| G[使用 read8]
D --> H[按 PIPELINE_BATCH 分批]
E --> H
F --> H
G --> H
H --> I[Promise.all 并行]
I --> J[合并结果到 Buffer]
J --> K[返回字节数组]关键参数:
| 参数 | 默认值 | 说明 |
|---|---|---|
PINE_PIPELINE_BATCH | 1 | 每批并发请求数 |
默认完全串行执行以避免 PCSX2 PINE 服务器请求队列丢失问题。资料来源:src/pine.ts:180-240
5. 配置系统
5.1 环境变量配置
| 变量名 | 默认值 | 说明 |
|---|---|---|
PINE_TARGET | pcsx2 | 模拟器名称,用于 Unix socket 文件路径前缀 |
PINE_SLOT | 28011 | PINE 槽位号/端口号 |
PINE_HOST | 127.0.0.1 | TCP 主机地址 |
PINE_SOCKET_PATH | - | 自定义 Unix socket 路径 |
PINE_PIPELINE_BATCH | 1 | 批量读取并发数 |
5.2 Socket 路径解析
graph TD
A[resolveSocket] --> B{平台判断}
B -->|Windows| C[TCP: 127.0.0.1:PINE_SLOT]
B -->|Linux/macOS| D{检查 PINE_SOCKET_PATH}
D -->|已设置| E[使用自定义路径]
D -->|未设置| F{检查 XDG_RUNTIME_DIR}
F -->|已设置| G[使用 $XDG_RUNTIME_DIR/<target>.sock.<slot>]
F -->|未设置| H[使用 /tmp/<target>.sock.<slot>]资料来源:src/pine.ts:50-80
6. MCP 工具清单
6.1 连接与信息工具
| 工具名 | 功能 | 返回值示例 |
|---|---|---|
pine_ping | 检测连接,获取模拟器版本 | OK — emulator: PCSX2 2.6.3 |
pine_get_info | 获取完整游戏信息 | 标题、序列号、CRC、版本、状态 |
pine_get_status | 获取运行状态 | Status: running |
6.2 内存读写工具
| 工具名 | 操作数 | 对齐要求 | 说明 |
|---|---|---|---|
pine_read8 | 1 字节 | 无 | 读取字节 |
pine_read16 | 2 字节 | 2 字节 | 读取 16 位整数 |
pine_read32 | 4 字节 | 4 字节 | 读取 32 位整数 |
pine_read64 | 8 字节 | 8 字节 | 读取 64 位整数 |
pine_write8 | 1 字节 | 无 | 写入字节 |
pine_write16 | 2 字节 | 2 字节 | 写入 16 位整数 |
pine_write32 | 4 字节 | 4 字节 | 写入 32 位整数 |
pine_write64 | 8 字节 | 8 字节 | 写入 64 位整数 |
pine_read_range | 1-4096 字节 | 自动适配 | 批量读取内存区域 |
6.3 状态管理工具
| 工具名 | 参数 | 说明 |
|---|---|---|
pine_save_state | slot: 0-255 | 保存游戏状态到指定槽位 |
pine_load_state | slot: 0-255 | 从指定槽位加载游戏状态 |
资料来源:src/tools.ts:150-300
7. 错误处理
7.1 已知错误模式
| 错误类型 | 表现 | 原因与处理 |
|---|---|---|
Cannot reach PINE server | 连接失败 | 模拟器未运行或 PINE 未启用 |
PINE FAIL response (0xFF) | 请求被拒绝 | 无游戏加载或地址未映射 |
| 读取返回零值 | 返回 0x0 | 地址在未分配内存区域 |
| 值损坏 | 数据异常 | 字节序或对齐问题 |
PINE call timed out (10s) | 超时 | PCSX2 PINE 服务器请求队列错位,需完全重启模拟器 |
7.2 PCSX2 PINE 服务器限制
PCSX2 的 PINE 服务器存在已知的请求队列脆弱性问题:
- 约 7-9 个在途请求时开始出现丢包
- 丢包后服务器回复管道永久错位
- 任何后续请求都会超时
- 仅重启模拟器可恢复
资料来源:README.md:60-80
8. 数据模型
8.1 模拟器状态枚举
type EmuStatus = "running" | "paused" | "shutdown" | "unknown";
状态值映射:
| 原始值 | 枚举值 | 含义 |
|---|---|---|
| 0 | running | 游戏正在运行 |
| 1 | paused | 游戏已暂停 |
| 2 | shutdown | 模拟器已关闭 |
| 其他 | unknown | 未知状态 |
8.2 64 位值处理
由于 JavaScript Number 类型精度限制(最大安全整数 2^53),64 位值通过字符串格式传输:
// 返回值格式
"12345678901234567890 (0xB54A3E8F9A4D6E2)"
// 使用 BigInt 内部处理
const n = v as bigint;
for (let j = 0; j < 8; j++) {
out[off + j] = Number((n >> BigInt(8 * j)) & 0xFFn);
}
资料来源:src/pine.ts:200-220
9. 依赖关系
graph TD
A[package.json] --> B[dependencies]
A --> C[devDependencies]
B --> D["@modelcontextprotocol/sdk ^1.12.0"]
C --> E["@types/node ^22.0.0"]
C --> F["typescript ^5.5.0"]项目使用 TypeScript 开发,通过 npm run dev 启动监听模式进行开发调试。资料来源:package.json:10-20
10. 总结
mcp-pine 的架构设计体现了几个关键原则:
- 分层解耦:MCP 协议层、工具定义层、PINE 客户端层完全分离
- 传输透明:根据平台自动选择 TCP 或 Unix Domain Socket
- 安全优先:默认串行请求避免模拟器 PINE 服务丢包
- 跨平台兼容:Windows/Linux/macOS 全平台支持
- 超时保护:10 秒超时防止请求永久挂起
资料来源:src/index.ts:1-50
PINE 协议详解
PINE(PlayStation Interactive Network Emulator)是一种用于 PlayStation 系列模拟器的进程间通信(IPC)协议。它允许外部工具直接读取和写入模拟器进程的内存空间,实现游戏状态检查、作弊修改、存档管理等高级功能。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
1. 概述
PINE(PlayStation Interactive Network Emulator)是一种用于 PlayStation 系列模拟器的进程间通信(IPC)协议。它允许外部工具直接读取和写入模拟器进程的内存空间,实现游戏状态检查、作弊修改、存档管理等高级功能。
mcp-pine 项目是一个 PINE 协议的 MCP(Model Context Protocol)服务端实现,使 AI 助手(如 Claude)能够通过标准化的工具接口与运行中的模拟器交互。
1.1 支持的模拟器
| 模拟器 | 协议实现 | 连接方式 | 备注 |
|---|---|---|---|
| PCSX2 | 原生 PINE | Unix Socket / TCP | 推荐使用 v1.7.x 及以上版本 |
| RPCS3 | 镜像 PINE | TCP | 兼容性未完全测试 |
| Duckstation | 原生 PINE | Unix Socket / TCP | 部分构建版本支持 |
资料来源:README.md:1-50
2. 架构设计
2.1 系统架构图
graph TB
subgraph "主机系统"
A[Claude / MCP 客户端] --> B[mcp-pine 服务端]
B --> C[PineClient]
C --> D{连接类型}
D -->|Linux/macOS| E[Unix Socket]
D -->|Windows| F[TCP 127.0.0.1]
end
subgraph "模拟器进程"
E --> G[PCSX2 PINE Server]
F --> G
end
G --> H[EE 主内存 0x00100000-0x01FFFFFF]
G --> I[IOP 内存 0x1C000000+]
G --> J[Scratchpad]2.2 核心组件
PineClient 是协议的核心实现类,负责:
- 底层的 Socket 连接管理
- PINE 操作码的发送与响应解析
- 读写请求的批量处理
- 超时控制与错误处理
资料来源:src/pine.ts:60-100
3. PINE 操作码定义
3.1 操作码表
| 操作码名称 | 数值 | 功能描述 | 返回类型 |
|---|---|---|---|
Op.Version | 0x01 | 获取模拟器版本 | UTF-8 字符串 |
Op.Title | 0x02 | 获取游戏标题 | UTF-8 字符串 |
Op.ID | 0x03 | 获取游戏序列号 | UTF-8 字符串 |
Op.UUID | 0x04 | 获取光盘 CRC | UTF-8 字符串 |
Op.GameVersion | 0x05 | 获取游戏版本 | UTF-8 字符串 |
Op.Status | 0x0F | 获取模拟器运行状态 | 32 位整数 |
Op.SaveState | 0x10 | 保存当前状态到指定槽位 | 无 |
Op.LoadState | 0x11 | 从指定槽位加载状态 | 无 |
Op.Read8 | 0x20 | 读取 8 位数据 | 字节 |
Op.Read16 | 0x21 | 读取 16 位数据 | 小端整数 |
Op.Read32 | 0x22 | 读取 32 位数据 | 小端整数 |
Op.Read64 | 0x23 | 读取 64 位数据 | 小端整数 |
Op.Write8 | 0x30 | 写入 8 位数据 | 无 |
Op.Write16 | 0x31 | 写入 16 位数据 | 无 |
Op.Write32 | 0x32 | 写入 32 位数据 | 无 |
Op.Write64 | 0x33 | 写入 64 位数据 | 无 |
资料来源:src/pine.ts:1-30
3.2 状态码定义
模拟器运行状态通过 32 位小端整数返回:
| 状态值 | 含义 |
|---|---|
| 0 | 运行中 (running) |
| 1 | 暂停 (paused) |
| 2 | 已关闭 (shutdown) |
| 其他 | 未知 (unknown) |
资料来源:src/pine.ts:40-45
4. 协议帧格式
4.1 请求帧结构
+----------------+----------------+----------------+
| Frame Size | Opcode (1B) | Arguments... |
| (UInt32 LE) | | |
+----------------+----------------+----------------+
- Frame Size: 4 字节,小端无符号整数,表示整个帧的字节数(包括自身)
- Opcode: 1 字节,操作码标识符
- Arguments: 可选参数数据
4.2 响应帧结构
+----------------+----------------+----------------+
| Frame Size | Status (1B) | Payload... |
| (UInt32 LE) | 0x00=OK 0xFF=FAIL |
+----------------+----------------+----------------+
- Status:
0x00表示成功,0xFF表示失败 - Payload: 成功时包含返回数据,失败时可能为空
4.3 帧解析流程
graph TD
A[接收数据块] --> B{缓冲区长度 >= 4?}
B -->|否| C[等待更多数据]
B -->|是| D[读取 Frame Size]
D --> E{缓冲区长度 >= Frame Size?}
E -->|否| F[等待更多数据]
E -->|是| G[提取完整帧]
G --> H[解析 Status 字节]
H --> I{Status == 0x00?}
I -->|是| J[解析 Payload]
I -->|否| K[抛出 PINE FAIL 错误]
J --> L[匹配 Pending 请求]
K --> L
L --> M[返回结果给调用者]资料来源:src/pine.ts:100-150
5. 连接管理
5.1 连接参数
| 参数 | 环境变量 | 默认值 | 说明 |
|---|---|---|---|
| 模拟器名称 | PINE_TARGET | pcsx2 | Unix Socket 文件名前缀 |
| 槽位/端口 | PINE_SLOT | 28011 | Unix Socket 序号或 TCP 端口 |
5.2 连接路径解析
// Linux/macOS: Unix Socket
$XDG_RUNTIME_DIR/<target>.sock.<slot>
$TMPDIR/<target>.sock.<slot>
/tmp/<target>.sock.<slot>
// Windows: TCP
127.0.0.1:<slot>
资料来源:src/pine.ts:50-80
5.3 超时控制
每个 PINE 调用都设置了 10 秒超时。如果模拟器未响应,请求将自动拒绝,防止无限等待。
资料来源:CHANGELOG.md:30-40
6. 内存读写机制
6.1 地址空间
PS2 模拟器的主要内存区域:
| 区域 | 地址范围 | 大小 | 说明 |
|---|---|---|---|
| EE 主内存 | 0x00100000 - 0x01FFFFFF | ~31 MB | 游戏状态主要存储区 |
| IOP 内存 | 0x1C000000+ | 可变 | I/O 处理器内存 |
| Scratchpad | 特定区域 | 较小 | 高速缓存 |
资料来源:src/tools.ts:20-30
6.2 数据编码
所有多字节数据均使用 小端序(Little Endian)编码:
地址: [0x00] [0x01] [0x02] [0x03]
数据: LSB ... ... MSB
字符串使用 UTF-8 编码,以空字符 (\0) 结尾,读取时自动去除尾部空字符。
资料来源:src/pine.ts:25-35
6.3 对齐要求
| 操作宽度 | 对齐要求 | 未对齐时的行为 |
|---|---|---|
| 8 位 | 无 | 正常工作 |
| 16 位 | 2 字节对齐 | 返回对齐地址下方的数据,静默损坏 |
| 32 位 | 4 字节对齐 | 返回对齐地址下方的数据,静默损坏 |
| 64 位 | 8 字节对齐 | 返回对齐地址下方的数据,静默损坏 |
重要警告: PCSX2 的 PINE 服务端不强制对齐访问,未对齐的地址会静默返回错误数据而无任何错误提示。
资料来源:src/tools.ts:30-45
6.4 批量读取实现
由于 PINE 协议本身不提供原生批量读取功能,read_range 工具通过客户端实现的序列读取实现:
graph LR
A[请求: 4096 字节] --> B{计算最优步长}
B --> C[起始地址 % 8 == 0 且剩余 >= 8?]
C -->|是| D[read64]
C -->|否| E{起始地址 % 4 == 0 且剩余 >= 4?}
E -->|是| F[read32]
E -->|否| G{起始地址 % 2 == 0?}
G -->|是| H[read16]
G -->|否| I[read8]
D --> J[移动游标]
F --> J
H --> J
I --> J
J --> K{还有剩余?}
K -->|是| B
K -->|否| L[组装 Buffer 返回]资料来源:src/pine.ts:50-80
7. PCSX2 已知限制
7.1 请求队列脆弱性
PCSX2 的 PINE 服务端存在一个关键缺陷:
- 当 同时发送约 7-9 个未完成请求时,服务端可能静默丢弃某些请求
- 一旦发生请求丢失,响应管道将与客户端失去同步
- 之后所有请求都会超时,包括
pine_ping - 唯一恢复方法: 完全重启 PCSX2
7.2 性能数据
| 操作 | 耗时 | 说明 |
|---|---|---|
| 单次 read64 | ~0.1 ms | 单次往返延迟 |
| 完整 4096 字节读取 | ~52 ms | 串行执行,约两帧 |
7.3 流水线配置
| 环境变量 | 默认值 | 说明 |
|---|---|---|
PINE_PIPELINE_BATCH | 1 | 每批发送的请求数,增大可降低延迟但有同步风险 |
资料来源:CHANGELOG.md:40-60
8. 错误处理
8.1 错误类型
| 错误现象 | 可能原因 | 解决方案 |
|---|---|---|
Cannot reach PINE server | 模拟器未运行、PINE 未启用、端口不匹配 | 检查 PINE_SLOT 设置 |
PINE FAIL response (0xFF) | 无游戏加载、地址未映射 | 确认游戏已加载,尝试 0x00100000 |
| 读取返回全零 | 地址在未分配区域 | 尝试 EE 主内存起始地址 |
| 数据看起来损坏 | 字节序误解 | PINE 返回小端序 |
PINE call timed out (10s) | PCSX2 队列同步失败 | 完全重启 PCSX2 |
资料来源:README.md:80-100
9. API 参考
9.1 核心方法签名
class PineClient {
// 元数据查询
async getVersion(): Promise<string>
async getTitle(): Promise<string>
async getId(): Promise<string>
async getUuid(): Promise<string>
async getGameVersion(): Promise<string>
async getStatus(): Promise<EmuStatus>
// 内存读取
async read8(addr: number): Promise<number>
async read16(addr: number): Promise<number>
async read32(addr: number): Promise<number>
async read64(addr: number): Promise<bigint>
async readRange(addr: number, length: number): Promise<Buffer>
// 内存写入
async write8(addr: number, value: number): Promise<void>
async write16(addr: number, value: number): Promise<void>
async write32(addr: number, value: number): Promise<void>
async write64(addr: number, value: bigint): Promise<void>
// 存档管理
async saveState(slot: number): Promise<void>
async loadState(slot: number): Promise<void>
}
9.2 64 位值处理
由于 JavaScript 的 number 类型只能安全表示 2^53 以内的整数,read64 和 write64 方法使用 bigint 类型:
// 读取
const value: bigint = await pine.read64(0x12345678)
// 写入
await pine.write64(0x12345678, 12345678901234n)
资料来源:src/pine.ts:40-50
10. 与 MCP 工具的映射
| MCP 工具名称 | 底层 PINE 操作 | 功能 |
|---|---|---|
pine_ping | Version | 连接测试 |
pine_get_info | Title + ID + UUID + GameVersion + Status | 批量获取游戏信息 |
pine_get_status | Status | 获取运行状态 |
pine_read8/16/32/64 | Read8/16/32/64 | 单值读取 |
pine_read_range | 串行调用 Read* | 批量读取 |
pine_write8/16/32/64 | Write8/16/32/64 | 单值写入 |
pine_save_state | SaveState | 保存存档 |
pine_load_state | LoadState | 加载存档 |
资料来源:src/tools.ts:100-200
11. 项目信息
| 项目元数据 | 值 |
|---|---|
| 项目名称 | mcp-pine |
| 许可证 | MIT |
| 主要依赖 | @modelcontextprotocol/sdk ^1.12.0 |
| 运行时 | Node.js / TypeScript |
资料来源:package.json:1-30
资料来源:README.md:1-50
工具参考
mcp-pine 提供了一套完整的 MCP 工具,用于与支持 PINE(Platform Independent Network Environment)协议的模拟器进行交互通信。通过这些工具,用户可以实现对模拟器运行状态的监控、内存读写操作以及存档管理等功能。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
工具分类概览
mcp-pine 的工具集按照功能可分为四大类别:
| 类别 | 工具数量 | 用途 |
|---|---|---|
| 连接与自检 | 2 | 连接验证、模拟器信息获取 |
| 内存读取 | 5 | 单字节到64位值的读取,支持范围读取 |
| 内存写入 | 4 | 8/16/32/64位值写入 |
| 存档管理 | 2 | 游戏状态保存与加载 |
资料来源:src/tools.ts:90-140
连接与自检工具
pine_ping
用途:验证与模拟器 PINE 服务器的连接状态,并获取模拟器版本信息。
参数:无
返回格式:OK — emulator: <version>
使用场景:在执行其他操作前验证连接是否正常。如果返回"Cannot reach PINE server"错误,表明模拟器未运行、PINE 未启用或端口配置不匹配。资料来源:src/tools.ts:95-98
pine_get_info
用途:获取当前运行游戏的完整信息摘要,包括游戏标题、序列号、Disc CRC、游戏版本和运行状态。
参数:无
返回格式:
Title: <游戏标题>
Serial: <序列号>
Disc CRC: <CRC校验值>
Game version: <游戏版本>
Status: <运行状态>
使用场景:快速确认当前加载的游戏及其状态,适用于多游戏环境下的上下文切换。资料来源:src/tools.ts:100-115
pine_get_status
用途:获取模拟器当前的运行状态。
参数:无
返回格式:Status: <running|paused|shutdown|unknown>
使用场景:在执行存档操作前确认游戏状态,或监控长时间运行任务的状态变化。资料来源:src/tools.ts:117-119
内存读取工具
所有内存读取工具均操作 PS2 主机的 EE(Emotion Engine)主地址空间,采用小端字节序。地址参数支持十六进制字面量(如 0x00200000)。
地址参数通用说明
| 字段 | 描述 |
|---|---|
| 有效范围 | 0x00100000-0x01FFFFFF(EE 主 RAM,99%的游戏状态在此区域) |
| 对齐要求 | 多字节访问必须按对应字节数对齐;PCSX2 的 PINE 不强制对齐,未对齐访问会静默返回损坏数据 |
| 错误处理 | 未映射或无效地址返回 PINE FAIL 响应 |
重要:如果需要读取未对齐的多字节数据,应使用 pine_read_range 并自行组装字节。
资料来源:src/tools.ts:30-50
pine_read8
用途:读取单个字节。
对齐要求:无对齐要求。
返回格式:ADDR_HEX: VAL_DEC (0xVAL_HEX)
pine_read16
用途:读取16位无符号小端值,适用于字符编码、游戏事件标志等。
对齐要求:2字节对齐。
返回格式:与 pine_read8 格式相同。
pine_read32
用途:读取32位无符号小端值,适用于 PS2 指针、计时器、分数等常见游戏状态字段。
对齐要求:4字节对齐。
返回格式:与 pine_read8 格式相同。
pine_read64
用途:读取64位无符号值,适用于完整的 PS2 指针、大型 ID、压缩双字状态。PS2 EE 是128位 MIPS 处理器,大量游戏状态确实存在于64位槽位中。
对齐要求:8字节对齐。
返回值说明:由于 JavaScript 无法精确表示超过 2^53 的整数,64位值以十进制字符串形式返回。资料来源:src/tools.ts:120-145
pine_read_range
用途:批量读取最多4096字节的连续内存区域。
参数:
| 参数 | 类型 | 描述 |
|---|---|---|
| address | integer | 起始地址 |
| length | integer | 要读取的字节数(最大4096) |
实现原理:PINE 协议本身没有原生批量读取功能,pine_read_range 在客户端通过序列化调用序列实现。每次调用选择最大对齐宽度(从8到1递减)。资料来源:src/pine.ts:90-130
性能指标:
- 完整4096字节读取耗时约52毫秒(PCSX2 v2.6.3,loopback TCP)
- 少于两个模拟帧,适合大多数工作负载
注意事项:PCSX2 的 PINE 服务器存在请求队列脆弱性问题。超过约7个飞行中请求时会静默丢弃请求,导致回复管道错位,后续所有请求超时,直至模拟器重启。因此默认情况下调用完全序列化执行。可通过环境变量 PINE_PIPELINE_BATCH 调整批处理大小(默认为1)。资料来源:src/pine.ts:95-110
内存写入工具
所有写入工具均具有破坏性,会覆盖指定地址的现有数据。
pine_write8 / pine_write16 / pine_write32 / pine_write64
用途:向模拟器内存写入指定宽度的值。
参数:
| 参数 | 类型 | 描述 |
|---|---|---|
| address | integer | 目标地址(必须符合对应宽度对齐要求) |
| value | integer/bigint | 要写入的值 |
返回格式:Wrote VAL_HEX → ADDR_HEX
对齐要求:与对应的读取工具相同。未对齐写入可能导致静默数据损坏。
错误场景:
- 地址未对齐:数据写入错误位置
- 地址未映射:PINE FAIL 响应
- 写入只读区域:行为取决于模拟器实现
资料来源:src/tools.ts:145-170
存档管理工具
pine_save_state
用途:将当前游戏状态保存到指定槽位。
参数:
| 参数 | 类型 | 范围 | 描述 |
|---|---|---|---|
| slot | integer | 0-255 | 存档槽位编号 |
槽位约定:PCSX2 图形界面通常使用 F1-F10 对应槽位0-9,但 PINE 协议支持完整的 0-255 范围。存档文件存储在模拟器的游戏专属目录中:
- Windows:
%USERPROFILE%\Documents\PCSX2\sstates - Linux/macOS:
~/.config/PCSX2/sstates
文件命名格式:<serial> (<crc>).<slot>.p2s
警告:保存到已有存档的槽位会覆盖原有数据,此操作不可逆。资料来源:src/tools.ts:70-85
pine_load_state
用途:从指定槽位加载游戏状态。
参数:与 pine_save_state 相同。
返回格式:Loaded state from slot <n>
使用场景:快速恢复之前的游戏进度,适用于:
- 尝试不同游戏决策前的快照
- 回归测试
- 调试时的状态重现
前置条件:需要先加载对应游戏的 ISO 文件,否则存档可能不兼容。
工具调用流程
以下流程图展示了典型工具调用序列的执行路径:
graph TD
A[MCP 客户端发起调用] --> B{工具类型}
B -->|查询类<br/>ping/get_info/get_status| C[构建请求帧]
B -->|读取类<br/>read8/16/32/64/range| C
B -->|写入类<br/>write8/16/32/64| C
B -->|存档类<br/>save/load_state| C
C --> D[加入待处理队列]
D --> E[PINE 协议编码]
E --> F{连接状态}
F -->|已连接| G[发送请求]
F -->|未连接| H[建立连接<br/>TCP Unix Socket]
H --> G
G --> I{等待响应}
I -->|超时 10s| J[抛出错误]
I -->|收到响应| K{响应码}
K -->|成功 0x00| L[解码响应]
K -->|失败 0xFF| M[返回 PINE FAIL]
L --> N[返回格式化结果]
M --> O[错误处理]环境变量配置
| 变量名 | 默认值 | 说明 |
|---|---|---|
PINE_TARGET | pcsx2 | 模拟器标识名,用于 Unix socket 文件路径前缀(Linux/macOS)或 TCP 连接标识 |
PINE_SLOT | 28011 | PINE 槽位/端口号 |
PINE_HOST | 127.0.0.1 | TCP 连接主机地址 |
PINE_SOCKET_PATH | 自动解析 | Unix socket 路径,覆盖其他 socket 相关配置 |
PINE_PIPELINE_BATCH | 1 | 批量请求数量,仅在了解 PCSX2 PINE 限制时调整 |
Socket 路径解析逻辑(Linux/macOS):
- 优先使用
XDG_RUNTIME_DIR - 其次尝试
TMPDIR - 最后回退到
/tmp
资料来源:README.md:40-55
错误处理与故障排除
| 错误现象 | 可能原因 | 解决方案 |
|---|---|---|
Cannot reach PINE server | 模拟器未运行、PINE 未启用、端口不匹配 | 检查 PINE_SLOT 配置,确认模拟器设置中已启用 PINE |
PINE FAIL response (0xFF) | 未加载游戏或地址未映射 | 确保游戏已加载,尝试地址 0x00100000 |
| 读取返回全零 | 地址在未分配区域 | 从 EE RAM 常用区域开始(0x00100000) |
| 值看起来损坏 | 字节序误解 | PINE 返回小端序;字符串使用 read_range 逐字节读取 |
PINE call timed out (10s) ping 后频繁使用 | PCSX2 PINE 请求队列错位 | 完全重启 PCSX2(仅重新连接无效) |
PCSX2 PINE 服务器已知限制
PCSX2 的 PINE 服务器存在一个已知的脆弱性问题:当飞行中请求超过约6个时,服务器会静默丢弃请求,导致回复管道错位。一旦发生这种情况,即使 pine_ping 也会超时,唯一恢复方法是完全重启模拟器。
为避免此问题:
- 默认情况下
mcp-pine完全序列化所有请求 - Loopback TCP 速度足够快,完整4KB读取仅需约52毫秒
- 如需更低延迟且可容忍偶发的模拟器重启,可设置
PINE_PIPELINE_BATCH=2或更高
资料来源:src/pine.ts:95-110
工具描述质量标准
mcp-pine 的每个工具描述都遵循 Glama 的工具定义质量评分(TDQS)标准,确保以下方面完整:
- 目的明确性:每个工具一句话说明其操作
- 使用指南:何时使用此工具而非其兄弟工具
- 行为透明:副作用、错误条件、破坏性操作说明
- 参数语义:超出 JSON Schema 的上下文信息
- 简洁性:无冗余描述
- 上下文完整性:错误模式、对齐要求、返回值格式
资料来源:CHANGELOG.md:20-35
快速参考表
| 工具名 | 参数 | 返回类型 | 破坏性 |
|---|---|---|---|
pine_ping | - | string | 否 |
pine_get_info | - | string | 否 |
pine_get_status | - | string | 否 |
pine_read8 | address | string | 否 |
pine_read16 | address | string | 否 |
pine_read32 | address | string | 否 |
pine_read64 | address | string | 否 |
pine_read_range | address, length | string | 否 |
pine_write8 | address, value | string | 是 |
pine_write16 | address, value | string | 是 |
pine_write32 | address, value | string | 是 |
pine_write64 | address, value | string | 是 |
pine_save_state | slot | string | 是 |
pine_load_state | slot | string | 是 |
资料来源:src/tools.ts:90-140
内存操作指南
mcp-pine 提供的核心功能之一是通过 PINE(PlayStation Interface for Networked Entertainment)协议对运行中的模拟器进行实时内存读写操作。该功能允许用户直接访问 PS2/PS3/PSP 等平台的内存地址空间,实现游戏状态检测、作弊修改、运行时数据分析等高级操作。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
概述
mcp-pine 提供的核心功能之一是通过 PINE(PlayStation Interface for Networked Entertainment)协议对运行中的模拟器进行实时内存读写操作。该功能允许用户直接访问 PS2/PS3/PSP 等平台的内存地址空间,实现游戏状态检测、作弊修改、运行时数据分析等高级操作。
内存操作由 8 个 MCP 工具组成,分为读取和写入两大类:
| 类别 | 工具 | 数据宽度 |
|---|---|---|
| 读取 | pine_read8 | 1 字节 |
| 读取 | pine_read16 | 2 字节 |
| 读取 | pine_read32 | 4 字节 |
| 读取 | pine_read64 | 8 字节 |
| 读取 | pine_read_range | 批量(最大 4096 字节) |
| 写入 | pine_write8 | 1 字节 |
| 写入 | pine_write16 | 2 字节 |
| 写入 | pine_write32 | 4 字节 |
| 写入 | pine_write64 | 8 字节 |
资料来源:src/tools.ts:56-130
地址空间与内存布局
PS2 EE 主内存
PS2 的 Emotion Engine(EE)主内存地址空间是最常用的访问区域:
| 地址范围 | 说明 |
|---|---|
0x00100000 - 0x01FFFFFF | EE 主 RAM(游戏状态主要存储区) |
0x1C000000+ | IOP RAM(声音/输入子系统) |
| 其他 | 预留/映射区域 |
💡 建议从 0x00100000 开始探测,该地址几乎所有游戏都会加载到 EE RAM 中。
资料来源:src/tools.ts:20-25
连接传输方式
| 平台 | 传输方式 | 地址格式 |
|---|---|---|
| Linux/macOS | Unix Domain Socket | $XDG_RUNTIME_DIR/<PINE_TARGET>.sock.<PINE_SLOT> |
| Windows | TCP | 127.0.0.1:<PINE_SLOT> |
资料来源:README.md:45-50
读取操作
单值读取工具
| 工具 | 对齐要求 | 返回格式 |
|---|---|---|
pine_read8 | 无 | ADDR: VAL_DEC (0xVAL_HEX) |
pine_read16 | 2 字节对齐 | ADDR: VAL_DEC (0xVAL_HEX) |
pine_read32 | 4 字节对齐 | ADDR: VAL_DEC (0xVAL_HEX) |
pine_read64 | 8 字节对齐 | 字符串格式(JS 无法原生表示完整 u64) |
#### 64 位值处理
由于 JavaScript Number 类型最大只能精确表示到 2^53,pine_read64 和 pine_write64 采用十进制字符串作为值编码,避免精度丢失。
// 资料来源:src/pine.ts:55-60
async read64(addr: number): Promise<bigint> {
const r = await this.call(Op.Read64, this._addr(addr));
return r.readBigUInt64LE(0);
}
资料来源:src/tools.ts:100-105
批量读取:pine_read_range
pine_read_range 是最高效的批量读取工具,单次调用最多读取 4096 字节:
// 资料来源:src/pine.ts:75-95
async readRange(start: number, length: number): Promise<Buffer> {
// 计算最优读取步骤:优先使用最大对齐宽度
let n: 1 | 2 | 4 | 8;
if (cursor % 8 === 0 && remaining >= 8) n = 8;
else if (cursor % 4 === 0 && remaining >= 4) n = 4;
else if (cursor % 2 === 0 && remaining >= 2) n = 2;
else n = 1;
}
#### 读取策略
工具内部采用自适应分块策略,在每个步骤选择最大可用对齐宽度:
graph TD
A[开始读取] --> B{地址 % 8 === 0 ?}
B -->|是 且 剩余 ≥ 8| C[read64 - 8字节]
B -->|否| D{地址 % 4 === 0 ?}
D -->|是 且 剩余 ≥ 4| E[read32 - 4字节]
D -->|否| F{地址 % 2 === 0 ?}
F -->|是 且 剩余 ≥ 2| G[read16 - 2字节]
F -->|否| H[read8 - 1字节]
C --> I[移动指针]
E --> I
G --> I
H --> I
I --> J{还有数据?}
J -->|是| B
J -->|否| K[组装结果 Buffer]#### 流水线控制
PINE 协议本身没有原生批量读取指令,pine_read_range 通过发送多个单次读取请求实现。默认配置为完全串行执行以避免 PCSX2 请求队列错位:
| 环境变量 | 默认值 | 说明 |
|---|---|---|
PINE_PIPELINE_BATCH | 1 | 每批发送请求数;1 = 完全串行(安全) |
⚠️ PCSX2 的 PINE 服务器存在已知缺陷:当流水线中有约 7-9 个请求时,会静默丢弃请求,导致后续所有回复与客户端错位,此时即使 pine_ping 也会超时,只能重启模拟器恢复。
实测性能(PCSX2 v2.6.3,localhost):
| 操作 | 耗时 |
|---|---|
| 完整 4096 字节读取 | ~52 ms |
| 单次 read32 | ~0.1 ms |
资料来源:src/pine.ts:100-115
写入操作
写入工具
| 工具 | 对齐要求 | 参数类型 |
|---|---|---|
pine_write8 | 无 | number |
pine_write16 | 2 字节对齐 | number |
pine_write32 | 4 字节对齐 | number |
pine_write64 | 8 字节对齐 | number(JS 会损失精度,模拟器端可能截断) |
⚠️ 破坏性操作:写入会直接修改游戏内存,可能导致游戏崩溃或存档损坏。
// 资料来源:src/tools.ts:62-70
case "pine_write8": {
await pine.write8(addr(), p.value as number);
return ok(`Wrote ${fmtHex(p.value as number)} → ${addrHex(addr())}`);
}
对齐要求与边界处理
PCSX2 对齐特性
关键警告:PCSX2 的 PINE 实现不强制对齐检查。对未对齐地址的多字节访问会静默返回对齐地址的数据,造成数据损坏。
| 访问宽度 | 对齐要求 | 错误行为 |
|---|---|---|
| 8 位 (read8/write8) | 无 | 无 |
| 16 位 (read16/write16) | 地址 % 2 === 0 | 未对齐时返回对齐地址数据 |
| 32 位 (read32/write32) | 地址 % 4 === 0 | 未对齐时返回对齐地址数据 |
| 64 位 (read64/write64) | 地址 % 8 === 0 | 未对齐时返回对齐地址数据 |
未对齐访问方案
如需读取未对齐地址的数据,建议:
- 使用
pine_read_range读取足够宽的字节范围 - 在客户端自行组装字节序
// 示例:读取未对齐的 32 位值
const bytes = await pine.readRange(0x00102001, 4); // 从 0x00102000 开始读 4 字节
const value = bytes.readUInt32LE(1); // 从偏移 1 开始解释
资料来源:src/tools.ts:10-20
字节序
PINE 协议使用 小端序(Little-Endian) 编码多字节值:
- LSB 存储在起始地址
- MSB 存储在起始地址 + 宽度 - 1
graph LR
subgraph "32 位值 0x12345678 在地址 0x00100000"
A0["0x00100000: 0x78 (LSB)"]
A1["0x00100001: 0x56"]
A2["0x00100002: 0x34"]
A3["0x00100003: 0x78 (MSB)"]
end💡 字符串读取(getTitle、getId等)使用 UTF-8 编码,通过readString方法处理截断空字节。
资料来源:src/pine.ts:35-40
超时与错误处理
超时机制
每个 PINE 调用都有 10 秒超时。超时后抛出 PINE call timed out 错误:
原因:
1. 模拟器未运行
2. PINE 服务器未启用(PCSX2: Settings → Advanced → Enable PINE Server)
3. PINE_SLOT 与模拟器配置不匹配
4. PCSX2 PINE 请求队列错位(需重启模拟器)
资料来源:README.md:70-80
常见错误
| 错误信息 | 原因 | 解决方案 |
|---|---|---|
Cannot reach PINE server | 连接失败 | 检查模拟器运行状态和 PINE_SLOT |
PINE FAIL response (0xFF) | 地址无映射或无游戏加载 | 确认游戏已加载,使用 0x00100000 探测 |
| 返回全零 | 地址未分配 | 尝试 0x00100000 |
| 数据异常 | 字节序误判或对齐错误 | 检查对齐,使用 read_range 手动组装 |
完整使用流程
graph TD
A[启动模拟器] --> B[启用 PINE 服务器]
B --> C[运行 mcp-pine]
C --> D[调用 pine_ping 验证连接]
D --> E{连接成功?}
E -->|否| F[检查配置和模拟器状态]
F --> B
E -->|是| G[调用 pine_get_info 获取游戏信息]
G --> H[选择目标地址]
H --> I[调用 read/write 工具]
I --> J{操作成功?}
J -->|否| K[检查对齐/地址/超时]
K --> I
J -->|是| L[处理返回值]工具调用示例
读取玩家分数(假设在 0x00201000)
工具: pine_read32
参数: { "address": 2113536 }
返回值: "0x00201000: 9500 (0x0000251c)"
批量扫描内存
工具: pine_read_range
参数: { "address": 1048576, "length": 4096 }
返回值: <Buffer: 4096 bytes>
修改生命值
工具: pine_write32
参数: { "address": 2097152, "value": 999 }
返回值: "Wrote 0x000003e7 → 0x00200000"
环境变量配置
| 变量 | 默认值 | 说明 |
|---|---|---|
PINE_TARGET | pcsx2 | 模拟器名称前缀(socket 文件名) |
PINE_SLOT | 28011 | PINE 槽位号(Linux/macOS 为 socket 后缀,Windows 为 TCP 端口) |
PINE_HOST | 127.0.0.1 | 主机地址(可覆盖) |
PINE_SOCKET_PATH | 自动 | 完整 socket 路径(完全覆盖自动解析) |
PINE_PIPELINE_BATCH | 1 | 批量请求数(建议仅在非 PCSX2 模拟器上增加) |
资料来源:README.md:45-55
安全注意事项
- 写入操作不可逆:修改内存会导致游戏状态永久改变,可能造成存档损坏
- 不要过度流水线:PCSX2 在超过 ~6 个并行请求时会静默失败
- 定期保存状态:使用
pine_save_state在修改前备份当前游戏状态 - 验证对齐:未对齐写入会产生静默数据损坏
相关工具
| 工具 | 功能 |
|---|---|
pine_ping | 验证连接(会话开始时调用) |
pine_get_info | 获取游戏元数据(标题、序列号、CRC) |
pine_get_status | 获取运行状态(running/paused/shutdown) |
pine_save_state | 保存当前游戏状态到槽位 |
pine_load_state | 从槽位加载游戏状态 |
资料来源:src/tools.ts:56-130
存档状态管理
存档状态管理(Save State Management)是 mcp-pine 项目为 PlayStation 2/3 模拟器提供的核心功能之一。该模块通过 PINE(Plugin Interface for Networked Emulation)协议实现模拟器内存状态的保存与恢复,允许 MCP 客户端在不中断游戏进程的情况下快速保存和加载游戏进度。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
概述
存档状态管理(Save State Management)是 mcp-pine 项目为 PlayStation 2/3 模拟器提供的核心功能之一。该模块通过 PINE(Plugin Interface for Networked Emulation)协议实现模拟器内存状态的保存与恢复,允许 MCP 客户端在不中断游戏进程的情况下快速保存和加载游戏进度。
mcp-pine 暴露了两个主要工具:
| 工具名称 | 功能 |
|---|---|
pine_save_state | 将当前模拟器状态保存到指定槽位 |
pine_load_state | 从指定槽位加载模拟器状态 |
资料来源:src/tools.ts:工具定义区域
系统架构
组件层次
┌─────────────────────────────────────────────┐
│ MCP 客户端 (Claude Code) │
└─────────────────────┬───────────────────────┘
│ 标准 MCP 协议 (stdio)
┌─────────────────────▼───────────────────────┐
│ mcp-pine 桥接服务 │
│ ┌─────────────────────────────────────┐ │
│ │ tools.ts - 工具定义层 │ │
│ │ • pine_save_state │ │
│ │ • pine_load_state │ │
│ └──────────────┬──────────────────────┘ │
│ ┌──────────────▼──────────────────────┐ │
│ │ pine.ts - PINE 客户端实现 │ │
│ │ • saveState(slot) │ │
│ │ • loadState(slot) │ │
│ └──────────────┬──────────────────────┘ │
└─────────────────┼───────────────────────────┘
│ TCP / Unix Socket
┌─────────────────▼───────────────────────────┐
│ 模拟器 PINE 服务器 │
│ • PCSX2 (Qt 版本 ≥ 1.7.x) │
│ • RPCS3 (IPC 服务器) │
│ • Duckstation (部分版本) │
└─────────────────────────────────────────────┘
数据流
sequenceDiagram
participant Client as MCP 客户端
participant Bridge as mcp-pine 桥接
participant Pine as PINE 服务器
participant Emu as 模拟器核心
Client->>Bridge: pine_save_state { slot: 5 }
Bridge->>Pine: Op.SaveState (0x09) + slot=5
Pine->>Emu: 触发状态保存
Emu-->>Pine: 保存完成确认
Pine-->>Bridge: PINE 回复帧
Bridge-->>Client: "Saved state → Slot 5"
Client->>Bridge: pine_load_state { slot: 5 }
Bridge->>Pine: Op.LoadState (0x0A) + slot=5
Pine->>Emu: 触发状态加载
Emu-->>Pine: 加载完成确认
Pine-->>Bridge: PINE 回复帧
Bridge-->>Client: "Loaded state from Slot 5"API 实现
PineClient 类方法
src/pine.ts 中定义了 PineClient 类的两个核心异步方法:
async saveState(slot: number): Promise<void> {
const args = Buffer.alloc(1); args.writeUInt8(slot, 0);
await this.call(Op.SaveState, args);
}
async loadState(slot: number): Promise<void> {
const args = Buffer.alloc(1); args.writeUInt8(slot, 0);
await this.call(Op.LoadState, args);
}
参数说明:
| 参数 | 类型 | 范围 | 说明 |
|---|---|---|---|
slot | number | 0-255 | 存档槽位编号 |
协议细节:
- 槽位参数写入 1 字节 Buffer
- 使用
Op.SaveState(0x09)和Op.LoadState(0x0A)操作码 - 调用通过
this.call()统一请求方法,携带 10 秒超时 - 返回
void,操作成功时静默完成
资料来源:src/pine.ts:saveState方法定义
协议层交互
graph LR
A[saveState/loadState] --> B[构造 1 字节参数 Buffer]
B --> C[call Op.SaveState/Op.LoadState]
C --> D{请求发送}
D -->|成功| E[PINE 服务器处理]
E --> F[模拟器核心执行]
F --> G[返回确认帧]
D -->|超时| H[抛出 TimeoutError]
G --> I[解析响应 - void]工具定义
pine_save_state
{
"name": "pine_save_state",
"description": "PURPOSE: Save the current emulator memory and execution state to a numbered slot...\nBEHAVIOR: DESTRUCTIVE: overwrites any existing state in the target slot without prompting...",
"inputSchema": {
"type": "object",
"required": ["slot"],
"properties": {
"slot": {
"type": "integer",
"minimum": 0,
"maximum": 255,
"description": "Save state slot number (0-255)..."
}
}
}
}
用途说明:
- 保存当前模拟器的完整运行状态到指定槽位
- 包括 CPU 寄存器、内存快照、硬件状态等
- 覆盖操作不可逆,无确认提示
pine_load_state
{
"name": "pine_load_state",
"description": "PURPOSE: Load and restore the emulator to a previously saved state in the given slot...",
"inputSchema": {
"type": "object",
"required": ["slot"],
"properties": {
"slot": {
"type": "integer",
"minimum": 0,
"maximum": 255,
"description": "Save state slot number (0-255)..."
}
}
}
}
用途说明:
- 从指定槽位恢复之前保存的模拟器状态
- 加载后模拟器立即进入加载的运行状态
- 不存在的槽位会返回 PINE FAIL 响应
资料来源:src/tools.ts:工具定义完整内容
槽位系统
槽位编号规范
| 槽位范围 | 典型用途 | 说明 |
|---|---|---|
| 0-9 | 快捷键映射 | PCSX2 GUI 中 F1-F10 对应槽位 0-9 |
| 10-99 | 常规存档 | 用户手动保存位置 |
| 100-255 | 扩展槽位 | 自动化脚本、批量实验使用 |
存档文件位置
| 平台 | 路径 |
|---|---|
| Windows | %USERPROFILE%\Documents\PCSX2\sstates |
| Linux | ~/.config/PCSX2/sstates |
| macOS | ~/.config/PCSX2/sstates |
文件名格式:
<游戏序列号> (<Disc CRC>).<slot>.p2s
例如:SLUS-21274 (ABCD1234).5.p2s 表示序列号为 SLUS-21274、CRC 为 ABCD1234 的游戏存档在第 5 槽位。
资料来源:src/tools.ts:SLOT_PARAM_DESC常量定义
配置参数
存档状态管理涉及以下环境变量配置:
| 环境变量 | 默认值 | 说明 |
|---|---|---|
PINE_TARGET | pcsx2 | 模拟器名称,用于 Unix socket 文件路径前缀 |
PINE_SLOT | 28011 | PINE 通信槽位/端口号 |
连接配置差异
| 平台 | 连接方式 | 地址格式 |
|---|---|---|
| Linux/macOS | Unix Socket | $XDG_RUNTIME_DIR/<target>.sock.<slot>,回退到 $TMPDIR 或 /tmp |
| Windows | TCP | 127.0.0.1:<slot> |
工作流程示例
基础保存-加载循环
graph TD
A[启动模拟器] --> B[加载游戏]
B --> C[正常游戏]
C --> D[关键时刻]
D --> E[pine_save_state slot=5]
E --> F[继续游戏]
F --> G[出错/想重来]
G --> H[pine_load_state slot=5]
H --> I[恢复到存档点]
I --> F自动化实验工作流
1. pine_get_info # 确认当前游戏
2. pine_save_state 0 # 保存基准状态
3. [执行多次实验]
3a. 修改内存
3b. 测试假设
3c. pine_load_state 0 # 快速重置到基准
4. pine_save_state 99 # 最终保存
已知问题与限制
PCSX2 PINE 服务器稳定性
根据项目发现并记录的问题:
| 问题 | 原因 | 解决方式 |
|---|---|---|
| 请求队列脆弱 | PINE 服务器对过多请求处理不稳定 | 限制并发请求数量 |
| 请求丢失静默 | 服务器丢弃请求时不报错 | 重启模拟器恢复 |
| 超时后状态不一致 | 错误响应与正确响应混淆 | 确保单一请求链 |
资料来源:CHANGELOG.md:PCSX2 pipeline drop bug说明
常见错误响应
| 错误信息 | 可能原因 |
|---|---|
PINE FAIL response (0xFF) | 游戏未加载、地址无映射、槽位不存在 |
PINE call timed out (10s) | 服务器无响应或队列错位 |
Cannot reach PINE server | 模拟器未运行、PINE 未启用、端口不匹配 |
与其他工具的配合
存档状态管理通常与以下工具配合使用:
| 工具 | 配合场景 |
|---|---|
pine_get_info | 保存前确认游戏和 CRC |
pine_get_status | 加载前确认模拟器状态 |
pine_read_* | 分析内存结构,决定保存时机 |
pine_write_* | 修改状态后保存为实验快照 |
技术规格
| 规格项 | 值 |
|---|---|
| 槽位范围 | 0-255(16 位无符号) |
| 单次调用超时 | 10 秒 |
| 协议操作码 | SaveState=0x09, LoadState=0x0A |
| 参数编码 | 单字节小端序 |
资料来源:src/pine.ts:协议操作码定义
总结
mcp-pine 的存档状态管理模块通过 PINE 协议与模拟器深度集成,提供了简洁但功能完整的保存/加载接口。开发者可以通过 MCP 客户端在 Claude Code 等工具中直接调用 pine_save_state 和 pine_load_state,实现自动化测试、内存分析、实验回溯等工作流程。
资料来源:src/tools.ts:工具定义区域
安装指南
mcp-pine 是一个基于 Model Context Protocol (MCP) 的服务器工具,用于连接 PlayStation 模拟器(PCSX2、RPCS3、Duckstation)的 PINE IPC 接口。通过该工具,AI 助手(如 Claude)可以直接与运行中的模拟器交互,执行内存读取、写入、保存状态等操作。
继续阅读本节完整说明和来源证据。
概述
mcp-pine 是一个基于 Model Context Protocol (MCP) 的服务器工具,用于连接 PlayStation 模拟器(PCSX2、RPCS3、Duckstation)的 PINE IPC 接口。通过该工具,AI 助手(如 Claude)可以直接与运行中的模拟器交互,执行内存读取、写入、保存状态等操作。
本指南涵盖三种安装方式、MCP 客户端配置以及各模拟器的 PINE 服务启用步骤。
来源:https://github.com/dmang-dev/mcp-pine / 项目说明书
模拟器配置
mcp-pine 是一个基于 Model Context Protocol (MCP) 的桥接工具,用于连接 Claude 等 AI 助手与支持 PINE 协议的 PS2/PS3/PSP 模拟器。通过标准化的 MCP 工具接口,用户可以读取/写入模拟器内存、保存/加载存档状态以及获取游戏元数据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
概述
mcp-pine 是一个基于 Model Context Protocol (MCP) 的桥接工具,用于连接 Claude 等 AI 助手与支持 PINE 协议的 PS2/PS3/PSP 模拟器。通过标准化的 MCP 工具接口,用户可以读取/写入模拟器内存、保存/加载存档状态以及获取游戏元数据。
当前项目支持的模拟器包括 PCSX2、RPCS3 和 Duckstation,每种模拟器在 PINE 功能的实现和配置方式上存在差异。
支持的模拟器
| 模拟器 | 平台 | PINE 状态 | 备注 |
|---|---|---|---|
| PCSX2 | Qt 1.7.x+ | ✅ 原生支持 | 主要测试平台,协议实现最完整 |
| RPCS3 | 最新版 | ⚠️ 部分兼容 | 使用自定义 IPC 实现,兼容性未充分测试 |
| Duckstation | 特定构建 | ⚠️ 需检查 | 部分构建版本包含 PINE 服务器 |
资料来源:README.md:1-60
环境变量配置
mcp-pine 通过环境变量控制连接行为,无需配置文件。
核心参数
| 环境变量 | 默认值 | 说明 |
|---|---|---|
PINE_TARGET | pcsx2 | 模拟器名称,用于构建 Unix socket 文件路径(Linux/macOS)或 TCP 连接标识(Windows) |
PINE_SLOT | 28011 | PINE 槽位编号,对应模拟器中配置的端口号 |
PINE_PIPELINE_BATCH | 1 | 请求流水线批次大小,控制并发请求数量 |
资料来源:README.md:40-45
连接地址解析
连接目标根据操作系统和配置的 PINE_TARGET 值自动确定:
graph TD
A[启动 mcp-pine] --> B{操作系统}
B -->|Linux/macOS| C{连接类型}
B -->|Windows| D[TCP 连接]
C -->|Unix Socket| E[使用 Unix Socket]
C -->|TCP| D
D --> F[127.0.0.1:{PINE_SLOT}]
E --> G[{$XDG_RUNTIME_DIR}/{PINE_TARGET}.sock.{PINE_SLOT}]
G --> H{fallback: TMPDIR}
H -->|失败| I[/tmp/{PINE_TARGET}.sock.{PINE_SLOT}]- Unix Socket 路径:
{PINE_TARGET}.sock.{PINE_SLOT} - 优先使用
$XDG_RUNTIME_DIR - 降级使用
$TMPDIR - 最终降级到
/tmp
资料来源:src/pine.ts:1-150
PCSX2 配置指南
PCSX2 是项目的主要测试平台,提供最完整的 PINE 支持。
启用 PINE 服务器
- 启动 PCSX2(Qt 版本 1.7.x 或更高)
- 进入 Settings → Advanced → Enable PINE Server
- 默认槽位为 28011,如需更改则同步设置
PINE_SLOT环境变量 - 加载任意游戏
注意:PINE 服务器启用后始终保持运行,无需额外脚本或控制台命令。
资料来源:README.md:50-60
连接配置
Linux/macOS:
PINE_TARGET=pcsx2 PINE_SLOT=28011 mcp-pine
Windows(TCP 强制):
set PINE_SLOT=28011
mcp-pine
RPCS3 配置指南
RPCS3 使用自有的 IPC 实现,其操作码集合与 PINE 类似,但线级兼容性尚未经过充分测试。
启用 IPC 服务器
- 进入 Configuration → Advanced → Enable IPC server(菜单位置可能因版本而异)
- 记录配置的端口号
- 使用以下命令运行:
PINE_TARGET=rpcs3 PINE_SLOT=<端口号> mcp-pine
资料来源:README.md:65-72
Duckstation 配置指南
Duckstation 的 PINE 支持因构建版本而异,并非所有发行版都包含该功能。
检查 PINE 支持
确认你的 Duckstation 构建版本包含 PINE 服务器后,使用以下配置:
PINE_TARGET=duckstation PINE_SLOT=<端口号> mcp-pine
其中 <端口号> 需要与 Duckstation 中配置的槽位端口一致。
资料来源:README.md:35-38
MCP 客户端集成
Claude Code (CLI)
claude mcp add pine --scope user mcp-pine
验证连接:
claude mcp list
# 输出: pine: mcp-pine - ✓ Connected
资料来源:README.md:15-22
Claude Desktop
编辑配置文件:
| 操作系统 | 配置文件路径 |
|---|---|
| macOS | ~/Library/Application Support/Claude/claude_desktop_config.json |
| Windows | %APPDATA%\Claude\claude_desktop_config.json |
| Linux | ~/.config/Claude/claude_desktop_config.json |
{
"mcpServers": {
"pine": {
"command": "mcp-pine"
}
}
}
修改配置后需重启 Claude Desktop。
资料来源:README.md:24-35
性能调优
批量读取配置
PINE_PIPELINE_BATCH 环境变量控制并发请求数量:
| 值 | 行为 | 延迟 | 风险 |
|---|---|---|---|
1 | 完全串行(默认) | ~52ms/4KB | 无 |
2 或更高 | 流水线并发 | 降低 | PCSX2 可能丢包 |
⚠️ PCSX2 的 PINE 服务器请求队列脆弱,约 7-9 个飞行中请求即可导致无声丢包,进而使回复管道永久失步。
资料来源:src/pine.ts:80-100
测量结果
在 PCSX2 v2.6.3 上通过 loopback TCP 测量:
- 完整 4096 字节读取:约 52ms
- 相当于不到两个模拟帧的时间
资料来源:CHANGELOG.md:30-35
故障排除
| 症状 | 原因 | 解决方案 |
|---|---|---|
Cannot reach PINE server | 模拟器未运行、PINE 未启用、端口不匹配 | 检查 PINE_SLOT 设置 |
PINE FAIL response (0xFF) | 模拟器拒绝请求,通常因为未加载游戏或地址未映射 | 确保已加载游戏,使用有效地址 |
| 读取返回零值 | 地址在未分配区域 | 尝试 0x00100000(EE 主 RAM) |
| 数值看起来损坏 | 字节序问题 | PINE 返回小端序 |
pine_ping 超时(10s) | PCSX2 PINE 服务器卡死 | 完全重启 PCSX2,重连无效 |
PCSX2 PINE 服务器卡死问题
现象:即使 ping 也超时
原因:请求管道失步,回复与等待客户端错位
恢复:必须完全重启 PCSX2,仅重连无法解决问题
资料来源:README.md:90-100
可用工具一览
配置完成后,以下 MCP 工具可用于与模拟器交互:
| 工具名称 | 功能 | PINE 调用次数 |
|---|---|---|
pine_ping | 获取模拟器版本 | 1 |
pine_get_info | 获取游戏标题、序列号、CRC、版本、状态 | 5(并行) |
pine_get_status | 获取运行状态(running/paused/shutdown) | 1 |
pine_read8/16/32/64 | 读取指定宽度的内存值 | 1 |
pine_read_range | 批量读取最多 4096 字节 | N(串行) |
pine_write8/16/32/64 | 写入指定宽度的内存值 | 1 |
pine_save_state | 保存游戏状态到槽位 | 1 |
pine_load_state | 从槽位加载游戏状态 | 1 |
资料来源:src/tools.ts:1-200
地址空间参考
PS2 (PCSX2) 地址区域
| 地址范围 | 描述 |
|---|---|
0x00100000 - 0x01FFFFFF | EE 主内存(99% 游戏状态所在) |
0x1C000000+ | IOP 内存 |
| 特定区域 | Scratchpad 等 |
⚠️ 多字节读写(16/32/64 位)必须对齐地址。PCSX2 的 PINE 不强制对齐,非对齐访问会静默返回损坏数据。
资料来源:src/tools.ts:50-80
安装方式
方式 A:npm 全局安装
npm install -g mcp-pine
方式 B:npx 临时运行
npx -y mcp-pine
方式 C:源码开发
git clone https://github.com/dmang-dev/mcp-pine
cd mcp-pine
npm install
资料来源:README.md:42-50
开发调试
运行开发模式(TypeScript 监视编译):
npm run dev
针对运行中的 PCSX2 进行冒烟测试:
node .scratch/smoke.cjs
资料来源:README.md:105-112
资料来源:README.md:1-60
配置参考
本文档详细说明 mcp-pine 的所有配置选项,包括环境变量、连接参数、运行时行为调优,以及不同平台下的连接机制差异。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
概述
mcp-pine 通过 PINE(PlayStation Interactive Networked Entertainment)协议与模拟器进行 IPC 通信。项目采用零配置默认值设计,用户只需在模拟器中启用 PINE 服务即可开箱即用。高级用户可通过环境变量或代码级配置自定义连接目标、传输方式及请求管道行为。
配置分为三个层级:
| 层级 | 来源 | 优先级 | 适用场景 |
|---|---|---|---|
| 环境变量 | process.env | 最高 | 容器化部署、CLI 用户 |
| 代码选项 | PineConnectOptions | 中 | SDK 集成开发者 |
| 编译默认值 | 源码硬编码 | 最低 | 快速尝鲜 |
环境变量配置
环境变量是用户最常用的配置方式,适用于所有运行场景。
完整配置表
| 环境变量 | 默认值 | 类型 | 说明 |
|---|---|---|---|
PINE_TARGET | pcsx2 | string | 模拟器标识名,用于 Unix socket 文件路径前缀 |
PINE_SLOT | 28011 | number | PINE 槽位号,TCP 模式下作为端口号 |
PINE_HOST | 127.0.0.1 | string | TCP 连接模式下的目标主机(覆盖自动检测) |
PINE_SOCKET_PATH | 自动解析 | string | Unix socket 文件路径(覆盖自动检测) |
PINE_PIPELINE_BATCH | 1 | number | 请求管道批大小,1=完全串行 |
资料来源:README.md:38-45
PINE_TARGET
指定目标模拟器名称,该值作为 Unix socket 文件路径的前缀部分。
# 连接到 PCSX2(默认)
PINE_TARGET=pcsx2 mcp-pine
# 连接到 Duckstation
PINE_TARGET=duckstation PINE_SLOT=28013 mcp-pine
# 连接到 RPCS3
PINE_TARGET=rpcs3 PINE_SLOT=28014 mcp-pine
在 Linux/macOS 上,socket 路径格式为 <socket_dir>/<target>.sock.<slot>。
PINE_SLOT
PINE 槽位号,映射到模拟器内部的 PINE 服务端口或 Unix socket 编号。
# 使用非标准槽位
PINE_SLOT=28015 mcp-pine
槽位必须与模拟器 PINE 设置中的配置一致。PCSX2 默认槽位为 28011,RPCS3 可能因版本而异。
PINE_HOST 与 PINE_SOCKET_PATH
这两个变量用于覆盖自动检测的连接方式:
# 强制使用 TCP 连接到指定主机
PINE_HOST=192.168.1.100 PINE_SLOT=28011 mcp-pine
# 强制使用指定的 Unix socket 路径
PINE_SOCKET_PATH=/var/run/pcsx2.sock.28011 mcp-pine
通常无需手动设置,除非需要连接非本机模拟器或使用非标准 socket 位置。
PINE_PIPELINE_BATCH
控制 pine_read_range 等批量读取操作的并行请求数。
# 完全串行(默认,最安全)
PINE_PIPELINE_BATCH=1 mcp-pine
# 允许 2 个请求并行(中等风险)
PINE_PIPELINE_BATCH=2 mcp-pine
# 允许 N 个请求并行(高风险)
PINE_PIPELINE_BATCH=8 mcp-pine
警告:PCSX2 的 PINE 服务器请求队列存在已知缺陷,当在飞请求超过约 6-7 个时会静默丢弃请求,导致回复管道永久错位。详情见故障排除章节。
资料来源:src/pine.ts:87-94
连接选项接口
对于通过 SDK 集成的开发者,PineClient 构造函数接受 PineConnectOptions 对象:
interface PineConnectOptions {
target?: string; // 模拟器标识,默认为 "pcsx2"
slot?: number; // 槽位号,默认为 28011
host?: string; // 覆盖 TCP 主机
socketPath?: string; // 覆盖 Unix socket 路径
}
使用示例:
import { PineClient } from "mcp-pine";
const pine = new PineClient({
target: "pcsx2",
slot: 28011,
});
// 或指定自定义连接
const pine2 = new PineClient({
host: "192.168.1.100",
slot: 28011,
});
传输层架构
平台自动检测
mcp-pine 根据运行平台自动选择最优传输方式:
graph TD
A[启动 mcp-pine] --> B{平台检测}
B -->|Windows| C[TCP 连接]
B -->|Linux/macOS| D[Unix Domain Socket]
C --> E[127.0.0.1:PINE_SLOT]
D --> F{XDG_RUNTIME_DIR}
F -->|存在| G[XDG_RUNTIME_DIR/target.sock.slot]
F -->|不存在| H{TMPDIR}
H -->|存在| I[TMPDIR/target.sock.slot]
H -->|不存在| J[/tmp/target.sock.slot]
style C fill:#e1f5fe
style D fill:#fff3e0
style E fill:#e1f5fe
style G fill:#e8f5e9
style I fill:#e8f5e9
style J fill:#e8f5e9Socket 路径解析优先级
在 Linux/macOS 上,Unix socket 路径按以下优先级确定:
| 优先级 | 环境变量 | 示例路径 |
|---|---|---|
| 1 | PINE_SOCKET_PATH(显式覆盖) | 用户指定 |
| 2 | $XDG_RUNTIME_DIR | /run/user/1000/pcsx2.sock.28011 |
| 3 | $TMPDIR | /tmp/pcsx2.sock.28011 |
| 4 | /tmp(最终回退) | /tmp/pcsx2.sock.28011 |
资料来源:src/pine.ts:30-48
TCP 连接模式
在 Windows 或显式指定 PINE_HOST 时,mcp-pine 使用标准 TCP 连接:
sequenceDiagram
participant MCP as MCP 客户端
participant Bridge as mcp-pine
participant PCSX2 as PCSX2 PINE
Note over Bridge: TCP 模式初始化
MCP->>Bridge: 连接请求
Bridge->>PCSX2: net.createConnection<br/>host: 127.0.0.1
PCSX2-->>Bridge: 连接成功
Bridge-->>MCP: 就绪连接地址格式:127.0.0.1:<PINE_SLOT>
请求处理机制
PINE 协议帧格式
mcp-pine 使用二进制 PINE 协议进行通信,帧格式如下:
graph LR
subgraph 发送帧
A[uint32<br/>总长度] --> B[uint8<br/>操作码] --> C[可变长度<br/>载荷数据]
end
subgraph 接收帧
D[uint32<br/>总长度] --> E[uint8<br/>状态] --> F[可变长度<br/>响应数据]
end所有多字节整数均采用小端序(Little-Endian)编码。
操作码映射
| 操作码 | 名称 | 方向 | 说明 |
|---|---|---|---|
| 0x01 | Version | R | 获取模拟器版本 |
| 0x02 | Title | R | 获取游戏标题 |
| 0x03 | ID | R | 获取游戏序列号 |
| 0x04 | UUID | R | 获取光盘 CRC |
| 0x05 | GameVersion | R | 获取游戏版本 |
| 0x06 | Status | R | 获取运行状态 |
| 0x10 | SaveState | W | 保存状态到槽位 |
| 0x11 | LoadState | W | 从槽位加载状态 |
| 0x20-0x23 | Read8/16/32/64 | R | 读取内存 |
| 0x30-0x33 | Write8/16/32/64 | W | 写入内存 |
资料来源:src/pine.ts:1-28
批量读取实现
pine_read_range 通过组合多种宽度的读取操作实现高效批量读取:
flowchart TD
A[pine_read_range<br/>length=4096] --> B{对齐检测}
B -->|cursor%8==0 且 remaining>=8| C[read64]
B -->|cursor%4==0 且 remaining>=4| D[read32]
B -->|cursor%2==0 且 remaining>=2| E[read16]
B -->|其他情况| F[read8]
C --> G{下一个字节}
D --> G
E --> G
F --> G
G -->|remaining>0| B
G -->|remaining=0| H[组装结果缓冲区]
I{PIPELINE_BATCH}
I -->|=1| J[串行执行<br/>每批1个请求]
I -->|>1| K[并行执行<br/>每批N个请求]
J --> L[安全但延迟较高]
K --> M[低延迟但可能丢包]
style J fill:#c8e6c9
style K fill:#ffcdd2默认 PIPELINE_BATCH=1 表示完全串行执行,避免 PCSX2 PINE 服务器的队列问题。
资料来源:src/pine.ts:58-86
模拟器配置要求
PCSX2 设置
- 启动 PCSX2 1.7.x 或更新版本
- 进入 Settings → Advanced → Enable PINE Server
- 默认槽位为 28011,如需更改同步修改环境变量
无需额外脚本或控制台命令,PINE 启用后始终可用。
RPCS3 设置
RPCS3 实现了兼容 PINE 的 IPC 接口,但线级兼容性尚未完全测试:
- 进入 Configuration → Advanced → Enable IPC server
- 记下配置的端口
- 使用
PINE_TARGET=rpcs3 PINE_SLOT=<port> mcp-pine运行
Duckstation 设置
部分 Duckstation 构建包含 PINE 服务器:
PINE_TARGET=duckstation PINE_SLOT=<port> mcp-pine
故障排除
连接问题诊断
flowchart TD
A[连接失败] --> B{PING 工具测试}
B -->|超时| C{模拟器运行中?}
C -->|否| D[启动模拟器并加载游戏]
C -->|是| E[PINE 已启用?]
E -->|否| F[在模拟器设置中启用 PINE]
E -->|是| G{端口匹配?}
G -->|否| H[对齐 PINE_SLOT 和模拟器设置]
B -->|FAIL 响应| I{是否有游戏加载?}
I -->|否| J[加载游戏后重试]
I -->|是| K[地址无效或未映射]
K --> L[从 0x00100000 开始尝试]
style D fill:#c8e6c9
style F fill:#c8e6c9
style H fill:#c8e6c9
style J fill:#c8e6c9
style L fill:#c8e6c9PCSX2 PINE 服务器卡死
PCSX2 的 PINE 服务器在收到约 7 个以上并行请求时会静默丢弃请求,导致回复管道永久错位。
症状:即使 pine_ping 也超时(10秒后)。
解决方案:完全重启 PCSX2。重新连接无效,损坏发生在模拟器端。
预防措施:
- 保持
PINE_PIPELINE_BATCH=1 - 如需更高性能,确保请求数量不超过 6 个
资料来源:README.md:98-115
内存读取问题
| 症状 | 可能原因 | 解决方案 |
|---|---|---|
| 返回全零 | 地址未分配 | 从 0x00100000 开始尝试 |
| 数据损坏 | 未对齐访问 | 使用 pine_read_range 自行组装 |
| 值不符合预期 | 大端序误解 | PINE 返回小端序 |
配置示例
基本用法(默认 PCSX2)
# 使用所有默认值
mcp-pine
等价于:
PINE_TARGET=pcsx2 PINE_SLOT=28011 mcp-pine
连接远程模拟器
PINE_HOST=192.168.1.50 PINE_SLOT=28011 mcp-pine
自定义 Unix Socket 路径
PINE_SOCKET_PATH=/home/user/my-socket mcp-pine
性能调优(风险自负)
# 允许 2 个并行请求
PINE_PIPELINE_BATCH=2 mcp-pine
NPM 方式运行
npx -y mcp-pine
环境变量速查表
PINE_TARGET 默认: pcsx2 模拟器标识名
PINE_SLOT 默认: 28011 槽位号/端口
PINE_HOST 默认: 127.0.0.1 TCP 主机
PINE_SOCKET_PATH 默认: 自动解析 Unix socket 路径
PINE_PIPELINE_BATCH 默认: 1 并行请求批大小
相关文档
资料来源:README.md:38-45
使用范例
mcp-pine 是一个基于 Model Context Protocol (MCP) 的桥接工具,用于连接 Claude 等 AI 助手与支持 PINE(PlayStation 2 Inter-Process Communication)协议的模拟器(如 PCSX2、RPCS3、Duckstation)。通过 mcp-pine,用户可以让 AI 直接读取和写入模拟器的内存...
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
概述
mcp-pine 是一个基于 Model Context Protocol (MCP) 的桥接工具,用于连接 Claude 等 AI 助手与支持 PINE(PlayStation 2 Inter-Process Communication)协议的模拟器(如 PCSX2、RPCS3、Duckstation)。通过 mcp-pine,用户可以让 AI 直接读取和写入模拟器的内存、获取游戏状态信息、保存和加载存档。
该项目的主要功能包括:
- 内存读写:支持 8/16/32/64 位数值读取以及批量内存读取
- 游戏状态查询:获取游戏标题、序列号、CRC、版本信息
- 存档管理:保存和加载指定槽位的存档
- 跨模拟器支持:兼容 PCSX2、RPCS3、Duckstation 等主流模拟器
资料来源:README.md:1-5
系统架构
graph TD
A["Claude / MCP 客户端"] -->|MCP stdio| B["mcp-pine 桥接工具"]
B -->|PINE 协议| C["PCSX2 PINE Server"]
B -->|PINE 协议| D["RPCS3 IPC Server"]
B -->|PINE 协议| E["Duckstation PINE Server"]
F["EE 主内存 0x00100000-0x01FFFFFF"] --> C
G["IOP 内存 0x1C000000+"] --> C
H["PS3 主内存"] --> Dmcp-pine 作为中间层,将 MCP 协议请求转换为 PINE 协议调用,与模拟器的内置 PINE 服务器通信。不同模拟器使用不同的连接方式:Unix 域套接字(Linux/macOS)或 TCP(Windows)。
资料来源:src/pine.ts:1-50
安装方式
方式一:npm 全局安装
npm install -g mcp-pine
方式二:npx 直接运行
npx -y mcp-pine
方式三:源码安装
git clone https://github.com/dmang-dev/mcp-pine
cd mcp-pine
npm install
资料来源:README.md:40-50
MCP 客户端配置
Claude Code (CLI)
claude mcp add pine --scope user mcp-pine
验证连接:
claude mcp list
# 输出: pine: mcp-pine - ✓ Connected
Claude Desktop
根据操作系统编辑配置文件:
| 平台 | 配置文件路径 |
|---|---|
| macOS | ~/Library/Application Support/Claude/claude_desktop_config.json |
| Windows | %APPDATA%\Claude\claude_desktop_config.json |
| Linux | ~/.config/Claude/claude_desktop_config.json |
配置内容:
{
"mcpServers": {
"pine": {
"command": "mcp-pine"
}
}
}
修改配置后需重启 Claude Desktop。
资料来源:README.md:18-38
环境变量配置
| 环境变量 | 默认值 | 说明 |
|---|---|---|
PINE_TARGET | pcsx2 | 模拟器名称,用于构建 Unix 套接字文件路径(<target>.sock.<slot>)。Windows 系统忽略此变量,仅使用 TCP 连接。 |
PINE_SLOT | 28011 | PINE 槽位号,对应 PCSX2 中的端口或槽位号。 |
PINE_PIPELINE_BATCH | 1 | 批量流水线深度。默认为 1(完全串行),设为更高值可降低延迟但可能导致 PCSX2 请求队列错位。 |
资料来源:README.md:42-48
模拟器配置
PCSX2 配置步骤
- 启动 PCSX2(1.7.x Qt 或更高版本)
- 进入 Settings → Advanced → Enable PINE Server
- 默认槽位为 28011,如需更改则在启动 mcp-pine 时设置
PINE_SLOT - 加载任意游戏
注意:PINE 服务启用后始终处于开启状态,无需脚本或控制台命令。
资料来源:README.md:52-60
RPCS3 配置步骤
- 进入 Configuration → Advanced → Enable IPC server
- 记下配置的端口号
- 运行命令:
PINE_TARGET=rpcs3 PINE_SLOT=<端口> mcp-pine
注意:RPCS3 的 IPC 实现参考了 PINE 操作码,但协议层面的兼容性尚未经过充分测试。
资料来源:README.md:62-68
Duckstation 配置步骤
检查当前构建的 Duckstation 是否包含 PINE 服务器(如有则设置 PINE_TARGET=duckstation PINE_SLOT=<端口>)。
资料来源:README.md:70-71
工具清单
mcp-pine 提供以下 MCP 工具:
| 工具名称 | 功能 | 用途场景 |
|---|---|---|
pine_ping | 检测连接状态 | 验证模拟器 PINE 服务是否可用 |
pine_get_info | 获取游戏元数据 | 获取游戏标题、序列号、CRC、版本、运行状态 |
pine_get_status | 获取运行状态 | 快速检查模拟器是运行中、暂停还是已关闭 |
pine_read8/16/32/64 | 定点内存读取 | 按指定字节宽度读取单个值 |
pine_read_range | 批量内存读取 | 一次性读取最多 4096 字节的连续内存区域 |
pine_write8/16/32/64 | 定点内存写入 | 向指定地址写入数值 |
pine_save_state | 保存存档 | 将当前状态保存到指定槽位 |
pine_load_state | 加载存档 | 从指定槽位恢复存档 |
资料来源:src/tools.ts:1-150
典型使用流程
graph TD
A[启动 PCSX2 并加载游戏] --> B[启动 mcp-pine]
B --> C{连接成功?}
C -->|是| D[调用 pine_ping 验证]
D --> E[调用 pine_get_info 获取游戏信息]
E --> F{需要读取内存?}
F -->|是| G[使用 pine_read_range 批量读取]
G --> H[分析内存数据]
F -->|否| I[使用 pine_read8/16/32/64 定点读取]
I --> H
H --> J{需要修改状态?}
J -->|是| K[使用 pine_write* 写入]
J -->|否| L[完成]
K --> L
C -->|否| M[检查 PINE_SLOT 配置]
M --> C内存地址说明
PS2 内存布局(PCSX2)
| 地址范围 | 说明 |
|---|---|
0x00100000 - 0x01FFFFFF | EE 主内存(99% 的游戏状态位于此处) |
0x1C000000+ | IOP 内存 |
| 0x10000000 区域 | Scratchpad(高速缓存) |
重要提示:对于 16/32/64 位读取,地址必须对齐(即地址值能被读写宽度整除)。PCSX2 的 PINE 服务不强制对齐,未对齐的地址会静默返回错误数据。如需读取未对齐的数据,请使用 pine_read_range 并自行组装字节。
资料来源:src/tools.ts:40-55
工具使用详解
连接验证
// 调用 pine_ping
工具输入: {}
预期输出: "OK — emulator: <版本号>"
获取游戏信息
// 调用 pine_get_info
工具输入: {}
预期输出:
"Title: <游戏标题>
Serial: <序列号>
Disc CRC: <CRC值>
Game version: <版本号>
Status: <运行状态>"
此工具并行发送 5 个 PINE 操作码(Title、ID、UUID、GameVersion、Status),任一字段失败时返回 (unavailable) 而不影响其他字段。
资料来源:src/tools.ts:75-95
内存读取示例
#### 定点读取
// 读取 32 位无符号整数
工具输入: { "address": 0x00100000 }
预期输出: "0x00100000: 12345 (0x3039)"
// 读取 64 位值(返回字符串形式的十进制数,因为 JS 无法精确表示超过 2^53 的整数)
工具输入: { "address": 0x00200000 }
预期输出: "0x00200000: 18446744073709551615 (0xFFFFFFFFFFFFFFFF)"
#### 批量读取
pine_read_range 是最高效的批量读取方式,内部实现为串行调用最大对齐宽度的读取操作。实测 PCSX2 v2.6.3 下读取 4096 字节约需 52ms。
工具输入: { "address": 0x00100000, "length": 256 }
预期输出: "0x00100000: <256字节的十六进制数据>"
默认情况下请求完全串行执行,以避免 PCSX2 请求队列错位。可通过设置 PINE_PIPELINE_BATCH 环境变量调整流水线深度。
资料来源:src/pine.ts:80-130
内存写入示例
// 写入 8 位值
工具输入: { "address": 0x00100000, "value": 255 }
// 写入 32 位值
工具输入: { "address": 0x00100004, "value": 12345678 }
预期输出: "Wrote 12345678 (0xBC614E) → 0x00100004"
注意:内存写入是破坏性操作,会覆盖原有数据。
资料来源:src/tools.ts:96-110
存档管理
// 保存到槽位 3
工具输入: { "slot": 3 }
预期输出: "Saved state to slot 3"
// 从槽位 5 加载
工具输入: { "slot": 5 }
预期输出: "Loaded state from slot 5"
PCSX2 存档文件通常位于:
- Windows:
%USERPROFILE%\Documents\PCSX2\sstates - Linux:
~/.config/PCSX2/sstates
文件名格式:<序列号> (<CRC>).<槽位号>.p2s
资料来源:src/tools.ts:130-140
常见问题排查
| 问题症状 | 原因与解决方案 |
|---|---|
Cannot reach PINE server | 模拟器未运行、PINE 未启用、或槽位号不匹配。检查 PINE_SLOT 配置。 |
PINE FAIL response (0xFF) | 模拟器拒绝请求,通常是因为未加载游戏或地址未映射。 |
| 读取返回零值 | 地址位于未分配区域,尝试从 0x00100000 开始(几乎总是在 EE RAM 范围内)。 |
| 数据看起来损坏 | 检查字节序,PINE 返回小端序;字符串应使用 read_range 风格的字节读取。 |
pine_ping 超时(10秒) | PCSX2 PINE 服务器可能卡死。请求队列在超过约 6 个飞行中请求时会静默丢包,导致后续所有回复错位。解决方案:完全重启 PCSX2,仅重新连接无效。 |
资料来源:README.md:100-115
开发相关
本地开发
npm install
npm run dev # 启动 tsc --watch 监听文件变化
快速测试
在运行 PCSX2 的环境下执行冒烟测试:
node .scratch/smoke.cjs
资料来源:README.md:118-125
版本历史
| 版本 | 日期 | 主要变更 |
|---|---|---|
| 0.2.1 | 2026-05-15 | 工具描述质量改进,符合 Glama Tool Definition Quality Score 规范 |
| 0.2.0 | 2026-05-10 | 新增 pine_read_range 批量读取、10秒超时机制 |
| 0.1.x | 早期版本 | 基础功能实现 |
资料来源:CHANGELOG.md:1-50
相关项目
资料来源:README.md:128-131
资料来源:README.md:1-5
故障排除
本文档提供 mcp-pine 与 PINE 协议模拟器通信时遇到的常见问题诊断与解决方案。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
连接问题
无法连接 PINE 服务器
症状: 调用任何工具时返回 Cannot reach PINE server 错误。
可能原因:
| 原因 | 检查方法 | 解决方案 |
|---|---|---|
| 模拟器未运行 | 检查模拟器进程是否启动 | 启动 PCSX2/RPCS3/Duckstation |
| PINE 功能未启用 | 模拟器设置中搜索 "PINE" | Settings → Advanced → Enable PINE Server |
| 端口/插槽号不匹配 | 检查 PINE_SLOT 环境变量 | 设置 PINE_SLOT=28011 (PCSX2 默认值) |
| 模拟器未加载游戏 | 确认游戏已加载 | 加载任意游戏后再尝试 |
排查步骤:
graph TD
A[连接失败] --> B{模拟器运行中?}
B -->|否| C[启动模拟器]
B -->|是| D{PINE已启用?}
D -->|否| E[启用PINE Server]
D -->|是| F{游戏已加载?}
F -->|否| G[加载游戏]
F -->|是| H{端口匹配?}
H -->|否| I[设置PINE_SLOT]
H -->|是| J[检查防火墙/权限]资料来源:README.md:1 README.md:52
连接方式差异
| 操作系统 | 连接方式 | 路径/地址格式 |
|---|---|---|
| Linux/macOS | Unix Domain Socket | $XDG_RUNTIME_DIR/<target>.sock.<slot> 或 $TMPDIR/<target>.sock.<slot> |
| Windows | TCP | 127.0.0.1:<slot> |
| 默认目标 | PCSX2 | PINE_TARGET=pcsx2 |
| 替代目标 | RPCS3/Duckstation | PINE_TARGET=rpcs3 或 PINE_TARGET=duckstation |
资料来源:src/pine.ts:88-100
PINE 协议错误响应
PINE FAIL 响应 (0xFF)
症状: 返回 PINE FAIL response (0xFF) 错误。
可能原因:
| 原因 | 诊断方法 | 解决方案 |
|---|---|---|
| 未加载游戏 | 检查模拟器状态 | 加载游戏后重试 |
| 地址未映射 | 尝试 0x00100000 (EE RAM) | 使用已确认有效的地址 |
| 只读区域写入 | 检查目标区域类型 | BIOS/IOP ROM 写入会被静默丢弃 |
地址空间参考:
| 区域 | 地址范围 | 说明 |
|---|---|---|
| EE Main RAM | 0x00100000 - 0x01FFFFFF | 99% 的游戏状态在此区域 |
| IOP RAM | 0x1C000000+ | IOP 处理器内存 |
| Scratchpad | 特定区域 | 高速缓存区域 |
资料来源:README.md:54 src/tools.ts:35-39
内存读取问题
读取返回全零
症状: pine_read8/16/32/64 返回值全为 0。
诊断流程:
graph LR
A[读取返回0] --> B{地址有效?}
B -->|尝试0x00100000| C{仍然返回0?}
C -->|是| D[游戏未正确加载]
C -->|否| E[原地址在未分配区域]
B -->|无效地址| F[使用有效地址重试]解决方案: 首先尝试 0x00100000,这是 EE 主 RAM 的起始地址,几乎总是可访问的。
数据损坏/值看起来不正确
症状: 读取的值看起来错误或损坏。
可能原因: 字节序问题。PINE 协议返回小端序数据。
| 操作类型 | 字节序 | 处理方式 |
|---|---|---|
| 字符串读取 | UTF-8 | 使用 read_range 逐字节读取 |
| 数值读取 | Little-Endian | LSB 在低位地址,MSB 在高位地址 |
解决方案: 对于字符串,使用 pine_read_range 进行字节级读取。对于数值,确认解析时使用小端序。
超时问题
PINE 调用超时 (10秒)
症状: pine_ping 在某些高频使用后开始超时。
根本原因: PCSX2 的 PINE 服务器请求队列存在已知问题。
graph TD
A[PCSX2 PINE Server] --> B[请求队列]
B --> C{请求数量 <= 6}
C -->|是| D[正常处理]
C -->|否| E[请求被静默丢弃]
E --> F[回复流水线失步]
F --> G[所有后续请求超时]
G --> H[甚至ping也超时]已知限制:
- PCSX2 的 PINE 服务器请求队列脆弱
- 同时有超过约 6 个进行中的请求时会静默丢弃请求
- 一旦失步,即使
pine_ping也会超时 - 重新连接无法恢复,必须完全重启 PCSX2
资料来源:README.md:55-56 CHANGELOG.md:45-52 src/pine.ts:60-68
恢复步骤:
- 完全关闭 PCSX2(不只是暂停)
- 重新启动 PCSX2
- 重新加载游戏
- 重新连接 mcp-pine
性能问题
`pine_read_range` 速度较慢
现象: 大范围内存读取比 mGBA 的 read_range 慢。
原因分析:
| 特性 | PINE | mGBA |
|---|---|---|
| 原生批量读取 | ❌ 无 | ✅ 有 |
| 实现方式 | 串行发送多个 read64/32/16/8 | 单次批量请求 |
| 4096字节读取耗时 | ~52ms (PCSX2 v2.6.3) | 显著更快 |
延迟基准:
| 操作 | 耗时 | 说明 |
|---|---|---|
| 完整 4096 字节读取 | ~52ms | 循环回程 TCP 性能 |
| 相当于模拟帧数 | < 2 帧 | 60fps 下约 33ms/帧 |
资料来源:README.md:57-58 CHANGELOG.md:37-40
流水线批处理选项
环境变量: PINE_PIPELINE_BATCH
| 值 | 行为 | 风险 |
|---|---|---|
| 1 (默认) | 完全串行请求 | 无 |
| >= 2 | 启用流水线 | 可能导致 PCSX2 失步 |
// src/pine.ts 中的实现逻辑
const PIPELINE_BATCH = Number.parseInt(process.env.PINE_PIPELINE_BATCH ?? "1", 10) || 1;
for (let i = 0; i < steps.length; i += PIPELINE_BATCH) {
const batch = steps.slice(i, i + PIPELINE_BATCH);
const promises = batch.map((s) => /* ... */);
const results = await Promise.all(promises);
}
警告: 启用流水线可能导致 PCSX2 PINE 服务器失步。仅在能够容忍偶尔模拟器重启的情况下使用。
资料来源:src/pine.ts:70-73 src/pine.ts:95-100
对齐问题
未对齐地址访问
症状: 多字节读取返回值看起来错误或损坏。
根本原因: PINE on PCSX2 不强制对齐要求。
| 操作 | 对齐要求 | 未对齐行为 |
|---|---|---|
pine_read8 / pine_write8 | 无 | 正常工作 |
pine_read16 / pine_write16 | 2字节对齐 | 返回对齐地址以下的字节 |
pine_read32 / pine_write32 | 4字节对齐 | 返回对齐地址以下的字节 |
pine_read64 / pine_write64 | 8字节对齐 | 返回对齐地址以下的字节 |
解决方案:
- 确保传入对齐的地址
- 如需未对齐访问,使用
pine_read_range逐字节读取后自行组装
graph LR
A[需要读取0x1003] --> B{可接受的访问方式?}
B --> C[使用read_range读取0x1000-0x1007]
B --> D[忽略不对齐的write64]
C --> E[自行组装字节]资料来源:src/tools.ts:11-15
工具调用错误
各工具的错误条件汇总
| 工具 | 失败模式 | 错误信息 |
|---|---|---|
pine_ping | 连接失败/PINE 失步 | Cannot reach PINE server / 超时 |
pine_get_info | 连接失败/部分字段不可用 | 返回 (unavailable) |
pine_get_status | 连接失败 | PINE FAIL response |
pine_read* | 无效地址/未对齐 | PINE FAIL 或错误值 |
pine_write* | 只读区域/无效地址 | PINE FAIL (静默丢弃) |
pine_save_state | 插槽超出范围 | Schema 拒绝 (0-255) |
pine_load_state | 插槽无存档 | PINE FAIL |
存档状态槽位
| 槽位范围 | 约定 | 实际协议支持 |
|---|---|---|
| 0-255 | 完整协议支持 | ✅ |
| 0-9 | PCSX2 GUI 映射到 F1-F10 | 推荐使用 |
资料来源:src/tools.ts:58-63
环境变量参考
| 变量名 | 默认值 | 说明 |
|---|---|---|
PINE_TARGET | pcsx2 | 模拟器名称,决定 Unix socket 文件前缀 |
PINE_SLOT | 28011 | PINE 插槽号,也是 TCP 端口号 |
PINE_PIPELINE_BATCH | 1 | 流水线批处理大小,1=完全串行 |
资料来源:README.md:29-32 src/pine.ts:70
调试技巧
快速冒烟测试
# 确保模拟器运行且 PINE 已启用
node .scratch/smoke.cjs
推荐的调试工作流
- 首先验证连接: 使用
pine_ping - 获取基本信息: 使用
pine_get_info确认游戏加载 - 检查状态: 使用
pine_get_status - 从已知地址开始: 尝试
0x00100000验证读取功能 - 逐步深入: 确认基础功能后再访问特定内存区域
连接质量检查
graph TD
A[开始调试] --> B[ping成功?]
B -->|否| C[检查模拟器/网络]
B -->|是| D[get_info成功?]
D -->|否| E[无游戏加载]
D -->|是| F[测试已知地址]
F --> G{0x00100000返回非零?}
G -->|否| H[EE RAM未映射]
G -->|是| I[基础连接正常]已知限制
| 限制 | 描述 | 状态 |
|---|---|---|
| PCSX2 请求队列脆弱 | >6 流水线请求会导致失步 | 已知问题,无法自动恢复 |
| 无原生批量读取 | read_range 串行实现 | 设计限制,性能可接受 |
| 未对齐访问静默损坏 | PCSX2 不强制对齐 | 需客户端保证 |
| RPCS3 兼容性未充分测试 | Wire-level 兼容性待验证 | 实验性支持 |
资料来源:README.md:45-51 CHANGELOG.md:48-54
获取帮助
如遇到本文档未覆盖的问题,请:
- 确认问题可重现
- 记录模拟器版本、PINE 设置、错误信息
- 在 GitHub Issues 创建 Issue
资料来源:README.md:1 README.md:52
失败模式与踩坑日记
保留 Doramagic 在发现、验证和编译中沉淀的项目专属风险,不把社区讨论只当作装饰信息。
可能阻塞安装或首次运行。
安装可能改变本机 AI 工具行为,用户需要知道写入位置和回滚方法。
假设不成立时,用户拿不到承诺的能力。
新项目、停更项目和活跃项目会被混在一起,推荐信任度下降。
Pitfall Log / 踩坑日志
项目:dmang-dev/mcp-pine
摘要:发现 8 个潜在踩坑项,其中 0 个为 high/blocking;最高优先级:安装坑 - 来源证据:Submit listing PR to punkpeye/awesome-mcp-servers。
1. 安装坑 · 来源证据:Submit listing PR to punkpeye/awesome-mcp-servers
- 严重度:medium
- 证据强度:source_linked
- 发现:GitHub 社区证据显示该项目存在一个安装相关的待验证问题:Submit listing PR to punkpeye/awesome-mcp-servers
- 对用户的影响:可能阻塞安装或首次运行。
- 建议检查:来源显示可能已有修复、规避或版本变化,说明书中必须标注适用版本。
- 防护动作:不得脱离来源链接放大为确定性结论;需要标注适用版本和复核状态。
- 证据:community_evidence:github | cevd_540ae479012f437fb2cb5e7d33636f1f | https://github.com/dmang-dev/mcp-pine/issues/2 | 来源讨论提到 npm 相关条件,需在安装/试用前复核。
2. 配置坑 · 可能修改宿主 AI 配置
- 严重度:medium
- 证据强度:source_linked
- 发现:项目面向 Claude/Cursor/Codex/Gemini/OpenCode 等宿主,或安装命令涉及用户配置目录。
- 对用户的影响:安装可能改变本机 AI 工具行为,用户需要知道写入位置和回滚方法。
- 建议检查:列出会写入的配置文件、目录和卸载/回滚步骤。
- 防护动作:涉及宿主配置目录时必须给回滚路径,不能只给安装命令。
- 证据:capability.host_targets | github_repo:1234265030 | https://github.com/dmang-dev/mcp-pine | host_targets=mcp_host, claude
3. 能力坑 · 能力判断依赖假设
- 严重度:medium
- 证据强度:source_linked
- 发现:README/documentation is current enough for a first validation pass.
- 对用户的影响:假设不成立时,用户拿不到承诺的能力。
- 建议检查:将假设转成下游验证清单。
- 防护动作:假设必须转成验证项;没有验证结果前不能写成事实。
- 证据:capability.assumptions | github_repo:1234265030 | https://github.com/dmang-dev/mcp-pine | README/documentation is current enough for a first validation pass.
4. 维护坑 · 维护活跃度未知
- 严重度:medium
- 证据强度:source_linked
- 发现:未记录 last_activity_observed。
- 对用户的影响:新项目、停更项目和活跃项目会被混在一起,推荐信任度下降。
- 建议检查:补 GitHub 最近 commit、release、issue/PR 响应信号。
- 防护动作:维护活跃度未知时,推荐强度不能标为高信任。
- 证据:evidence.maintainer_signals | github_repo:1234265030 | https://github.com/dmang-dev/mcp-pine | last_activity_observed missing
5. 安全/权限坑 · 下游验证发现风险项
- 严重度:medium
- 证据强度:source_linked
- 发现:no_demo
- 对用户的影响:下游已经要求复核,不能在页面中弱化。
- 建议检查:进入安全/权限治理复核队列。
- 防护动作:下游风险存在时必须保持 review/recommendation 降级。
- 证据:downstream_validation.risk_items | github_repo:1234265030 | https://github.com/dmang-dev/mcp-pine | no_demo; severity=medium
6. 安全/权限坑 · 存在评分风险
- 严重度:medium
- 证据强度:source_linked
- 发现:no_demo
- 对用户的影响:风险会影响是否适合普通用户安装。
- 建议检查:把风险写入边界卡,并确认是否需要人工复核。
- 防护动作:评分风险必须进入边界卡,不能只作为内部分数。
- 证据:risks.scoring_risks | github_repo:1234265030 | https://github.com/dmang-dev/mcp-pine | no_demo; severity=medium
7. 维护坑 · issue/PR 响应质量未知
- 严重度:low
- 证据强度:source_linked
- 发现:issue_or_pr_quality=unknown。
- 对用户的影响:用户无法判断遇到问题后是否有人维护。
- 建议检查:抽样最近 issue/PR,判断是否长期无人处理。
- 防护动作:issue/PR 响应未知时,必须提示维护风险。
- 证据:evidence.maintainer_signals | github_repo:1234265030 | https://github.com/dmang-dev/mcp-pine | issue_or_pr_quality=unknown
8. 维护坑 · 发布节奏不明确
- 严重度:low
- 证据强度:source_linked
- 发现:release_recency=unknown。
- 对用户的影响:安装命令和文档可能落后于代码,用户踩坑概率升高。
- 建议检查:确认最近 release/tag 和 README 安装命令是否一致。
- 防护动作:发布节奏未知或过期时,安装说明必须标注可能漂移。
- 证据:evidence.maintainer_signals | github_repo:1234265030 | https://github.com/dmang-dev/mcp-pine | release_recency=unknown
来源:Doramagic 发现、验证与编译记录