diff --git a/docs/sdk/MCP.md b/docs/sdk/MCP.md index c1f7224..40af95b 100644 --- a/docs/sdk/MCP.md +++ b/docs/sdk/MCP.md @@ -39,6 +39,7 @@ PPCLOCK_MCP_TOKEN= ppclock-mcp --transport http --host 0.0.0.0 --port 897 | `--mac` | 无 | 设备 MAC;缺省自动扫描 `NRF-` 前缀并记住实际地址 | | `--connect-timeout` | `90.0` | 守候连接秒数(覆盖设备 30-60s 广播窗口) | | `--idle-timeout` | `300.0` | 空闲断开秒数;`0` = 每次调用独立连接 | +| `--allowed-hosts` | 无 | HTTP Host 白名单(逗号分隔)。HTTP 模式绑非回环地址时**必须**把本机的局域网地址加进来(如 `192.168.61.35:8972,localhost:8972`),否则 LAN Host 请求被 DNS 重绑定防护拒绝(421);缺省仅 localhost 族 | 环境变量:`PPCLOCK_MCP_TOKEN` —— HTTP 模式 Bearer 鉴权令牌(仅 HTTP 使用;stdio 无鉴权, 信任本机进程)。 @@ -60,8 +61,8 @@ streamable HTTP 端点为 `http://:8972/mcp`,请求头携带 | 工具 | 参数 | 说明 | |---|---|---| -| `scan_devices` | `timeout: float = 5.0` | 扫描附近 NRF- 前缀墨水屏设备,返回 `[{name,address,rssi}]` | -| `device_status` | 无 | 查询连接状态/绑定 MAC/设备 ID/空闲秒数 | +| `scan_devices` | `timeout: float = 5.0` | 扫描附近 NRF- 前缀墨水屏设备;返回形状 `data.devices = [{name,address,rssi}]` | +| `device_status` | 无 | 查询连接状态/绑定 MAC/设备 ID/空闲秒数。会触发连接守候(最长 `--connect-timeout` 秒)并刷新空闲计时——周期轮询本工具会阻止 idle 自动断开 | | `set_time` | `tz: float = 8.0` | 对时;tz 为时区偏移(默认 8=北京时间) | | `set_mode` | `mode: str` | 切换显示模式:`clock1-3`/`calendar1-3`/`image0-3`/`tricolor`/`mono` | | `toggle` | `name: str` | 单字节切换:`invert`/`font`/`rotate180`/`hour_format`/`clock_color` | @@ -107,8 +108,8 @@ streamable HTTP 端点为 `http://:8972/mcp`,请求头携带 设备回睡眠。`--idle-timeout 0` 退化为每次调用独立连接。 - **串行**:所有设备操作经一把锁串行执行(BLE 适配器独占),并发调用自动排队,不会 互相打断。 -- **重连一次**:操作中连接丢失(TransportError)→ 断开重连并重试一次,再失败才向上 - 返回错误。 +- **重连一次**:操作中连接丢失(传输层异常全家:SDK `TransportError`、bleak 裸抛 + `BleakError`、平台 `OSError`/超时)→ 断开重连并重试一次,再失败才向上返回错误。 ## Windows 部署(蓝牙所在机) @@ -116,10 +117,13 @@ streamable HTTP 端点为 `http://:8972/mcp`,请求头携带 python -m venv .venv .venv\Scripts\pip install -e ".[mcp]" setx PPCLOCK_MCP_TOKEN "" # 或按服务方式注入环境变量 -.venv\Scripts\ppclock-mcp --transport http --host 0.0.0.0 --port 8972 +.venv\Scripts\ppclock-mcp --transport http --host 0.0.0.0 --port 8972 ` + --allowed-hosts "192.168.61.35:8972,localhost:8972" # 换成本机局域网地址 ``` - 防火墙放行 **8972/tcp**,仅对局域网段开放(勿暴露公网;token 是唯一防线)。 +- `--allowed-hosts` 必须包含本机的局域网地址(Host 头按 `:` 匹配), + 漏配时局域网请求被 mcp 的 DNS 重绑定防护拒绝(421)。 - 不动既有 **8971** bridge 与其他服务:8972 为独立端口、独立进程。 - `--mac` 建议显式指定设备地址,避免自动扫描误绑其他 `NRF-` 设备。 diff --git a/docs/superpowers/specs/2026-07-30-ppclock-mcp-design.md b/docs/superpowers/specs/2026-07-30-ppclock-mcp-design.md index 1acf8f1..f2d24a1 100644 --- a/docs/superpowers/specs/2026-07-30-ppclock-mcp-design.md +++ b/docs/superpowers/specs/2026-07-30-ppclock-mcp-design.md @@ -122,3 +122,26 @@ mac 解析顺序:工具参数 > 启动 `--mac` > 自动扫描首个 NRF-(并 - 多设备并发(单适配器串行已够;mac 参数可切换设备) - MCP resources/prompts(本期只 tools) - Windows 服务化、Linux 硬件实测(后续迭代) + +## 实测与终审偏离记录(2026-07-30) + +实现与终审后对原设计的偏离,逐条留痕: + +- **错误码实为 5 码**:`DEVICE_DISCONNECTED` 未 emit——操作中途掉线被 + DeviceManager 的"重连+重试一次"吸收,重试仍败按 `BLE_ERROR` 上报;工具面 + 始终只看到最终失败,无法区分"中途掉过线"。契约保留 5 码: + `INVALID_PARAM` / `DEVICE_NOT_FOUND` / `CONNECT_TIMEOUT` / `BLE_ERROR` / + `INTERNAL`。 +- **mac 解析顺序修正**:实际为"启动 `--mac` > 自动扫描首个 `NRF-` 前缀并记住", + 工具面无 mac 参数(原设计的"工具参数 > …"未实现;多设备切换靠重启服务器 + 换 `--mac`,归入 YAGNI 多设备并发)。 +- **异常面适配真实硬件**:全部测试 fake 原来只抛英文 `transports.base. + TransportError`;真实 bleak 路径抛的是中文 `transports.local.TransportError` + (与 base 同名但互不继承)与**未包装**的 `BleakError`(如 `Not connected`)。 + 终审后 MCP 层捕获面扩为异常全家(两个 TransportError + BleakError + OSError + + asyncio.TimeoutError),错误码按类型优先、消息(中英文)次之映射。 +- **2026-07-30 熠管家 Windows 实测**:stdio 全链路通过(scan → connect → + set_time → upload_image 15000+15000 字节 → set_mode image0;远程无法目视 + 确认上屏)。HTTP 模式 LAN Host 被 mcp DNS 重绑定防护 421——已修:新增 + `--allowed-hosts`(逗号分隔 Host 白名单,`TransportSecuritySettings` + 实现,mcp 1.29.0 实测 API 与设计一致),Windows 部署文档同步更新。