whatsapp-mcp-server

mcp
Security Audit
Fail
Health Warn
  • No license — Repository has no license file
  • Description — Repository has a description
  • Active repo — Last push 0 days ago
  • Low visibility — Only 5 GitHub stars
Code Fail
  • process.env — Environment variable access in bin/cli.mjs
  • process.env — Environment variable access in scripts/test-tools.mjs
  • exec() — Shell command execution in src/db/index.ts
Permissions Pass
  • Permissions — No dangerous permissions requested

No AI report is available for this listing yet.

SUMMARY

Local MCP server that exposes WhatsApp as 4 tools for Claude — send, read, and search messages via Baileys + SQLite. Single-user, no cloud, QR-based auth.

README.md

whatsapp-mcp-server

A local Model Context Protocol (MCP) server that exposes your personal
WhatsApp account as four tools for Claude Desktop or Claude Code.
Backed by Baileys for the
WhatsApp Web protocol and better-sqlite3
for the local message cache.

Single-user, local, no cloud dependencies. Once you scan the QR code
once, your session is saved and you don't have to scan again.

Features

Four MCP tools:

Tool What it does
list_chats Recent WhatsApp conversations, newest first. Optional JID-prefix filter (e.g. 120363 for groups).
read_messages Last N messages from a chat. Pagination via beforeTimestamp. Optional sender filter.
search_messages FTS5 full-text search over message history with relevance ranking. Supports pizza, pizza OR burger, piz*, "exact phrase".
send_message Send a text message via WhatsApp. The sent message is also persisted locally so subsequent reads/searches return it.

Architecture

┌─────────────┐
│ Claude Code │  JSON-RPC over stdio
└──────┬──────┘
       │
┌──────▼──────────────┐
│  src/index.ts       │  MCP server (4 tools registered)
│  src/db/queries.ts  │  Read side: listChats, readMessages, searchMessages
│  src/wa/send.ts     │  Write side: sendMessage
└──────┬──────────────┘
       │
┌──────▼─────────────────┐
│  src/wa/persist.ts     │  persistMessage, persistChat, attachPersistence
│  src/wa/socket.ts      │  connectToWhatsApp (Baileys socket + QR)
│  src/wa/auth.ts        │  loadAuthState
└──────┬─────────────────┘
       │
┌──────▼──────────┐
│  data/history.db│  SQLite (chats + messages + FTS5)
│  auth/          │  Baileys session files (gitignored)
└─────────────────┘

Prerequisites

  • Node.js 18+ (this project uses ESM and modern Node APIs)
  • A WhatsApp account on your phone (you'll scan a QR code once)
  • A C/C++ toolchain for better-sqlite3 native build:
    • macOS: Xcode Command Line Tools (xcode-select --install)
    • Linux: build-essential + python3
    • Windows: Visual Studio Build Tools with the "Desktop development with C++" workload

Install

The package lives on NPM at
@muhammadmustafa18/whatsapp-mcp-server.

NPM package page

⚠️ Don't confuse with whatsapp-mcp-server (no scope) — that's a
different package by BitNex Technologies that talks to the WhatsApp
Business Cloud API instead of WhatsApp Web. Always use the scoped
name with @muhammadmustafa18/.

Option A: Global npm install (recommended)

npm install -g @muhammadmustafa18/whatsapp-mcp-server
whatsapp-mcp

Install command on NPM page

NPM install completed

Option B: From source (development)

git clone https://github.com/MuhammadMustafa18/whatsapp-mcp-server.git
cd whatsapp-mcp-server
npm install
npm run build
npm start

The whatsapp-mcp wrapper lives in bin/cli.mjs. It resolves a
per-user storage location (~/.whatsapp-mcp/ by default, overridable
via WHATSAPP_MCP_HOME), then spawns the bundled server. This way
your auth + data follow you, not the install location.

First run

The first time you start the server, WhatsApp will need to authenticate
you. The server prints a QR code to its stderr (not stdout — stdout
is reserved for JSON-RPC).

npm start          # Option A
# or
whatsapp-mcp       # Option B (if globally installed)

You'll see something like:

[wa-mcp] starting...
[wa-mcp] db ready at .../data/history.db
[wa-mcp] QR code (scan with WhatsApp):
[QR code here]

QR code in terminal

Open WhatsApp on your phone → Settings → Linked Devices → Link a Device,
then scan the QR code. The session is saved and reused on subsequent
runs — no need to scan again unless you log out.

When installed globally, the session is stored at ~/.whatsapp-mcp/auth/.
When running from a source clone, it's stored at <project>/auth/.
Override either with the WHATSAPP_MCP_HOME env var.

Wire it up to Claude

Claude Code

# Source clone
claude mcp add whatsapp-mcp -- node /path/to/whatsapp-mcp-server/bin/cli.mjs

# Global install
claude mcp add whatsapp-mcp -- whatsapp-mcp

Slash MCP command in Claude

Claude command in terminal

Claude Desktop

Add this to your Claude Desktop config file:

macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows: %APPDATA%\Claude\claude_desktop_config.json

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

Restart Claude Desktop. You should see "whatsapp" listed in the tools
panel (bottom-left hammer icon).

Usage examples

Once connected, Claude can use the tools naturally. Some examples:

  • "List my recent WhatsApp chats"
  • "Show me the last 20 messages with Ali"
  • "Search my messages for 'pizza'"
  • "Send Ali a message: lunch at 8?"

Send example

The tools' full JSON-Schema input definitions are visible to Claude
automatically. See src/index.ts for the schema.

How messages are stored

  • Incoming messages (messages.upsert event) are persisted into
    messages and the parent chats row is updated with last_message_at
    and a short last_message_preview.
  • Chat metadata changes (chats.update — name, mute, archive) update
    the chats row.
  • Initial sync (chats.upsert) seeds the chats table.
  • Outgoing messages are also persisted, so read_messages returns
    both sent and received.
  • FTS5 over messages.text is kept in sync via SQLite triggers.

Troubleshooting

Cannot find module '...dist/db/schema.sql'

You forgot to copy the SQL asset. Run npm run build (not just tsc).

QR code doesn't show

Make sure you're looking at the stderr output, not stdout. stdout
is the JSON-RPC stream. If you redirected both to the same place, the
QR code text would corrupt the protocol.

send_message returns "Cannot read properties of undefined"

The socket isn't actually connected to WhatsApp. This happens if:

  • The saved session in auth/ expired (log out + log in again)
  • The phone lost internet connectivity mid-session
  • WhatsApp Web is rate-limiting

Restart the server and rescan the QR if needed.

First-run errors about "no such file or directory" for auth/

The auth/ directory is created automatically on first run. If you're
running the built version (dist/index.js), make sure you're running
it from the project root, not from inside dist/.

Uninstall

The package, your auth session, and your message history are all
separate. Pick what you want to remove:

# Remove the global `whatsapp-mcp` command
npm uninstall -g @muhammadmustafa18/whatsapp-mcp-server

# Remove the MCP entry from Claude Code
claude mcp remove whatsapp-mcp

# Wipe your saved WhatsApp session (forces QR re-scan on next run)
rm -rf ~/.whatsapp-mcp/auth

# Wipe your local message history
rm -rf ~/.whatsapp-mcp/data

# Full reset (everything above + the home folder itself)
rm -rf ~/.whatsapp-mcp

On Windows PowerShell use Remove-Item -Recurse -Force instead of rm -rf,
and $env:USERPROFILE\.whatsapp-mcp instead of ~/.whatsapp-mcp.

If you installed via source clone (with npm link) instead of the
global npm install, also drop the local link:
npm unlink -g @muhammadmustafa18/whatsapp-mcp-server.

Development

The project was built one function at a time, with one commit per
function. The git history reads as a tutorial:

git log --oneline

Project structure

src/
├── index.ts              # MCP server entrypoint + 4 tool registrations
├── db/
│   ├── index.ts          # initDb
│   ├── queries.ts        # listChats, readMessages, searchMessages
│   └── schema.sql        # SQLite schema (chats, messages, FTS5 + triggers)
└── wa/
    ├── auth.ts           # loadAuthState
    ├── socket.ts         # connectToWhatsApp
    ├── persist.ts        # persistMessage, persistChat, attachPersistence
    └── send.ts           # sendMessage

Build artifacts

dist/                     # Compiled JS + copied schema.sql
data/history.db           # SQLite (gitignored)
auth/                     # Baileys session (gitignored)

Design notes

  • Statement caching: every db.prepare is cached on the db handle
    via a Symbol.for key, scoped to the db's lifetime. See
    getOrPrepare in persist.ts.
  • FK race in attachPersistence: Baileys sometimes delivers
    messages.upsert before chats.upsert for a brand-new chat. We
    wrap the batch in db.transaction(...) and persistChat first, so
    the FK on messages.chat_jid is satisfied by the time commit
    happens.
  • Dynamic UPSERT in persistChat: built per-call so the SET
    clause only touches the fields the caller actually has data for.
    Avoids the NOT-NULL-sentinel trap that bit us on the first attempt.
  • FTS5 contentless + triggers: the messages_fts virtual table
    is kept in sync via INSERT/UPDATE/DELETE triggers on messages,
    so we never have to remember to update the search index manually.

License

ISC

Reviews (0)

No results found