Skip to main content
Glama
Malaccamaxgit

WhatsApp MCP Server

WhatsApp MCP Server

WhatsApp integration for AI agents — Send messages, search chats, share media, approval workflows, and intelligent activity summaries via any MCP client. Designed to run as a containerized MCP server through Docker MCP Toolkit.

Acknowledgment: The author is well aware of kapso.ai, which provides a more robust and feature-complete WhatsApp integration solution. This project is what happens when a hobbyist coder with too much curiosity, decent system design instincts, and modern AI assistance decides to see how far they can push their weekend project. It's a learning exercise in MCP servers, containerization, and WhatsApp protocol integration — the kind of thing that would have been unreasonable to attempt alone a few years ago, but is now very much within reach thanks to better tooling and a lot of help from AI.

License: Apache 2.0 Runtime: Node.js Language: TypeScript Protocol: MCP Platform: Docker

Note: This project requires Docker Desktop with MCP Toolkit enabled. Alternatively, you can run it with Docker Engine + Docker Compose (without the MCP Gateway features).


Why Docker MCP Toolkit?

This server runs inside a Docker container managed by Docker MCP Toolkit, rather than directly on your host machine.

Benefit

Docker MCP Toolkit

Running on Host

Isolation

WhatsApp session keys, messages, and media are confined to Docker volumes — not mixed with your regular files

Session data ends up somewhere in your home directory

Security boundary

Non-root, read-only filesystem, capabilities dropped

Runs with your full user permissions

Multi-client gateway

One server instance serves all your MCP clients (Cursor, Claude Code, VS Code) simultaneously through the MCP Gateway

Each client needs its own server configuration

Secrets management

Encryption keys stored in OS Keychain via docker mcp secret set, configurable from Docker Desktop UI

Must manage .env files manually

Host dependencies

No Node.js, native compilation, or Go binary management required on your machine

Must install Node.js 18+, build tools for native addons, and manage platform-specific binaries

Portability

Works on Windows, macOS, and Linux (in theory — tested primarily on Windows)

Platform-specific issues with native dependencies

Lifecycle management

Health checks, auto-restart, graceful shutdown — all handled by Docker

Manual process management

Clean teardown

docker compose down -v removes everything

Must manually find and clean up data files, sessions, and processes


Related MCP server: MCP WPPConnect Server

Features

  • 34 MCP Tools — Full WhatsApp control: messaging, media, search, contacts, groups, message actions, approvals, status, live interaction, session management, and layered tool documentation

  • Fuzzy Name Matching — Say "John" or "book club" and the server finds the right chat via Levenshtein distance

  • Media Support — Download received media and send images, videos, audio, and documents

  • Full-Text Search — SQLite FTS5 indexes all messages with keyword, phrase, and boolean operators

  • Intelligent Catch-Up — One tool that summarizes active chats, pending questions, and unread highlights

  • Approval Workflows — Send approval requests via WhatsApp; recipients reply APPROVE or DENY

  • Pairing Code Auth — Text-based 8-digit code authentication with QR code image fallback (rendered in-container, viewable in any browser via data URI)

  • Encryption at Rest — AES-256-GCM field-level encryption for message bodies, sender names, and media metadata

  • Auto-Purge — Automatic deletion of messages and media older than a configurable retention period

  • Read Receipts & Presence — Delivery receipts (double checkmarks), read receipts (blue checkmarks), and online presence — all configurable

  • Session Resilience — Automatic reconnection on transient disconnects, startup retry with exponential backoff, 60-second health heartbeat, and MCP notifications on session expiry

  • Session Persistence — WhatsApp session survives container restarts via Docker named volumes

  • Long-Lived Containers — The server keeps the WhatsApp WebSocket connection alive across tool calls

  • Welcome Group — Optionally creates a WhatsApp group and sends a hello message on first connection


Quick Start

🚀 Minimal Viable Setup (5 Minutes)

Want to try it fast? No clone or build needed — the image is pulled automatically from Docker Hub.

PowerShell Users: Replace \ (backslash) with ` (backtick) for line continuation in the commands below.

Example:

docker mcp catalog create my-mcp --title "My MCP Servers" `
  --server file://./whatsapp-mcp-docker-server.yaml
# 0. Find your profile name (Docker Desktop creates "default" on install)
docker mcp profile list

# 1. Download the server definition file (no full clone needed)
curl -O https://raw.githubusercontent.com/Malaccamaxgit/whatsapp-mcp-docker/main/whatsapp-mcp-docker-server.yaml

# 2. Create catalog (one-time setup)
docker mcp catalog create my-mcp --title "My MCP Servers" \
  --server file://./whatsapp-mcp-docker-server.yaml

# 3. Add to your profile (replace "default" with your profile name from step 0)
docker mcp profile server add default \
  --server file://./whatsapp-mcp-docker-server.yaml

# 4. Connect your MCP client (replace "cursor" with your client — see table below)
docker mcp client connect cursor --profile default

# 5. Restart / reload your MCP client so it picks up the new tools

# 6. In your client, say: "Authenticate WhatsApp with +1234567890"

Supported clients for step 4: cursor, claude-code, claude-desktop, vscode, gemini, goose, and more. Run docker mcp client connect --help to see the full list.

That's it! You're now connected to WhatsApp. The image (malaccamax/whatsapp-mcp-docker:latest) is pulled automatically from Docker Hub on first use — no build step required.

Note: After connecting, you must restart or reload your MCP client before the WhatsApp tools appear (see Step 5 in the Full Setup below for client-specific instructions). Running docker mcp tools ls in the terminal shows only 8 MCP Toolkit meta-tools — the 34 WhatsApp tools appear inside your client after the gateway starts the container on first use.

Important: In some MCP sessions, you may need to explicitly activate the profile before tools are available. If you get Error: Tool 'get_connection_status' not found in current session, run:

docker mcp profile activate <your-profile>

Or inside an MCP client session, use the mcp-activate-profile meta-tool. This is a known gap in the Docker MCP Gateway auto-loading behavior.

Next steps: Set up encryption and recommended configuration (below) for secure use.


0. Find Your Profile Name

Docker Desktop creates a profile named default on install. Check yours before proceeding:

docker mcp profile list

Use the name shown there wherever <your-profile> appears in the steps below.

Need a dedicated profile? In Docker Desktop go to MCP Toolkit → Profiles and click + to create one, then use that name.

1. Get the Project

Option A — Just the server YAML (recommended for most users):

The image is pulled automatically from Docker Hub. You only need the server definition file:

curl -O https://raw.githubusercontent.com/Malaccamaxgit/whatsapp-mcp-docker/main/whatsapp-mcp-docker-server.yaml

Option B — Full clone + local build (for developers or self-hosters):

Clone and build locally if you want to modify the source or run without Docker Hub:

git clone https://github.com/Malaccamaxgit/whatsapp-mcp-docker.git
cd whatsapp-mcp-docker
docker compose build --no-cache

Why --no-cache? BuildKit's layer caching can incorrectly cache TypeScript compilation even when source files change. Always use --no-cache to ensure the latest compiled code is used.

Stay in the directory containing whatsapp-mcp-docker-server.yaml for all subsequent commands — the file://./ path in catalog and profile commands is relative to your working directory.

2. Set the Encryption Key

Set the encryption key (stored in OS Keychain — no .env files needed):

# Option A: Using Node.js (if installed on host) — bash/zsh only
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))" | docker mcp secret set whatsapp-mcp-docker.data_encryption_key

# Option B: Using Docker (no Node.js required on host) — bash/zsh only
docker run --rm node:22-alpine node -e "console.log(require('crypto').randomBytes(32).toString('base64'))" | docker mcp secret set whatsapp-mcp-docker.data_encryption_key

Windows PowerShell users: The pipe (|) and redirect (<) operators do not work with docker mcp secret set on PowerShell. Use this two-step approach instead:

# Option A: Using Python (recommended — avoids require() escaping issues)
$key = docker run --rm python:3-alpine python3 -c "import base64,os; print(base64.b64encode(os.urandom(32)).decode())"
docker mcp secret set "whatsapp-mcp-docker.data_encryption_key=$key"

# Option B: Using Node.js — requires careful quoting; base64 chars + / = may cause issues
$key = docker run --rm node:22-alpine node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"
docker mcp secret set "whatsapp-mcp-docker.data_encryption_key=$key"

If you see a "logon session does not exist" error but the key still appears in docker mcp secret ls, the key was stored successfully in the docker-pass backend — the error is a misleading warning from a secondary Windows Credential Manager backend and can be safely ignored.

Verify the key was stored (docker mcp secret set produces no confirmation on success):

# bash/zsh
docker mcp secret ls | grep whatsapp
# PowerShell
docker mcp secret ls | findstr whatsapp

You should see a line containing whatsapp-mcp-docker.data_enc….

Tip: Keep the generated key safe! If you lose it, encrypted messages cannot be recovered. Back it up to a password manager.

Security posture: Running without DATA_ENCRYPTION_KEY is an insecure mode. Only do this with an explicit operator override and documented risk acceptance.

3. Create a Custom Catalog

Register the server in a custom catalog so it appears in Docker Desktop's Catalog tab alongside the official Docker MCP Catalog:

PowerShell Users: On Windows PowerShell, use backtick (`) instead of backslash (\) for line continuation.

docker mcp catalog create my-custom-mcp-servers `
  --title "My Custom MCP Servers" `
  --server file://./whatsapp-mcp-docker-server.yaml
docker mcp catalog create my-custom-mcp-servers \
  --title "My Custom MCP Servers" \
  --server file://./whatsapp-mcp-docker-server.yaml

In Docker Desktop, go to MCP Toolkit → Catalog — the WhatsApp MCP server now appears under your custom catalog with all 34 tools, configuration options, and secrets.

Tip: To update the catalog after code changes, re-run the same command — it replaces the existing entry. To add more servers later, use multiple --server flags.

4. Add to a Profile and Apply Configuration

Add the server to a profile so MCP clients can use it.

Option A — From the Catalog UI:

  1. In MCP Toolkit → Catalog, find WhatsApp MCP under your custom catalog.

  2. Select the checkbox on the server card.

  3. Choose a profile from the drop-down and confirm.

Option B — CLI:

docker mcp profile server add <your-profile> \
  --server file://./whatsapp-mcp-docker-server.yaml

Option C — Docker Desktop Profiles tab:

  1. Open Docker Desktop and go to MCP Toolkit → Profiles.

  2. Select an existing profile (or create a new one).

  3. In the Servers section, click + and add the server.

All options register the server with longLived: true (persistent container), secrets (encryption key from OS Keychain), and all 34 tools.

After adding, apply the recommended configuration:

PowerShell Users: Use backtick (`) instead of backslash (\) for line continuation.

docker mcp profile config <your-profile> `
  --set whatsapp-mcp-docker.rate_limit_per_min=60 `
  --set whatsapp-mcp-docker.message_retention_days=90 `
  --set whatsapp-mcp-docker.send_read_receipts=true `
  --set whatsapp-mcp-docker.auto_read_receipts=true `
  --set whatsapp-mcp-docker.presence_mode=available `
  --set whatsapp-mcp-docker.welcome_group_name=WhatsAppMCP `
  --set whatsapp-mcp-docker.auth_wait_for_link=false `
  --set whatsapp-mcp-docker.auth_link_timeout_sec=120 `
  --set whatsapp-mcp-docker.auth_poll_interval_sec=5
docker mcp profile config <your-profile> \
  --set whatsapp-mcp-docker.rate_limit_per_min=60 \
  --set whatsapp-mcp-docker.message_retention_days=90 \
  --set whatsapp-mcp-docker.send_read_receipts=true \
  --set whatsapp-mcp-docker.auto_read_receipts=true \
  --set whatsapp-mcp-docker.presence_mode=available \
  --set whatsapp-mcp-docker.welcome_group_name=WhatsAppMCP \
  --set whatsapp-mcp-docker.auth_wait_for_link=false \
  --set whatsapp-mcp-docker.auth_link_timeout_sec=120 \
  --set whatsapp-mcp-docker.auth_poll_interval_sec=5

Or configure via Docker Desktop: MCP Toolkit → WhatsApp MCP → Configuration / Secrets.

This populates the configuration fields in Docker Desktop so you can see and adjust them from the UI. Without this step, the server still works (defaults are applied at runtime), but the UI fields appear blank.

The auth_* keys set defaults for the authenticate tool when you omit waitForLink, linkTimeoutSec, or pollIntervalSec: whether to wait for the device to link after showing the pairing code or QR, the maximum wait time (seconds), and how often to poll (seconds). auth_wait_for_link=false is the default and recommended setting for most clients — it returns the pairing code immediately without blocking, avoiding tool-call timeouts. Set auth_wait_for_link=true only in environments that have no tool-call timeout and where you want the tool to automatically confirm once the device links.

5. Connect Your MCP Client

Connect your AI client to the profile with one command, substituting your client name:

docker mcp client connect <your-client> --profile <your-profile>

Supported client names: cursor, claude-code, claude-desktop, vscode, gemini, goose, and others. Run docker mcp client connect --help to see the full list.

This automatically writes the MCP Gateway entry to your client's config file. After connecting, restart or reload your client so it picks up the new server:

Client

How to reload

Cursor

Ctrl+Shift+PReload Window

Claude Desktop

Quit and reopen the app

Claude Code

Run claude mcp restart or restart the session

VS Code

Reload the window or restart the MCP extension

Goose / Gemini CLI

Restart the session

Why doesn't docker mcp tools ls show my 34 tools? That command shows only the 8 MCP Toolkit meta-tools (e.g. mcp-add, mcp-find). The 34 WhatsApp tools appear inside your MCP client after the gateway starts the whatsapp-mcp-docker container on the first tool call. They are not visible from the terminal.

To connect manually instead, add the MCP Gateway entry directly to your client's config file. Ready-made snippets for every supported client are in examples/client-configs.md. Each client stores its config in a different location — consult your client's documentation for the exact path. The entry format is:

{
  "mcpServers": {
    "MCP_DOCKER": {
      "command": "docker",
      "args": ["mcp", "gateway", "run", "--profile", "<your-profile>"]
    }
  }
}

Windows users: The docker mcp client connect command automatically injects required Windows environment variables (LOCALAPPDATA, ProgramData, ProgramFiles). If you edit the config file manually, you must add them yourself or the gateway cannot locate credentials:

{
  "mcpServers": {
    "MCP_DOCKER": {
      "command": "docker",
      "args": ["mcp", "gateway", "run", "--profile", "<your-profile>"],
      "env": {
        "LOCALAPPDATA": "C:\\Users\\<you>\\AppData\\Local",
        "ProgramData": "C:\\ProgramData",
        "ProgramFiles": "C:\\Program Files"
      }
    }
  }
}

6. Authenticate

From your MCP client:

Authenticate WhatsApp with my number +1234567890

The server returns an 8-digit pairing code. Enter it in: WhatsApp → Settings → Linked Devices → Link a Device → Link with phone number instead

If pairing code fails (rate-limited or 400 error), the server falls back to QR code authentication:

  • MCP image block — displayed inline by clients that support image rendering (e.g., Cursor, Claude Desktop)

  • Data URI — a data:image/png;base64,... string included in the text response; paste it into any browser's address bar to view the QR code — no host tools needed

The session persists across container restarts in the whatsapp-sessions Docker volume.

Compatible Clients: Claude Code, Claude Desktop, Cursor, VS Code, Gemini CLI, Goose, Cline, Gordon, Codex, and any client supporting the Model Context Protocol. Run docker mcp client connect --help to see the full list.


Available Tools (34)

Authentication & Status

Tool

Description

disconnect

Log out and disconnect from WhatsApp, clearing the session

authenticate

Link device via 8-digit pairing code (QR image fallback with data URI)

get_connection_status

Connection state + database statistics

Messaging

Tool

Description

send_message

Send text with fuzzy contact/group name matching

send_file

Send image, video, audio, or document with optional caption

list_messages

Get messages from a chat with date range filtering and pagination

search_messages

Full-text search across all messages (SQLite FTS5)

Contacts & Chats

Tool

Description

list_chats

List conversations sorted by recent activity

search_contacts

Find contacts/groups by name or phone number

export_chat_data

Export complete chat history for a contact or group (JSON or CSV)

Media

Tool

Description

download_media

Download media from a received message to persistent storage

Intelligence

Tool

Description

catch_up

Activity summary: active chats, questions, unread highlights

mark_messages_read

Mark messages as read in a chat

Approval Workflows

Tool

Description

request_approval

Send approval request — recipient replies APPROVE/DENY

check_approvals

Check specific approval or list all pending

Group Management

Tool

Description

create_group

Create a new group with a name and list of participants

get_group_info

Get participants, admins, description, and settings for a group

get_joined_groups

List all groups this account belongs to

get_group_invite_link

Get the shareable invite link for a group (admin only)

join_group

Join a group via invite link or code

leave_group

Leave a group permanently

update_group_participants

Add, remove, promote, or demote participants (admin only)

set_group_name

Rename a group (admin only)

set_group_topic

Set or clear the group description (admin only)

Message Actions

Tool

Description

send_reaction

React to a message with an emoji, or remove an existing reaction

edit_message

Edit a previously sent message (own messages, within ~15 min)

delete_message

Delete a sent message for everyone (revoke)

Contacts & User Info

Tool

Description

get_user_info

Get profile information for one or more phone numbers (optional save_names to store names locally)

is_on_whatsapp

Check whether phone numbers have WhatsApp accounts

get_profile_picture

Get the profile picture URL for a contact or group

set_contact_name

Set a local custom display name for a JID or phone number (shown in lists and search)

sync_contact_names

Fetch WhatsApp profile names and store them for chats that still show as raw JIDs

Interactive Workflows

Tool

Description

wait_for_message

Block until an incoming message arrives — use during interactive tests so the AI detects phone messages automatically without user prompting

Tool Documentation

Tool

Description

get_tool_info

Get structured docs for any tool (usage, examples, response format, errors, related tools, pitfalls)


Example Usage

# Authenticate
Authenticate WhatsApp with +1234567890

# Send a message (fuzzy name matching)
Send "I'll be 10 minutes late" to John

# Send a photo
Send the file /data/sessions/media/image/photo.jpg to the Engineering group as an image

# Search across all chats
Search my WhatsApp messages for "project deadline"

# Get a summary
Catch me up on today's WhatsApp activity

# View a conversation
Show me the last 20 messages with the Engineering group

# Find a contact
Search for contacts named "Sarah"

# Download media from a message
Download media from message ID abc123

# Approval workflow
Send an approval request to Sarah: "Deploy v2.1 to production?"
Check approval status for approval_1234567890_abc

Project Structure

whatsapp-mcp-docker/
├── src/
│   ├── index.ts              # Entry point — stdio transport + lifecycle
│   ├── server.ts             # Server factory — wires tools, store, security
│   ├── whatsapp/
│   │   ├── client.ts         # whatsmeow-node wrapper + media operations
│   │   └── store.ts          # SQLite persistence + FTS5 search + media metadata
│   ├── tools/
│   │   ├── auth.ts           # disconnect, authenticate
│   │   ├── status.ts         # get_connection_status
│   │   ├── messaging.ts      # send_message, list_messages, search_messages
│   │   ├── chats.ts          # list_chats, search_contacts, catch_up, mark_messages_read, export_chat_data
│   │   ├── media.ts          # download_media, send_file
│   │   ├── approvals.ts      # request_approval, check_approvals
│   │   ├── groups.ts         # create_group, get_group_info, get_joined_groups, get_group_invite_link, join_group, leave_group, update_group_participants, set_group_name, set_group_topic
│   │   ├── reactions.ts      # send_reaction, edit_message, delete_message
│   │   ├── contacts.ts       # get_user_info, is_on_whatsapp, get_profile_picture, set_contact_name, sync_contact_names
│   │   ├── wait.ts           # wait_for_message
│   │   └── tool-info.ts      # get_tool_info + layered documentation helpers
│   ├── security/
│   │   ├── audit.ts          # SQLite audit logging
│   │   ├── crypto.ts         # AES-256-GCM field-level encryption
│   │   ├── file-guard.ts     # Path confinement, extension/magic checks, quota
│   │   └── permissions.ts    # Whitelist, rate limiting, tool disabling, auth throttle
│   └── utils/
│       ├── fuzzy-match.ts    # Levenshtein + substring matching
│       └── phone.ts          # E.164 validation + JID conversion
├── docs/
│   ├── README.md             # Documentation index
│   ├── API.md                # Full MCP tool API reference (all 34 tools)
│   ├── TROUBLESHOOTING.md    # Symptom → cause → fix guide
│   ├── architecture/
│   │   └── OVERVIEW.md       # Architecture overview
│   ├── guides/
│   │   ├── DEVELOPER.md      # Build, test, deploy procedures
│   │   └── ERRORS.md         # Error taxonomy and recovery
│   ├── testing/
│   │   └── TESTING.md        # Test strategy, structure, and commands
│   └── bugs/                 # Bug reports and fixes
├── scripts/
│   ├── diagnostics.js        # Health check and diagnostic script
│   ├── cleanup.ps1           # Full teardown script (Windows — git-ignored)
│   ├── cleanup.sh            # Full teardown script (Linux/macOS — git-ignored)
│   ├── test.ps1              # Test runner (Windows)
│   └── test.sh               # Test runner (Linux/macOS)
├── examples/
│   └── client-configs.md     # Manual MCP client config snippets (Cursor, Claude, VS Code, Gemini, Goose, Cline)
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   └── workflows/
│       └── security-audit.yml
├── test/
│   ├── unit/                 # Unit tests (node:test)
│   ├── integration/          # MCP protocol tests (mock WhatsApp client)
│   └── e2e/                  # Live session tests (persistent auth)
├── Dockerfile                # 4-stage (prod-deps → builder → test → runtime), ~80 MB runtime
├── docker-compose.yml        # Main + tester-container (Compose profiles)
├── whatsapp-mcp-docker-server.yaml  # Docker MCP Toolkit server definition
├── recommended-config.yaml   # Reference config values for docker mcp profile config
├── .env.example              # Environment variable template (docker-compose fallback)
├── package.json
├── package-lock.json
├── CHANGELOG.md
├── CONTRIBUTING.md
├── CODE_OF_CONDUCT.md
├── SECURITY.md
└── PRIVACY.md

Security

  • Non-root user (UID 1001, all capabilities dropped)

  • Read-only root filesystem (only /data volumes and /tmp tmpfs writable)

  • TLS transport — direct WhatsApp protocol (whatsmeow-node), no browser automation or TLS bypasses

  • Encryption at rest — AES-256-GCM for sensitive database fields (DATA_ENCRYPTION_KEY)

  • Secrets in OS Keychain — encryption key stored via docker mcp secret set, never in config files

  • Auto-purge — messages and media auto-deleted after retention period (MESSAGE_RETENTION_DAYS)

  • File security — upload path confinement, extension blocklist, magic bytes verification, 512 MB media quota

  • Contact whitelist — restrict who can receive messages (ALLOWED_CONTACTS)

  • Rate limiting — outbound messages, media downloads, and authentication attempts

  • Input validation — Zod schemas with max-length limits on all tool parameters

  • Audit trail — all tool invocations logged to SQLite with timestamps

See SECURITY.md for the full security policy and vulnerability reporting.


Configuration

Configuration is managed through Docker MCP Toolkit (preferred) or docker-compose.yml environment variables.

Docker MCP Toolkit (Preferred)

Settings and secrets are managed via Docker Desktop UI or CLI:

Recommended initial setup (run all commands when first deploying):

# 1. Set encryption key (stored in OS Keychain)
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))" | \
  docker mcp secret set whatsapp-mcp-docker.data_encryption_key

# 2. Apply complete recommended configuration to your profile
# Replace <your-profile> with your profile name (e.g., default-with-portainer)
docker mcp profile config <your-profile> \
  --set whatsapp-mcp-docker.rate_limit_per_min=60 \
  --set whatsapp-mcp-docker.message_retention_days=90 \
  --set whatsapp-mcp-docker.send_read_receipts=true \
  --set whatsapp-mcp-docker.auto_read_receipts=true \
  --set whatsapp-mcp-docker.presence_mode=available \
  --set whatsapp-mcp-docker.welcome_group_name=WhatsAppMCP \
  --set whatsapp-mcp-docker.auth_wait_for_link=false \
  --set whatsapp-mcp-docker.auth_link_timeout_sec=120 \
  --set whatsapp-mcp-docker.auth_poll_interval_sec=5

Or configure via Docker Desktop: MCP Toolkit → WhatsApp MCP → Configuration / Secrets.

Environment Variables

Variable

Description

Default

TZ

Container timezone — affects log timestamps and time-based features (e.g. catch_up "today" window). Use IANA names: America/Toronto, Europe/Paris, Asia/Tokyo. Full list at Wikipedia.

UTC

STORE_PATH

Session + message database directory

/data/sessions

AUDIT_DB_PATH

Audit log database path

/data/audit/audit.db

RATE_LIMIT_PER_MIN

Max outbound messages per minute

60

DOWNLOAD_RATE_LIMIT_PER_MIN

Max media downloads per minute

30

DATA_ENCRYPTION_KEY

Passphrase for AES-256-GCM field encryption

(set via docker mcp secret set)

MESSAGE_RETENTION_DAYS

Auto-delete messages/media older than N days (0 = keep forever)

90

ALLOWED_CONTACTS

Comma-separated phone whitelist (empty = allow all)

""

DISABLED_TOOLS

Comma-separated tool names to disable

""

SEND_READ_RECEIPTS

Send read receipts to WhatsApp when mark_messages_read is called

true

AUTO_READ_RECEIPTS

Auto-read incoming messages (senders see blue checkmarks immediately)

true

PRESENCE_MODE

Online presence: available or unavailable

available

WELCOME_GROUP_NAME

WhatsApp group created on first connection (empty = disable)

WhatsAppMCP

AUTH_WAIT_FOR_LINK

Default: after authenticate shows code/QR, wait and poll until linked (false = return immediately; recommended for most clients to avoid tool-call timeouts)

false

AUTH_LINK_TIMEOUT_SEC

Default max seconds to wait for link when waiting (15–600)

120

AUTH_POLL_INTERVAL_SEC

Default seconds between connection checks when waiting (2–60)

5

Variable Details

DATA_ENCRYPTION_KEY — Encrypts sensitive fields (message bodies, sender names, media metadata, approval details) using AES-256-GCM. Store it via docker mcp secret set whatsapp-mcp-docker.data_encryption_key (OS Keychain) or in .env for docker-compose. Generate a strong key: node -e "console.log(require('crypto').randomBytes(32).toString('base64'))". If you lose the key, encrypted data becomes unrecoverable. Running without this key is an insecure mode and should only be done through an explicit operator override with documented risk acceptance.

MESSAGE_RETENTION_DAYS — Runs on startup and then hourly. Deletes messages, associated media files, and expired approvals older than the configured number of days. Set to 0 to disable.

RATE_LIMIT_PER_MIN — Applies to outbound messages (send_message, send_file). Default is 60 (1/sec sustained), comfortable for AI assistant conversations.

DOWNLOAD_RATE_LIMIT_PER_MIN — Applies to media downloads (download_media). Default is 30/min. Authentication attempts are separately limited to 5 per 30 minutes with exponential backoff.

ALLOWED_CONTACTS — Comma-separated E.164 phone numbers (e.g. +15145551234,+353871234567). When set, only these contacts can receive outbound messages.

DISABLED_TOOLS — Comma-separated tool names (e.g. send_file,download_media). Disabled tools return an error when invoked.

SEND_READ_RECEIPTS — When true, calling mark_messages_read sends actual read receipts to WhatsApp (blue double checkmarks visible to senders), in addition to updating the local database. Set to false to only update the local database.

AUTO_READ_RECEIPTS — When true, incoming messages are automatically marked as read on WhatsApp (senders see blue checkmarks immediately). Since the server is an automated agent, this is enabled by default. Set to false to control read receipt timing manually via mark_messages_read.

PRESENCE_MODE — Controls the online/offline status of the linked device. Set to available (default) so the device appears online and delivery receipts (grey double checkmarks) are sent automatically. Set to unavailable to appear offline.

WELCOME_GROUP_NAME — On first connection, the server creates a WhatsApp group with this name and sends a hello message. Set to empty string to disable.

AUTO_CONNECT_ON_STARTUP — When true (default), the server automatically reconnects to WhatsApp at container startup if a valid session exists, without needing to call authenticate. Set to false to start in disconnected mode and connect manually. Note: This variable is not exposed in the Docker MCP Toolkit profile config schema and cannot be set via Docker Desktop UI or docker mcp profile config. Set it via .env or the environment: block in docker-compose.yml only.

AUTH_WAIT_FOR_LINK, AUTH_LINK_TIMEOUT_SEC, AUTH_POLL_INTERVAL_SEC — Defaults for the authenticate tool when the client omits waitForLink, linkTimeoutSec, or pollIntervalSec. In Docker MCP Toolkit they are driven by profile config whatsapp-mcp-docker.auth_wait_for_link, auth_link_timeout_sec, and auth_poll_interval_sec. Tool arguments always override these for that call.

DEBUG — When set to true, emits verbose diagnostic output to stderr. Useful when diagnosing connection or protocol issues. Don't leave it on permanently unless you enjoy reading walls of text.


Data Persistence

Docker MCP Toolkit automatically provisions named volumes for session persistence:

Data

Volume

Mount

Description

Session + Messages

whatsapp-sessions

/data/sessions

WhatsApp session DB, message DB, downloaded media

Audit

whatsapp-audit

/data/audit

Audit log SQLite DB

Session data survives container restarts. Use docker volume rm whatsapp-sessions whatsapp-audit to delete all data.


Data Management

Backup Your Data

Backup WhatsApp sessions and messages:

bash/zsh:

mkdir -p whatsapp-backup

docker run --rm \
  -v whatsapp-sessions:/data \
  -v $(pwd)/whatsapp-backup:/backup \
  alpine tar czf /backup/sessions-$(date +%Y%m%d).tar.gz /data

# Backup audit volume (optional)
docker run --rm \
  -v whatsapp-audit:/data \
  -v $(pwd)/whatsapp-backup:/backup \
  alpine tar czf /backup/audit-$(date +%Y%m%d).tar.gz /data

PowerShell:

New-Item -ItemType Directory -Force -Path whatsapp-backup

$date = Get-Date -Format "yyyyMMdd"
docker run --rm `
  -v whatsapp-sessions:/data `
  -v "${PWD}\whatsapp-backup:/backup" `
  alpine tar czf /backup/sessions-$date.tar.gz /data

# Backup audit volume (optional)
docker run --rm `
  -v whatsapp-audit:/data `
  -v "${PWD}\whatsapp-backup:/backup" `
  alpine tar czf /backup/audit-$date.tar.gz /data

Backup encryption key:

# If using docker mcp secrets, export from OS Keychain manually
# On macOS: security find-generic-password -s "docker-mcp" -a "whatsapp-mcp-docker.data_encryption_key" -w
# On Windows: Stored in Windows Credential Manager (search for "docker/mcp/whatsapp-mcp-docker")
# On Linux: Stored in secret-service (GNOME Keyring or KWallet)

# Save to password manager - DO NOT commit to git!

Restore from Backup

bash/zsh:

docker compose down

docker run --rm \
  -v whatsapp-sessions:/data \
  -v $(pwd)/whatsapp-backup:/backup \
  alpine tar xzf /backup/sessions-20260331.tar.gz -C /

docker compose up -d

PowerShell:

docker compose down

docker run --rm `
  -v whatsapp-sessions:/data `
  -v "${PWD}\whatsapp-backup:/backup" `
  alpine tar xzf /backup/sessions-20260331.tar.gz -C /

docker compose up -d

Migrate to New Host

# On old host: create backup (see above)
# Copy backup files to new host (scp, rsync, etc.)

# On new host:
# 1. Install Docker Desktop and enable MCP Toolkit
# 2. Restore volumes (see above)
# 3. Set encryption key
docker mcp secret set whatsapp-mcp-docker.data_encryption_key
# 4. Register catalog and add to profile
# 5. Start container - session should resume automatically

Delete All Data

# Stop container and remove volumes
docker compose down -v

# Verify volumes are gone (bash/zsh)
docker volume ls | grep whatsapp
# Verify volumes are gone (PowerShell)
docker volume ls | findstr whatsapp
# Both should return nothing

Warning: This permanently deletes all WhatsApp sessions, messages, and audit logs. You'll need to re-authenticate.


Tech Stack

Category

Technology

Runtime

Node.js 22 (Alpine)

WhatsApp Protocol

whatsmeow-node (Go binary)

MCP SDK

@modelcontextprotocol/sdk

Database

SQLite (better-sqlite3) with FTS5

QR Code

qrcode (in-container PNG generation)

Validation

Zod

Container

Docker (4-stage, ~80 MB runtime)

Orchestration

Docker MCP Toolkit + MCP Gateway


Documentation


Common Pitfalls & Troubleshooting

⚠️ Authentication Issues

Problem: Pairing code returns 400 error

  • Cause: WhatsApp rate-limits pairing attempts (5 per 30 minutes)

  • Solution: Wait 10-15 minutes, then retry. The server automatically falls back to QR code.

  • Prevention: Use QR code authentication if pairing codes frequently fail.

Problem: QR code expired

  • Cause: QR codes expire in ~20 seconds

  • Solution: Call authenticate again for a fresh QR code

  • Prevention: Have WhatsApp open and ready before calling authenticate

Problem: "Already authenticated" but messages not sending

  • Cause: Session may have expired (20 days of inactivity) or WhatsApp disconnected

  • Solution:

    1. Check status: docker compose logs --tail 50 whatsapp-mcp-docker

    2. Look for "logged_out" or "session expired" messages

    3. Call authenticate again to re-link

Problem: Can't scan QR code in time

  • Cause: QR codes expire quickly and you need to navigate WhatsApp menus

  • Solution:

    1. Open WhatsApp → Settings → Linked Devices before calling authenticate

    2. Tap "Link a Device" and have the camera ready

    3. Then call authenticate to generate QR code

  • Alternative: Use pairing code instead (lasts 60 seconds)

⚠️ Rate Limiting

Problem: "Rate limit exceeded" for messages

  • Default: 60 messages per minute

  • Solution: Wait for the window to reset, or adjust RATE_LIMIT_PER_MIN

  • Warning: Sending too many messages too quickly can get your account banned by WhatsApp

Problem: "Authentication cooldown active"

  • Cause: Failed auth attempts trigger exponential backoff (60s → 120s → 240s → 480s → 900s)

  • Solution: Wait for the cooldown period to expire

  • Prevention: Ensure phone number is correct before calling authenticate

⚠️ Session & Data Issues

Problem: Session lost after container restart

  • Cause: Docker volume not persisting

  • Check: docker volume ls | grep whatsapp-sessions

  • Solution: Ensure whatsapp-sessions volume exists and is mounted

Problem: Messages not appearing in search

  • Cause: FTS5 index may be out of sync or messages lack text body

  • Solution:

    1. Check if messages exist: Use list_messages for the chat

    2. Verify search query: Try simpler keywords

    3. FTS5 limitation: Media-only messages (no text) aren't searchable

Problem: Media download fails

  • Cause 1: Media expired on WhatsApp servers (30-day limit)

  • Cause 2: Message ID invalid or from before metadata tracking

  • Solution: Download media soon after receiving; old media may be unavailable

⚠️ Contact & Name Resolution

Problem: Wrong contact matched by fuzzy search

  • Cause: Multiple contacts have similar names

  • Solution: Use exact JID instead of name: send_message({ to: "1234567890@s.whatsapp.net", ... })

  • Prevention: Use search_contacts first to find the exact JID

Problem: Contact name shows as phone number

  • Cause: Name resolution from WhatsApp may take time or fail

  • Solution: Names are resolved asynchronously; wait for messages to arrive

  • Note: This is cosmetic; messaging still works with JIDs

⚠️ Backup & Restore

Problem: Need to backup/restore sessions

  • Backup (bash/zsh):

    docker run --rm -v whatsapp-sessions:/data -v $(pwd):/backup alpine \
      tar czf /backup/whatsapp-backup.tar.gz /data
  • Backup (PowerShell):

    docker run --rm -v whatsapp-sessions:/data -v "${PWD}:/backup" alpine `
      tar czf /backup/whatsapp-backup.tar.gz /data
  • Restore (bash/zsh):

    docker run --rm -v whatsapp-sessions:/data -v $(pwd):/backup alpine \
      tar xzf /backup/whatsapp-backup.tar.gz -C /data --strip-components 1
  • Restore (PowerShell):

    docker run --rm -v whatsapp-sessions:/data -v "${PWD}:/backup" alpine `
      tar xzf /backup/whatsapp-backup.tar.gz -C /data --strip-components 1

📞 Getting Help

If issues persist:

  1. Check logs: docker compose logs --tail 100 whatsapp-mcp-docker

  2. Run diagnostics: node scripts/diagnostics.js --verbose

  3. Search issues: GitHub Issues

  4. Report bug: Include logs, Docker version, and steps to reproduce


Testing

Tests run inside Docker via tester-container — no local build tools needed. The npm test scripts are intentionally blocked on the host; use the container's default CMD instead.

# Build the test image (tester-container is behind the 'test' Compose profile)
docker compose --profile test build tester-container

# Run all unit + integration tests (uses container default CMD)
docker compose --profile test run --rm tester-container

# Run a specific test file
docker compose --profile test run --rm tester-container npx tsx --test test/unit/crypto.test.ts

# One-time WhatsApp auth for e2e tests
docker compose --profile test run --rm tester-container npx tsx test/e2e/setup-auth.ts

# Run e2e tests with live session
docker compose --profile test run --rm tester-container npx tsx --test test/e2e/live.test.ts

Layer

What's covered

Unit

phone, fuzzy-match, crypto, file-guard, permissions, audit, store, reconnect, receipts

Integration

Full MCP protocol round-trip with mock WhatsApp client

E2E

Live WhatsApp session (read-only, requires auth)

See docs/guides/DEVELOPER.md for the full testing guide.


Troubleshooting

🔍 Diagnostic Commands

Run the diagnostic script to check system health:

Requires Node.js on the host. If you don't have Node.js installed locally, use the manual diagnostics below instead — the server itself runs Node inside Docker and needs no host installation for normal use.

# Quick status check
node scripts/diagnostics.js

# Verbose output with full logs
node scripts/diagnostics.js --verbose

# JSON output for automation
node scripts/diagnostics.js --json

Manual diagnostics:

# Check if container is running
docker compose ps

# View last 50 lines of logs
docker compose logs --tail 50 whatsapp-mcp-docker

# Check volumes exist (bash/zsh)
docker volume ls | grep whatsapp
# Check volumes exist (PowerShell)
docker volume ls | findstr whatsapp

# Verify encryption key is set (bash/zsh)
docker mcp secret ls | grep whatsapp-mcp-docker
# Verify encryption key is set (PowerShell)
docker mcp secret ls | findstr whatsapp-mcp-docker

# Check catalog registration
docker mcp catalog ls

# List all servers across all profiles
docker mcp profile server ls

# List servers for a specific profile (--profile flag does not exist; use --filter)
docker mcp profile server ls --filter profile=<your-profile>

# Test WhatsApp connection status (from MCP client)
get_connection_status

Common Pitfalls & Solutions

1. Authentication Issues

Problem: Pairing code returns 400 error or expires

Cause: WhatsApp rate-limits pairing attempts or code expires in 60 seconds

Solution:

  • Wait 10-15 minutes between attempts if rate-limited

  • Have WhatsApp mobile app ready before calling authenticate

  • If pairing code fails, server automatically falls back to QR code

  • QR codes expire in ~20 seconds — request a fresh one if expired

Prevention: Open WhatsApp → Settings → Linked Devices → "Link a Device" before calling authenticate, so you're ready to enter the code immediately. Pass waitForLink: true if you want the tool to poll until linked (note: this can exceed tool-call timeouts on some MCP clients — see auth_wait_for_link config).


Problem: "Already authenticated" but messages aren't sending

Cause: Session may be stale or WhatsApp disconnected

Solution:

# Check connection status
get_connection_status

# If disconnected, check logout reason
# If reason is "revoked", "banned", or "unlinked" → re-authenticate
authenticate({ phoneNumber: "+1234567890" })

2. Session Expiry

Problem: "WhatsApp not connected" after period of inactivity

Cause: WhatsApp linked device sessions expire after ~20 days of inactivity

Solution:

  1. Call authenticate again with the same phone number

  2. Session will resume (no need to re-link if still within 20 days)

  3. If expired, you'll need to re-link with pairing code or QR

Prevention: Send a message or use the server at least once every 2 weeks.


3. Rate Limiting

Problem: "Rate limit exceeded (60 messages/min)"

Cause: WhatsApp may ban accounts that send too many messages too quickly

Solution:

  • Wait 60 seconds for the rate limit to reset

  • Reduce message frequency

  • Increase RATE_LIMIT_PER_MIN only if you have legitimate high-volume use

Warning: Aggressive messaging can get your WhatsApp account banned.


Problem: "Too many authentication attempts (5 per 30 min)"

Cause: Authentication is limited to 5 attempts per 30 minutes

Solution:

  • Wait for the cooldown period (exponential backoff: 60s → 120s → 240s → 480s → 900s)

  • Don't retry immediately after failed attempts

  • If pairing code fails, wait for the QR code fallback instead of retrying


4. Contact Resolution Issues

Problem: "Could not resolve recipient" or wrong contact matched

Cause: Fuzzy matching found multiple candidates or no matches

Solution:

# Get exact JID from list_chats
list_chats()

# Use JID directly (bypasses fuzzy matching)
send_message({ to: "1234567890@c.us", message: "Hello" })

Tip: Contact names may not resolve immediately after first message — wait a few seconds for name resolution.


5. Media Download Failures

Problem: "No media metadata stored for this message"

Cause: Media was received before metadata tracking was enabled, or media expired

Solution:

  • Media on WhatsApp servers expires after 30 days

  • Only media received after enabling the server can be downloaded

  • Check has_media flag in list_messages to see if media is available


Problem: "Media storage quota exceeded"

Cause: Total media directory has reached 512 MB limit

Solution:

# Check media directory size
docker compose exec whatsapp-mcp-docker du -sh /data/sessions/media

# Delete old media files manually or wait for auto-purge
# Auto-purge runs hourly if MESSAGE_RETENTION_DAYS > 0

6. Search Returns No Results

Problem: search_messages finds nothing

Cause:

  • FTS5 index may not have caught up (delayed indexing)

  • Messages are encrypted but search index has plaintext

  • Query syntax error (boolean operators need capitalization)

Solution:

# Try simpler query without operators
search_messages({ query: "hello" })  # Instead of "hello AND world"

# Use quotes for exact phrases
search_messages({ query: "\"exact phrase\"" })

# Check if messages exist
list_messages({ chat: "Contact Name", limit: 10 })

7. Container Issues

Problem: Container won't start

Solution:

# Check logs
docker compose logs whatsapp-mcp-docker

# Verify volumes exist
docker volume ls | grep whatsapp

# Rebuild image
docker compose build --no-cache

# Remove and recreate
docker compose down -v
docker compose up -d

Problem: High memory usage

Cause: Message history sync loads many messages into memory

Solution:

  • Set MESSAGE_RETENTION_DAYS to limit stored messages

  • Use pagination in list_messages (limit: 50, page: 0, 1, 2...)

  • Restart container periodically to clear memory


Authentication Fails

If pairing code returns a 400 error, the server automatically falls back to QR code authentication — the QR code is returned directly in the tool response as an image and a data URI (paste the URI into any browser to view it). Check container logs for details: docker compose logs whatsapp-mcp-docker.

Rate Limited (429)

WhatsApp rate-limits pairing attempts. Wait 10-15 minutes before retrying.

Session Expired

WhatsApp sessions expire after ~20 days of inactivity. The server detects this automatically: it sends a notifications/disconnected MCP notification to your client, cleans up the stale session file, and reports the reason in get_connection_status. Call authenticate again to re-link.

For transient disconnects (network blips), the server automatically attempts reconnection after 5 seconds. A 60-second health heartbeat detects silent connection drops. Startup retries 5 times with exponential backoff before giving up.

Container Won't Start

docker compose up -d
docker compose logs -f whatsapp-mcp-docker

License

Apache License 2.0 — see LICENSE for details.


Disclaimer

This project is not affiliated with WhatsApp or Meta. WhatsApp does not officially support unofficial API clients. Use at your own risk and in compliance with WhatsApp's Terms of Service.


Contact

A
license - permissive license
-
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Malaccamaxgit/whatsapp-mcp-docker'

If you have feedback or need assistance with the MCP directory API, please join our Discord server