Skip to content

verygoodplugins/whatsapp-mcp

Repository files navigation

WhatsApp MCP Server

CI License: MIT Python 3.11+ Go 1.24+

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_display format ("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

  1. Clone the repository

    git clone https://github.com/verygoodplugins/whatsapp-mcp.git
    cd whatsapp-mcp
  2. Start the WhatsApp bridge

    cd whatsapp-bridge
    go run .

    Scan the QR code with WhatsApp on your phone to authenticate.

  3. Configure Claude Desktop

    Add to ~/Library/Application Support/Claude/claude_desktop_config.json:

    {
      "mcpServers": {
        "whatsapp": {
          "command": "uv",
          "args": [
            "--directory",
            "/path/to/whatsapp-mcp/whatsapp-mcp-server",
            "run",
            "main.py"
          ]
        }
      }
    }

    Replace /path/to/whatsapp-mcp with your actual path.

  4. Restart Claude Desktop

Updating

Pull the latest changes, then refresh whichever components moved:

git pull
You changed What to do
Bridge code (whatsapp-bridge/*.go) and you run go run . Nothing — go run recompiles each launch. Just restart the bridge.
Bridge code and you run a built binary cd whatsapp-bridge && go build -o whatsapp-bridge && ./whatsapp-bridge
MCP server (whatsapp-mcp-server/*.py, pyproject.toml, uv.lock) Restart Claude Desktop / Cursor — uv re-resolves from the lockfile on next launch. Force a sync with cd whatsapp-mcp-server && uv sync if needed.

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": "uv",
        "args": [
          "--directory",
          "/path/to/whatsapp-mcp/whatsapp-mcp-server",
          "run",
          "main.py"
        ]
      }
    }
  }
}

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 JID
  • limit (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 JID
  • message (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 JID
  • file_path (required): Path to the file
  • caption (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 JID
  • file_path (required): Path to audio file

download_media

Download media from a received message.

Parameters:

  • message_id (required): ID of the message with media
  • chat_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 message
  • chat_jid (required): JID of the chat
  • before (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
WHATSAPP_BRIDGE_PORT 8080 Port for Go bridge REST API
WEBHOOK_URL http://localhost:8769/whatsapp/webhook Webhook for incoming messages
FORWARD_SELF false Forward messages sent by self
WHATSAPP_DB_PATH ../whatsapp-bridge/store/messages.db Path to SQLite database
WHATSMEOW_DB_PATH ../whatsapp-bridge/store/whatsapp.db whatsmeow DB used for LID ↔ phone resolution
WHATSAPP_API_URL http://localhost:8080/api Go bridge REST API URL

CLI flags (Go bridge)

Flag Default Description
--full-history-pair false Request full history at pair time. Only takes effect on a fresh pair (no existing whatsapp.db); no-op for already-paired sessions. The phone ultimately decides the actual history window sent — see Requesting full history below.

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 manager

Caveats:

  • 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.db already 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'. CallOffer events don't expose media type directly (it's buried in the binary call data). Group calls via CallOfferNotice include a Media field 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| EXT
Loading

Component 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| GoAPI
Loading

Data 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"
Loading

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 message
Loading

Development

Running Tests

cd whatsapp-mcp-server
uv pip install -e ".[dev]"
uv run pytest -v

Linting

# Python
cd whatsapp-mcp-server
uv run ruff check .
uv run ruff format .

# Go
cd whatsapp-bridge
golangci-lint run

Building

# 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/*.db files 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_contact tool, sender_display field, and LID ↔ phone resolution via the whatsmeow store
  • Live capture of incoming voice/video calls into a calls table
  • --full-history-pair flag to request extended history at pair time
  • Resilience: recovers from StreamReplaced session conflicts; pinned anyio to dodge a cancel-scope regression
  • CI/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)
  • @davidsimoesStreamReplaced recovery (#27)
  • @davidggphy — LID → phone JID consistency (#12)
  • @maikol-solis — bridge run command fix (#23)
  • @DeetBotanyio cancel-scope pin (#44)

And to Luke for creating the original project. See CONTRIBUTING.md if you'd like to join in.

Links

About

WhatsApp MCP server - Connect Claude to WhatsApp for reading and sending messages

Topics

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors