LingTai WeChat MCP server
Project description
lingtai-wechat
LingTai WeChat MCP server — iLink Bot API client with QR-code login, multi-modal messaging, and LICC inbox callback.
This is the canonical setup, configuration, and troubleshooting doc for the lingtai-wechat MCP. It is fetched by LingTai agents (or anyone else) when they need to install or configure this server.
MCP / LICC contract spec: see the
lingtai-anatomyskill,reference/mcp-protocol.md, for the canonical specification of the catalog → registry → activation chain, environment-variable injection, and the LICC v1 inbox callback protocol. The reference client implementation issrc/lingtai_wechat/licc.pyin this repo (vendored verbatim into all first-party LingTai MCP repos — copy it if you're writing your own).
Tools
One omnibus MCP tool: wechat(action=...). Actions: send, check, read, reply, search, contacts, add_contact, remove_contact. Supports text, images, voice, video, and files (auto-detected from media_path extension).
Inbound messages (LICC)
Inbound WeChat messages flow into the host agent's inbox via the LingTai Inbox Callback Contract. Each new message is delivered as a LICC event with:
from— contact alias (or rawwxid_...if no contact saved).subject—"wechat message from <name>".body— a ~300 char preview, including bracketed media references like[Image: /path],[Voice: /path],[Video: /path].metadata.message_id— forreply.metadata.from_user_id— rawwxid_....metadata.item_types— list of message item types (text, image, voice, video, file).
Session expiry events are also delivered via LICC with metadata.event_type: "session_expired" so the agent knows to ask for re-login.
Install
# Into the LingTai agent's venv (typically ~/.lingtai-tui/runtime/venv/)
pip install git+https://github.com/Lingtai-AI/lingtai-wechat.git
After install, python -m lingtai_wechat (or the lingtai-wechat script) starts the MCP server over stdio.
QR-code login (one-time, before first use)
WeChat doesn't issue static bot tokens. Authenticate by scanning a QR code with the WeChat mobile app.
⚠ The login QR is admin-only — do not share it. Scanning it logs a WeChat account in as the bot's backend identity. If a friend or end user scans it instead of you, their account binds in place of yours and your credentials are replaced. This is not a contact / group / customer-service QR — those are shared from inside WeChat after login, and are how users actually chat with the bot.
Recommended: browser bootstrap
lingtai-wechat-bootstrap .secrets/wechat
This is the preferred first-time setup. It:
- Creates
.secrets/wechat/config.jsonwith defaults if missing. - Renders the QR as an SVG inside a self-contained HTML page (with an admin-only warning banner) and opens it in your default browser.
- Polls iLink for confirmation, auto-refreshing the QR if it expires.
- Writes
credentials.json(chmod 600) and tells you to refresh the MCP.
The page auto-refreshes every 3s and prints a clear admin-only warning so the scanner cannot mistake it for a chat QR.
Headless fallback: terminal QR
If the host has no browser or you're connecting over SSH without X-forwarding:
python -c "from lingtai_wechat.login import cli_login; cli_login('.secrets/wechat')"
This prints an ASCII QR directly to the terminal. Scan it with WeChat. On success, credentials.json is written into the directory you passed.
After login
credentials.json contains bot_token, user_id, and base_url. Sessions expire periodically (typically ~30 days). When expired, you'll see a LICC event with metadata.event_type: "session_expired" — re-run the bootstrap.
Configure
The server reads two files from the directory pointed at by LINGTAI_WECHAT_CONFIG:
config.json— user-controlled options.credentials.json— written bylingtai-wechat-bootstrap(or thecli_loginfallback). Don't edit by hand.
config.json schema
{
"poll_interval": 1.0,
"allowed_users": ["wxid_abc123"],
"cdn_base_url": "https://..."
}
poll_interval— seconds between iLink long-polls (default 1.0).allowed_users— optional allow-list of WeChat user IDs. When set, messages from other senders are silently ignored. Omit to accept any sender.cdn_base_url— usually omit; the default works.
Activation in LingTai
{
"addons": ["wechat"],
"mcp": {
"wechat": {
"type": "stdio",
"command": "/path/to/your/python",
"args": ["-m", "lingtai_wechat"],
"env": {
"LINGTAI_WECHAT_CONFIG": ".secrets/wechat/config.json"
}
}
}
}
Then run system(action="refresh") from the agent. The MCP subprocess starts, the iLink long-poll begins, and the omnibus wechat tool becomes available.
Troubleshooting
LINGTAI_WECHAT_CONFIG env var not set— yourinit.jsonmcp.wechat.enventry is missing theLINGTAI_WECHAT_CONFIGkey.WeChat config not found— the path resolves but no file exists. Relative paths are resolved againstLINGTAI_AGENT_DIR.WeChat credentials not found— config exists butcredentials.jsondoesn't. Run the QR-code login flow above.WeChat session expiredevent in agent inbox — re-run the QR-code login flow.All connection attempts failedin stderr — usually a stalebase_urlin credentials. Re-run login.- MCP server failed to start — usually the
commandpath ininit.jsondoesn't havelingtai_wechatinstalled. Confirm with<command> -m lingtai_wechat --helpfrom a shell. - Tool calls return
WeChat manager not initialized— server boot failed (missing config or expired creds). Check stderr.
Multiple pollers after upgrading
iLink's getUpdates is a single-consumer long-poll: two processes holding the same bot_token race over inbound messages and each one gets a different subset. To prevent this, every lingtai-wechat poller takes an exclusive fcntl.flock on ~/.lingtai-wechat/locks/poller-<sha256(bot_token)[:16]>.lock at startup. If a second poller starts for the same account, it refuses with a PollerLockBusy error that includes the holder's PID.
After upgrading to a lingtai-wechat version with the lock, the new process may refuse to start because a pre-upgrade poller from another LingTai project is still running. Diagnose and stop it:
# 1. Find every lingtai_wechat poller currently running.
pgrep -af 'python.*lingtai_wechat'
# 2. The PollerLockBusy error names the holder PID. Inspect it:
ps -p <pid> -o pid,command
lsof -p <pid> 2>/dev/null | grep cwd # which working dir launched it
# 3. Stop the old poller (refresh that project's MCP after).
kill -TERM <pid>
Notes:
- Lockfiles in
~/.lingtai-wechat/locks/are intentionally left on disk after process exit — theflockkernel state is what's authoritative, not the file's presence. A leftover lockfile from a dead process is harmless; a new poller will reacquire the lock cleanly. - The lock is keyed on
sha256(bot_token), so two accounts produce different lockfiles and don't conflict. Multiple LingTai projects sharing one account is the case the lock prevents. - On Windows,
fcntlis unavailable;lingtai-wechatwill refuse to start withUnsupportedPlatformErrorrather than silently re-introducing the race.
License
MIT.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file lingtai_wechat-0.1.2.tar.gz.
File metadata
- Download URL: lingtai_wechat-0.1.2.tar.gz
- Upload date:
- Size: 30.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0ce0998a683a25d27ec6d148f11eada44e8bf4fbf7bb5d2950bde3c697f2d79b
|
|
| MD5 |
4efec1af334616fb148cc955cc8a5e93
|
|
| BLAKE2b-256 |
fcfe423dda277d8a9fea67e9c2daefeadc537c2bdbadd80e077c7c69a62661bf
|
File details
Details for the file lingtai_wechat-0.1.2-py3-none-any.whl.
File metadata
- Download URL: lingtai_wechat-0.1.2-py3-none-any.whl
- Upload date:
- Size: 35.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0e9dc9d8d09100c4f2f1e551d6fb0711a6697f1efed60d765662eb1b68f488b9
|
|
| MD5 |
73a99119028f0e96b2cf45407695a7ce
|
|
| BLAKE2b-256 |
4bbb3066b3fd0487223014b013f4d86da5065a5141974b2dac21433c0c85fd47
|