Skip to main content
Glama
vimal-v-2006

whatsapp-mcp

by vimal-v-2006

whatsapp-mcp 📱

WhatsApp as an MCP server — link your phone once by scanning a QR code, then let any agent read and write your chats. Works with pi, Hermes, Claude Code, Cursor, or literally any Model Context Protocol client. Also usable headless for plain automation (cron, CI, scripts) with no agent at all.

┌──────────────────────────────────────────────────────────────────┐
│  core/whatsapp.mjs — Baileys session + all read/write actions    │
│   • QR pairing (scan once, session persists)                     │
│   • auto-reconnect, auto-restart, QR re-pair on unlink           │
│   • on-demand history (no bulk download), date-ranged reads      │
│   • one connection shared across pi + hermes + scripts           │
└───────────────┬──────────────────────────────────┬───────────────┘
                │                                  │
      ┌─────────▼─────────────┐          ┌─────────▼───────────────┐
      │  server.mjs           │          │  pi-extension/index.ts  │
      │  MCP stdio server     │          │  native pi extension    │
      │  → Hermes, Claude     │          │  (pi ships no MCP by    │
      │    Code, Cursor, any  │          │  design — so pi gets    │
      │    MCP client         │          │  the same tools natively)│
      └───────────────────────┘          └─────────────────────────┘

Quickstart

git clone https://github.com/vimal-v-2006/whatsapp-mcp.git
cd whatsapp-mcp
npm install

### First-time pairing (do this first, once)

```bash
node pair.mjs          # prints the QR and waits for you to scan

On your phone: WhatsApp → Settings → Linked devices → Link a device → scan the terminal QR (a PNG copy is saved to ~/.whatsapp-mcp/qr.png if the terminal QR is unreadable).

That's it — you scanned once. The session lives in ~/.whatsapp-mcp/; every future start reconnects silently, even headless.

No QR-friendly terminal? Use a pairing code instead: WhatsApp → Linked devices → Link with phone number. The tool whatsapp_pairing_code returns an 8-character code.

Starting the MCP server (any harness, any time after pairing)

node server.mjs

With a saved session it just reconnects silently — no QR.

Pairing got interrupted / stuck? Run rm -rf ~/.whatsapp-mcp and node pair.mjs again. Also make sure no old node server.mjs process is still running (pkill -f server.mjs) — a zombie server holds the session and blocks fresh QRs.


Related MCP server: WhatsApp MCP Stream

Tools

Tool

Description

whatsapp_status

Link state (online / qr_pending / starting / closed), linked device, data dir

whatsapp_link

Get the pairing QR (ASCII + PNG path). Scan once; session persists

whatsapp_unlink

Unlink device, wipe local session, generate a fresh QR

whatsapp_pairing_code

8-char code pairing instead of QR (phone number input)

whatsapp_list_chats

Recent DMs/groups: name, unread count, last message preview

whatsapp_read_messages

Latest N messages — or a window via since / until (e.g. since: "2026-09-20"). Accepts JID, phone number, or name

whatsapp_send_message

Send a text message

whatsapp_send_media

Send image / video / audio / document / sticker from a local file path

whatsapp_delete_message

Revoke a message (by id from whatsapp_read_messages)

whatsapp_list_contacts

Known contacts (JID + name) from synced history

whatsapp_get_contact

Resolve contact by JID/phone/name → existence, LID, profile picture

whatsapp_search_messages

Full-text search. Defaults to the last 2 days; tune with days, since/until, maxChats

whatsapp_group_info

Group subject, description, size, participants

Identifiers are flexible everywhere: 15551234567@s.whatsapp.net (JID), +15551234567 (phone), or a contact/group name are all accepted by to / chat / group parameters.

Examples

whatsapp_send_message  { to: "+15551234567", text: "Deploy done ✅" }
whatsapp_read_messages { chat: "Alice", limit: 10 }
whatsapp_read_messages { chat: "Alice", since: "2026-09-20", limit: 50 }   # one specific day
whatsapp_send_media    { to: "Team", path: "/tmp/report.pdf", caption: "Q2 numbers" }
whatsapp_search_messages { query: "invoice", days: 7 }

With pi

pi intentionally ships no MCP — so this repo includes a native extension that registers the exact same 13 tools. It lives in pi-extension/ and reuses the same core.

Permanent (recommended): add the directory to your pi settings (~/.pi/agent/settings.json):

{
  "extensions": ["/path/to/whatsapp-mcp/pi-extension"]
}

One-off:

pi -e /path/to/whatsapp-mcp/pi-extension/index.ts

The WhatsApp session starts in the background when pi boots. First run prints the QR to the terminal — scan it, and whatsapp_* tools are live in every pi session. (Project-local .pi/extensions/ also works if you copy the folder in.)


With Hermes

Hermes is a first-class MCP client. One command:

hermes mcp add whatsapp --command node --args /path/to/whatsapp-mcp/server.mjs

Hermes connects, discovers the 13 tools, and registers them. Verify any time:

hermes mcp list          # shows configured servers
hermes mcp test whatsapp # connection + tool discovery check

Equivalent raw config (in Hermes's mcp_servers section of config.yaml):

mcp_servers:
  whatsapp:
    command: node
    args: ["/path/to/whatsapp-mcp/server.mjs"]
    # env:            # optional, e.g. custom session dir
    #   WHATSAPP_MCP_DATA_DIR: /home/me/.whatsapp-mcp

Per-server extras Hermes supports for this server: tools.include / tools.exclude filters (e.g. expose only send/read tools to a sub-agent) and sampling settings.

Tip: the WhatsApp session keeps running inside the hermes mcp-spawned server process, so pairing state persists across Hermes restarts — scan once, ever.


With Claude Code

claude mcp add whatsapp -- node /path/to/whatsapp-mcp/server.mjs

With Cursor

Add to ~/.cursor/mcp.json (or the workspace .cursor/mcp.json):

{
  "mcpServers": {
    "whatsapp": {
      "command": "node",
      "args": ["/path/to/whatsapp-mcp/server.mjs"]
    }
  }
}

Any other MCP client (generic stdio JSON)

{
  "mcpServers": {
    "whatsapp": {
      "command": "node",
      "args": ["/path/to/whatsapp-mcp/server.mjs"]
    }
  }
}

Cline, Continue, Zed, Gemini CLI, OpenCode, Goose… anything that speaks MCP stdio works with this block.


Headless automation (no agent)

The core is a plain ES module — after the first QR pairing you can drive WhatsApp from cron, CI, systemd timers, whatever:

// my-automation.mjs
import * as wa from "/path/to/whatsapp-mcp/core/whatsapp.mjs";

await wa.getSocket({ wait: true });                    // reconnects silently (paired already)
await wa.sendMessage("+15551234567", "Backup finished ✅");

const chats = await wa.listChats({ limit: 10 });
const msgs  = await wa.readMessages(chats.chats[0].id, { limit: 25 });
const hits  = await wa.searchMessages("invoice");

Cron example — daily 9am digest of yesterday's unread chats:

0 9 * * * cd /path/to/whatsapp-mcp && node -e '
  import("./core/whatsapp.mjs").then(async wa => {
    await wa.getSocket({ wait: true });
    const { chats } = await wa.listChats({ limit: 20 });
    const lines = chats.filter(c => c.unread > 0)
                       .map(c => `• ${c.name}: ${c.unread} unread`);
    await wa.sendMessage("me@s.whatsapp.net", "Morning digest:\n" + (lines.join("\n") || "no unread"));
  });' >> /tmp/wa-digest.log 2>&1

Run node examples/send-once.mjs for a working sample script.


How it works

  • Baileys (WhatsApp Web multi-device protocol, pinned to 6.5.0 — the last line with the full chat-store API) opens a linked-device session on first run.

  • Pairing: the QR from connection.update is rendered to the terminal (stderr) and to ~/.whatsapp-mcp/qr.png. Credentials are stored on disk by useMultiFileAuthState, so restarts are silent.

  • On-demand history (no bulk download): nothing is fetched at process start. The first tool that actually needs history (list/read/search/contacts) triggers a one-time sync of the recent history WhatsApp retains for this account (the same window WhatsApp Web loads), which is cached in memory; later reads/searches filter that cache by since/until/days with zero extra network. (Baileys 6.5 exposes no per-date fetch API — the sync window is the protocol's finest granularity — so "fetch only the last 2 days" is enforced by fetching-once + date-filtering + caching.)

  • One connection, many processes: WhatsApp allows a single active connection per linked device. This project elects one owner process (first to run — pi, hermes, or a script) that holds the Baileys socket; every other process joins it as a client over a loopback-only, token-authenticated local bridge (lock file: ~/.whatsapp-mcp/daemon.json). If the owner exits, the next tool call picks the session back up from the saved credentials. Run pi + hermes + cron scripts at the same time without conflicts.

  • Resilience: connection drops auto-restart (2s; conflict closes back off 20–30s with jitter so stray processes can't ping-pong), logout events wipe creds and re-emit a QR (3s), QR is re-rendered whenever the server rotates it.

  • Protocol hygiene: only the MCP JSON-RPC goes to stdout; all logs and the QR go to stderr, so MCP clients stay clean.

Configuration

Env var

Default

Meaning

WHATSAPP_MCP_DATA_DIR

~/.whatsapp-mcp

Session credentials + qr.png location


Troubleshooting

Symptom

Fix

No QR appears

Check stderr (MCP clients often hide it — run node server.mjs directly). QR only shows when no saved session exists; whatsapp_unlink forces one

Device is not linked yet error from tools

Scan the QR first (whatsapp_link returns it)

Keeps reconnecting

Unstable network, or the phone itself unlinked the device (battery-saver modes can do this)

Logged out / QR after restart

Someone unlinked the device from the phone; just re-scan

Empty reads right after a restart

The in-memory cache starts empty; the first read triggers the on-demand sync (a few seconds for the initial snapshot). Retry once, or ask for a specific window (since/until)

connection closed (conflict) in logs

A process from before an upgrade still holds the same session. Normal operation handles ownership automatically — restart your pi/hermes sessions once so they run the new code

Update broke things

Baileys is pinned for a reason — don't npm update it casually; WhatsApp protocol changes happen fast

Security & fair use

  • ~/.whatsapp-mcp/ contains your full WhatsApp session key — treat it like a password. Don't commit it, don't share it, keep file perms tight (chmod 700).

  • This uses the unofficial WhatsApp Web protocol. It's fine for personal use and moderate automation; aggressive bulk messaging can get your number banned. Be a good citizen: keep volumes human.

  • Agents can send messages as you. If you expose tools to a sub-agent, consider Hermes-style tools.exclude filters to hide whatsapp_send_* from agents that shouldn't write.

Project layout

whatsapp-mcp/
├── core/whatsapp.mjs        # Baileys session + actions (the core)
├── core/daemon.mjs          # single-owner election + loopback client bridge
├── core/api.mjs             # facade: routes each call local or to the owner
├── server.mjs               # MCP stdio server
├── pi-extension/index.ts    # native pi extension (same 13 tools)
├── examples/send-once.mjs   # headless automation sample
├── pair.mjs                 # node pair.mjs — one-shot QR pairing helper
├── test-client.mjs          # npm test — MCP smoke test
└── README.md

License

MIT — do whatever, no warranty. Not affiliated with WhatsApp/Meta.

Related MCP Connectors

  • Drive WhatsApp from any MCP client: pair devices, send text and media, manage contacts and groups.

  • WhatsApp (Web + Business API), SMS, contacts, and call records via 2Chat's MCP server.

  • WhatsMCP connects Claude and other MCP-compatible AI agents directly to WhatsApp. Send and receive text, images, documents, and voice notes; manage groups (create, add/remove members, promote admins); look up contacts and profiles; follow channels; and read call and message history — all through a standard MCP interface. For voice use cases, WhatsMCP offers SIP-based calling plans (inbound-only, or full inbound/outbound) so AI voice agents can answer and place WhatsApp calls, plus low-latency WebSocket integrations with voice agent providers like ElevenLabs. Multiple WhatsApp accounts can be paired and managed per workspace, with webhook support for real-time inbound message delivery to your own infrastructure.

  • Let Claude or ChatGPT search, read and send your WhatsApp messages over MCP. OAuth sign-in.

Related MCP Servers