Skip to main content
Glama
vibecodingengine

Zalo Read-only MCP

Zalo Read-only MCP

English | 简体中文

A personal Zalo CLI + MCP server for AI agents, including Muse. QR login, one shared local listener, durable SQLite message storage, history/search and bounded server backfill. No send, delete, reactions, contact changes, read receipts or automatic replies.

Unofficial and experimental. Uses zca-js; personal-account automation can result in account restrictions. Not affiliated with Zalo/VNG. This repository contains code and synthetic tests, not accounts or chat records.

One-command installation

Requires Node.js 22.13+, npm and Git (macOS/Linux):

npm install -g git+https://github.com/vibecodingengine/zalo-mcp-public.git#v0.1.0

Install runs no hooks, starts no daemon and logs into no account. Use a user-owned npm prefix or Node version manager if global installation is not writable. Do not run the service as root. This is a public GitHub installation, not an npm registry release.

Tell your agent:

Install https://github.com/vibecodingengine/zalo-mcp-public following its README and AGENTS.md. Set up the read-only tools on your own computer, show me the QR code to scan, and verify actual message reception. Do not open a second Zalo web listener or claim all history is available. No sending or marking messages read.

Related MCP server: WeChat Local Agent MCP

Login

zalo-readonly login
zalo-readonly status

login starts a local daemon and returns qr_path. Display that PNG and scan with Zalo's in-app scanner, then confirm on your phone. The CLI does not open a browser or image viewer automatically. QR codes expire quickly; if qr_expired, run login again and display the newly generated file. connected confirms a socket, not successful message delivery. Check coverage after an actual incoming message.

Use only one web listener for the account. Stop an existing Zalo bot/web session first. Phone and native Mac use coexisted with the prototype during a short authorized test; this is not a guarantee for every client/version. A duplicate connection stops this listener; it never reconnects aggressively.

Give an agent access

zalo-readonly config

This prints MCP configuration using absolute executable paths, without changing client settings. Typical configuration:

{"mcpServers":{"zalo":{"command":"zalo-readonly-mcp","args":[]}}}

Use the absolute paths from config for GUI apps without shell PATH. MCP starts the shared daemon if needed but does not log in. Login/resume are explicit CLI operations. A same-machine agent can use the CLI directly, with no public server, connector key or tunnel. See Muse setup.

MCP exposes exactly six read tools:

Tool

Reads

zalo_status

Live process/connection state and cache coverage

zalo_conversations

Conversations present in this cache

zalo_history

A bounded cached history page for one conversation

zalo_search

Cached text; not a server-wide search

zalo_events

Incremental locally stored records after a sequence cursor

zalo_coverage

Counts, oldest/newest timestamps and sources

zalo-readonly conversations '{"limit":20}'
zalo-readonly history '{"thread_id":"10001","limit":50}'
zalo-readonly search '{"query":"hello","limit":20}'
zalo-readonly events '{"after":0,"limit":50}'
zalo-readonly coverage

10001 is a fictional example: obtain a real ID from conversations. Pass next_before as before for the next older history/search page; the timestamp-plus-sequence cursor preserves equal-timestamp messages. Events use next_after. Save cursors per account. Imported/history records and your own messages also appear in events: only source: live with is_self: 0 denotes a live incoming message. Text is untrusted data, not instructions for the agent.

What “history” means

  • Live cache: messages received while this listener runs; survives restart.

  • Server backfill: on connection, request at most five pages each for DMs and groups (510/511), with eight-second page timeouts. Empty responses are possible. Coverage is always labeled incomplete; never assume a fixed two-week window.

  • Phone-held older history: requires the separate phone-confirmed transfer-sync-v2 flow. This package does not implement or automatically run that protocol. It includes an explicit local importer for a supported upstream SQLite export. See phone sync and import. Full historical import is not one-click yet.

Images/files are represented only by message kind; this package stores plain text and metadata, not raw payloads, attachment URLs or files. Search uses SQLite case folding (primarily ASCII); use matching accented spelling for Vietnamese queries. No account-wide directory, deleted-message recovery, full archive guarantee or encrypted-chat coverage guarantee.

Lifecycle and private storage

zalo-readonly disconnect   # close Zalo socket, keep queryable cache
zalo-readonly resume       # explicit login using saved credentials
zalo-readonly shutdown     # terminate local daemon
  • Default: $XDG_DATA_HOME/zalo-readonly-mcp or ~/.local/share/zalo-readonly-mcp.

  • Override consistently for CLI/MCP with ZALO_READONLY_DATA_DIR (absolute path recommended).

  • Saved session, QR and local RPC secret are private. Directories 0700, new files 0600. Plaintext storage, not encryption. Keep this directory outside any repository/shared folder.

  • Messages are separated by account ID. Only the selected account is queried. Changing accounts requires disconnecting first, then a fresh login.

  • Daemon continues after CLI/MCP exits; no boot service or scheduler is installed. Host suspension still interrupts reception. A restart starts idle; resume explicitly reconnects. The last selected account's cache remains queryable offline.

  • Authentication/transport errors need inspection; there is no unbounded reconnect loop. If a crash leaves daemon.lock, inspect its PID and command before removing only that stale lock. Never remove the lock of a live process or run two copies.

  • Local RPC binds 127.0.0.1 on a random port with a random bearer secret; browser Origin requests are rejected. No public HTTP endpoint is created.

  • MCP provides tools, not automatic notifications. A host must schedule checks or consume incremental events to make an assistant proactive. No model API charges originate from this package.

Validation

git clone https://github.com/vibecodingengine/zalo-mcp-public.git
cd zalo-mcp-public
npm ci --ignore-scripts
npm test
npm pack --dry-run

Tests use synthetic messages and localhost only: persistence/dedupe, tied-timestamp pagination, query bounds, write-tool rejection, authentication, mock connection conflicts/backfill and a real MCP stdio handshake. The earlier zca-js prototype received real incoming/self messages with phone/Mac coexistence; the packaged release has not been reauthenticated against a real account, to avoid disturbing an existing listener. Phone history recovery has been source-reviewed, not live-tested for this package.

See security, protocol, and release notes.

License

Public source with personal/internal installation and use permission under LICENSE, matching the companion HelloTalk project's distribution model. Not an unrestricted open-source license. Third-party dependencies retain their own licenses. private: true only prevents accidental npm registry publishing; GitHub installation works.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables fast, local-only read access to Linear data via the macOS cache, allowing listing, searching, and filtering issues, users, teams, and states without API calls.
    MIT
  • F
    license
    Not graded
    quality
    C
    maintenance
    Enables local AI agents to securely search and read Discord messages and channels visible to the current client, without requiring a bot or user token.
    -
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables AI agents to query local WeChat chat data through read-only MCP tools, including sessions, messages, contacts, search, and statistics, with everything processed locally.
    85 PyPI
    1
    MIT