8.5 KiB
ppclock-mcp · MCP 服务器参考(v0.2.0-beta)
面向 agent 挂载与部署的工具级参考。SDK 签名见
docs/sdk/API.md,协议细节见docs/protocol.md。
一句话
ppclock-mcp 是跑在蓝牙物理所在机器上的单层 MCP 服务器:13 个工具覆盖对时 / 模式 / 切换 / 传图 / 模板 / 倒计时 / 日历文字 / 休眠 / 刷屏 / raw 逃生舱,agent 经 stdio 或 streamable HTTP 直接驱动 4.2" 墨水屏设备。
安装
pip install -e ".[mcp]" # 追加依赖:mcp>=1.23,<2
要求:Python ≥ 3.10、本机蓝牙适配器(bleak)。服务器与被控设备在同一台蓝牙主机上; 不需要互联网。
启动
入口为 console script ppclock-mcp(python -m ppclock.mcp_server 不受支持)。
# stdio —— 本机 agent 直挂
ppclock-mcp --mac AA:BB:CC:DD:EE:FF
# streamable HTTP —— 局域网 agent 平台远程调用
PPCLOCK_MCP_TOKEN=<token> ppclock-mcp --transport http --host 0.0.0.0 --port 8972
启动参数:
| 参数 | 默认 | 说明 |
|---|---|---|
--transport |
stdio |
stdio / http |
--host |
127.0.0.1 |
HTTP 绑定地址;绑非回环地址必须设 PPCLOCK_MCP_TOKEN,否则拒绝启动 |
--port |
8972 |
HTTP 端口 |
--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 无鉴权,
信任本机进程)。
Agent 挂载示例
# Claude Code(stdio)
claude mcp add ppclock -- ppclock-mcp --mac AA:BB:CC:DD:EE:FF
streamable HTTP 端点为 http://<host>:8972/mcp,请求头携带
Authorization: Bearer <PPCLOCK_MCP_TOKEN>;token 不符返回 401。HTTP 为无状态模式
(stateless_http),无需会话保持。
13 工具参考
全部工具 async,返回统一契约(见下节)。参数与行为逐字对齐 src/ppclock/mcp_tools.py。
| 工具 | 参数 | 说明 |
|---|---|---|
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 |
upload_image |
source: str, slot: int = 0, algo: str = "atkinson", mono: bool = False, threshold/diffusion/brightness/contrast/saturation/rotate: float|None = None |
传图到指定槽位。source = 服务器路径或 base64(可带 data URI 前缀);algo ∈ none/floydsteinberg/atkinson/bayer/stucki/jarvis;6 个可选 adjust 参数:threshold/diffusion/brightness/contrast/saturation/rotate(仅显式传入时生效) |
render_template |
name: str, payload: dict|None = None, slot: int = 0, algo: str = "atkinson" |
本地渲染模板并上传:schedule/businesscard/memo/course/qrcode/custom |
countdown |
date: str, mode: str = "clock", prefix: str|None = None |
倒计时;date 为 ISO YYYY-MM-DD;mode = clock/calendar |
countdown_off |
无 | 关闭倒计时 |
calendar_text |
text: str |
设置日历中文文字并切到日历模式一 |
set_sleep |
on: bool, start_h: int, end_h: int |
设置休眠时段(0-23 时) |
clear_screen |
无 | 刷屏(EPD 清屏) |
raw_send |
data_hex: str, channel: str = "alt" |
逃生舱:直发十六进制字节串;channel = rxtx/epd/alt |
device_status 返回字段:connected / mac / device_id(尽力而为,失败省略)/
idle_seconds / connect_count / connect_timeout / idle_timeout。
输出契约与错误码
与 CLI --json 同构的统一契约:
成功 {"ok": true, "tool": "<name>", "data": {...}}
失败 {"ok": false, "tool": "<name>", "error": {"code", "message", "hint"?}}
错误码(5 种):
| code | 触发 | hint |
|---|---|---|
CONNECT_TIMEOUT |
守候窗口内未连上设备 | 设备长睡眠、广播窗口 30-60s;调大 --connect-timeout 或稍后重试 |
DEVICE_NOT_FOUND |
扫描未发现设备 | 未发现 NRF- 前缀设备;确认设备在位且未处于深睡 |
BLE_ERROR |
其他传输层错误 | 无 hint |
INVALID_PARAM |
参数校验失败(非法 base64/非图片/非法日期/非法 hex 等) | 无 hint,message 给出具体原因 |
INTERNAL |
工具层兜底未分类异常 | 无 hint,message 为 类型: 详情 |
hint 仅对 CONNECT_TIMEOUT / DEVICE_NOT_FOUND 出现,语义是给 agent 的可执行恢复建议
(调参重试 / 检查设备),不是给人看的错误描述。
连接行为(方案 C:按需连接 + 空闲保持)
- 守候窗口:首次操作发起连接,守候
--connect-timeout秒(默认 90s,覆盖设备 30-60s 广播窗口);设备在深睡中也能等到其广播。 - 空闲超时:操作完成后保持连接;空闲
--idle-timeout秒(默认 300s)自动断开让 设备回睡眠。--idle-timeout 0退化为每次调用独立连接。 - 串行:所有设备操作经一把锁串行执行(BLE 适配器独占),并发调用自动排队,不会 互相打断。
- 重连一次:操作中连接丢失(传输层异常全家:SDK
TransportError、bleak 裸抛BleakError、平台OSError/超时)→ 断开重连并重试一次,再失败才向上返回错误。
Windows 部署(蓝牙所在机)
python -m venv .venv
.venv\Scripts\pip install -e ".[mcp]"
setx PPCLOCK_MCP_TOKEN "<token>" # 或按服务方式注入环境变量
.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 头按<IP>:<port>匹配), 漏配时局域网请求被 mcp 的 DNS 重绑定防护拒绝(421)。- 不动既有 8971 bridge 与其他服务:8972 为独立端口、独立进程。
--mac建议显式指定设备地址,避免自动扫描误绑其他NRF-设备。
常驻部署(2026-07-31 生产性测试实例)
参考实现(banyWinServer 192.168.61.35,v0.2.0-beta,熠管家运维):
- 常驻方式:SYSTEM 计划任务
BanyTech-PPCLOCK-MCP(AtStartup、最高权限、 执行时间不限、Task Scheduler 999 次/1 分钟恢复);runner 脚本另有 5 秒子进程重启。 - token:Windows CSPRNG 生成,存
C:\ProgramData\OpenClaw\ppclock-mcp\token.txt, ACL 仅 SYSTEM/Administrators 可读;不入仓、不写日志。 - 日志:同目录
logs\——supervisor.log记版本/启动参数/退出, stdout/stderr 按次落文件。 - 验证基线:真实 LAN Host initialize 200、
device_statusok; 杀 8972 进程后 5 秒自动拉起、initialize 复 200。 - agent 挂载:
http://192.168.61.35:8972/mcp+Authorization: Bearer <token>。 - 运维记录原件:
/home/cwmine/vps/host-win/maintain_logs/2026-07-31_ppclock-mcp-production-test-service.md。
不暴露的能力
以下 SDK 能力有意不注册为 MCP 工具:
| 能力 | 原因 |
|---|---|
OTA 固件升级(ota) |
高危写闪存,agent 误调用可砖机;只走 CLI 显式 --yes 授权路径 |
激活(activate/activate_auto) |
一次性出厂级操作,日常 agent 场景不需要;保留在 SDK/CLI |
LUT 校准(lut) |
面板厂商级调试参数,误刷影响显示质量 |
WiFi 配置(wifi) |
本固件为 no-op(固件证据),暴露只会误导 agent |
轮播(rotation) |
本固件为 no-op(固件证据),同上 |
需要这些能力时按 docs/sdk/API.md 走 SDK/CLI,不经 MCP 面。