wechat-mcp
An MCP server for sending and receiving WeChat messages, built on Tencent's iLink bot protocol.
README
wechat-mcp
A Model Context Protocol (MCP) server for sending and receiving WeChat (Weixin) messages, built on Tencent's iLink bot protocol.
This is a port of the messaging core of
Tencent/openclaw-weixin (an
OpenClaw channel plugin) into a standalone MCP server usable from Claude Code,
Claude Desktop, or any MCP client. The WeChat protocol logic — QR login, the
getUpdates long-poll receive loop, sendMessage, and the AES-128-ECB CDN
media pipeline — is preserved; the OpenClaw runtime/SDK coupling has been
removed and replaced with a thin MCP tool layer.
What it does
| MCP tool | Purpose |
|---|---|
wechat_login |
QR-code login. Prints a QR to the terminal (STDERR); scan with WeChat mobile and confirm. Persists the bot token. |
wechat_list_accounts |
List logged-in WeChat bot accounts. |
wechat_logout |
Remove an account's stored credentials, sync cursor, and context tokens. |
wechat_send |
Send text and/or a media attachment (image / video / file) to a user. Accepts a local path or a remote http(s) URL. Text is markdown-filtered by default (WeChat-unsupported syntax stripped); pass filterMarkdown: false for raw text. |
wechat_receive |
Poll for new inbound messages (one long-poll cycle). Tracks a per-account sync cursor so repeated calls don't return duplicates. Inbound media is downloaded + decrypted to local temp files. |
wechat_listen |
Continuously poll until a message arrives, an error occurs, or the window elapses (default 2 min). Re-polls back-to-back — the reliable way to wait for a message, since a single wechat_receive cycle often returns empty early. |
wechat_typing |
Show (or cancel) the "typing…" indicator for a user. The typing ticket is resolved automatically. |
Requirements
- Node.js >= 20
- A WeChat (Weixin) mobile app to scan the login QR code
Install
Clone and install. The prepare hook compiles TypeScript to dist/
automatically on npm install, so there is no separate build step.
git clone https://github.com/jimingyuan7/wechat-mcp.git wechat-mcp
cd wechat-mcp
npm install
To rebuild after editing source: npm run build.
Optional: voice-message transcoding (SILK → WAV) requires the optional
silk-wasm package. Without it, inbound voice is saved as raw .silk.
npm install silk-wasm
First-time login
Run the interactive login in a real terminal (the QR renders to STDERR):
npm run login
Scan the QR code with the WeChat mobile app and confirm. Credentials are saved
under ~/.wechat-mcp/openclaw-weixin/accounts/.
You can also trigger login through the wechat_login MCP tool, but a real
terminal is friendlier for scanning the QR.
Register with an MCP client
Claude Code
Prerequisites: the project is built (npm install already ran the prepare
build, so dist/mcp/server.js exists) and you have logged in once
(npm run login).
1. Add the server. Run this from anywhere — use the absolute path to the built entry point:
claude mcp add wechat -- node /absolute/path/to/wechat-mcp/dist/mcp/server.js
Tip: if you're inside the project dir, $(pwd) fills the path in for you:
claude mcp add wechat -- node "$(pwd)/dist/mcp/server.js"
By default the server is added at local scope (only this project on this machine). To make it available across all your projects, use user scope:
claude mcp add -s user wechat -- node /absolute/path/to/wechat-mcp/dist/mcp/server.js
To override a config env var (e.g. a custom state dir), pass -e:
claude mcp add wechat \
-e WECHAT_MCP_STATE_DIR=/data/wechat \
-- node /absolute/path/to/wechat-mcp/dist/mcp/server.js
2. Verify it's connected:
claude mcp list # should show: wechat ✓ connected
claude mcp get wechat # shows the full command + health
3. Use it. Start claude, and the 7 wechat_* tools are available. Just
ask in natural language, e.g.:
"Use wechat_listen to wait for a WeChat message, then reply with a friendly greeting."
Remove when you no longer need it:
claude mcp remove wechat
Note: first-time WeChat login (
npm run login) needs a real terminal to scan the QR code, so do that before relying on the tools inside Claude Code. Credentials persist under~/.wechat-mcp/, so you only log in once.
Claude Desktop (claude_desktop_config.json)
{
"mcpServers": {
"wechat": {
"command": "node",
"args": ["/absolute/path/to/wechat-mcp/dist/mcp/server.js"]
}
}
}
Usage notes
- Recipient ids look like
xxxxxxxx@im.wechat. You normally obtain one from an inbound message (wechat_receive→ messageFrom). - Context tokens: the WeChat backend issues a per-conversation
context_tokenon each inbound message that must be echoed on outbound sends. The server caches these automatically (in memory + on disk) as messages arrive, sowechat_sendto a user who has recently messaged the bot "just works". Sending to a user with no cached token may be rejected by the backend. - Receiving is poll-based: call
wechat_receiverepeatedly (e.g. in a loop). Each call holds the connection open up totimeoutMs(default 35s) waiting for new messages, then returns. The sync cursor is persisted, so you never see the same message twice across calls or restarts. - Media: outbound media is auto-classified by file extension
(
video/*,image/*, else generic file). Inbound media is downloaded, AES-128-ECB decrypted, and written to~/.wechat-mcp/tmp/media/inbound/; the local path comes back in the message'sMediaPath.
Usage examples
In a chat with an MCP client (e.g. Claude Code / Claude Desktop) you just ask in natural language — "reply to the last WeChat message", "send this photo to the user", etc. The tool-call payloads below show what the client sends under the hood, and are also handy for direct/manual testing.
The recommended flow is receive first, then reply: an inbound message caches
the context_token that outbound sends require.
1. See who's logged in
{ "name": "wechat_list_accounts", "arguments": {} }
// → result
{ "count": 1, "accounts": [
{ "accountId": "bfa52ff0d915-im-bot",
"userId": "o9cq...@im.wechat", "configured": true }
] }
2. Wait for an incoming message (recommended over wechat_receive)
wechat_listen re-polls until a message arrives or the window elapses:
{ "name": "wechat_listen", "arguments": { "windowMs": 120000 } }
// → result (returns as soon as a message arrives)
{ "messages": [
{ "From": "o9cq...@im.wechat", "Body": "Hello",
"MediaPath": null, "context_token": "AARz..." }
], "pollCycles": 3, "timedOut": false }
Copy From — that's the to you reply to. The context_token is now cached,
so the next send will actually deliver.
3. Reply with text
{ "name": "wechat_send", "arguments": {
"to": "o9cq...@im.wechat", "text": "Hi! Got it 👍" } }
// → result
{ "messageId": "wechat-mcp:...", "hadContextToken": true, "markdownFiltered": false }
hadContextToken: truemeans it will be delivered. If it'sfalse, the recipient hasn't messaged the bot yet — have them send one message first.
4. "Typing…" indicator before a slow reply
{ "name": "wechat_typing", "arguments": { "to": "o9cq...@im.wechat" } }
…do your slow work (call an LLM, fetch data), then wechat_send the result.
Cancel the indicator early with { "to": "...", "status": "cancel" }.
5. Send an image or file
Local path (absolute recommended) or a remote URL — type is auto-detected:
{ "name": "wechat_send", "arguments": {
"to": "o9cq...@im.wechat", "text": "Here's the photo", "media": "/tmp/photo.png" } }
{ "name": "wechat_send", "arguments": {
"to": "o9cq...@im.wechat", "media": "https://example.com/cat.jpg" } }
6. Markdown handling
Outbound text is markdown-filtered by default — WeChat-unsupported syntax
(H5/H6 headings, CJK italics *…*, inline images) is stripped so users see
clean text instead of stray symbols. Pass filterMarkdown: false to send raw:
{ "name": "wechat_send", "arguments": {
"to": "o9cq...@im.wechat", "text": "raw **markdown** stays", "filterMarkdown": false } }
Note: WeChat chat bubbles do not render rich text at all — filtering only removes noisy markers; it cannot make text bold/italic on the WeChat side.
Quick CLI smoke test (no MCP client)
You can drive the server over stdio directly:
printf '%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"cli","version":"0"}}}' \
'{"jsonrpc":"2.0","method":"notifications/initialized"}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"wechat_list_accounts","arguments":{}}}' \
| node dist/mcp/server.js
Configuration (environment variables)
| Variable | Default | Description |
|---|---|---|
WECHAT_MCP_STATE_DIR |
~/.wechat-mcp |
Where credentials, sync cursors, and context tokens are stored. |
WECHAT_MCP_TMP_DIR |
<state>/tmp |
Temp dir for downloaded / decrypted media. |
WECHAT_MCP_LOG_LEVEL |
INFO |
TRACE DEBUG INFO WARN ERROR. Logs go to STDERR. |
WECHAT_MCP_BOT_AGENT |
WeChatMCP |
UA-style self-identifier sent on every request (for backend log attribution). |
WECHAT_MCP_CDN_BASE_URL |
Tencent C2C CDN | Override the media CDN base. |
WECHAT_MCP_ROUTE_TAG |
— | Optional SKRouteTag header. |
Architecture
src/
api/ iLink HTTP+JSON protocol (getUpdates, sendMessage, getUploadUrl, …) + types
auth/ QR login flow + per-account credential store
cdn/ AES-128-ECB encrypt/decrypt + CDN upload/download
media/ MIME mapping, media download/decrypt, optional SILK→WAV transcode
messaging/ send (text/image/video/file), inbound normalization + context tokens,
receive (single cycle + receiveUntil listen loop), outbound (high-level
send w/ markdown filter), typing (indicator), markdown-filter
storage/ state-dir resolution + sync-buf (getUpdates cursor) persistence
util/ logger (STDERR-only), redaction, id/account-id helpers
mcp/ MCP stdio server exposing the 5 tools
The STDOUT stream is reserved exclusively for the MCP JSON-RPC protocol; all human-facing output (logs, QR codes, prompts) goes to STDERR.
Credits
Protocol implementation ported from
Tencent/openclaw-weixin (MIT).
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。