whatsapp-mcp

作者 verygoodplugins已验证

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

150
Stars
120
Forks
Go
语言
2026/8/23
添加时间

⚠️ 第三方软件声明

本 Skill 为第三方开源软件,独立托管于 GitHub。SkillTip 仅为信息目录,不控制或维护底层仓库。所显示的安全检查为自动化且范围有限,安装前请自行审查源码。

阅读服务条款

安装

添加到你的 Claude Code skills 目录:

# Add to your Claude Code skills
git clone https://github.com/verygoodplugins/whatsapp-mcp

快速入门

使用 whatsapp-mcp 等 Skills 的指南。

安全报告

已验证

上次扫描:—

{
  "status": "PASSED",
  "issues": []
}

README.md

WhatsApp MCP Server

CI License: MIT Python 3.11+ Go 1.25+

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.

Watch the WhatsApp MCP demo video

Product demo generated with Remotion using simulated data.

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
  • Read Receipts: Explicitly mark selected messages as read across linked devices
  • 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.25+
  • 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 .
    

    On first start, the bridge prints and stores a local REST API token at whatsapp-bridge/store/.bridge-token. 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 changedWhat 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 binarycd 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).

For v0.2.1 and later, restart both the bridge and MCP server after updating so the MCP server can read the bridge token. If the two components do not share the same checkout, set the same WHATSAPP_BRIDGE_TOKEN value in both environments.

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, optionally as a quoted reply.

Parameters:

  • recipient (required): Phone number or group JID
  • message (required): Text content to send
  • quoted_message_id (optional): ID of the message to reply to. When provided, the sent message appears as a quoted reply in WhatsApp.
  • quoted_sender_jid (optional): Full JID of the author of the quoted message. Required for group replies so WhatsApp renders the correct attribution header.
  • quoted_content (optional): Text content of the quoted message, used for the reply preview. Only plain text is supported.
  • mentions (optional): List of users to @-mention, as phone numbers with country code (e.g. ["12025551234"]) or JIDs. For each entry the message text must contain a matching @<number> token (e.g. "thanks @12025551234!"), which recipients' devices render as a highlighted, tappable mention that also notifies the user. Only meaningful in group chats.

Inbound quoted replies are stored automatically. The quoted_message_id field in each message returned by list_messages indicates which message it is replying to (or null for non-replies).

Natural Language Examples:

  • "Send 'Hello!' to +1234567890"
  • "Message the team group saying 'Meeting at 3pm'"
  • "Reply to that message saying 'Sounds good'"

mark_messages_read

Mark one or more messages from the same chat and sender as read. This explicitly sends WhatsApp read receipts; reading or searching messages never does so automatically.

Parameters:

  • message_ids (required): IDs of messages from the same chat and sender
  • chat_jid (required): JID of the chat containing the messages
  • sender_jid (required for groups): Full JID or bare phone number of the original message sender
  • timestamp (optional): RFC 3339 read timestamp; defaults to the current time

Natural Language Examples:

  • "Mark those messages as read"
  • "Mark the last three messages from Alice in the team group as read"

send_reaction

Send (or remove) an emoji reaction to a message.

Parameters:

  • recipient (required): Chat JID the message belongs to (phone JID or group JID)
  • message_id (required): ID of the message to react to
  • emoji (required): Reaction emoji (e.g. "👍"). Pass an empty string "" to remove an existing reaction.
  • from_me (optional, default false): Whether the original message was sent by the current user
  • sender_jid (optional): Full JID of the original message sender — required for group messages when from_me is false so the correct WhatsApp key is built

Inbound reactions received from others are stored automatically as messages with media_type = "reaction". The reaction_to_message_id field in each reaction message indicates which message was reacted to.

When webhook forwarding is enabled, inbound reactions are also posted to WEBHOOK_URL as typed events. Reaction removals use an empty content/reactionEmoji and reactionRemoved: true.

{
  "eventType": "reaction",
  "sender": "15551234567",
  "chatJID": "15551234567@s.whatsapp.net",
  "isFromMe": true,
  "content": "👍",
  "messageId": "reaction-stanza-id",
  "mediaType": "reaction",
  "reactionToMessageId": "target-message-id",
  "reactionEmoji": "👍",
  "reactionRemoved": false
}

Natural Language Examples:

  • "React to that message with a thumbs up"
  • "Remove my reaction from the last message in the group chat"

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

The bridge only reads files inside configured media roots. By default this is ~/.local/share/whatsapp-mcp/outbox; set WHATSAPP_MEDIA_ROOTS to allow additional absolute directories.

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

Converted audio is sent through the same media-path confinement as send_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

All chat tools (list_chats, get_chat, get_direct_chat_by_contact, get_contact_chats) return the same chat shape:

{
  "jid": "1234567890@s.whatsapp.net",
  "name": "Alice",
  "is_group": false,
  "last_message_time": "2024-01-15T10:30:00+00:00",
  "last_message": "hello world",       // null when include_last_message=false
  "last_sender": "1234567890",         // null when include_last_message=false
  "last_is_from_me": false,
  "last_read_time": "2024-01-15T09:00:00+00:00", // how far the chat is read
  "unread": true                       // last message is inbound and unread
}

Read state (last_read_time / unread)

last_read_time is the bridge's read marker for the chat, fed by read receipts from your own devices and backfilled from history sync. unread is derived from it: true when the chat's last message is inbound and newer than the marker. This distinguishes a genuinely unread chat from one whose last message merely happens to be inbound but was already read on the phone.

Caveats:

  • The marker only moves forward. Marking an already-read chat as unread again on the phone is not reflected.
  • No marker means no read was ever reported — for a chat with an inbound last message, unread then falls back to the old heuristic and reports true. Stores written by a bridge older than the chats.last_read_time column report last_read_time: null and behave the same way.
  • unread is a chat-level flag, not an unread count. WhatsApp's unread counter is not persisted.

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:

VariableDefaultDescription
WHATSAPP_BRIDGE_PORT8080Port for Go bridge REST API
WEBHOOK_URLhttp://localhost:8769/whatsapp/webhookWebhook for incoming messages
WEBHOOK_ENABLEDtrueSet to false to disable outbound webhooks
FORWARD_SELFtrueForward messages sent by self
WHATSAPP_DB_PATH../whatsapp-bridge/store/messages.dbPath to SQLite database
WHATSMEOW_DB_PATH../whatsapp-bridge/store/whatsapp.dbwhatsmeow DB used for LID ↔ phone resolution
WHATSAPP_API_URLhttp://localhost:8080/apiGo bridge REST API URL
WHATSAPP_BRIDGE_TOKENgenerated next to WHATSMEOW_DB_PATH as .bridge-tokenBearer token for bridge REST calls; also signed onto outbound webhook POSTs
WHATSAPP_MEDIA_ROOTS~/.local/share/whatsapp-mcp/outboxPath-list of directories allowed for outbound media files
WHATSAPP_DEVICE_NAMEwhatsmeow (whatsmeow default)Label shown for this connection under WhatsApp > Linked Devices. Set to a recognisable name. Applies at pair time only (re-pair to change)
WHATSAPP_MCP_TRANSPORTstdioMCP transport to serve clients: stdio, http, or sse
WHATSAPP_MCP_HOST127.0.0.1Bind address for the http/sse transports
WHATSAPP_MCP_PORT8000Port for the http/sse transports
WHATSAPP_PARENT_WATCHDOG_S30Stdio parent-liveness poll interval (seconds); exits on parent reparent only

MCP transport (stdio vs http/sse)

By default the server speaks MCP over stdio, which is what local clients like Claude Desktop and Cursor launch. To serve the server over the network instead, set WHATSAPP_MCP_TRANSPORT:

# Streamable HTTP (current spec transport for remote MCP), endpoint at /mcp
WHATSAPP_MCP_TRANSPORT=http WHATSAPP_MCP_PORT=8000 uv run main.py

# Legacy Server-Sent Events transport (deprecated in the MCP spec), endpoint at /sse
WHATSAPP_MCP_TRANSPORT=sse uv run main.py

http is an alias for the spec's streamable-http transport and is the recommended choice for remote connections; sse is kept for older clients.

Security: WHATSAPP_MCP_HOST defaults to 127.0.0.1, so the HTTP/SSE server is reachable only from the local machine. The server has no built-in authentication, and the underlying bridge can read and send WhatsApp messages on your account. Only bind to a non-loopback address (e.g. 0.0.0.0) if you place an authenticating reverse proxy or tunnel in front of it.

Bridge authentication and media paths

The bridge requires bearer-token authentication for every /api/* request and accepts only exact loopback Host headers for its configured port. This protects the local REST API from other local processes and browser DNS-rebinding attacks.

On first start, the bridge generates a 256-bit token, writes it to .bridge-token in the active bridge store directory with owner-only permissions, and prints a setup banner. The MCP server reads WHATSAPP_BRIDGE_TOKEN first, then falls back to .bridge-token in the same directory as WHATSMEOW_DB_PATH. For split deployments, containers, or process managers that do not share the store directory, set the same WHATSAPP_BRIDGE_TOKEN value for both the bridge and MCP server.

The bridge also signs its outbound webhook POSTs (to WEBHOOK_URL) with this same token, sent as an X-Bridge-Token: <token> header — a dedicated header rather than Authorization, so it never collides with a receiver's own Authorization-based auth (e.g. HTTP Basic auth embedded in WEBHOOK_URL as http://user:pass@host/..., which net/http applies automatically as long as the bridge doesn't set its own Authorization header). The header is attached only when a token is configured and WEBHOOK_URL was explicitly set — never to the built-in local default. The bridge token also authorizes /api/* calls like sending messages, and nothing has vetted the implicit default address, so it must never be handed to whatever process happens to be listening there. Upgrades that predate the token rollout, or that never set WEBHOOK_URL, keep working unchanged. The webhook client also never follows redirects, so a misconfigured or malicious endpoint can't redirect the bridge into leaking the token to a different host. If your webhook receiver enforces the token, set its copy to this exact value: e.g. the AutoHub hub's WHATSAPP_BRIDGE_TOKEN must equal this bridge's token (from .bridge-token or its own env) — the hub accepts it via X-Bridge-Token or Authorization: Bearer. The bridge always sends the token it has; the hub rejects unauthenticated forwards only once its WHATSAPP_BRIDGE_TOKEN is set to the matching value.

Outbound media_path values are confined to WHATSAPP_MEDIA_ROOTS. The default outbox is ~/.local/share/whatsapp-mcp/outbox, created on bridge startup. Move files there before calling send_file or send_audio_message, or set WHATSAPP_MEDIA_ROOTS to a colon-separated list of absolute directories.

Run automatically on macOS

macOS users can install optional per-user launchd jobs that start the Go bridge at login and monitor it every 60 seconds for API health, disconnects, and QR relink signals. The installer does not require sudo and does not install or start the MCP server.

scripts/install-launchd-macos.sh

The installer builds whatsapp-bridge/whatsapp-bridge with go build when Go is available, writes generated support files to ~/Library/Application Support/whatsapp-mcp/, writes LaunchAgents to ~/Library/LaunchAgents/, and writes logs to ~/Library/Logs/whatsapp-mcp/. It safely reloads only these labels:

  • com.whatsapp-mcp.bridge
  • com.whatsapp-mcp.bridge-monitor

To customize the launchd environment, export values before running the installer. Re-run the installer after changing them.

export WHATSAPP_BRIDGE_PORT=8080
export WEBHOOK_URL=http://localhost:8769/whatsapp/webhook
export FORWARD_SELF=false
export WHATSAPP_MEDIA_ROOTS="$HOME/.local/share/whatsapp-mcp/outbox"
scripts/install-launchd-macos.sh

Verify the jobs and inspect logs:

launchctl print gui/$(id -u)/com.whatsapp-mcp.bridge
launchctl print gui/$(id -u)/com.whatsapp-mcp.bridge-monitor
tail -n 100 ~/Library/Logs/whatsapp-mcp/bridge.err.log
tail -n 100 ~/Library/Logs/whatsapp-mcp/monitor.err.log

The monitor sends a macOS notification once per failure type until recovery. It alerts when the bridge LaunchAgent is unloaded, the token is missing, the health endpoint is unreachable, WhatsApp is disconnected, or recent logs indicate that QR relinking is needed.

Uninstall the generated LaunchAgents and support files with:

scripts/uninstall-launchd-macos.sh

Uninstall preserves whatsapp-bridge/store/, including WhatsApp session DBs, message DBs, media, and .bridge-token. Logs are left in ~/Library/Logs/whatsapp-mcp/ for manual cleanup.

CLI flags (Go bridge)

FlagDefaultDescription
--full-history-pairfalseRequest 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-mcp.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.

Requesting history for a single chat (on-demand)

--full-history-pair only applies to a fresh pair, so recovering a gap in one chat otherwise means deleting whatsapp.db and re-syncing everything. To ask the phone for older messages in a single chat without re-pairing:

curl -X POST http://127.0.0.1:8080/api/history \
  -H "Authorization: Bearer $(cat whatsapp-bridge/store/.bridge-token)" \
  -H "Content-Type: application/json" \
  -d '{"chat_jid": "1234567890@s.whatsapp.net", "count": 50}'

The request is anchored on the oldest message already stored for that chat, so the phone returns messages from before it. Call it repeatedly to page further back. Results arrive asynchronously through the normal history-sync handler and land in messages.db — typically within a few seconds.

FieldRequiredDescription
chat_jidyesChat to backfill (...@s.whatsapp.net or ...@g.us)
countnoMessages to request; default 50, capped at 500

Caveats:

  • The phone decides how much it returns, exactly as with pair-time sync, so count is a request rather than a guarantee.
  • At least one message for the chat must already be stored, since it is used as the anchor. Chats with no local messages return 404; send or receive one message first.
  • Messages the phone has deleted are not recoverable, as above.

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

Component Details

flowchart LR
    subgraph GoAPI["Go Bridge REST API"]
        direction TB
        SEND["/api/send"]
        READ["/api/mark-read"]
        DOWN["/api/download"]
        REACT["/api/react"]
        TYPE["/api/typing"]
        HIST["/api/history"]
        HEALTH["/api/health"]
    end

    subgraph MCPTools["MCP Tools (15 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

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"

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

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

  • Pairing fails with Client outdated or HTTP 405: Update to the latest release and rebuild the bridge. WhatsApp periodically raises the minimum supported linked-device client version, which can make older whatsmeow builds fail before pairing completes.
  • 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: Back up whatsapp-bridge/store, then move whatsapp-bridge/store/whatsapp.db aside and re-authenticate. Keep messages.db unless you intentionally want to discard local message history.
  • Bridge returns 401 Unauthorized: Restart the bridge so it creates .bridge-token next to WHATSMEOW_DB_PATH, then restart the MCP server. If the MCP server cannot read that file, set WHATSAPP_BRIDGE_TOKEN to the same value in both environments.
  • Bridge returns 403 Forbidden for Host: Use WHATSAPP_API_URL with http://127.0.0.1:<port>/api, http://localhost:<port>/api, or http://[::1]:<port>/api; custom hostnames and missing ports are rejected.
  • Bridge returns 403 Forbidden for media_path: Move the file into ~/.local/share/whatsapp-mcp/outbox or add its absolute parent directory to WHATSAPP_MEDIA_ROOTS.

App State / LTHash Conflicts

Some WhatsApp account state is managed by whatsmeow in whatsapp-bridge/store/whatsapp.db. If the bridge reports errors like:

SendAppState failed: server returned error updating app state (regular_low):
<error code="409" text="conflict"/>
failed to verify patch v12345: mismatching LTHash

then WhatsApp's app-state patch chain for the linked device is out of sync. This usually affects operations that write chat settings such as archive, mute, or pin state. Incoming and outgoing messages may still work because message storage lives separately in messages.db.

Known manual resync attempts such as FetchAppState(..., fullSync=true) may still fail on this upstream app-state error class. The practical recovery path is to reset the whatsmeow session and re-pair:

# Stop the bridge first.
launchctl bootout gui/$UID/com.whatsapp-mcp.bridge    # or however you manage it

# Back up the whole runtime store.
cp -a whatsapp-bridge/store whatsapp-bridge/store.bak.$(date +%Y%m%d%H%M%S)

# Reset only the whatsmeow session/app-state DB.
mv whatsapp-bridge/store/whatsapp.db whatsapp-bridge/store/whatsapp.db.lthash.bak

# Restart the bridge and scan the new QR code.
cd whatsapp-bridge
./whatsapp-bridge    # or `go run .` during development

Do not remove whatsapp-bridge/store/messages.db for this recovery unless you also want to delete the local message archive.

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

常见问题

What is whatsapp-mcp?

whatsapp-mcp is an open-source mcp servers skill for AI coding assistants such as Claude Code, Codex CLI, and ChatGPT, built by verygoodplugins. WhatsApp MCP server - Connect Claude to WhatsApp for reading and sending messages. It has 150 GitHub stars.

Is whatsapp-mcp safe to use?

Yes. whatsapp-mcp passed SkillsLLM's automated security scan — a dependency vulnerability audit plus prompt-injection heuristics — with no high-severity issues. You can read the full report in the Security Report section on this page.

How do I install whatsapp-mcp?

Clone the repository with "git clone https://github.com/verygoodplugins/whatsapp-mcp" and add it to your Claude Code skills directory (see the Installation section above).

What programming language is whatsapp-mcp written in?

whatsapp-mcp is primarily written in Go. It is open-source under verygoodplugins on GitHub, so you can review or fork the full source.

Are there alternatives to whatsapp-mcp?

Yes. SkillsLLM lists many other MCP Servers skills you can browse and compare side by side. Open the MCP Servers category from the badge at the top of this page, or use the Related Skills and comparison links further down to weigh whatsapp-mcp against similar tools.

评论 (0)

暂无评论,成为第一个分享想法的人!

n8n

by n8n-io

12

Fair-code workflow automation platform with native AI capabilities. Combine visual building with custom code, self-host or cloud, 400+ integrations.

201,88160,308TypeScript
MCP 服务器apisai-tools
查看详情

Scrapling

by D4Vinci

🕷️ An adaptive Web Scraping framework that handles everything from a single request to a full-scale crawl!

75,9137,581Python
MCP 服务器
查看详情

TrendRadar

by sansan0

⭐AI-driven public opinion & trend monitor with multi-platform aggregation, RSS, and smart alerts.🎯 告别信息过载,你的 AI 舆情监控助手与热点筛选工具!聚合多平台热点 + RSS 订阅,支持关键词精准筛选。AI 智能筛选新闻 + AI 翻译 + AI 分析简报直推手机,也支持接入 MCP 架构,赋能 AI 自然语言对话分析、情感洞察与趋势预测等。支持 Docker ,数据本地/云端自持。集成微信/飞书/钉钉/Telegram/邮件/ntfy/bark/slack 等渠道智能推送。

61,65224,883Python
MCP 服务器
查看详情

context7

by upstash

Context7 Platform -- Up-to-date code documentation for LLMs and AI code editors

61,0602,938TypeScript
MCP 服务器
查看详情

High-performance code intelligence MCP server. Indexes codebases into a persistent knowledge graph — average repo in milliseconds. 158 languages, sub-ms queries, 99% fewer tokens. Single static binary, zero dependencies.

39,9393,219C
MCP 服务器
查看详情

开发者还喜欢

基于喜欢此 Skill 的开发者投票和收藏

ECC

by affaan-m

10

The agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Cursor and beyond.

242,21936,702JavaScript
AI 智能体ai-agentsanthropicclaude-code
查看详情
15

An agentic skills framework & software development methodology that works.

234,96620,863Shell
AI 智能体ai-agentsbrainstorming
查看详情

hermes-agent

by NousResearch

10

The agent that grows with you

234,43747,175Python
AI 智能体ai-agentsagent-orchestration
查看详情

n8n

by n8n-io

12

Fair-code workflow automation platform with native AI capabilities. Combine visual building with custom code, self-host or cloud, 400+ integrations.

201,88160,308TypeScript
MCP 服务器apisai-tools
查看详情

The agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Cursor and beyond.

185,94028,768JavaScript
AI 智能体ai-agentsanthropicclaude-code
查看详情

cc-switch

by farion1231

3

A cross-platform desktop All-in-One assistant for Claude Code, Codex, OpenCode, OpenClaw, Grok Build & Hermes Agent. Only official website: ccswitch.io

128,8688,826Rust
AI 智能体claude-codeai-tools
查看详情