WeChat AI agent bridge: chat with Claude Code, Codex, Gemini, Kimi, Cursor, OpenCode and OpenClaw from WeChat. Built on the WeChat ClawBot (iLink) protocol, a single Go binary; Claude runs over ACP (claude-agent-acp).
This is an actively maintained fork of fastclaw-ai/weclaw. Upstream has had no commits since April 2026. This repository continues from upstream v0.7.1 with bug fixes and new features. Config and login data (
~/.weclaw) are compatible with upstream, so it is a drop-in replacement. See CHANGELOG.md.
This project is inspired by @tencent-weixin/openclaw-weixin. For personal learning only, not for commercial use.
![]() |
![]() |
![]() |
Open upstream issues that are resolved here:
| Problem | Upstream issue | This fork |
|---|---|---|
Sending a message before the previous reply arrives interrupts it or fails with agent returned empty response |
#48 | Messages to the same session are queued and each gets its own reply (v0.9.5, ACP mode) |
/new with Codex fails with unknown variant session/new |
#60 | Fixed (v0.9.0) |
| Cannot send local images or files back to WeChat | #57, #34 | weclaw send --media <local path> for any file type; the agent is told to use it (v0.9.3, v0.9.4) |
| Images sent from WeChat are ignored by the agent | #26 | Images are downloaded and forwarded to ACP agents that accept images (v0.9.0) |
| Switching the default agent overwrites newer config on disk | #38 | Only the default_agent field is written (v0.9.0) |
Also:
- Tools work with the official
claude-agent-acp. Upstream only accepted a permission option of kindallow; the official adapter offersallow_once/allow_always, so every tool that needed approval was rejected. modelandmodeapply to Claude. They are set on each new session via the standard ACPsession/set_config_option;mode: bypassPermissionsmatchesclaude --dangerously-skip-permissions.- Single-instance lock and a macOS login service.
weclaw service installregisters a launchd agent (start at login, restart on crash); two bridges can no longer fight over one message queue. - Restarts don't swallow replies. If the bridge restarts (update,
weclaw restart, even one issued from WeChat) while a reply is being generated, the chat is told afterwards to resend instead of never hearing back;/newand/cwdalso close the old session's Claude Code process. /new <message>starts a new session and asks it right away;/cwdtakes effect immediately and is saved to the config.- Each session gets a short WeChat context, and
system_promptis appended to Claude Code's built-in system prompt instead of replacing it.
Full list in CHANGELOG.md (written in Chinese).
Requires Go 1.25+. Used and tested mainly on macOS; Linux builds and runs (manage it with systemd yourself); Windows builds but is untested.
git clone https://github.com/Tespera/weclaw.git
cd weclaw
# Build and install to ~/.local/bin (override with PREFIX); make sure it is on PATH
make install
# First start shows a WeChat QR code to log in, then runs in the background
weclaw start
# Recommended on macOS: register as a login service (start at login, restart on crash)
weclaw service installLater, weclaw update pulls the latest code and reinstalls.
On first start, WeClaw will:
- Show a QR code — scan it with WeChat to log in
- Auto-detect installed AI agents (Claude, Codex, Gemini, etc.)
- Save config to
~/.weclaw/config.json - Start receiving and replying to WeChat messages
Use weclaw login to add more WeChat accounts.
Config and login data live in ~/.weclaw and are compatible; no need to scan the QR code again.
- Stop the old version:
weclaw stop - Remove the old binary so it does not shadow the new one on PATH: upstream
install.shinstalls to/usr/local/bin/weclaw,go installto$(go env GOPATH)/bin/weclaw - Install as in Quick Start and run
weclaw start - For Claude, prefer ACP:
npm i -g @agentclientprotocol/claude-agent-acp; see ACP session options below
Agent modes:
| Mode | How it works | Agents |
|---|---|---|
| ACP | Long-running subprocess, JSON-RPC over stdio. Fastest — reuses the process and sessions. | Claude, Codex, Kimi, Gemini, Cursor, OpenCode, OpenClaw |
| CLI | Spawns a new process per message. Resumes sessions via --resume. |
Claude (claude -p), Codex (codex exec) |
| HTTP | OpenAI-compatible Chat Completions API. | OpenClaw (HTTP fallback) |
When both ACP and CLI are available, ACP is chosen.
Send these as WeChat messages:
| Command | Description |
|---|---|
hello |
Send to the default agent |
/codex write a sort function |
Send to a specific agent |
/cc explain this code |
Send via an alias |
/claude |
Switch the default agent to Claude |
/cwd /path/to/project |
Switch workspace: applies to all agents, saved to config, starts a new session |
/cwd |
Show the current workspace |
/new |
Start a new conversation (clear the session) |
/new look at this error |
Start a new conversation and send this message to it |
/info |
Show current agent info (including workspace) |
/help |
Show help |
| Alias | Agent |
|---|---|
/cc |
Claude |
/cx |
Codex |
/cs |
Cursor |
/km |
Kimi |
/gm |
Gemini |
/ocd |
OpenCode |
/oc |
OpenClaw |
You can define custom triggers per agent in the config:
{
"agents": {
"claude": {
"type": "acp",
"aliases": ["ai", "c"]
}
}
}Then /ai hello or /c hello routes to claude.
Switching the default agent is saved to the config file and survives restarts.
WeClaw sends and receives images, video, files and voice messages.
Voice: WeChat's own speech-to-text transcription is forwarded to the agent as text. Duplicate voice events are de-duplicated.
Images in agent replies: when the agent returns markdown images (), WeClaw downloads them, uploads to the WeChat CDN (AES-128-ECB encrypted) and sends them as image messages.
Markdown: replies are converted to plain text before sending — code fences removed, links reduced to their text, bold/italic markers stripped, and so on.
Push messages to a WeChat user without waiting for them to write first.
CLI:
# Text
weclaw send --to "user_id@im.wechat" --text "Hello from weclaw"
# Image
weclaw send --to "user_id@im.wechat" --media "https://example.com/photo.png"
# Text + image
weclaw send --to "user_id@im.wechat" --text "Check this out" --media "https://example.com/photo.png"
# File
weclaw send --to "user_id@im.wechat" --media "https://example.com/report.pdf"
# Local files (any type; ~, relative paths and file:// work; --media is repeatable)
weclaw send --to "user_id@im.wechat" --media ~/Desktop/report.pdf --media ./chart.pngAll --media values are validated first (exists and is a regular file); nothing is sent unless all pass, so you never get half a message.
HTTP API (while weclaw start is running, default 127.0.0.1:18011):
# Text
curl -X POST http://127.0.0.1:18011/api/send \
-H "Content-Type: application/json" \
-d '{"to": "user_id@im.wechat", "text": "Hello from weclaw"}'
# Image
curl -X POST http://127.0.0.1:18011/api/send \
-H "Content-Type: application/json" \
-d '{"to": "user_id@im.wechat", "media": "https://example.com/photo.png"}'
# Local file (absolute path required)
curl -X POST http://127.0.0.1:18011/api/send \
-H "Content-Type: application/json" \
-d '{"to": "user_id@im.wechat", "media": "/Users/me/Desktop/report.pdf"}'
# Text + media
curl -X POST http://127.0.0.1:18011/api/send \
-H "Content-Type: application/json" \
-d '{"to": "user_id@im.wechat", "text": "Check this out", "media": "https://example.com/photo.png"}'media takes a URL or an absolute local path (the old media_url field still works). Local paths are only accepted from loopback (127.0.0.1 / ::1) callers, so binding the API to another address does not let remote callers read local files.
Media is sent by type: images (png, jpg, gif, webp, bmp) as images, video (mp4, mov, webm, mkv, avi) as video, anything else as a file.
Set WECLAW_API_ADDR to change the listen address (e.g. 0.0.0.0:18011).
Config file: ~/.weclaw/config.json
{
"default_agent": "claude",
"agents": {
"claude": {
"type": "acp",
"command": "/usr/local/bin/claude-agent-acp",
"env": {
"ANTHROPIC_API_KEY": "sk-ant-xxx"
},
"model": "opus"
},
"codex": {
"type": "acp",
"command": "/usr/local/bin/codex-acp",
"env": {
"OPENAI_API_KEY": "sk-xxx"
}
},
"openclaw": {
"type": "http",
"endpoint": "https://api.example.com/v1/chat/completions",
"api_key": "sk-xxx",
"model": "openclaw:main"
}
}
}Environment variables:
WECLAW_DEFAULT_AGENT— override the default agentOPENCLAW_GATEWAY_URL— OpenClaw HTTP fallback endpointOPENCLAW_GATEWAY_TOKEN— OpenClaw API token
Per-agent environment variables are set with env:
{
"agents": {
"claude": {
"env": {
"ENV_NAME": "ENV_VALUE"
}
}
}
}Some CLI agents ask for interactive permission confirmation, which cannot be answered from WeChat and hangs the request. Skip it with args:
| Agent | Argument | Effect |
|---|---|---|
| Claude (CLI) | --dangerously-skip-permissions |
Skip all tool permission prompts |
| Codex (CLI) | --skip-git-repo-check |
Allow running outside a git repository |
{
"claude": {
"type": "cli",
"command": "/usr/local/bin/claude",
"cwd": "/home/user/my-project",
"args": ["--dangerously-skip-permissions"]
},
"codex": {
"type": "cli",
"command": "/usr/local/bin/codex",
"cwd": "/home/user/my-project",
"args": ["--skip-git-repo-check"]
}
}cwd sets the agent's working directory (workspace); default ~/.weclaw/workspace.
Note: these arguments bypass safety checks. Enable them only if you understand the risk.
For ACP agents, model and mode are applied to each new session via the standard ACP session/set_config_option; env is passed to the agent subprocess. For example, to make Claude in WeChat behave like TZ=Europe/Oslo claude --dangerously-skip-permissions in a terminal:
{
"claude": {
"type": "acp",
"command": "/opt/homebrew/bin/claude-agent-acp",
"model": "opus",
"mode": "bypassPermissions",
"env": { "TZ": "Europe/Oslo" }
}
}The session workspace decides which project context loads: the project CLAUDE.md, .mcp.json, project skills and Claude's per-directory auto memory. To match a Claude started from some directory in your terminal, set cwd to that directory. Connectors (MCP) from your claude.ai account are off in SDK mode by default; add "ENABLE_CLAUDEAI_MCP_SERVERS": "true" to env to enable them.
Each new session also gets a short WeChat context (the current user's WeChat ID and how to send files with weclaw send --media <local path>), followed by the configured system_prompt. Both are appended after the agent's built-in system prompt via _meta.systemPrompt.append on session/new; they do not replace it.
Valid mode values depend on the agent (claude-agent-acp: default, acceptEdits, plan, bypassPermissions, …). Without mode, ACP mode still auto-approves every permission request. Run weclaw restart after changing the config.
weclaw start # start (in the background; via launchd once the service is installed)
weclaw status # show status
weclaw restart # restart
weclaw stop # stop
weclaw start -f # run in the foreground (debugging)Logs go to ~/.weclaw/weclaw.log.
Only one bridge may run at a time: the process takes a flock on ~/.weclaw/weclaw.lock and exits if it cannot (the lock is released when the process exits, including crashes). Two instances would compete for the same WeChat message queue, so this is a hard rule.
weclaw service install # writes ~/Library/LaunchAgents/com.weclaw.bridge.plist and starts it
weclaw service uninstall # stops and removes itOnce installed, start/stop/restart go through launchctl instead of forking a background process. stop unloads the job; it comes back at next login or weclaw start. The plist records the shell PATH at install time so node (for ACP adapters), claude, codex and friends can be found; if they move, run weclaw service install again.
Linux has no built-in service management yet. Run weclaw start --foreground under systemd (--foreground is required; otherwise the process forks to the background and exits, and systemd keeps restarting it).
# Build
docker build -t weclaw .
# Log in (interactive, scan the QR code)
docker run -it -v ~/.weclaw:/root/.weclaw weclaw login
# Start with an HTTP agent
docker run -d --name weclaw \
-v ~/.weclaw:/root/.weclaw \
-e OPENCLAW_GATEWAY_URL=https://api.example.com \
-e OPENCLAW_GATEWAY_TOKEN=sk-xxx \
weclaw
# Logs
docker logs -f weclawACP and CLI modes need the agent binaries inside the container. The default image contains only WeClaw; mount the binaries or build a custom image. HTTP mode works out of the box.
weclaw update # rebuild and reinstall from the source checkout (git pull --ff-only first if it has a remote); restarts a running bridge
weclaw version # show the versionupdate uses the source directory recorded at build time (written by make install) and is equivalent to running make install there. The binary is replaced by writing a temp file and renaming it, never overwritten in place.
Versions come from git tags (git describe). Release flow: update CHANGELOG.md → commit → git tag vX.Y.Z → make install.
make dev # hot reload
make build # build to ./bin/weclaw
make test # tests
# ACP end-to-end test (calls the real claude-agent-acp; skipped by default)
WECLAW_ACP_E2E=1 go test ./agent -run TestACPModelE2E -vBased on the work of fastclaw-ai/weclaw and its contributors.



