Skip to main content
Glama
mcp-wacli
by grrek

mcp-wacli

MCP (Model Context Protocol) server that wraps wacli — a WhatsApp CLI built on whatsmeow. Lets any MCP-compatible AI client (Claude Code, Claude Desktop, Cursor, Cline, etc.) read, search, and send WhatsApp messages through your personal account.

Why a wrapper?

Instead of reimplementing the WhatsApp Web protocol, mcp-wacli delegates everything to wacli's --json mode. This means:

  • Zero duplicate sessions — uses the same authenticated session as your existing wacli install

  • Zero data duplication — one SQLite DB, shared with wacli

  • Full feature parity — any wacli command becomes an MCP tool

  • Tiny codebase — ~540 lines of Python glue

Related MCP server: WAHA MCP

Architecture

Two transport modes are supported:

                 ┌─────────────────────────────────┐
                 │  AI Client                       │
                 │  Claude / Cursor / GPT / Gemini  │
                 └──────┬────────────┬──────────────┘
                        │            │
              SSH+stdio │            │ HTTP/SSE
                        ▼            ▼
                 ┌──────────────────────────┐
                 │  server.py (FastMCP)      │
                 │  27 tools                 │
                 │  Bearer token auth (HTTP) │
                 └──────────┬───────────────┘
                            │ subprocess
                            ▼
                 ┌──────────────────────┐
                 │  wacli --json        │
                 │  (Go / whatsmeow)    │
                 └──────────┬───────────┘
                            │
                            ▼
                    WhatsApp servers

All data stays local. Messages are only sent to the AI when it explicitly invokes a tool.

Prerequisites

Dependency

Version

Notes

wacli

dev+

Must be authenticated (wacli auth)

Python

>= 3.11

Managed by uv

uv

>= 0.10

Python package manager

Quick start

# 1. Clone
git clone https://github.com/grrek/mcp-wacli.git
cd mcp-wacli

# 2. Install dependencies
uv sync

# 3. Verify wacli is authenticated
wacli doctor --json

# 4. Test the MCP server (stdio mode)
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}' | uv run server.py

Transport modes

Mode 1: stdio (over SSH) — default

Best for Claude Code and Claude Desktop when accessing a remote server via SSH.

uv run server.py

Mode 2: HTTP/SSE with Bearer token auth

Best for network access from any MCP client, including non-Anthropic LLMs. Can run as a persistent systemd service.

uv run server.py --http

On first run, a random 32-character token is generated and saved to ~/.mcp-wacli-token (mode 0600). The server prints the token to stderr on startup. All HTTP requests must include Authorization: Bearer <token>.

Customize with environment variables:

  • MCP_HOST — bind address (default: 0.0.0.0)

  • MCP_PORT — port (default: 9800)

Note: The MCP library's built-in DNS rebinding protection (TrustedHostMiddleware) is disabled in mcp-wacli because it rejects connections from non-localhost IPs. Authentication is handled instead by the ASGI Bearer token middleware, which validates every request at the transport level before it reaches the MCP handler.

mkdir -p ~/.config/systemd/user

cat > ~/.config/systemd/user/mcp-wacli.service << 'EOF'
[Unit]
Description=mcp-wacli HTTP/SSE server
After=network-online.target

[Service]
Type=simple
WorkingDirectory=/home/YOUR_USER/mcp-wacli
ExecStart=/home/YOUR_USER/.local/bin/uv run server.py --http
Environment=MCP_HOST=0.0.0.0
Environment=MCP_PORT=9800
Restart=on-failure
RestartSec=10

[Install]
WantedBy=default.target
EOF

systemctl --user daemon-reload
systemctl --user enable mcp-wacli
systemctl --user start mcp-wacli

# Allow service to run without an active SSH session
loginctl enable-linger YOUR_USER

Useful commands:

  • systemctl --user status mcp-wacli — check status

  • systemctl --user restart mcp-wacli — restart after updates

  • journalctl --user -u mcp-wacli -f — follow logs

Configure your AI client

Use the Claude CLI to add the MCP server:

claude mcp add --transport sse -s user whatsapp http://YOUR_SERVER:9800/sse \
  --header "Authorization: Bearer YOUR_TOKEN_HERE"

This writes the config to ~/.claude.json. Alternatively, add it manually:

{
  "mcpServers": {
    "whatsapp": {
      "type": "sse",
      "url": "http://YOUR_SERVER:9800/sse",
      "headers": {
        "Authorization": "Bearer YOUR_TOKEN_HERE"
      }
    }
  }
}

Claude Code — SSH (stdio)

claude mcp add -s user whatsapp -- ssh \
  -o LogLevel=ERROR \
  -o ClearAllForwardings=yes \
  your-server \
  "export PATH=\$HOME/.local/bin:\$PATH && cd ~/mcp-wacli && uv run server.py"

Important SSH caveats:

  • Use -o LogLevel=ERROR to suppress SSH warnings on stderr (they break the MCP JSON-RPC handshake)

  • Use -o ClearAllForwardings=yes if your ~/.ssh/config has LocalForward entries for this host (port-forward bind warnings also break the handshake)

Claude Desktop — HTTP/SSE

Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS):

{
  "mcpServers": {
    "whatsapp": {
      "type": "sse",
      "url": "http://YOUR_SERVER:9800/sse",
      "headers": {
        "Authorization": "Bearer YOUR_TOKEN_HERE"
      }
    }
  }
}

Claude Desktop — SSH

{
  "mcpServers": {
    "whatsapp": {
      "command": "ssh",
      "args": [
        "-o", "LogLevel=ERROR",
        "-o", "ClearAllForwardings=yes",
        "your-server",
        "export PATH=$HOME/.local/bin:$PATH && cd ~/mcp-wacli && uv run server.py"
      ]
    }
  }
}

Local (no SSH, no HTTP)

If wacli and mcp-wacli are on the same machine:

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

Any MCP-compatible client (GPT, Gemini, etc.)

Start the HTTP server and point the client to http://YOUR_SERVER:9800/sse with the Bearer token from ~/.mcp-wacli-token.

Available tools (27)

Chats (2)

Tool

Description

list_chats

List chats with optional name search

show_chat

Show details of a single chat by JID

Messages (4)

Tool

Description

list_messages

List recent messages with date/chat filters

search_messages

Full-text search (FTS5 or LIKE fallback)

show_message

Show a single message by ID

message_context

Show surrounding messages for context

Contacts (5)

Tool

Description

search_contacts

Search contacts by name or phone

show_contact

Show contact details by JID

set_contact_alias

Set a local nickname for a contact

remove_contact_alias

Remove a local nickname

refresh_contacts

Re-import contacts from session store

Send (2)

Tool

Description

send_message

Send a text message

send_file

Send image, video, audio, or document

Groups (9)

Tool

Description

list_groups

List groups with optional search

group_info

Fetch live group info

group_rename

Rename a group

group_leave

Leave a group

group_join

Join a group by invite code

group_participants_add

Add members to a group

group_participants_remove

Remove members from a group

group_participants_promote

Promote members to admin

group_participants_demote

Demote admins

Media (1)

Tool

Description

download_media

Download media from a message

Sync & History (2)

Tool

Description

sync_once

Sync new messages (connect, fetch, exit)

history_backfill

Request older messages from primary device

Diagnostics (2)

Tool

Description

doctor

Check store, auth, and search status

auth_status

Show authentication status

Usage examples

Once configured, you can ask your AI client things like:

  • "Show me my recent WhatsApp chats"

  • "Search my messages for 'invoice' from last week"

  • "Send Aurora a message saying I'll be 10 minutes late"

  • "List all my WhatsApp groups"

  • "Who are the participants in the family group?"

  • "Download the image from that last message"

JID format reference

Type

Format

Example

Individual

{country}{number}@s.whatsapp.net

573001234567@s.whatsapp.net

Group

{id}@g.us

120363001234567890@g.us

Phone number

{country}{number}

573001234567

Security considerations

  • All messages are stored locally in ~/.wacli/

  • Data is only sent to the AI model when a tool is explicitly invoked

  • No data leaves the machine except through WhatsApp's own protocol and MCP tool calls

  • The send_message and send_file tools require the AI client to request permission before execution

  • HTTP mode uses a 192-bit Bearer token (secrets.token_urlsafe(32)) stored with mode 0600

  • Recommended: restrict HTTP access to a VPN (e.g. Tailscale) rather than exposing to the public internet

  • wacli uses the unofficial WhatsApp Web API — use at your own risk

Known limitations

  • Re-authentication: WhatsApp sessions expire every ~20 days. Re-scan QR with wacli auth

  • Client outdated errors: WhatsApp updates protocol versions. Keep wacli updated

  • FTS5: Full-text search requires SQLite compiled with FTS5 support. Falls back to LIKE

  • No real-time events: This is a pull-based model (query when asked), not push-based

  • wacli sync must run: For fresh messages, wacli sync should be running or sync_once must be called

License

MIT

Available Tools

27 tools
auth_statusA

Show authentication status of the current wacli session.

Inputs: no named parameters.

doctorB

Run wacli diagnostics — check store, authentication, and search capabilities.

Inputs: no named parameters.

download_mediaB

Download media from a WhatsApp message.

Args:
    message_id: The message ID containing media
    chat: Chat JID where the message is
    output: Optional output file or directory (defaults to wacli media dir)

Inputs: message_id (required), chat (required), output.

group_infoB

Fetch live group info from WhatsApp and update local DB.

Args:
    jid: Group JID (e.g. '120363001234567890@g.us')

Inputs: jid (required).

group_joinA

Join a WhatsApp group by invite code.

Args:
    code: Invite code from a group link (the part after https://chat.whatsapp.com/)

Inputs: code (required).

group_leaveC

Leave a WhatsApp group.

Args:
    jid: Group JID

Inputs: jid (required).

group_participants_addC

Add participants to a WhatsApp group.

Args:
    jid: Group JID
    users: List of phone numbers or JIDs to add

Inputs: jid (required), users (required).

group_participants_demoteA

Demote admin participants in a WhatsApp group.

Args:
    jid: Group JID
    users: List of phone numbers or JIDs to demote

Inputs: jid (required), users (required).

group_participants_promoteB

Promote participants to admin in a WhatsApp group.

Args:
    jid: Group JID
    users: List of phone numbers or JIDs to promote

Inputs: jid (required), users (required).

group_participants_removeB

Remove participants from a WhatsApp group.

Args:
    jid: Group JID
    users: List of phone numbers or JIDs to remove

Inputs: jid (required), users (required).

group_renameC

Rename a WhatsApp group.

Args:
    jid: Group JID
    name: New group name

Inputs: jid (required), name (required).

history_backfillC

Request older messages for a chat from your primary device (best-effort).

Args:
    chat: Chat JID to backfill history for

Inputs: chat (required).

list_chatsB

List WhatsApp chats from local DB.

Args:
    query: Optional search string to filter chats by name
    limit: Max number of chats to return (default 50)

Inputs: query, limit.

list_groupsB

List WhatsApp groups from local DB.

Args:
    query: Optional search string to filter groups
    limit: Max results (default 50)

Inputs: query, limit.

list_messagesB

List recent WhatsApp messages with optional filters.

Args:
    chat: Optional chat JID to filter by
    after: Only messages after this date (YYYY-MM-DD or RFC3339)
    before: Only messages before this date (YYYY-MM-DD or RFC3339)
    limit: Max results (default 50)

Inputs: chat, after, before, limit.

message_contextA

Show surrounding messages around a specific message ID for context.

Args:
    message_id: The message ID to center on
    chat: Chat JID where the message is
    before: Number of messages before (default 5)
    after: Number of messages after (default 5)

Inputs: message_id (required), chat (required), before, after.

refresh_contactsB

Import/refresh contacts from the WhatsApp session store into local DB.

Inputs: no named parameters.

remove_contact_aliasC

Remove a local alias from a contact.

Args:
    jid: Contact JID

Inputs: jid (required).

search_contactsB

Search WhatsApp contacts by name or phone number.

Args:
    query: Search term
    limit: Max results (default 50)

Inputs: query (required), limit.

search_messagesA

Full-text search across WhatsApp messages (FTS5 if available, LIKE fallback).

Args:
    query: Search text
    chat: Optional chat JID filter
    sender: Optional sender JID filter
    after: Only after this date (YYYY-MM-DD or RFC3339)
    before: Only before this date (YYYY-MM-DD or RFC3339)
    media_type: Filter by media type (image|video|audio|document)
    limit: Max results (default 50)

Inputs: query (required), chat, sender, after, +3 more.

send_fileA

Send a file (image, video, audio, document) via WhatsApp.

Args:
    to: Recipient phone number or JID
    file_path: Path to the file on the server
    caption: Optional caption for the file
    filename: Optional display name (defaults to file basename)
    mime: Optional MIME type override

Inputs: to (required), file_path (required), caption, filename, +1 more.

send_messageB

Send a text message via WhatsApp.

Args:
    to: Recipient phone number (e.g. '573001234567') or JID
    message: Text message to send

Inputs: to (required), message (required).

set_contact_aliasA

Set a local alias (nickname) for a contact.

Args:
    jid: Contact JID
    alias: Alias to set

Inputs: jid (required), alias (required).

show_chatB

Show details of a single chat.

Args:
    jid: Chat JID (e.g. '573001234567@s.whatsapp.net' or '...@g.us' for groups)

Inputs: jid (required).

show_contactC

Show details of a single contact.

Args:
    jid: Contact JID (e.g. '573001234567@s.whatsapp.net')

Inputs: jid (required).

show_messageC

Show details of a single message.

Args:
    message_id: The message ID
    chat: Chat JID where the message is

Inputs: message_id (required), chat (required).

sync_onceA

Sync messages once (connect, fetch new messages, then exit).

Args:
    refresh_contacts: Also refresh contacts from session store
    refresh_groups: Also refresh joined groups (live)
    download_media: Download media in the background during sync

Inputs: refresh_contacts, refresh_groups, download_media.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.5/5.0

    Scored across 27 tools

    Disambiguation4/5

    Most tools have distinct purposes, but there is some overlap between list_chats and list_groups (both list entities), and between search_messages and list_messages (both retrieve messages). The descriptions clarify the differences, but an agent might occasionally confuse them.

    Naming Consistency5/5

    Tool names follow a consistent snake_case pattern with clear verb_noun structures (e.g., download_media, group_leave, search_contacts). There are no deviations in naming conventions across all 27 tools.

    Tool Count3/5

    With 27 tools, the count feels heavy for a WhatsApp CLI toolset. While it covers many features, it borders on being overwhelming and could be streamlined without losing core functionality.

    Completeness5/5

    The toolset provides comprehensive coverage for WhatsApp operations, including authentication, messaging, group management, contact handling, media, and syncing. There are no obvious gaps; it supports full CRUD-like workflows for the domain.

    Maintenance

    ActivityInactive
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      C
      quality
      D
      maintenance
      A Model Context Protocol server that connects your personal WhatsApp account to AI agents like Claude, enabling them to search messages, view contacts, retrieve chat history, and send messages via WhatsApp.
      7
      11 npm
      76
      ISC
    • F
      license
      Not graded
      quality
      Not graded
      maintenance
      A self-hosted MCP server that connects AI clients to WhatsApp via the WAHA HTTP API. It enables users to manage sessions, search contacts, and send or receive messages and media directly through natural language interfaces.
      8 npm
      -
    • A
      license
      Not graded
      quality
      B
      maintenance
      WhatsApp MCP server that exposes messaging, groups, contacts, and profile management as tools and resources for AI agents, supporting Baileys and Meta Cloud API.
      273 npm
      29
      MIT
    • A
      license
      Not graded
      quality
      D
      maintenance
      MCP server that connects AI agents to WhatsApp using the multi-device API, enabling messaging, group management, and more as a regular user.
      6 npm
      1
      MIT