Content
# stackchan-cloud-mcp
Turn a StackChan desk robot into a device claude.ai can talk to directly —
no laptop in the loop. Speak to Claude on your phone; the robot on your desk speaks back.
```
claude.ai / phone
│ MCP over HTTPS (OAuth 2.1: DCR + PKCE)
▼
Cloudflare Tunnel → oauth_proxy :8770 → stackchan-mcp gateway :8767
│ WebSocket
▼
StackChan (M5Stack CoreS3)
```
The entire setup runs on an affordable VPS. The device connects directly to the VPS, and the home computer can be shut down.
## What This Repository Solves
The upstream [kisaragi-mochi/stackchan-mcp](https://github.com/kisaragi-mochi/stackchan-mcp)
has already done a great job with the "gateway ↔ device" part. This repository addresses the issues encountered
when moving it to the public internet and connecting it to claude.ai:
| Issue | Symptoms | Solution Here |
| --- | --- | --- |
| claude.ai only supports OAuth 2.1 | Static token cannot connect to custom connector | `cloud/oauth_proxy.py` — Minimal OAuth authorization server (DCR + PKCE + single-tenant) |
| NAT timeout for long connections | Device stable for ~90 seconds, then disconnects; neither end sends close frames | Heartbeat every 20s/60s (`patches/`) |
| Audio output issue | TTS synthesis successful, but device does not produce sound | Missing `opuslib` (`patches/`) |
| Custom avatar rejected | Device reverts to firmware default avatar; `set_avatar` ineffective | Relax gateway size check (`patches/`) |
| STT model loading issue (Mainland China) | `listen` stuck at Loading model | HF mirror + disable Xet (`patches/`) |
| Boot-time version check timeout | Device takes a minute or two to connect to gateway after boot | `cloud/fake_ota.py` — Always report "latest version" |
| Post-disconnect issues | Reconnection results in lost expressions, reset volume and brightness | `cloud/reflex.py` — Reflex arc守护进程 |
Detailed notes on the issues and solutions are in [`patches/README.md`](patches/README.md),
including symptoms, root causes, modifications, and verification.
## Components
| Directory | Content |
| --- | --- |
| `cloud/oauth_proxy.py` | OAuth 2.1 gateway: allows claude.ai custom connector to connect to your gateway. Single-tenant, one gate key. |
| `cloud/fake_ota.py` | Fake OTA endpoint: lets device skip version check on boot, speeding up connection to gateway. |
| `cloud/reflex.py` | Reflex arc守护进程: automatic reconnection, expression recovery, and device preference restoration. |
| `cloud/restore.sh` | Restore upstream patches (after `uv tool install --force`); reapply custom changes. |
| `patches/` | Four modifications to upstream gateway: explanations and idempotent patch scripts. |
| `sprites/` | Pixel avatar generator (pure JS, no dependencies): 16 expressions × A/B frames, RGB565, generates `.bin` for device. |
| `docs/` | Deployment guide. |
## Quick Start
Prerequisites: a VPS accessible from the public internet, a domain (or Cloudflare Tunnel),
and a StackChan with [xiaozhi-esp32](https://github.com/78/xiaozhi-esp32) firmware.
```bash
# 1. Install upstream gateway (with STT and opus encoding)
uv tool install "stackchan-mcp[stt-faster-whisper]" --with opuslib
# 2. Apply patches (NAT heartbeat and avatar size check)
sh patches/apply_patches.sh
# 3. Start OAuth gateway (env variables in docs/DEPLOY.md)
MCP_GATE_KEY=... MCP_ISSUER=https://your.domain python3 cloud/oauth_proxy.py
# 4. Start reflex arc守护进程
STACKCHAN_TOKEN=... AVATAR_SET_PATH=/root/avatar_set.bin python3 cloud/reflex.py
```
Complete steps (including systemd unit, Cloudflare Tunnel, and claude.ai connector setup)
are in [`docs/DEPLOY.md`](docs/DEPLOY.md).
## Security Notes
- **All keys and tokens are environment variables**; no actual tokens, domains, or IPs are stored in the repository. Set your own during deployment.
- OAuth gateway is **single-tenant**: `MCP_GATE_KEY` is the sole gate key; only those who know it can obtain an access token.
Not intended for multi-user SaaS; do not use for public services.
- Gateway's `STACKCHAN_TOKEN` is strictly validated; OAuth gateway rewrites `Authorization` during forwarding.
Therefore, claude.ai's token and gateway's internal token are separate.
- Robot comes with microphone and camera. Consider your privacy boundaries before enabling these features;
recommended practice: provide visible indication (screen prompt or light ring color change), do not save to disk,
and capture only one image per invocation.
## Status
Successfully tested and long-term usage on real devices: speaking (Edge TTS), listening (faster-whisper),
subtitles, 16 expressions + scene images, head movements, light ring, automatic reconnection,
and claude.ai mobile driver.
Known limitations:
- Edge TTS relies on Microsoft's free API, which may be rate-limited on mainland China VPS;
consider paid or self-hosted TTS for production environments.
- "Wake word → local LLM response" requires gateway-side access to an LLM;
if your VPS cannot reach the API, this feature will not work (this repository uses claude.ai as the LLM,
with VPS acting as a relay).
## Acknowledgments
- [kisaragi-mochi/stackchan-mcp](https://github.com/kisaragi-mochi/stackchan-mcp) — Gateway and device protocol
- [stack-chan](https://github.com/stack-chan/stack-chan) — Robot本体
- [78/xiaozhi-esp32](https://github.com/78/xiaozhi-esp32) — Firmware base
## License
MIT