wechatbot-mcp
# wechatbot-mcp
OpenCode / MCP server wrapping [`@wechatbot/wechatbot`](https://github.com/corespeed-io/wechatbot) — the **official WeChat iLink Bot SDK** (Tencent partner program, *not* a hook / reverse-engineered client).
Lets any Agent (OpenCode, Claude Code, Cursor, etc.) read and reply to WeChat messages over MCP stdio.
## Quick start
```bash
npm install
npm run build
node dist/index.js # stdio MCP server
```
Register with OpenCode in `~/.config/opencode/opencode.json`:
```json
{
"mcp": {
"wechatbot": {
"type": "local",
"command": ["node", "<absolute-path>/wechatbot-mcp/dist/index.js"],
"enabled": true
}
}
}
```
Then ask your Agent to call `wechat_login` once. A QR code PNG is auto-opened on screen; scan with WeChat. Credentials are persisted to `~/.wechatbot/credentials/` for ~14 days — subsequent restarts re-auth silently without a QR.
## Tools
| Tool | Description |
|------|-------------|
| `wechat_login` | Login. Silent re-auth from disk if creds exist; otherwise renders a QR PNG. |
| `wechat_logout` | Stop polling. `clearCredentials:true` wipes on-disk creds. |
| `wechat_status` | Connection state, account, queue depth, known-user count. |
| `wechat_send_text` | Send a text message. SDK auto-splits on natural paragraphs. |
| `wechat_send_image` | Send a local image file (optional caption). |
| `wechat_send_file` | Send any local file — auto-routes by extension (`.png` → image, `.mp4` → video, else file). |
| `wechat_send_url` | Send media from a remote URL. SDK downloads and uploads to WeChat CDN. |
| `wechat_send_typing` / `wechat_stop_typing` | Show / cancel "对方正在输入中…". |
| `wechat_pending_messages` | Drain received messages (images/files auto-downloaded to `~/.wechatbot/pending-media/`). |
| `wechat_known_users` | List user IDs we have a context_token for (i.e. who can be replied to). |
## Protocol note (anti-spam)
WeChat iLink requires the **recipient to message you first** before you can reply. `wechat_send_*` tools return a clear error listing known users when the target isn't in that set. Always `wechat_pending_messages` first to learn user IDs from inbound messages.
## Storage layout
```
~/.wechatbot/
├── credentials/ # iLink tokens (auto-managed, ~14-day TTL)
├── mcp/ # rendered QR PNGs (qr-*.png) + ASCII fallback (qr-latest.txt)
└── pending-media/ # media auto-downloaded from received messages
```
## Architecture
```
┌────────────────┐
│ WeChat user │
└────────┬───────┘
│ iLink Bot protocol (Tencent)
┌────────▼───────┐
│ @wechatbot/ │ ← npm SDK (long-poll, login, send, media)
│ wechatbot │
└────────┬───────┘
│ IncomingMessage + SendContent
┌────────▼───────┐
│ BotManager │ ← this repo: lifecycle, queue, snapshot
└────────┬───────┘
│ tools/call JSON-RPC
┌────────▼───────┐
│ MCP server │ ← stdio transport, 11 tools
│ (this repo) │
└────────┬───────┘
│ stdin/stdout
┌────────▼───────┐
│ OpenCode / LLM │
└────────────────┘
```
## Tested with
- Node.js ≥ 22 (tested on 24.11)
- `@wechatbot/wechatbot` ^2.2.0
- `@modelcontextprotocol/sdk` ^1.0
- Windows 10/11 (PowerShell + cmd)
## 24/7 background auto-reply (optional)
A standalone daemon (`scripts/wechat-daemon.mjs`) runs the MCP server in a child process and auto-replies to incoming messages. Useful when OpenCode is closed or you're AFK.
```bash
# Default: none mode — log only, NO auto-reply (recommended unless you
# explicitly want a bot running 24/7)
node scripts/wechat-daemon.mjs
# LLM mode: shell out to any command, message JSON via stdin, reply = stdout
node scripts/wechat-daemon.mjs --reply-mode=llm --llm-cmd "opencode run --message"
# echo mode: just echo back the user's text (testing only — NOT recommended)
node scripts/wechat-daemon.mjs --reply-mode=echo
# Whitelist specific userIds (others get silently logged but no reply)
node scripts/wechat-daemon.mjs --reply-mode=llm --llm-cmd "..." \
--allowed-users="o9cq804w_6mDSHAhyIQnPntiKI4c@im.wechat"
# Customize the AI-label prefix (default: "[ai助手]")
node scripts/wechat-daemon.mjs --reply-mode=llm --llm-cmd "..." --reply-prefix="[bot]"
# Tune cadence and log path
node scripts/wechat-daemon.mjs --poll-interval=1000 --log-file=/path/to/daemon.log
```
**Every non-empty reply is auto-prefixed with `[ai助手]`** (configurable via `--reply-prefix`) so recipients can tell the message came from an AI, not from the user themselves. This applies to all reply modes that produce output (echo, llm).
Logs go to both stderr (visible in your terminal) and `~/.wechatbot/daemon.log` (persistent). Use Task Scheduler / `nohup` / `pm2` / Windows Service to keep it running across reboots.
**Concurrency note**: only ONE listener per WeChat account. If you also run the OpenCode skill below, pick one — both will race on `wechat_pending_messages` (the queue is consumed atomically on each drain).
## OpenCode skill (optional)
A user-global skill `wechat-monitor` makes OpenCode itself run the polling loop within an interactive session. The agent sees each incoming message and replies through the same MCP tools, with full LLM reasoning.
Install location: `~/.claude/skills/wechat-monitor/SKILL.md` (auto-discovered by OpenCode alongside the bundled `wps-*` / `mmx-cli` skills).
Trigger: type `/skill wechat-monitor` or say "monitor wechat" / "wechat 监控".
The skill's `SKILL.md` documents the loop, concurrency rules, and reply strategies — agent reads it on invocation.
## License
MITTDQS
Scored across 11 tools
Most tools target distinct operations, but send_image and send_file overlap in that both can send local images, differing only in caption handling and auto-detection; send_url vs send_file could also confuse when dealing with remote files. Overall, descriptions clarify most boundaries.
All tools share a consistent wechat_ prefix and snake_case. However, not all follow verb_noun: login/logout/status are bare verbs/nouns, and pending_messages/known_users are adjective-noun phrases, slightly deviating from a strict verb-object pattern though still predictable.
11 tools is well within the 3-15 ideal range. Each tool addresses a distinct need: auth, connection status, sending text/media/URLs/typing indicators, receiving messages, and contact listing. No unnecessary redundancy.
Covers the core lifecycle of a WeChat bot: login/logout, send various content types, receive messages, and list contacts. Minor gaps include lacking user detail lookups, message history search, and group management, but these are not essential for basic bot operation.