WeChat Desktop MCP
by Wirkflow
README.md
# WeChat Desktop MCP
A production-oriented MCP server that exposes task-level WeChat Desktop automation to AI clients. The MCP layer runs on Bun and TypeScript with the stable Model Context Protocol TypeScript SDK v2. A small native Swift bridge owns all macOS Accessibility and input-event work.
This project automates the visible WeChat for macOS application. It does not use, intercept, or reverse-engineer the WeChat network protocol.
## Capabilities
| MCP tool | Behavior | Side effect |
| --- | --- | --- |
| `wechat_status` | Reports platform, permissions, bridge, installation, process, and sign-in readiness | None |
| `fetch_messages_by_chat` | Resolves an exact contact/group chat and returns up to 200 newest text messages | Navigates and scrolls the UI |
| `reply_to_messages_by_chat` | Opens an exact chat and optionally sends a text reply | Can send a message |
| `add_contact_by_wechat_id` | Submits a friend request with remark/privacy options | Can modify the account/contact graph |
| `publish_moment_without_media` | Prepares or publishes a text-only Moment | Can publish account content |
The four tool names from the original Python project are preserved for client compatibility. `wechat_status` is new and provides a safe readiness probe.
## Why this design
- One dependency-injected MCP server factory is shared by stdio, HTTP, and tests.
- Full Zod v4 input/output schemas produce model documentation, runtime validation, and validated structured results.
- All GUI operations pass through one cancellable FIFO mutex. Concurrent MCP calls cannot race the single WeChat UI state machine.
- Recoverable desktop failures become actionable MCP tool errors with stable codes.
- Exact contact/group matching fails closed and returns candidates instead of clicking a similar result.
- The native bridge is a compiled process with a minimal JSON protocol. MCP handlers do not contain shell, Accessibility, or mouse logic.
- Operational logs are structured JSON on stderr. stdout remains exclusively available to the stdio transport.
- A deterministic fake driver supports complete development and MCP contract testing without a WeChat account.
The primary-source design research is in [docs/research/mcp-typescript-sdk-v2-best-practices.md](docs/research/mcp-typescript-sdk-v2-best-practices.md).
## Requirements
- macOS 14 or newer on Apple Silicon or Intel
- [Bun](https://bun.sh/) 1.2.20 or newer
- Xcode Command Line Tools (`xcode-select --install`)
- WeChat for macOS for native operation
- Accessibility permission for the terminal or MCP host application
Screen Recording permission is reported by the status tool but is not currently required; sender classification uses Accessibility geometry rather than screenshots.
## Setup
```bash
bun install
bun run build:native
bun run check
```
The native bridge is built at `.build/wechat-bridge`. No WeChat login is required to build, type-check, or run the automated test suite.
Before live use:
1. Start WeChat and sign in.
2. Open **System Settings → Privacy & Security → Accessibility**.
3. Grant access to the application that starts this MCP server (for example Terminal, Codex, or the relevant desktop host).
4. Restart that host and call `wechat_status`.
## Run over stdio
stdio is the default and recommended transport for a local desktop MCP:
```bash
bun run start
```
Example MCP host configuration:
```json
{
"mcpServers": {
"wechat-desktop": {
"command": "bun",
"args": ["/Users/YOU/Code/wechat-desktop-mcp/src/index.ts"],
"env": {
"WECHAT_MCP_LOG_LEVEL": "info"
}
}
}
}
```
All runtime diagnostics go to stderr. Do not redirect application logs to stdout when using stdio.
## Run as HTTP ingress
The Bun Web Standard transport serves modern Streamable HTTP and compatible older clients through the SDK v2 handler:
```bash
bun run start -- --transport http
# http://127.0.0.1:3210/mcp
```
It binds to loopback by default and validates Host and Origin headers. Binding to another interface is rejected unless `WECHAT_MCP_AUTH_TOKEN` contains a token of at least 16 characters:
```bash
WECHAT_MCP_AUTH_TOKEN='replace-with-a-long-random-secret' \
bun run start -- --transport http --host 0.0.0.0
```
For use outside one trusted machine, place the endpoint behind TLS and an authenticated reverse proxy. The bearer token is a deployment guard, not a complete multi-user authorization system. Because this server controls a personal desktop session, do not expose it directly to the public internet.
Configuration is documented in [.env.example](.env.example). CLI flags override their equivalent environment values.
## Development without WeChat
Use the deterministic driver only for development and protocol tests:
```bash
bun run start -- --driver fake
```
The fake exposes one chat named `Test Chat`. It never accesses the native WeChat app.
## Verification
```bash
bun run check # type-check, lint/format verification, all tests
bun run build # compile Swift bridge and bundle the Bun entry point
```
The tests cover pure application behavior, exact-match failures, FIFO serialization, cancellation, every tool through a real SDK client, schema rejection, and a spawned stdio process.
Optional interactive inspection:
```bash
bunx @modelcontextprotocol/inspector bun ./src/index.ts --driver fake
```
## Operational constraints
- The native tools require an unlocked macOS user session with WeChat running. The window may be minimized, in the background, behind other apps, or off-screen: the bridge restores minimized windows through Accessibility, then drives reads and navigation in place with process-targeted input. Sending briefly activates WeChat because Qt only enables its Return-to-send shortcut for the active window; the bridge restores the previous foreground app immediately after the verified send attempt. A hidden app may not expose a populated Accessibility tree.
- WeChat UI changes can invalidate Accessibility identifiers. Failures return a stage and stable error category rather than attempting an unsafe fallback click.
- English and common Simplified Chinese labels are supported in the current selector profile.
- Reading is limited to Accessibility-exposed text. Rich media, reactions, quoted-message structure, and sender identity inside group chats are not fully represented.
- Sender is classified as `ME`, `OTHER`, or `UNKNOWN` from message geometry. Clients must handle `UNKNOWN`.
- Mutations are not automatically retried. A timeout after a click or Return key can leave the remote result indeterminate.
- Logs intentionally omit contact names and message bodies.
See [docs/architecture.md](docs/architecture.md) for module boundaries and [docs/migration-from-python.md](docs/migration-from-python.md) for the migration inventory.
## License and attribution
MIT. This implementation preserves the behavior and acknowledges the original MIT-licensed `BiboyQG/WeChat-MCP` project while replacing its Python/PyObjC architecture.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessUnresponsive