WhatsApp MCP Server
Allows reading, searching, and sending WhatsApp messages (text, images, videos, documents, audio), managing contacts and chats, and handling call history, all through the WhatsApp platform.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@WhatsApp MCP Serversend a message to John: meeting at 3pm"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
WhatsApp MCP Server
A Model Context Protocol (MCP) server for WhatsApp, enabling Claude to read and send WhatsApp messages.
Originally created by Luke Harries. Maintained by Very Good Plugins.
Features
Message Management: Search and read personal WhatsApp messages (text, images, videos, documents, audio)
Contact Search: Search contacts by name or phone number with
sender_displayformat ("Name (phone)")Send Messages: Send text messages to individuals or groups
Media Support: Send and download images, videos, documents, and voice messages
Call History: Capture incoming voice/video calls into a local SQLite table (live, 1:1 and group)
Webhook Integration: Forward incoming messages to external services
Local Storage: All messages stored locally in SQLite - only sent to Claude when you allow it
Installation
Prerequisites
Go 1.24+
Python 3.11+
uv package manager
Claude Desktop or Cursor
FFmpeg (optional, for voice message conversion)
Quick Start
Clone the repository
git clone https://github.com/verygoodplugins/whatsapp-mcp.git cd whatsapp-mcpStart the WhatsApp bridge
cd whatsapp-bridge go run .Scan the QR code with WhatsApp on your phone to authenticate.
Configure Claude Desktop
Add to
~/Library/Application Support/Claude/claude_desktop_config.json:{ "mcpServers": { "whatsapp": { "command": "whatsapp-mcp" } } }The
whatsapp-mcpconsole script is installed bypip install -e .(orpipx install .) from the repo root. If you'd rather not install editable, you can call the module directly:{ "mcpServers": { "whatsapp": { "command": "python", "args": ["-m", "whatsapp_mcp"], "cwd": "/path/to/whatsapp-mcp" } } }Replace
/path/to/whatsapp-mcpwith your actual repo path.Restart Claude Desktop
Updating
Pull the latest changes, then refresh whichever components moved:
git pullYou changed | What to do |
Bridge code ( | Nothing — |
Bridge code and you run a built binary |
|
MCP server ( | Restart Claude Desktop / Cursor. If you installed with |
Updates do not require re-pairing or deleting whatsapp.db — your session and message history are preserved. Re-pairing is only needed when explicitly requesting full history (see Requesting full history).
Cursor IDE Configuration
Add to your Cursor MCP settings (~/.cursor/mcp.json):
{
"mcp": {
"servers": {
"whatsapp": {
"command": "whatsapp-mcp"
}
}
}
}Tools
Messages include sender_display showing "Name (phone)" format for easy identification by agents.
Contact Operations
search_contacts
Search contacts by name or phone number.
Parameters:
query(required): Name or phone number to search
Natural Language Examples:
"Find contacts named John"
"Search for phone number 555-1234"
"Who has the phone number starting with +1?"
get_contact
Resolve a WhatsApp contact name from a phone number, LID, or full JID.
Parameters:
identifier(required): Phone number, LID, or full JID (aliases:phone_number,phone)Examples:
12025551234,184125298348272,12025551234@s.whatsapp.net,184125298348272@lid
Natural Language Examples:
"What's the name for phone number 5551234567?"
"Look up who owns this number"
"Who is 184125298348272@lid?"
Message Operations
list_messages
Get messages with filters, date ranges, and sorting.
Parameters:
chat_jid(optional): Filter by specific chat JIDlimit(optional): Number of messages (default 50, max 500)before_date(optional): Messages before this date (YYYY-MM-DD)after_date(optional): Messages after this date (YYYY-MM-DD)sort_by(optional): "newest" or "oldest" (default "newest")
Natural Language Examples:
"Show me the last 100 messages from today"
"Get messages from the family group chat"
"Find messages from last week"
send_message
Send a text message to a contact or group.
Parameters:
recipient(required): Phone number or group JIDmessage(required): Text content to send
Natural Language Examples:
"Send 'Hello!' to +1234567890"
"Message the team group saying 'Meeting at 3pm'"
send_file
Send a media file (image, video, document).
Parameters:
recipient(required): Phone number or group JIDfile_path(required): Path to the filecaption(optional): Caption for the media
send_audio_message
Send a voice message (automatically converts to Opus .ogg format).
Parameters:
recipient(required): Phone number or group JIDfile_path(required): Path to audio file
download_media
Download media from a received message.
Parameters:
message_id(required): ID of the message with mediachat_jid(required): JID of the chat containing the message
Chat Operations
list_chats
List all chats with metadata.
Parameters:
limit(optional): Number of chats (default 50, max 200)
get_chat
Get specific chat metadata by JID.
Parameters:
jid(required): Chat JID
get_direct_chat_by_contact
Find a direct message chat with a contact.
Parameters:
phone(required): Phone number of the contact
get_contact_chats
List all chats involving a specific contact.
Parameters:
phone(required): Phone number of the contact
get_last_interaction
Get the last message exchanged with a contact.
Parameters:
phone(required): Phone number of the contact
get_message_context
Get messages around a specific message for context.
Parameters:
message_id(required): ID of the target messagechat_jid(required): JID of the chatbefore(optional): Number of messages before (default 5)after(optional): Number of messages after (default 5)
Configuration
Copy .env.example to .env and configure as needed:
Variable | Default | Description |
|
| Port for Go bridge REST API |
|
| Webhook for incoming messages |
|
| Forward messages sent by self |
|
| Path to SQLite database |
|
| whatsmeow DB used for LID ↔ phone resolution |
|
| Go bridge REST API URL |
CLI flags (Go bridge)
Flag | Default | Description |
|
| Request full history at pair time. Only takes effect on a fresh pair (no existing |
Requesting full history
whatsmeow's default pairing asks for "recent sync" — roughly the last 3 months, with the exact window decided by the phone. If you want to pull more history at pair time:
# Stop the bridge
launchctl bootout gui/$UID/com.whatsapp-bridge # or however you manage it
# Back up, then remove the auth session (keeps messages.db intact)
cp whatsapp-bridge/store/whatsapp.db{,.bak}
rm whatsapp-bridge/store/whatsapp.db
# Re-pair with the flag
cd whatsapp-bridge
./whatsapp-bridge --full-history-pair
# Scan the QR with WhatsApp → Settings → Linked Devices → Link a Device
# Wait for "History sync complete" in the logs (can take 10-30 minutes)
# Ctrl+C when sync has quiesced, then restart under your normal process managerCaveats:
The phone decides the actual cap. The flag requests up to 10 years / 100 GB, but WhatsApp's iOS primary device enforces its own retention policy. iPad companion is documented at ~1 year max; other linked devices appear to follow similar logic.
Only effective on a fresh pair. With
whatsapp.dbalready present, no new pair handshake fires and the flag is a no-op.Messages the phone has deleted are not recoverable — auto-expire, low-storage cleanup, and manual delete all leave no trace for the phone to share.
Call History
The bridge captures incoming WhatsApp voice and video calls live into a
dedicated calls table in messages.db. When a 1:1 call arrives
(CallOffer) or a group call is announced (CallOfferNotice), a row is
inserted with result='in_progress'. Subsequent CallAccept /
CallReject / CallTerminate events update the row — final result becomes
answered, rejected, missed, or ended depending on the event
sequence. See the state-machine comment above StoreCallOffer in main.go
for the exact transitions.
Schema
CREATE TABLE calls (
call_id TEXT,
chat_jid TEXT, -- group JID for group calls, call creator JID for 1:1
from_jid TEXT, -- JID of whoever started the call
timestamp TIMESTAMP, -- call start time
is_from_me BOOLEAN,
call_type TEXT, -- 'voice' or 'video'
is_group BOOLEAN,
result TEXT, -- 'in_progress' | 'answered' | 'ended' |
-- 'missed' | 'rejected'
duration_sec INTEGER, -- computed when the call terminates
ended_at TIMESTAMP,
reason TEXT, -- terminate reason string from whatsmeow
PRIMARY KEY (call_id, chat_jid)
);Caveats
Outbound calls are not captured. WhatsApp's primary device handles calls it initiates without notifying linked devices, so the bridge never sees an event for them.
Call results only reflect what the bridge saw. If the bridge is offline when a call happens, the events are lost.
1:1 calls default to
call_type='voice'.CallOfferevents don't expose media type directly (it's buried in the binary call data). Group calls viaCallOfferNoticeinclude aMediafield and are recorded accurately as voice or video.
Architecture
flowchart TB
subgraph Clients["AI Clients"]
CD[Claude Desktop]
CU[Cursor IDE]
CC[Claude Code]
end
subgraph MCP["MCP Layer"]
PY[Python MCP Server<br/>FastMCP]
end
subgraph Bridge["WhatsApp Bridge"]
GO[Go Bridge<br/>whatsmeow]
DB[(SQLite<br/>messages.db)]
WH[Webhook Handler]
end
subgraph External["External Services"]
WA[WhatsApp Web API]
EXT[External Webhook<br/>Receiver]
end
CD & CU & CC -->|MCP Protocol| PY
PY -->|REST API| GO
PY -->|Read| DB
GO -->|Store| DB
GO <-->|WebSocket| WA
GO -->|Forward Messages| WH
WH -->|POST| EXTComponent Details
flowchart LR
subgraph GoAPI["Go Bridge REST API"]
direction TB
SEND["/api/send"]
DOWN["/api/download"]
TYPE["/api/typing"]
HEALTH["/api/health"]
end
subgraph MCPTools["MCP Tools (14 total)"]
direction TB
CONT["Contact Tools<br/>search_contacts, get_contact"]
MSG["Message Tools<br/>list_messages, send_message, etc."]
CHAT["Chat Tools<br/>list_chats, get_chat, etc."]
MEDIA["Media Tools<br/>send_file, download_media, etc."]
end
MCPTools -->|HTTP Requests| GoAPIData Flow
sequenceDiagram
participant User as User
participant Claude as Claude Desktop
participant MCP as Python MCP Server
participant Bridge as Go Bridge
participant WA as WhatsApp
User->>Claude: "Send 'Hello' to Mom"
Claude->>MCP: send_message(recipient, message)
MCP->>Bridge: POST /api/send
Bridge->>WA: Send via WebSocket
WA-->>Bridge: Delivery confirmation
Bridge-->>MCP: Success response
MCP-->>Claude: Message sent
Claude-->>User: "Message sent to Mom"Incoming Message Flow
sequenceDiagram
participant WA as WhatsApp
participant Bridge as Go Bridge
participant DB as SQLite
participant WH as Webhook
participant EXT as External Service
WA->>Bridge: New message
Bridge->>DB: Store message
Bridge->>Bridge: Auto-download media
Bridge->>WH: Forward to webhook
WH->>EXT: POST with message data
Note over EXT: Process incoming messageDevelopment
Running Tests
pip install -e ".[dev]"
pytest -m "not integration"Linting
# Python
ruff check .
ruff format .
mypy src
# Go
cd whatsapp-bridge
golangci-lint runBuilding
# Go bridge
cd whatsapp-bridge
go build -o whatsapp-bridge
# Run the binary
./whatsapp-bridge
# During development (avoids stale binaries)
go run .Releasing (Maintainers)
Releases use Release Please automation; maintainer steps and fallback procedures are documented in docs/RELEASING.md.
Troubleshooting
Authentication Issues
QR Code Not Displaying: Restart the bridge. Check terminal QR code support.
Device Limit Reached: Remove a linked device from WhatsApp Settings > Linked Devices.
No Messages Loading: Initial sync can take several minutes for large chat histories.
Out of Sync: Delete
whatsapp-bridge/store/*.dbfiles and re-authenticate.
Windows
Windows requires CGO for go-sqlite3. Install MSYS2 and enable CGO:
go env -w CGO_ENABLED=1
go run .Security Notice
Caution: As with many MCP servers, this is subject to the lethal trifecta. Prompt injection could lead to private data exfiltration. Use with awareness.
License
MIT License - see LICENSE for details.
Credits & History
This project is a maintained fork of lharries/whatsapp-mcp, originally created by Luke Harries.
Why we forked: The original repository hasn't been updated since April 2025. We needed continued maintenance, bug fixes, and new features for production use.
Highlights since the fork:
/api/typing,/api/health, and webhook forwarding (with reply context + image media)Auto-download of incoming media with collision-safe filenames
get_contacttool,sender_displayfield, and LID ↔ phone resolution via the whatsmeow storeLive capture of incoming voice/video calls into a
callstable--full-history-pairflag to request extended history at pair timeResilience: recovers from
StreamReplacedsession conflicts; pinnedanyioto dodge a cancel-scope regressionCI/CD with GitHub Actions, Release Please for automated versioning, and Dependabot
The full release-by-release list lives in CHANGELOG.md.
Recent contributors (huge thanks):
@edmenendez — call capture (#39), full-history flag (#37), caption surfacing (#42), media filename collisions (#40), download race fix (#41), LID matching (#43), contact resolution via whatsmeow store (#30)
@davidsimoes —
StreamReplacedrecovery (#27)@davidggphy — LID → phone JID consistency (#12)
@maikol-solis — bridge run command fix (#23)
@DeetBot —
anyiocancel-scope pin (#44)
And to Luke for creating the original project. See CONTRIBUTING.md if you'd like to join in.
Links
This server cannot be installed
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/DimitrisPasakaleris/whatsapp-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server