Skip to main content
Glama
zuuyun

sa697a-mcp

by zuuyun
README.md
# sa697a-mcp

[English](#english) · [中文](#中文)

Control a SVAKOM SA697A pair (TX / RX) from official ChatGPT through a remote MCP server and a phone-side Web Bluetooth bridge, with every safety rule enforced in code rather than left to the AI.

通过远程 MCP 服务和手机上的 Web Bluetooth 桥页面,让官方 ChatGPT 控制 SVAKOM SA697A(TX / RX)。所有安全规则都由代码强制执行,不依赖 AI 自觉遵守。

> **Unofficial. Not affiliated with, endorsed by, or supported by SVAKOM.** Reverse-engineered for interoperability. Use at your own risk; see [Disclaimer](#disclaimer--免责声明).

---

## English

### What's here

| Path | What it is |
|---|---|
| [`PROTOCOL.md`](PROTOCOL.md) | The BLE protocol: commands, linked TX+RX mode, button reports, quirks |
| [`DESIGN.md`](DESIGN.md) | Architecture and the safety rules (in Chinese) |
| `bridge/` | Phone bridge page (Android Chrome, Web Bluetooth). `lib/controller.js` is the **only** place safety rules live |
| `server/` | VPS service (Python): MCP tools `toy_status` / `toy_control` / `toy_stop`, bridge WebSocket, 6-digit pairing |
| `deploy/` | Install/update/rollback scripts for a systemd VPS behind Cloudflare Tunnel + OAuth proxy |
| `tools/` | Local LAN test server, a CLI that plays "ChatGPT", a dependency-free btsnoop parser |
| `docs/hardware-checklist.md` | Real-device acceptance checklist (in Chinese) |

### How it works

```
ChatGPT ──HTTPS──▶ Cloudflare Tunnel ─▶ OAuth proxy ─▶ server /mcp
                                                          │ WebSocket (bridge connects out)
phone Chrome bridge page ◀────────────────────────────────┘
   controller.js (all safety rules) ──BLE──▶ SA697A-TX / RX
```

The server authenticates, validates and relays, and fails fast. It keeps no session or plan of its own. The phone page enforces the safety rules, so a server bug can't bypass them:

- **Stop:** stop always wins and never waits in a queue.
- **Authorization:** a 60-minute authorization can only be started from the phone page, and each action is capped at 10 minutes.
- **Ramping:** intensity rises by at most one level every 2 s.
- **Automatic stops:** everything stops, heating included, when the page goes to the background or the bridge loses its VPS connection.
- **Latches:** a physical power-off or a BLE drop latches that device until the user clears it on the page.
- **Page stop:** after the user presses stop on the page, the AI is held until the user releases it there.

### Quick start

```bash
npm test                                   # JS unit tests (fake clock + fake BLE)
npm run serve                              # Local-only bridge page on your LAN (no AI)
python -m venv .venv && .venv/bin/pip install -r server/requirements.lock
.venv/bin/python -m unittest discover -s server/tests   # integration tests (needs node)
.venv/bin/python tools/dev_lan.py          # server on your LAN; tools/mcp_cli.py acts as ChatGPT
```

Web Bluetooth needs a secure context. For LAN testing over plain http, allow your origin once in Android Chrome at `chrome://flags/#unsafely-treat-insecure-origin-as-secure`. In production the page is served over HTTPS by the VPS. For a VPS deployment, see [`deploy/README.md`](deploy/README.md).

`tools/parse_btsnoop.py` prints the Bluetooth addresses found in your capture. Remove them before sharing its output.

### Acknowledgements

These projects gave us the ideas. No code was copied from them. Thanks also to Claude, which handled the whole thing end to end.

- [vickyldr/svakom-ble-ai](https://github.com/vickyldr/svakom-ble-ai): the idea of an AI → relay → local BLE bridge for a SVAKOM device, and the "never write the AE00/AE01 OTA service" warning.
- [daningzi50-hub/svakom-sl278h-ble](https://github.com/daningzi50-hub/svakom-sl278h-ble): an HTTP relay plus an Android Chrome Web Bluetooth bridge page, and the `55 …` command framing on FFE1.
- [buttplug.io](https://github.com/buttplugio/buttplug): its `svakom` protocol implementations helped identify the function bytes.
- [sigbit/mcp-auth-proxy](https://github.com/sigbit/mcp-auth-proxy) (OAuth for MCP) and [cloudflared](https://github.com/cloudflare/cloudflared) (tunnel) are used unmodified in deployment.

---

## 中文

### 内容

| 路径 | 说明 |
|---|---|
| [`PROTOCOL.md`](PROTOCOL.md) | 蓝牙协议:指令、TX+RX 联动、物理键上报、各种怪癖 |
| [`DESIGN.md`](DESIGN.md) | 架构与安全规则 |
| `bridge/` | 手机桥页面(Android Chrome,Web Bluetooth)。安全规则**只**在 `lib/controller.js` 里实现 |
| `server/` | VPS 服务(Python):MCP 工具 `toy_status` / `toy_control` / `toy_stop`、桥 WebSocket、6 位配对码 |
| `deploy/` | 部署到 systemd VPS 的安装、更新、回滚脚本(Cloudflare 隧道 + OAuth 代理) |
| `tools/` | 局域网测试服务器、在电脑上扮演 ChatGPT 的命令行、无依赖的 btsnoop 解析脚本 |
| `docs/hardware-checklist.md` | 真机验收清单 |

### 工作方式

服务端只做鉴权、校验、转发和快速失败,自己不保存会话或动作计划。安全规则全部在手机页面上执行,服务端即使有 bug 也绕不过去:

- **停止**:停止永远优先,不排队。
- **授权**:60 分钟授权只能在手机页面开始,每条动作最长 10 分钟。
- **升档**:每 2 秒最多升 1 档。
- **自动全停**:页面切到后台、与 VPS 断开时全停,包括加热。
- **锁存**:长按关机或蓝牙断开会锁住该设备,要用户在页面解除。
- **页面停止**:用户在页面按停止后,AI 被锁住,要用户在页面放行。

快速上手、部署方法见上面英文部分和 [`deploy/README.md`](deploy/README.md)。

`tools/parse_btsnoop.py` 会打印抓包里的蓝牙地址,分享输出前请先去掉。

### 致谢

以下项目给我们提供了思路。本项目没有复制它们的代码。也感谢claude,全程包办。

- [vickyldr/svakom-ble-ai](https://github.com/vickyldr/svakom-ble-ai):AI → 中继 → 本地蓝牙桥的思路,以及“永远不要写 AE00/AE01 固件升级口”的提醒。
- [daningzi50-hub/svakom-sl278h-ble](https://github.com/daningzi50-hub/svakom-sl278h-ble):HTTP 中继加 Android Chrome Web Bluetooth 桥页的做法,以及 FFE1 上 `55 …` 的指令格式。
- [buttplug.io](https://github.com/buttplugio/buttplug):其中 `svakom` 的协议实现帮助确认了功能字节。
- 部署时原样使用 [sigbit/mcp-auth-proxy](https://github.com/sigbit/mcp-auth-proxy)(MCP 的 OAuth)和 [cloudflared](https://github.com/cloudflare/cloudflared)(隧道)。

---

## Disclaimer / 免责声明

- Unofficial and unaffiliated with SVAKOM. "SVAKOM" and product names belong to their owners. / 非官方项目,与 SVAKOM 无关;相关商标归其所有者。
- **Never write to the AE00/AE01 service**: it is the firmware update (OTA) channel and can brick the device. / **永远不要写 AE00/AE01**:那是固件升级口,写错可能让设备变砖。
- The safety rules reduce risk but cannot remove it. Keep the physical power button and the phone page's stop button within reach. Test on real hardware before relying on anything. / 安全规则只能降低风险,不能消除风险。物理关机键和页面停止按钮始终放在手边;实际使用前先在真机上验证。
- Provided "as is" under the MIT License, without warranty. / 按 MIT 许可证“原样”提供,不作任何担保。

## License

[MIT](LICENSE)