内容
<div align="center">
# 问答大师
**用于 AI 编程代理的物理人工参与。**
当模型需要你时,它会 ping 你桌上的一个小屏幕 —— 而不是你的聊天窗口。
[](https://github.com/mhrsntrk/ask-master/releases)
[](https://github.com/mhrsntrk/ask-master/actions)
[](https://go.dev)
[](LICENSE)
[](https://skills.sh/mhrsntrk/ask-master-skill)
</div>
---
`问答大师` 是一个 [MCP](https://modelcontextprotocol.io) 服务器,为 Claude Code、OpenCode、Cursor 或 Windsurf 提供一个侧通道:一个放在你桌上的 [M5Stack Cardputer](https://shop.m5stack.com/products/m5stack-cardputer-kit-w-m5stamps3)。代理会向你提问,你通过设备的键盘回答,聊天记录保持干净。
**使用场景。** 长期运行的代理循环,你可以离开。结对编程会话,你不想让审批提示混淆聊天。关键决策,你需要一个蜂鸣器。任何模型需要人工输入,但用户可能离开的情况。
## 工作原理
```
┌──────────────────────────────────────────────────────────────────┐
│ MCP 客户端 (Claude Code / OpenCode / Cursor / Windsurf) │
│ │
│ 调用:ask-human, confirm, choose, escalate-to-human │
│ │ │
│ ▼ 标准输入输出 (JSON-RPC 2.0) │
├──────────────────────────────────────────────────────────────────┤
│ 问答大师 (Go 守护进程) │
│ ├── main.go — MCP 服务器初始化 + 服务标准输入输出 │
│ ├── bridge.go — WebSocket 桥接 + /notify HTTP 端点 │
│ ├── daemon.go — 多会话 Unix 套接字 + 锁 │
│ └── tools.go — ask-human, confirm, choose, escalate-to-human│
│ │ │
│ ▼ WebSocket ws://0.0.0.0:8765 │
├──────────────────────────────────────────────────────────────────┤
│ Cardputer ADV 固件 (Arduino / C++) │
│ ├── WiFi + WebSocket 自动重新连接 (3秒) │
│ ├── UDP 信标 (端口 8766) — 存在 + 唤醒 │
│ ├── 设备设置菜单 (无需源代码编辑) │
│ └── TFT 渲染器 + 键盘 │
└──────────────────────────────────────────────────────────────────┘
```
## 工具
| 工具 | 何时使用 | 设备显示 | 回复方式 |
|------|-------------|--------------|--------------|
| `ask-human` | 开放式问题,自由文本答案 | 蓝色 **ASK** 屏幕,1000 Hz 蜂鸣 | 输入,回车 |
| `confirm` | 是/否决策 | 红色 **CONFIRM** 屏幕,1300 Hz 蜂鸣 | `Y` / `N` |
| `choose` | 2-6 个多选题 | 青色 **CHOOSE** 屏幕,900 Hz 蜂鸣 | 数字键 |
| `escalate-to-human` | 紧急 — 较大警报,谨慎使用 | 橙色 **ESCALATE** 屏幕,1500 Hz 蜂鸣 | 输入,回车 |
当设备离线时,工具返回安全回退(`[CARDPUTER OFFLINE] …`,`false`,或第一个选项),因此代理不会挂起。
## 快速开始
### 1. 安装服务器
**macOS (Homebrew):**
```bash
brew install mhrsntrk/ask-master/ask-master
```
**其他平台 / 源代码:**
```bash
git clone https://github.com/mhrsntrk/ask-master && cd ask-master
make build # 生成 ./ask-master
```
### 2. 刷写 Cardputer
**M5Burner(推荐 — 首次刷写后无需 PC):**
1. 安装 [M5Burner](https://docs.m5stack.com/en/uiflow/m5burner/introduction)。
2. 在 **用户自定义** 选项卡中,输入共享代码 `KXgpvtfPA52RKfQK`(或搜索 `ask-master`)。
3. 选择您的 Cardputer ADV 并点击 **烧录**。
4. 首次启动时,设备会扫描 WiFi,提示输入密码和计算机的 IP 地址。
其他路径:[M5Launcher(OTA)](#固件-其他路径) · [PlatformIO 编译](#固件-其他路径)
### 3. 连接到 Claude Code
```text
/plugin marketplace add mhrsntrk/ask-master
/plugin install ask-master@ask-master
```
插件捆绑了 MCP 服务器注册、技能、斜杠命令、`SessionStart` 上下文钩子和 `Notification` 钩子,当 Claude Code 空闲时会自动 ping 设备。`问答大师` 二进制文件必须位于 `$PATH` 中(由步骤 1 处理)。
**需要一次手动编辑** — Claude Code 插件无法预先授予 MCP 权限。在 `~/.claude/settings.json` 中追加,以便代理不会在每次调用时提示(这会破坏 AFK 的目的):
```json
{
"permissions": {
"allow": [
"mcp__ask-master__ask-human",
"mcp__ask-master__confirm",
"mcp__ask-master__choose",
"mcp__ask-master__escalate-to-human"
]
}
}
```
就这样。向代理提问 AFK 并观察 Cardputer 亮起。
## 其他 MCP 客户端
<details>
<summary><b>Claude Code(手动,无插件)</b></summary>
```bash
claude mcp add ask-master --scope user -- /path/to/ask-master --ws-addr 0.0.0.0:8765
```
或手动编辑 `~/.claude/settings.json`:
```json
{
"mcpServers": {
"ask-master": {
"command": "/path/to/ask-master",
"args": ["--ws-addr", "0.0.0.0:8765"]
}
}
}
```
</details>
<details>
<summary><b>OpenCode</b></summary>
`opencode.jsonc`:
```jsonc
{
"mcp": {
"ask-master": {
"type": "local",
"command": ["/path/to/ask-master", "--ws-addr", "0.0.0.0:8765"],
"enabled": true,
"timeout": 310000
}
},
"instructions": "仅当聊天 2 分钟内无人回答时,通过 ask-master 工具将问题升级到物理设备。"
}
```
</details>
<details>
<summary><b>Cursor</b></summary>
`~/.cursor/mcp.json`:
```json
{
"mcpServers": {
"ask-master": { "command": "/path/to/ask-master" }
}
}
```
`.cursorrules`:
```text
如果我离开键盘(2 分钟内无回复),请使用 ask-master MCP 工具 ping 我的 Cardputer。
```
</details>
<details>
<summary><b>Windsurf</b></summary>
`~/.codeium/windsurf/mcp_config.json`:
```json
{
"mcpServers": {
"ask-master": { "command": "/path/to/ask-master" }
}
}
```
Windsurf 规则:
```text
始终优先使用聊天。如果 120 秒内没有回复,请使用 ask-master MCP 服务器将问题升级到我的物理 Cardputer。
```
</details>
## 插件功能(Claude Code)
捆绑插件为您提供的不只是 MCP 服务器:
| 斜杠命令 | 操作 |
|---------------|--------|
| `/ask-master:ask <q>` | 立即强制调用 `ask-human` |
| `/ask-master:escalate <q>` | 立即强制调用 `escalate-to-human`(较大警报) |
| `/ask-master:status` | 打印守护进程 + 套接字健康状态 |
| `/ask-master:notify-test [msg]` | 在循环回 `/notify` 端点触发通知 |
**通知钩子。** 每当 Claude Code 发出 `Notification` 事件(权限提示,空闲等待)时,插件的 `scripts/notify.sh` 向 `http://127.0.0.1:8765/notify` 发送 POST 请求。守护进程调度 `escalate` 风格的警报到设备 — 发送后即忘,挂钩从不被阻塞。设备离线 → 503,挂钩静默无操作。
**仅限环回。** `/notify` HTTP 端点拒绝非 `127.0.0.1` / `::1` 调用者,状态码为 403,即使 WebSocket 监听器绑定到 `0.0.0.0` 以供设备 LAN 访问。
**可选状态栏。** 添加到 `~/.claude/settings.json`:
```json
{
"statusLine": {
"type": "command",
"command": "${HOME}/.claude/plugins/ask-master/scripts/statusline.sh"
}
}
```
在 Claude Code 状态栏中打印 `[ask-master:on]` / `[ask-master:off]`。
参见 [`plugin/README.md`](plugin/README.md) 以获取完整的插件参考。
## 命令行标志
| 标志 | 默认值 | 描述 |
|------|---------|-------------|
| `--ws-addr` | `0.0.0.0:8765` | WebSocket 桥接监听地址。`/notify` HTTP 端点共享此监听器,但仅限环回。 |
| `--timeout` | `300` | 默认工具答案超时(秒)。 |
| `--log-level` | `info` | `debug` / `info` / `warn` / `error`。 |
| `--version` | — | 打印版本并退出。 |
## 技能(非 Claude Code 代理)
对于 OpenCode、Cursor、Windsurf 或任何其他 [skills.sh](https://skills.sh) 感知代理,请单独安装升级剧本:
```bash
npx skills add mhrsntrk/ask-master-skill
```
> **Claude Code 用户 — 跳过此步骤。** 插件已经包含技能(作为符号链接到同一源文件)。安装两者会在代理上下文中加载它两次。
## 故障排除
<details>
<summary><b>设备显示 <code>CONNECTING…</code> 无限循环</b></summary>
- Cardputer 无法访问您输入的 IP 地址。按下 **S** 以重新打开设置并重新输入主机 IP 地址。
- 您的防火墙可能阻止传入 TCP 连接到 `8765`。为您的本地子网允许它。
- 服务器实际上并未运行。检查 `pgrep -f ask-master` 或 `lsof -i :8765`。
</details>
<details>
<summary><b>代理输出中的 <code>[CARDPUTER OFFLINE]</code></b></summary>
设备在过去 2 分钟内未发送 UDP 信标。检查设备的 WiFi(右上角指示器)并确认 UDP `8766` 在设备和您的机器之间未被阻止。
</details>
<details>
<summary><b><code>/notify</code> 返回 503</b></summary>
与上述相同 — 未检测到设备存在。通知钩子将其视为无操作。
</details>
<details>
<summary><b>端口 8765 已被占用</b></summary>
使用 `--ws-addr 0.0.0.0:8865`(或其他空闲端口)运行,并通过 **S** 设置菜单在设备上重新输入该端口。
</details>
<details>
<summary><b>多个 MCP 客户端想要使用相同的服务器</b></summary>
`ask-master` 作为守护进程运行。第一个实例声明 `/tmp/ask-master.sock` 处的 Unix 套接字;后续实例将标准输入输出代理到它。多个 Claude Code、OpenCode、Cursor 会话可以干净地共享一个设备。
</details>
## 固件:其他路径
<details>
<summary><b>M5Launcher(OTA,初始设置后无需 PC)</b></summary>
1. 在 Cardputer 上安装 [M5Launcher](https://github.com/bmorcelli/Launcher)。
2. 打开 M5Launcher → **OTA** → 搜索 `ask-master`。
3. 通过 WiFi 安装,然后通过设备设置向导进行设置。
</details>
<details>
<summary><b>PlatformIO(从源代码编译)</b></summary>
```bash
cd firmware/ask_master
pio run -t upload
```
生成单个合并的二进制文件用于 M5Burner v3:
```bash
./merge_bin.sh
```
输出:`ask-master-cardputer.bin`。
在任何时候,按 **S** 重新进入设置。
</details>
## 开发
```bash
make build # 生成 ./ask-master,版本由 git describe 标记
make test # go test ./... -race -v
make lint # golangci-lint
make firmware # PlatformIO 编译
```
布局:
```
.
├── bridge.go / bridge_test.go — WebSocket + /notify 端点
├── daemon.go — Unix 套接字多会话守护进程
├── tools.go / tools_test.go — MCP 工具注册
├── config.go / config_test.go — 标志
├── main.go — 入口,守护进程/客户端路由
├── internal/truncate/ — 字符串剪切帮助函数
├── firmware/ask_master/ — Arduino / PlatformIO 源代码
├── skill/SKILL.md — 升级剧本(真相源)
├── plugin/ — Claude Code 插件(技能符号链接)
└── .claude-plugin/ — 仓库的 marketplace.json
```
`skill/SKILL.md` 文件是真相源。插件的副本是一个符号链接;独立技能仓库(`mhrsntrk/ask-master-skill`)在 `master` 推送到 GitHub Actions 时同步。
## 安全
`ask-master` 在您的笔记本电脑上作为守护进程运行。WebSocket 桥接绑定到所有接口(`0.0.0.0:8765`),以便 Cardputer 可以从您的 LAN 访问它。为了保持该接口诚实:
- **WS 握手上的 IP 绑定身份验证。** 对等方只有在其 IP 地址与最近的 UDP 信标源(真实设备每隔几秒钟信标一次)匹配时,才能完成 WS 升级。随机 LAN 对等方以 `403` 失败。
- **帧大小上限。** WS 读取器限制每个帧 64 KB — 恶意对等方无法通过超大消息崩溃守护进程。
- **队列上限。** 最多 32 个问题待处理;进一步调用返回错误到 MCP 客户端,而不是无限增长内存。
- **`/notify` 速率限制。** 环回通知端点每 2 秒限制一个警报(超过时返回 `429`)。插件的 `Notification` 钩子在客户端添加了 10 秒的防抖。
- **仅限环回 `/notify`。** 端点拒绝非 `127.0.0.1` / `::1` 调用者,状态码为 `403`,即使监听器本身绑定到 LAN。
- **每个用户的运行时状态。** 守护进程 Unix 套接字位于 `$XDG_RUNTIME_DIR`(Linux)或 `$HOME/Library/Caches/ask-master`(macOS),不再位于世界可写的 `/tmp`。
**已知限制。** IP 绑定身份验证是尽力而为的 — 同一 LAN 段上的攻击者可以同时(a)击倒真实设备和(b)伪造其 IP 地址,从而仍然可以与守护进程配对。计划为下一个版本添加预共享密钥配对流程,带有设备令牌输入。
如果您在不受信任的网络上运行且没有连接 Cardputer,请传递 `--ws-addr 127.0.0.1:8765` 以仅绑定到环回。
## 路线图
- [x] 多会话共享的守护进程模式
- [x] 设备 WiFi + 服务器设置(无需源代码编辑)
- [x] M5Burner 分发
- [x] Claude Code 插件:技能 + 斜杠命令 + `Notification` 钩子
- [x] 环回 `/notify` HTTP 端点
- [x] IP 绑定 WS 身份验证 + 帧大小上限 + 通知速率限制(v1.4.1)
- [ ] 预共享密钥配对流程,带有设备令牌输入
- [ ] 每个问题的元数据(哪个代理/项目提出)
- [ ] 多设备扇出(一个服务器,多个 Cardputer)
- [ ] 设备离线时的 Web UI 备用
## 为什么是“问答大师”?
> **警告:** 引用 Git 分支名称和名字词源。
2020 年,许多人对“master”一词感到非常沮丧,并花费大量精力将 Git 默认分支重命名为“main”。这是英雄般的行为。
然后我将这个工具命名为 `ask-master`。我的名字是 **Mahir**,字面意思是 **“大师”**。我的父母在大约 30 年前选择了它,对你的分支命名约定一无所知。
如果这个名字惹恼了你,**Fork** 按钮就在那里 — `ask-main`、`ask-primary`、`ask-trunk` 都是有效的生活选择。我不会生气。我可能会笑。
*Mahir out.*
## 许可证
[MIT](LICENSE) — © Mahir Şentürk
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器