npm package: @paean-ai/wechat-mcp
Description: WeChat MCP middleware — shared WeChat connection for multiple AI agents with @mention routing and multi-mode message delivery
Author: Paean AI
Latest version: 0.2.0

# @paean-ai/wechat-mcp

WeChat MCP middleware — shared WeChat connection for multiple AI agents with @mention routing and multi-mode message delivery.

## Problem

WeChat's official iLink protocol only allows **one bot per WeChat account**. If you use multiple AI agents (Claude Code, OpenClaw, Paean CLI, etc.), you're forced to choose which one gets WeChat access.

**wechat-mcp** solves this by providing a shared middleware layer: one WeChat connection, many agents.

## How It Works

```
                     WeChat iLink API
                          │
                 ┌────────┴────────┐
                 │  wechat-mcp     │
                 │  daemon         │
                 │  (background)   │
                 └───┬────┬────┬───┘
                     │    │    │    HTTP IPC (localhost)
              ┌──────┘    │    └──────┐
              ▼           ▼           ▼
         MCP Server  MCP Server  MCP Server
         (claude)    (openclaw)  (paean)
         channel     gateway     poll
              ▲           ▲           ▲
              │           │           │    stdio MCP
         Claude Code  OpenClaw   Paean CLI
```

1. A **background daemon** manages the single WeChat connection (QR login, message polling, token cache)
2. Each agent connects via its own **MCP stdio server** (thin client)
3. Incoming messages are **routed by @mention**: `@claude hello` goes to Claude, `@openclaw run task` goes to OpenClaw
4. Messages are **delivered differently** based on agent mode (channel / gateway / poll)
5. Any agent can **send messages** through the shared connection

## Message Delivery Modes

Different agents consume messages differently. The `--mode` flag controls how incoming WeChat messages reach your agent:

| Mode | How It Works | Best For |
|---|---|---|
| **channel** | Push via Claude Code's proprietary channel protocol. Messages appear in the agent's active conversation automatically. | Claude Code |
| **gateway** | Daemon POSTs message to agent's HTTP endpoint, collects SSE response, sends it back through WeChat. Fully automatic bridging. | OpenClaw, 0claw, any HTTP-based agent |
| **poll** | Agent calls `wechat_read_messages` tool to pull queued messages on demand. | Generic MCP agents, custom bots |

## Quick Start

### 1. Install

```bash
npm install -g @paean-ai/wechat-mcp
```

### 2. Authenticate

```bash
wechat-mcp setup
```

Scan the QR code with WeChat. Credentials are saved to `~/.wechat-mcp/`.

### 3. Add to Your Agent

Choose the configuration that matches your agent type:

**Claude Code** (channel mode — messages arrive automatically):

```json
{
  "mcpServers": {
    "wechat": {
      "command": "npx",
      "args": ["@paean-ai/wechat-mcp", "serve", "--agent", "claude", "--mode", "channel"]
    }
  }
}
```

**OpenClaw / 0claw** (gateway mode — auto-bridges to HTTP endpoint):

```json
{
  "mcpServers": {
    "wechat": {
      "command": "npx",
      "args": [
        "@paean-ai/wechat-mcp", "serve",
        "--agent", "openclaw",
        "--mode", "gateway",
        "--gateway-url", "http://localhost:3000"
      ]
    }
  }
}
```

For OpenAI-compatible endpoints, add `--gateway-type openai`.

**Generic MCP agent** (poll mode — agent reads messages via tool):

```json
{
  "mcpServers": {
    "wechat": {
      "command": "npx",
      "args": ["@paean-ai/wechat-mcp", "serve", "--agent", "my-agent"]
    }
  }
}
```

Each agent uses a different `--agent` name. The daemon auto-starts when any agent connects.

## How Each Mode Works in Detail

### Channel Mode (Claude Code)

Claude Code supports a proprietary `notifications/claude/channel` protocol. The MCP server long-polls the daemon for messages and pushes them as channel notifications into Claude's active conversation. Claude sees the message and can reply using `wechat_send`.

### Gateway Mode (OpenClaw, HTTP agents)

The daemon acts as a bridge (similar to [AnyClaw](https://github.com/nicepkg/anyclaw)):

1. WeChat user sends `@openclaw help me with code`
2. Daemon POSTs `{ message: "help me with code" }` to `http://localhost:3000/api/chat`
3. Reads the SSE response stream from the agent
4. Sends the complete response back to the WeChat user

Supported gateway types:
- **claw** (default) — `POST /api/chat`, SSE response with `{type: "content", text: "..."}` events
- **openai** — `POST /v1/chat/completions`, SSE response with OpenAI chunk format

The daemon maintains per-user conversation IDs so the agent can track multi-turn conversations.

### Poll Mode (Generic)

The MCP server exposes an extra tool `wechat_read_messages` that returns any queued messages. The agent decides when and how often to check. Messages queue up in the daemon (bounded to 200 per agent) until read.

## Message Routing

| WeChat Message | Routed To | Agent Receives |
|---|---|---|
| `@claude hello` | agent "claude" | `hello` |
| `@openclaw run task` | agent "openclaw" | `run task` |
| `hello` (no @) | default agent (first registered) | `hello` |
| `@unknown hi` | default agent | `@unknown hi` |

## CLI Commands

```bash
wechat-mcp setup                    # QR login
wechat-mcp serve --agent X [opts]   # Start MCP server (used in agent config)
wechat-mcp daemon                    # Start daemon in foreground
wechat-mcp stop                      # Stop daemon
wechat-mcp status                    # Show status (with mode info)
wechat-mcp contacts                  # List known contacts
wechat-mcp send --to X --text Y     # One-shot send
wechat-mcp logout                    # Remove credentials & stop daemon
```

### `serve` Options

| Flag | Description | Default |
|---|---|---|
| `-a, --agent <id>` | Agent identifier (required) | — |
| `-m, --mode <mode>` | Delivery mode: `channel`, `gateway`, `poll` | `poll` |
| `--gateway-url <url>` | Agent's HTTP endpoint (required for gateway mode) | — |
| `--gateway-type <type>` | Gateway protocol: `claw` or `openai` | `claw` |

## MCP Tools

When connected as an MCP server, agents can use these tools:

| Tool | Description | Available In |
|---|---|---|
| `wechat_send` | Send a text message to a WeChat user | All modes |
| `wechat_get_contacts` | List known WeChat contacts | All modes |
| `wechat_get_status` | Get connection status and registered agents | All modes |
| `wechat_read_messages` | Read pending messages (non-blocking) | Poll mode only |

## Proactive Messaging

Agents can send messages proactively (e.g., from cron jobs or scheduled tasks) using the shared WeChat connection:

```bash
# CLI
wechat-mcp send --to "friend@im.wechat" --text "Task completed!"

# Or via MCP tool: agents call wechat_send at any time
```

## Architecture

- **Daemon** (`~/.wechat-mcp/daemon.json`): Auto-started background process that owns the WeChat connection. Listens on `127.0.0.1` only. Handles gateway bridging for HTTP-based agents.
- **MCP Server**: Thin stdio process spawned per-agent. Connects to daemon over local HTTP. Mode determines message delivery strategy.
- **Gateway Adapters**: Protocol translators for different agent types (claw SSE, OpenAI completions).
- **Credentials**: Stored in `~/.wechat-mcp/credentials.json` with `0600` permissions.

## Programmatic API

```typescript
import {
  ensureDaemon,
  DaemonClient,
  loadCredentials,
  parseMention,
  getAdapter,
} from "@paean-ai/wechat-mcp";

// Connect to daemon
const daemon = await ensureDaemon();
const client = new DaemonClient(daemon.port);

// Register with mode
await client.registerAgent("my-bot", "My Bot", "poll");

// Poll for messages
const messages = await client.pollMessages("my-bot");

// Send
await client.send("user@wxid", "Hello from my bot!", "my-bot");

// Or use a gateway adapter directly
const adapter = getAdapter("claw");
const result = await adapter.send("http://localhost:3000", "Hello");
```

## Security

- Credentials stored with `0600` file permissions
- Daemon listens only on `127.0.0.1` (no network exposure)
- No secrets in environment variables or logs
- Token stored locally, never transmitted to third parties
- Gateway connections are local only (localhost)

## License

MIT