Skip to main content

Documentation Index

Fetch the complete documentation index at: https://zapo.to/llms.txt

Use this file to discover all available pages before exploring further.

zapo ships two optional packages aimed purely at development and testing — neither is meant for production:
  • MCP server (@zapo-js/mcp-server) — expose a live WaClient to an AI agent (Claude Code, Cursor) so it can connect, pair, send, and inspect state interactively.
  • Fake server (@zapo-js/fake-server) — an in-process fake WhatsApp Web server that drives the real WaClient end-to-end, so you can test deterministically without touching WhatsApp’s servers.

MCP server

Development & testing only. @zapo-js/mcp-server is a debugging aid, not a production protocol server. It exposes a live WaClient — and the whole zapo-js module — to an AI agent that can send messages, read state, and run arbitrary library calls on a real WhatsApp account. Run it only against accounts you control.
It exposes a live WaClient instance and the zapo-js module namespace as MCP tools. An LLM agent can then drive end-to-end WhatsApp flows — connect, pair, send, query groups/newsletters, inspect events, walk SQL state — without you writing throwaway scripts.

Tool surface

ToolWhat it does
call / inspectWalk dotted paths against client (the WaClient) or lib (the zapo-js namespace, including proto.* and helpers like parsePhoneJid). call invokes functions; inspect lists members.
events / events_clearBounded ring buffer of every WaClientEventMap event — filter by types / since / limit / drain.
logs / logs_clearQueryable buffer mirroring every runtime + lib log line (also stderr; JSONL to MCP_LOG_FILE if set).
lifecyclestatus / start / destroy for the client.
restartsoft (drop the client + clear buffers) or process_exit (also exit so a supervisor respawns it).
Each tool inlines its own schema and examples — the agent reads them at runtime rather than memorizing flags.

Install & register

npm install @zapo-js/mcp-server
# peers: zapo-js, @zapo-js/store-sqlite, @zapo-js/media-utils (better-sqlite3 optional, for SQLite persistence)
Register with Claude Code at user scope (build first), so it works from any directory:
npm run build --workspace @zapo-js/mcp-server
claude mcp add zapo --scope user -- node <abs-path>/packages/mcp-server/dist/bin.js
For tight iteration on the library itself, register the source through tsx — no build step, and zapo-js resolves straight from src/:
claude mcp add zapo --scope user -- node --import tsx <abs-path>/packages/mcp-server/src/bin.ts
An HTTP transport with node --watch gives the smoothest dev loop: edit a .ts → the process restarts → the next tool call reconnects automatically, with no manual /mcp reconnect. See the package README for the dev script and HTTP setup.

Pairing gotcha

client.connect() blocks until pairing finishes, so always start it without awaiting, then poll the event buffer:
call({ path: 'connect', noAwait: true })
events({ types: ['auth_qr', 'auth_pairing_code', 'auth_paired', 'connection'] })
Surface the auth_qr string to the user, wait for auth_paired, then continue.

Key environment variables

VarDefaultPurpose
MCP_AUTH_PATH<cwd>/.auth/state.sqliteSQLite credential store path
MCP_SESSION_IDdefault_2sessionId passed to WaClient
MCP_LOG_LEVELinfotrace / debug / info / warn / error
MCP_TRANSPORTstdiostdio or http
MCP_HTTP_HOST / MCP_HTTP_PORT / MCP_HTTP_PATH127.0.0.1 / 3737 / /mcpHTTP listener config
MCP_EVENT_BUFFER_SIZE / MCP_LOG_BUFFER_SIZE1000 / 500In-memory ring sizes
One WaClient per process (multi-session needs multiple servers with distinct MCP_AUTH_PATH + MCP_SESSION_ID); no auto-reconnect (call connect again on connection: close); restart soft does not pick up code changes while process_exit + a supervisor does. Full reference: packages/mcp-server/README.md.

Fake server

@zapo-js/fake-server is an in-process fake WhatsApp Web server that drives the real zapo-js WaClient end-to-end — full Noise XX/IK handshake, QR pairing, Signal Protocol (X3DH + Double Ratchet), group SenderKey, media upload/download over self-signed HTTPS, and app-state sync — all without touching WhatsApp’s servers. It powers the library’s own cross-check test suite and benchmarks, and you can use it to test your integration deterministically.

Quick start

import { FakeWaServer } from '@zapo-js/fake-server'
import { createStore, WaClient } from 'zapo-js'

const server = await FakeWaServer.start()

const client = new WaClient({
  store: createStore({
    providers: { auth: 'memory', signal: 'memory', senderKey: 'memory', appState: 'memory' }
  }),
  sessionId: 'test',
  chatSocketUrls: [server.url],
  testHooks: { noiseRootCa: server.noiseRootCa },
  proxy: { mediaUpload: server.mediaProxyAgent, mediaDownload: server.mediaProxyAgent }
})

await client.connect()
const pipeline = await server.waitForAuthenticatedPipeline()
// drive pairing, create peers, send/receive messages, assert on both sides
await server.stop()
The wiring uses three client options built for exactly this: chatSocketUrls (point at the fake WebSocket), testHooks.noiseRootCa (trust the fake server’s certificate without bypassing verification — the full cert-chain check still runs), and proxy.mediaUpload / proxy.mediaDownload (route media to the fake HTTPS server).

What it simulates

  • FakeWaServer — the WebSocket listener, Noise handshake, an IQ router that answers every IQ the lib emits during normal operation (prekey upload/fetch, usync, media-conn, app-state sync, groups, privacy, profile, blocklist, …), plus state registries for peers and groups.
  • FakePeer — a simulated contact with real Signal crypto: peer.sendConversation(text) / peer.sendGroupConversation(groupJid, text) push messages to the client, and peer.expectMessage() captures and decrypts what the client sends.
  • Pairingserver.runPairing(pipeline, { deviceJid }, materialFn) drives the full QR-pairing handshake; afterward the lib reconnects with the IK handshake (capture it via waitForNextAuthenticatedPipeline()).

Standalone CLI

Run it as a standalone server for manual experiments:
npm --workspace=@zapo-js/fake-server run cli -- --port 5222 --peer 5511888@s.whatsapp.net --log

Benchmarking

The package ships a messaging profiler (send/recv × 1:1/group) used to track the library’s performance:
npm --workspace=@zapo-js/fake-server run bench:messaging
Tune the workload with ZAPO_BENCH_* env vars (ZAPO_BENCH_CONTACTS, ZAPO_BENCH_GROUP_MEMBERS, ZAPO_BENCH_MESSAGES, ZAPO_BENCH_SCENARIOS, …) and add --cpu / --heap / --separate-process for profiles. See packages/fake-server/README.md for the full flag reference.
The fake server is an in-process testing harness, not a runtime you deploy. Pair it with the memory store for fast, isolated tests that reset on every run.
Last modified on May 28, 2026