Skip to main content
Glama
wa-web-mcp

wa-web-mcp

by myi1

wa-web-mcp

MCP server that lets Claude read and send WhatsApp for your team's personal number, driving the genuine WhatsApp Web client (via WPPConnect) behind a code-enforced human-behavior middleware.

Setup / template note: this is a shareable copy with infrastructure specifics replaced by placeholders — YOUR_TAILNET_IP, your-host, your-tailnet.ts.net, unix user youruser, bundle id com.example.wa-web-mcp. Search-and-replace those (or drive them via WA_WEB_URL / env) for your own host before deploying. Config lives in config.sample.json (copy to config.json); nothing sensitive ships in the repo.

Built after two WhatsApp restrictions from an unofficial whatsmeow bridge. Two principles (see SPEC.md):

  1. Real client, not forged. WPPConnect runs actual WhatsApp Web, so the on-sight fingerprint ban vector that killed the whatsmeow bridge is closed.

  2. Guards in code, not in the prompt. Every limit is service-side middleware the model cannot talk past.

Status

Layer

State

Human-behavior middleware (all gates, reciprocity governor, warm-up)

built + unit-tested (38 tests)

WPPConnect client wrapper, outbound ritual executor

built, compiles; runtime-validated on the host

MCP HTTP server + tools + override header

built, compiles; runtime-validated on the host

Deploy (m2 LaunchDaemon)

artifacts ready in deploy/; needs SSH + one QR scan

The safety-critical core is proven with deterministic tests. The WPPConnect + MCP-transport glue compiles and follows the verified v2.2.1 / SDK 1.29 APIs, but end-to-end behavior is validated on first deploy (needs a browser + a linked session + a live MCP client — none reproducible in CI here).

Related MCP server: WhatsApp MCP Server

Layout

src/
  config.ts            defaults (SPEC §6) + JSON override loader; dryRun starts TRUE
  core/                clock (injectable), logger (decision log), tz helpers
  state/db.ts          SQLite: messages, new_contact_log (reciprocity), kv singletons
  middleware/
    ratelimit.ts       interval + hourly + usync (peek/commit)
    text.ts            normalize + token-set-Jaccard similarity (dedup)
    pipeline.ts        the 9 gates + reciprocity governor + warm-up ramp + health
  wa/
    jids.ts            @c.us / @g.us normalisation
    client.ts          WPPConnect wrapper: send/read/typing/presence + inbound/ack events
    outbound.ts        dry-run + serialised read→type→send ritual
  mcp/server.ts        tools + Streamable HTTP transport (bearer) + override header
  index.ts             wiring

Run locally (dry-run, no send)

npm install          # local: set PUPPETEER_SKIP_DOWNLOAD=true (browser only needed on the host that links)
npm run build
npm test             # 38 unit tests

Deploy (m2)

  1. rsync this repo to m2:~/wa-web-mcp/ (exclude node_modules/dist/store).

  2. Put deploy/token.txt (a bearer, e.g. openssl rand -hex 32) and config.json (from config.sample.json) in place.

  3. ssh -t m2 "bash ~/wa-web-mcp/deploy/install-m2.sh" — installs deps (incl. Chromium), builds, starts the LaunchDaemon.

  4. tail -f ~/wa-web-mcp/store/daemon.log, scan the QR once.

  5. Watch decisions in dry-run. When satisfied, set config.json dryRun:false and re-run the installer. Reads/warm replies first; new-contact outreach ramps from 2/day.

Point Claude Code at http://YOUR_TAILNET_IP:8765/mcp with the bearer.

Safety controls

  • dryRun (config): evaluate + log, never send. Default true.

  • halt_outbound / resume_outbound tools: persisted kill switch, never bypassable.

  • X-Rate-Limit-Override header: operator bypass of pacing gates (not kill switch, not dedup). LLM cannot set it.

  • get_send_health tool: live posture (caps, reply rate, warm-up, cooldown).

Sending a local file — from any Claude Code machine

Text sends are just the send_message tool. Sending a local file needs the bytes to reach the daemon first, so bin/wa-send-file does upload (POST /media) + send_file in one shot, entirely over the daemon's HTTP endpoint (no scp/SSH). Every send still passes the full server-side pipeline.

wa-send-file <local-file> <recipient> [caption]

recipient is a chat id — E.164 digits, <digits>@c.us, <id>@lid, or a <id>@g.us group. Resolve a contact name to its id with the list_chats tool first. A bare number is resolved through the daemon's resolveTarget path before send, so first-contact sends can work without a pre-existing chat when WhatsApp's lookup returns a canonical id.

Set up a new machine (any Mac on the tailnet with Claude Code):

# 1. put the script on PATH
install -m 0755 bin/wa-send-file /usr/local/bin/wa-send-file
# 2. drop the bearer token where the script looks for it
mkdir -p ~/.config/wa-web && printf '%s' '<TOKEN>' > ~/.config/wa-web/token && chmod 600 ~/.config/wa-web/token

Token lookup order: WA_WEB_TOKEN env → ~/.config/wa-web/token → ./deploy/token.txt. Base URL override: WA_WEB_URL. Operator bypass for one call: WA_WEB_OVERRIDE=1. Exit codes: 0 sent, 3 held by a gate, 1 error.

The controls are the same everywhere because they live in the one daemon — every machine is a thin client of it. Don't run a second daemon/linked device.

Limitations / follow-ups

  • resolveTarget falls back to the raw jid, with a warning log, when checkNumberStatus fails or reports no canonical id. A first-contact send can still target a stale jid if WhatsApp's lookup path is down.

  • Staged uploads accumulate under store/uploads/; swept every 30 minutes (TTL 1h).

  • quietHours.outsideBehavior: 'defer' is declared but not implemented — quiet-hours holds reject and rely on the queue to retry transient gates.

Related MCP Connectors

Related MCP Servers

  • A
    license
    C
    quality
    D
    maintenance
    A Model Context Protocol server that connects your personal WhatsApp account to AI agents like Claude, enabling them to search messages, view contacts, retrieve chat history, and send messages via WhatsApp.
    7
    16 npm
    75
    ISC
  • A
    license
    Not graded
    quality
    D
    maintenance
    MCP server for sending and reading WhatsApp messages through Claude, with human approval on sends and no auto-replies.
    1
    Apache 2.0
  • A
    license
    Not graded
    quality
    D
    maintenance
    A local MCP server that connects WhatsApp to Claude via QR code, enabling chat listing, message retrieval, and sending with automatic rate limiting for anti-ban protection.
    37 npm
    MIT