Skip to main content
Glama
wxmon

wxmon

by code2rich

wxmon

Monitor new WeChat Official Account articles on macOS and deliver them to your own tools — as a JSONL stream, signed webhooks, or an MCP server for AI assistants.

License: MIT Python Platform CI

English | 简体中文


wxmon watches the local WeChat database on your Mac and surfaces articles from the Official Accounts your own WeChat follows. Instead of scraping any remote endpoint, it reads messages that WeChat has already synced to this machine — so there is no remote polling and no risk of tripping account rate limits.

It is designed to feed downstream automation: pipe the JSONL stream into a script, POST signed events to your backend, or attach the built-in MCP server to an AI assistant such as Claude Code.

Table of contents

Related MCP server: wechat-article-reader

Features

  • Local-first. Reads WeChat's on-disk database; no remote scraping, no rate-limit risk.

  • Incremental and deduplicated. Tracks a per-database cursor and a stable article_key (__biz + mid + idx) so each article is reported exactly once.

  • Schema-adaptive parser. Discovers message tables and columns at runtime rather than hard-coding a layout, making it resilient to WeChat version differences.

  • Optional full text. Fetches the article page and converts the body to Markdown, using a plain browser identity that is decoupled from your WeChat account.

  • Multiple outputs. A JSONL event stream, HMAC-signed webhooks, and an MCP server — use any combination.

  • AI-ready. The MCP server exposes list_recent, search_articles, and get_article so assistants can query collected articles directly.

How it works

biz_message_*.db (SQLCipher-encrypted)
   │  1. Watch for changes (size/mtime, including the -wal file)
   ▼
 Decrypt    2. Export a plaintext copy via the official sqlcipher CLI + your key
   │           (the copy is temporary and deleted after each scan)
   ▼
 Parse      3. Scan tables adaptively for appmsg / mmreader message payloads
   │           and extract one record per article
   ▼
 Dedupe     4. article_key = __biz_mid_idx; a cursor stores each DB's max timestamp
   ▼
 Output     JSONL append · webhook (HMAC-SHA256) · local store for MCP and `list`

wxmon does not implement any cryptography and does not extract keys. Decryption is delegated to the standard sqlcipher command-line tool, and the key is supplied by you. In other words, wxmon only ever opens your own database with a key you already hold.

Requirements

  • macOS with the WeChat desktop client installed and signed in.

  • Python 3.11 or newer.

  • sqlcipher available on PATH (brew install sqlcipher).

  • The SQLCipher key for your own WeChat database — see Obtaining the database key.

The monitored scope is limited to Official Accounts that this WeChat account follows and whose messages have reached this machine.

Installation

git clone https://github.com/code2rich/wxmon.git
cd wxmon
python3 -m venv .venv && source .venv/bin/activate
pip install -e .
brew install sqlcipher   # skip if already installed

Obtaining the database key

wxmon requires the SQLCipher key for your own WeChat database (a 32-byte value, i.e. 64 hexadecimal characters).

The extraction itself is out of scope for this project. The key lives in the running WeChat process's memory, and reading it requires a dedicated tool. Use a well-maintained, community WeChat local-database key tool to retrieve it from your own account on your own machine (e.g. wechat-dump-rs, PyWxDump, or a comparable actively-maintained project — check that it supports your macOS and WeChat version, and follow its own documentation). Extraction typically requires WeChat to be running and signed in, and may involve temporarily adjusting system security settings such as SIP; restore them afterwards.

Once you have the hex string, wxmon manages it for you. The key is validated (format and an actual decrypt check) and stored in the macOS Keychain — never in a plaintext config file:

wxmon key set                         # paste the key interactively (hidden input), verify, store
wxmon key import -- <extractor-cmd>   # run your extractor tool and capture its key automatically
wxmon key status                      # show whether a key is stored / present in the environment
wxmon key verify                      # confirm the current key can actually decrypt
wxmon key rm                          # remove the key from the Keychain

wxmon key import runs whatever extractor command you point it at, parses the 64-hex key out of its output, verifies it, and stores it — for example:

wxmon key import -- wechat-dump-rs --key-only

Key resolution order at run time: explicit --key > the WXMON_KEY environment variable > the Keychain. The environment variable remains available as a per-session override:

export WXMON_KEY=<64-hex-characters>   # optional; not persisted

wxmon never extracts keys or reads process memory itself — it only validates, stores, and uses a key you already hold, and (for import) shells out to the external extractor you choose.

Usage

wxmon doctor                 # Environment check: WeChat dir, account, sqlcipher, key
wxmon probe                  # Detect the working cipher profile and print DB structure
wxmon scan                   # Scan once; print newly discovered articles as JSON events
wxmon scan --dry-run         # Scan without writing JSONL or pushing webhooks
wxmon watch                  # Run continuously; stream new articles to stdout
wxmon list -n 20             # List recently collected articles
wxmon list -q keyword --json # Search collected articles
wxmon mcp                    # Run as an MCP stdio server for AI tools
wxmon config --set fetch_body=false webhook_url=https://... webhook_token=xxx

First run

  1. wxmon doctor — confirm the account and sqlcipher are detected and the key is set.

  2. wxmon probe — decrypt with your key. On success, the working cipher profile is saved to the config and the matched tables/columns plus parsed samples are printed. If the samples are empty or incomplete, share that output so the parser in wxmon/parse.py can be tuned to your WeChat version's real schema.

  3. wxmon watch — start monitoring. On startup it only establishes a baseline (existing articles are recorded but not pushed); only articles published afterwards are streamed.

Integrating with AI tools

Run wxmon watch in the background to collect articles, and register wxmon mcp as an MCP server. Assistants can then call:

Tool

Purpose

list_recent(limit, since_hours, account)

Most recently discovered articles

search_articles(query, limit)

Keyword search over title / account / digest

get_article(article_key, with_body)

A single article, including Markdown body

The server supports two transports:

wxmon mcp                                   # stdio (default; local process)
wxmon mcp --transport http --port 8765      # streamable-http at http://127.0.0.1:8765/mcp
wxmon mcp --transport http --host 0.0.0.0 --port 8765 --path /mcp   # expose to the network

stdio — example Claude Code configuration (~/.claude.json or a project .mcp.json):

{
  "mcpServers": {
    "wxmon": {
      "command": "/absolute/path/to/wxmon/.venv/bin/wxmon",
      "args": ["mcp"],
      "env": { "WXMON_HOME": "/Users/you/.wxmon" }
    }
  }
}

HTTP — run wxmon mcp --transport http as a long-lived service, then point clients at the URL:

{
  "mcpServers": {
    "wxmon": { "type": "http", "url": "http://127.0.0.1:8765/mcp" }
  }
}

The HTTP transport binds to 127.0.0.1 by default. Only use --host 0.0.0.0 on a trusted network; the server has no built-in authentication, so place it behind a reverse proxy or firewall if it must be reachable remotely.

The MCP server only reads the already-collected local store and never triggers decryption, so it does not need the key. Decryption is handled by the background wxmon watch process, which holds WXMON_KEY.

B. JSONL stream / webhook

wxmon watch > articles.jsonl    # any agent or script can tail this file

Or configure a webhook. Each new article is POSTed with HMAC-SHA256-signed headers:

wxmon config --set webhook_url=https://your/api webhook_token=yourkey customer=your-id
wxmon watch

Request headers:

Header

Value

Content-Type

application/json; charset=utf-8

X-Api-Token

the configured token

X-Signature

hex(HMAC_SHA256(token, raw_body)), lowercase

X-Machine

hostname|12-hex-MAC

X-Request-Id

the event's event_id

Verify the signature against the raw request body (do not re-serialize the JSON first):

import hmac, hashlib
def verify(raw: bytes, token: str, sig: str) -> bool:
    expect = hmac.new(token.encode(), raw, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expect, sig)

Configuration

Configuration lives at ~/.wxmon/config.json (override the base directory with WXMON_HOME). Inspect or change it with wxmon config [--set key=value ...].

Key

Default

Description

wx_root

WeChat container path

WeChat data root directory

account

""

Specific wxid directory; empty selects the most recently active

cipher_profile

wechat4

SQLCipher parameter set (set automatically by probe)

key_source

env

env / config / arg

fetch_body

true

Fetch article body and convert to Markdown

webhook_url

""

Destination for push events

webhook_token

""

HMAC key / X-Api-Token

customer

""

Customer identifier included in events

poll_idle_seconds

60

Poll interval when idle

poll_active_seconds

5

Poll interval right after a change

accounts_filter

[]

Keep only these account names; empty keeps all

The key is read from WXMON_KEY by default and is not written to the config file.

State files:

  • ~/.wxmon/state.db — cursor, dedup set, collected articles

  • ~/.wxmon/articles.jsonl — event stream

Project layout

wxmon/
├── article.py      Article model and article_key derivation
├── cli.py          Command-line entry point
├── config.py       Paths, config file, cipher profiles
├── core.py         Scan orchestration (decrypt → parse → dedupe → fetch → output)
├── decrypt.py      sqlcipher CLI backend (plaintext export)
├── fetch.py        Article body → Markdown
├── locate.py       Locate WeChat accounts and message databases
├── mcp_server.py   MCP stdio server
├── output.py       JSONL writer and signed webhook push
├── parse.py        Schema-adaptive article extraction
├── state.py        SQLite state store
└── watch.py        Monitoring loop

Development

pip install -e ".[dev]"
pytest

The test suite validates the parser, deduplication, and output against a synthetic plaintext database, so it runs without sqlcipher or a real WeChat key.

This project is intended solely for notifying and archiving content you are already subscribed to, using your own WeChat account on your own machine. You are responsible for complying with applicable laws and with WeChat's platform rules. Do not use it to access data that is not yours.

License

Released under the MIT License.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables AI to read full-text WeChat Official Account articles, returning title, author, publish time, and clean Markdown content.
    33 npm
    9
    AGPL 3.0
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables fetching WeChat public account articles, caching them in SQLite, and reading them as paginated Markdown via MCP, with optional DeepSeek summarization and HTML/Markdown export.
    MIT
  • A
    license
    A
    quality
    B
    maintenance
    Enables AI tools to preview and publish Markdown articles to WeChat Official Accounts, automatically converting content into WeChat-optimized HTML and handling cover images. Also lets users query publish status and read/like/comment statistics through natural language.
    5
    1
    MIT