Skip to main content
Glama
ianaleck

pywa-mcp-server

๐Ÿ’ฌ pywa MCP Server

Python MCP pywa License: MIT Tests

Unofficial Model Context Protocol (MCP) server that exposes the pywa WhatsApp Cloud API client as MCP tools.

โš ๏ธ Disclaimer: Unofficial, third-party integration. Not affiliated with, endorsed by, or sponsored by WhatsApp, Meta, or the pywa project.

โœจ Features

  • ๐Ÿ”— Full pywa surface โ€” ~100 tools auto-discovered from the pywa WhatsApp client

  • ๐Ÿ›ก๏ธ Type coercion โ€” auto-parses JSON strings, hydrates dataclasses, coerces enums

  • ๐Ÿ“ฆ Smart serialization โ€” bytes โ†’ base64, datetime โ†’ ISO, Path โ†’ string, enum โ†’ value, dataclass โ†’ dict

  • ๐ŸŽ›๏ธ Allowlist control โ€” restrict exposed tools via PYWA_MCP_TOOLS env

  • โš™๏ธ Var-args support โ€” pywa methods with *args exposed via an _args field

  • ๐Ÿงช Unit-tested โ€” 37 tests covering serialization, coercion, schema, discovery

  • ๐Ÿ“– MCP compliant โ€” works with Claude Code, Claude Desktop, Cursor, Cline, etc.

๐Ÿš€ Quick Start

Prerequisites

  • Python 3.13+

  • uv

  • A Meta-approved WhatsApp Business phone number + access token

  • An MCP-compatible client

1. Get pywa credentials

From the Meta Business dashboard:

  • Phone Number ID โ€” Meta โ†’ WhatsApp โ†’ API Setup

  • Access token โ€” system-user token (permanent) or temporary

  • WABA ID โ€” required for templates / flows / QR codes

  • App ID / App secret โ€” required for update validation + callback registration

2. Register with Claude Code

Option A โ€” claude mcp add (recommended, zero-install)

Runs the server directly from PyPI via uvx โ€” no clone required.

Basic syntax:

claude mcp add [options] <name> -- <command> [args...]

Real example:

claude mcp add --transport stdio --scope user \
  --env WHATSAPP_PHONE_ID=<phone-id> \
  --env WHATSAPP_TOKEN=<token> \
  --env WHATSAPP_WABA_ID=<waba-id> \
  --env WHATSAPP_APP_ID=<app-id> \
  --env WHATSAPP_APP_SECRET=<app-secret> \
  pywa \
  -- uvx --from pywa-mcp-server pywa-mcp-server

Scope (--scope / -s):

  • local (default) โ€” private to this project

  • user โ€” across all your projects

  • project โ€” committed via .mcp.json

Other useful commands:

claude mcp list                 # list all configured servers
claude mcp get pywa             # show details + health-check pywa
claude mcp remove pywa          # remove from config
claude mcp add-json pywa '<json>'  # add via raw JSON string
claude mcp add-from-claude-desktop # import servers from Claude Desktop (Mac/WSL)
claude mcp reset-project-choices   # reset approved/rejected project-scoped servers
claude mcp serve                   # run Claude Code itself as an MCP server

To run from latest git (unreleased changes) instead of PyPI:

... -- uvx --from git+https://github.com/ianaleck/pywa-mcp-server pywa-mcp-server

Option B โ€” .mcp.json in the project

cp .mcp.json.example .mcp.json
# edit credentials; .mcp.json is gitignored

Option C โ€” Claude Desktop / Cursor / Cline / other clients

Any MCP client that supports stdio works. Add:

{
  "mcpServers": {
    "pywa": {
      "command": "uvx",
      "args": ["--from", "pywa-mcp-server", "pywa-mcp-server"],
      "env": {
        "WHATSAPP_PHONE_ID": "...",
        "WHATSAPP_TOKEN": "...",
        "WHATSAPP_WABA_ID": "...",
        "WHATSAPP_APP_ID": "...",
        "WHATSAPP_APP_SECRET": "..."
      }
    }
  }
}

Local development install

If you're modifying the server, clone and run from source:

git clone https://github.com/ianaleck/pywa-mcp-server.git
cd pywa-mcp-server
uv sync
# then point claude mcp add at:
#   uv --directory /absolute/path/to/pywa-mcp-server run pywa-mcp-server

๐ŸŽฏ What You Can Do

Once connected, you can ask Claude to:

๐Ÿ’ฌ Messaging

  • "Send a WhatsApp text to +27..., 'Order confirmed'"

  • "Send the totp template to +27... with code 483921"

  • "React to message wamid.xxx with ๐ŸŽ‰"

๐Ÿ“‹ Templates

  • "List all my approved templates"

  • "Create a UTILITY template called 'order_shipped' with one body variable"

  • "Show me the totp template details"

๐ŸŒŠ Flows

  • "List all published flows"

  • "Show metrics for the customer_pick flow over the last 30 days"

  • "Fetch the JSON for flow 903243286082414"

๐Ÿ–ผ๏ธ Media

  • "Upload this image URL and send it to +27..."

  • "Download media wamid.xxx to /tmp"

๐Ÿ”— QR Codes

  • "Create a QR code with the message 'Hi Orana!'"

  • "Update QR code XYZ123 with a new prefilled message"

๐Ÿฅ Health & Diagnostics

  • "Check my WABA health status"

  • "What's blocking my business-initiated sends?"

๐Ÿ› ๏ธ Tool Catalog

Auto-discovered from pywa.WhatsApp. Full pywa docs at pywa.readthedocs.io.

  • send_text / send_message

  • send_image / send_video / send_audio / send_voice

  • send_document / send_sticker

  • send_location / send_contact

  • send_reaction / remove_reaction

  • send_template

  • send_catalog / send_product / send_products

  • request_location

  • get_template / get_templates

  • create_template / update_template / delete_template

  • compare_templates (uses _args for second template ID)

  • unpause_template

  • upsert_authentication_template

  • migrate_templates

  • get_flow / get_flows / get_flow_assets / get_flow_metrics

  • create_flow / update_flow_json / update_flow_metadata

  • publish_flow / deprecate_flow / delete_flow

  • migrate_flows

  • upload_media / download_media

  • get_media_url / get_media_bytes (returns base64-wrapped bytes)

  • delete_media

  • โš ๏ธ stream_media returns a Python generator โ€” unusable via MCP. Use get_media_bytes or download_media instead.

  • create_qr_code

  • get_qr_code / get_qr_codes

  • update_qr_code

  • delete_qr_code

  • get_business_profile / update_business_profile

  • get_business_account

  • get_commerce_settings / update_commerce_settings

  • get_business_phone_number / get_business_phone_numbers

  • get_business_phone_number_settings / update_business_phone_number_settings

  • update_display_name

  • register_phone_number / deregister_phone_number

Requires SIP configuration; may be blocked if not enabled.

  • initiate_call / terminate_call

  • pre_accept_call / accept_call / reject_call

  • get_call_permissions

Note: this server doesn't run a webhook receiver โ€” registration only.

  • set_app_callback_url

  • override_phone_callback_url / override_waba_callback_url

  • delete_phone_callback_url / delete_waba_callback_url

  • set_business_public_key

  • block_users / unblock_users

  • get_blocked_users

  • mark_message_as_read

  • indicate_typing

  • update_conversational_automation

  • get_app_access_token

โš™๏ธ Environment Variables

Variable

Required

Notes

WHATSAPP_PHONE_ID / WA_PHONE_ID

โœ…

Phone Number ID

WHATSAPP_TOKEN / WA_TOKEN

โœ…

System-user or business access token

WHATSAPP_WABA_ID / WA_BUSINESS_ACCOUNT_ID

โš ๏ธ

WABA-scoped methods (templates, flows, QR codes)

WHATSAPP_APP_ID / WA_APP_ID

โš ๏ธ

Callback registration with APP scope

WHATSAPP_APP_SECRET / WA_APP_SECRET

โš ๏ธ

Update validation + APP-scoped callbacks

PYWA_MCP_TOOLS

โŒ

Comma-separated allowlist. Unset = all minus DEFAULT_SKIP.

๐ŸŽ›๏ธ Tool Filtering

Default: every public pywa method except listen (which blocks indefinitely).

Allowlist: set PYWA_MCP_TOOLS=send_text,get_templates,send_template to expose only those.

โš ๏ธ Caveats

  • No inbound webhook. This server only sends/admin. listen, on_*, webhook_* register handlers / wait for inbound updates, but no FastAPI/Flask is run. For inbound support, run pywa's webhook in a separate process.

  • stream_media returns a Python generator โ€” unusable via MCP JSON transport. Use get_media_bytes (base64) or download_media (writes to disk, returns path).

  • 24-hour window. Free-form sends to a user require they messaged you in the last 24h. Otherwise use an approved template.

  • WABA health. Check get_business_account / get_business_phone_number โ€” payment, calling SIP, and other issues cause silent send failures (can_send_message=BLOCKED).

  • Schema drops type info for unions. MCP transport may stringify complex args; this server compensates by JSON-parsing strings when target type expects list/dict/dataclass.

๐Ÿ”„ Response Serialization

Input

Output

bytes

{"_bytes_b64": "...", "size": N}

pathlib.Path

string

datetime / date / time

ISO 8601

timedelta

seconds (float)

enum.Enum

.value

dataclass

recursive dict

pydantic BaseModel

model_dump(mode="json")

other

JSON if serializable, else repr()

๐Ÿงช Development

git clone https://github.com/ianaleck/pywa-mcp-server.git
cd pywa-mcp-server
uv sync

# Run tests
uv run pytest tests/

# With coverage
uv run pytest tests/ --cov=main --cov-report=term-missing

37 unit tests, ~70% coverage. Remainder is async server boot (not unit-testable).

๐Ÿ“‹ API Requirements

This server requires a WhatsApp Cloud API account. You must comply with:

๐Ÿค Contributing

  1. Fork the repository

  2. Create a feature branch (git checkout -b feature/amazing-feature)

  3. Make your changes with tests

  4. Ensure all tests pass (uv run pytest tests/)

  5. Commit your changes

  6. Open a Pull Request

๐Ÿ“„ License

MIT โ€” see LICENSE.

๐Ÿ™ Acknowledgments

๐Ÿ“ž Support


Made with โค๏ธ for the MCP community

โญ Star this project if you find it useful!

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

Maintenance

โ€“Maintainers
โ€“Response time
โ€“Release cycle
1Releases (12mo)

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/ianaleck/pywa-mcp-server'

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