【免费下载链接】peon-ping
Warcraft III Peon voice notifications (+ more!) for Claude Code, Codex, IDEs, and any AI agent. Stop babysitting your terminal. Employ a Peon today.
peon-ping 除了经典的 hook 事件触发模式外,还内置了一个基于 Model Context Protocol(MCP)的 Node.js 服务,让任何支持 MCP 的客户端(Claude Desktop、Cursor 等)都能以工具调用的方式播放 75+ 款游戏音效包,无需配置任何 hook。本文以 mcp/README.md 为主体,逐节展开其工具与资源设计、客户端接入配置、环境变量,并结合 mcp/peon-mcp.js 源码深入讲解目录发现、清单解析、音量优先级与各平台播放器调度的底层实现。
它解决什么问题:Agent 自选音效,而非事件固定音效
peon-ping 的默认工作方式是 hook 模式:Agent 会话事件(会话开始、等待输入、完成等)映射到固定音效。而 MCP 模式的核心差异在于:由 Agent 自己决定播什么。如 README.md 中所述,Agent 可以在构建失败时调用play_sound播放duke_nukem/SonOfABitch,在读取文件时播放sc_kerrigan/IReadYou。
整个服务端只暴露一个工具、两个资源:
| MCP 特性 | 作用 |
|---|---|
Tool:play_sound | 播放一个或多个音效,{ sound: "pack/Name" }或{ sounds: ["a/B", "c/D"] } |
Resource:peon-ping://catalog | 完整目录——所有 pack 和音效,客户端一次读取,模型即有上下文 |
Resource:peon-ping://pack/{name} | 按类别列出某个 pack 内的全部音效 |
目录之所以做成 MCP Resource 而不是工具,是一个明确的工程取舍:客户端把目录预取一次放入模型上下文,避免模型反复发起“浏览工具”来查看有哪些音效可播。
前置要求与安装
运行前提(见 mcp/README.md):
- Node.js 18+
- 已安装 peon-ping(macOS、Linux 或 WSL2)
安装步骤:
# 若尚未安装 peon-ping curl -fsSL https://peonping.com/install | bash # 安装 MCP 服务端依赖 cd mcp && npm installmcp/package.json 显示这是一个"type": "module"的 ES Module 包,唯一运行时依赖是@modelcontextprotocol/sdk(^1.12.1),并声明了bin入口peon-mcp。
主文档 README.md 补充了一个 Homebrew 场景:通过 Homebrew 安装 peon-ping 时,服务端入口位于$(brew --prefix peon-ping)/libexec/mcp/peon-mcp.js。
在 MCP 客户端中接入
Claude Desktop
将配置追加到~/Library/Application Support/Claude/claude_desktop_config.json:
{ "mcpServers": { "peon-ping": { "command": "node", "args": ["/path/to/peon-ping/mcp/peon-mcp.js"] } } }Cursor
将同样的结构写入项目下的.cursor/mcp.json:
{ "mcpServers": { "peon-ping": { "command": "node", "args": ["/path/to/peon-ping/mcp/peon-mcp.js"] } } }任意 MCP 客户端
任何支持 stdio 传输的 MCP 客户端都使用如下通用配置,可通过env注入音量等环境变量:
{ "command": "node", "args": ["/path/to/peon-ping/mcp/peon-mcp.js"], "env": { "PEON_VOLUME": "0.5" } }将/path/to/peon-ping替换为你本地仓库或安装的根目录即可。服务端通过StdioServerTransport连接(mcp/peon-mcp.js 的await server.connect(new StdioServerTransport())),因此所有走 stdio 的 MCP 客户端协议上都开箱即用。
服务端架构:一个工具、两个资源
工具play_sound的输入与边界
源码中play_sound用 zod 定义入参(mcp/peon-mcp.js):
server.tool( "play_sound", 'Play a sound effect. Keys: "pack/sound" (e.g. "duke_nukem/Groovy"). Read the catalog resource for available sounds.', { sound: z.string().optional().describe("Sound key"), sounds: z.array(z.string()).optional().describe("Multiple sound keys"), }, ... );处理逻辑中有几个值得注意的细节:
- 键合并:
const keys = arr || (sound ? [sound] : []),两个字段都缺省时返回No sound specified.; - 单批上限 5 个:
keys.slice(0, 5),一次调用最多处理 5 个音效键,防止模型误传超长列表; - 逐键解析与分级报错:每个键按
key.split("/", 2)拆出pack与sound,依次经过“格式非法(❌ Invalid)→ 目录中不存在该 pack 或音效(❌ Not found)→ 清单有记录但文件缺失(❌ Missing file)→ 成功入队(🔊 key ("label"))四个结果分支,最终以换行拼接的文本一次性返回。这种逐键反馈让模型能根据错误信息自我纠正键名。
音效解析:从pack/sound到真实文件路径
resolveSound(pack, sound)(mcp/peon-mcp.js)是键到路径的映射核心:它遍历该 pack 清单manifest.categories下每个类别的sounds数组,把file字段取 basename 并去掉扩展名后与请求的音效名做精确匹配,命中后返回{ path: <packs_dir>/<pack>/<s.file>, label: s.label || sound }。也就是说音效键是大小写敏感的字符串匹配,目录里到底有哪些可用键,完全由各 pack 的 CESP 清单决定。
队列:串行播放保证音效不重叠
playFile通过spawn启动播放器并等待其close/error事件。所有音效先进入queue,再由drain()逐个串行消费(mcp/peon-mcp.js)。一次play_sound调用传入多个键时,它们按顺序排队播放,互不覆盖;播放失败不会阻塞后续队列(error事件同样触发 resolve)。
两个 Resource 的目录输出格式
peon-ping://catalog(mcp/peon-mcp.js)按 pack 名字典序输出:
N packs. Use play_sound with "pack/SoundName". packA (显示名): packA/Sound1 ("label 1"), packA/Sound2 ("label 2") packB (显示名): ...peon-ping://pack/{name}(mcp/peon-mcp.js)则按类别分组,每行一个音效键及其 label:
显示名: 类别名: pack/Sound1 — "label 1" pack/Sound2 — "label 2"两个资源都在模块加载时惰性构建并缓存目录(loadCatalog()首次调用后复用),pack 不存在时返回Pack "<name>" not found.,未安装任何 pack 时 catalog 返回No packs installed.——这些文案都是给模型读的,可直接作为决策依据。
目录发现与 CESP 清单解析
pack 目录的搜索顺序(findPacksDir,mcp/peon-mcp.js):
- 环境变量
PEON_PACKS_DIR(存在即采用); ~/.openpeon/packs;~/.claude/hooks/peon-ping/packs;- 都没有则返回
null,所有资源返回空目录提示。
这与 peon-ping 主程序的用户数据目录策略一致:peon.sh 中同样是~/.claude/hooks/peon-ping与~/.openpeon二选一的发现逻辑,MCP 服务端只是复用了同一约定,保证无论安装方式如何,MCP 都能找到与 hook 模式相同的音效库。
目录加载时(loadCatalog)只认含openpeon.json的子目录,其余条目被跳过,解析失败(JSON 损坏)同样静默跳过。这里的openpeon.json即 CESP 清单,其结构可从 skills/peon-ping-create-pack/SKILL.md 得到印证:cesp_version、name、display_name、version、description,以及categories——每个类别映射到{"sounds": [{"file": "sounds/xxx.wav", "label": "..."}]}。MCP 服务端正是读取display_name与每个s.file/s.label来渲染 catalog 文本的。
音量与配置的解析优先级
源码顶部的配置加载(mcp/peon-mcp.js)定义了明确优先级:
const cfg = readConfig(); const volume = process.env.PEON_VOLUME ? (parseFloat(process.env.PEON_VOLUME) || 0.5) : (typeof cfg.volume === "number" ? cfg.volume : 0.5); const useSoundEffectsDevice = cfg.use_sound_effects_device !== false;- 配置文件候选路径依次为
~/.claude/hooks/peon-ping/config.json与~/.openpeon/config.json,取第一个可解析的;仓库根目录的 config.json 展示了包含"use_sound_effects_device": true在内的完整字段形态; - 音量优先级:
PEON_VOLUME环境变量 > 配置文件volume字段 > 默认0.5; use_sound_effects_device缺省视为true,仅在显式配置为false时关闭。
macOS 上这个开关有实际意义:当其为true且~/.claude/hooks/peon-ping/scripts/peon-play存在时,服务端不走afplay,而是调用自研的 scripts/peon-play.swift 编译产物——它是一个将音频路由到系统“Sound Effects”输出设备的afplay替代实现,支持-v <volume>音量参数,避免提示音打断用户正在听的音乐设备。
版本号同样有兜底:先尝试读取仓库根的VERSION文件(当前为 2.37.0),失败则回落到硬编码的2.1.0,注册为new McpServer({ name: "peon-ping", version })。
跨平台播放实现
detectPlatform()(mcp/peon-mcp.js)把运行时映射为mac/wsl/linux/windows四类,其中 Linux 会通过检查/proc/version是否含microsoft字样进一步识别 WSL。各平台的实际播放命令:
| 平台 | 播放器 | 音量参数 |
|---|---|---|
| macOS | afplay(或peon-play) | -v <volume> |
| WSL2 | powershell.exe -NoProfile -Command调Media.SoundPlayer | 不支持,固定音量 |
| Linux | 按pw-play→paplay→ffplay→mpv→play→aplay顺序探测第一个可用的 | 见下 |
Linux 各播放器的音量换算在getPlayCommand(mcp/peon-mcp.js)中逐一特化,因为各工具的量纲不同:
if (player === "pw-play") return [player, ["--volume", String(v), filePath]]; if (player === "paplay") return [player, ["--volume", String(Math.round(v * 65536)), filePath]]; if (player === "ffplay") return [player, ["-nodisp", "-autoexit", "-volume", String(Math.round(v * 100)), filePath]]; if (player === "mpv") return [player, ["--no-video", `--volume=${Math.round(v * 100)}`, filePath]]; return [player, [filePath]]; // play / aplay 不传音量PEON_VOLUME的 0–1 归一化值被换算成 paplay 的 0–65536 线性刻度或 ffplay/mpv 的 0–100 百分比刻度;play(sox)与aplay则直接裸播、无法控音量。WSL2 走 PowerShellSoundPlayer时会将路径斜杠转换为反斜杠(mcp/peon-mcp.js)。与 mcp/README.md 的“Platform support”一节描述完全一致:macOS 用afplay,Linux 按序取第一个可用播放器,WSL2 用SoundPlayer。
环境变量汇总
| 变量 | 默认值 | 说明 |
|---|---|---|
PEON_PACKS_DIR | ~/.openpeon/packs | 音效包目录;仅当目录存在时生效,否则回落到内置搜索顺序 |
PEON_VOLUME | 0.5 | 播放音量(0–1),优先级高于配置文件中的volume字段 |
两个变量都可在任意 MCP 客户端配置的env段中注入,无需改动服务端代码。
实际使用方式与适用边界
接入完成后的典型交互是:客户端启动时读取peon-ping://catalog资源(或peon-ping://pack/{name}查看单个 pack),模型据此得知全部可用音效键;随后在合适的时机发起工具调用,例如:
{ "sound": "duke_nukem/Groovy" }或批量播放:
{ "sounds": ["peon/PeonReady1", "sc_kerrigan/IReadYou"] }返回文本中逐键的🔊/❌前缀既是给用户的可读反馈,也是给模型的纠错信号——遇到Not found时模型可重新读取 catalog 校正键名。
适用前提与限制需要明确:
- 该服务端只读本地已安装的 pack 目录,不负责下载或安装音效包;未安装任何 pack 时 catalog 会明确提示
No packs installed.; - 音效键基于清单精确匹配,且单次调用最多处理 5 个键;
- 音量控制依赖底层播放器能力,WSL2 的
SoundPlayer与 Linux 的play/aplay路径无法应用PEON_VOLUME; - 服务端是纯本地 stdio 进程,不监听网络端口,行为完全由本地文件系统与系统播放器决定。
小结
peon-ping 的 MCP 服务端是一个约 200 行、零外部业务依赖的 Node.js 实现,设计上有三个可借鉴的点:目录信息以 Resource 形式一次性交付上下文,避免工具调用风暴;键的解析结果以带状态前缀的文本逐键返回,让模型能自我纠错;平台差异全部收敛在一个getPlayCommand函数内,用统一的 0–1 音量值向各播放器换算量纲。对于任何希望让 AI Agent 具备“发声反馈”能力的本地工具,这套“单工具 + 目录资源 + 本地串行队列”的结构都可直接参照 mcp/peon-mcp.js 的实现来移植。
【免费下载链接】peon-ping
Warcraft III Peon voice notifications (+ more!) for Claude Code, Codex, IDEs, and any AI agent. Stop babysitting your terminal. Employ a Peon today.
相关推荐
peon-ping 声音静音开关实战指南:`/peon-ping-toggle` 技能与 `peon toggle` 命令的完整解析
peon ping 声音静音开关实战指南: /peon ping toggle 技能与 peon toggle 命令的完整解析 导读 peon ping 是面向
mcp-for-beginners 实战:为 Calculator MCP Server 扩展 Square Root 工具并让 AI Agent 通过自然语言调用
mcp for beginners 实战:为 Calculator MCP Server 扩展 Square Root 工具并让 AI Agent 通过自然语言
教程文档人工智能peon-ping 实战指南:为 Claude Code 与任意 AI 编码代理接入游戏语音通知
peon ping 实战指南:为 Claude Code 与任意 AI 编码代理接入游戏语音通知 导读 :本文基于 peon ping 仓库的官方文档( REA
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考