Skip to main content
Glama

WhatsApp MCP Platform (core engine)

Link a WhatsApp number by QR code, send/read messages over a REST API, and expose the same connection to Claude (or any MCP client) as a remote MCP server -- the core engine behind a "Blueticks-style" product.

This is the core engine only: QR linking, the REST API, and the MCP server. No dashboard UI, auth beyond API keys, or billing yet -- see "Turning this into a sellable product" below for what to add next.

How it fits together

Customer's phone  <-- QR / WhatsApp Web protocol -->  Baileys socket (per account)
                                                              |
                                                    in-memory chat/message store
                                                              |
                                    -----------------------------------------
                                    |                                       |
                              REST API (/qr, /send,                  MCP server (/mcp)
                              /chats, /messages, ...)                 tools: whatsapp_status,
                              -- for your own dashboard/scripts       whatsapp_get_qr,
                                                                       whatsapp_send_message,
                                                                       whatsapp_list_chats,
                                                                       whatsapp_read_messages

Each customer = one "account": a WhatsApp connection identified by an API key. You (the operator) create accounts with your MASTER_KEY; each customer's key then scopes every REST and MCP call to their connection only -- one account can never see another's messages.

WhatsApp connectivity is via Baileys, an open-source library that speaks the WhatsApp Web protocol directly (the same QR-linking flow as web.whatsapp.com). This is not the official WhatsApp Business API -- see "Important: ToS and ban risk" below.

Setup

npm install
cp .env.example .env
# edit .env and set a real MASTER_KEY

npm run build
npm start
# or, for auto-reload during development:
npm run dev

The server listens on PORT (default 3000) and logs the admin and MCP URLs on startup.

Provisioning a customer (admin API)

All /admin/* routes require Authorization: Bearer <MASTER_KEY>.

# Create an account -- do this once per customer, store the apiKey for them
curl -X POST http://localhost:3000/admin/accounts \
  -H "Authorization: Bearer $MASTER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "Customer Name"}'
# -> { "id": "...", "name": "...", "apiKey": "wap_live_...", "createdAt": "..." }

# List accounts (status only -- apiKey is only ever shown once, at creation)
curl http://localhost:3000/admin/accounts -H "Authorization: Bearer $MASTER_KEY"

# Remove an account (logs the number out and deletes its session)
curl -X DELETE http://localhost:3000/admin/accounts/<id> -H "Authorization: Bearer $MASTER_KEY"

Linking a WhatsApp number (customer's own key)

# Start the connection and get a QR code (base64 PNG data URL)
curl http://localhost:3000/qr -H "Authorization: Bearer $CUSTOMER_API_KEY"
# -> { "status": "qr_pending", "qrAvailable": true, "qrDataUrl": "data:image/png;base64,..." }

Render qrDataUrl as an <img> in a dashboard, or decode and display it any other way. Have the customer scan it from their phone: WhatsApp > Settings > Linked Devices > Link a device. Poll /status (or /qr again) until status becomes "connected".

REST API reference

All routes below (except /admin/* and /health) require Authorization: Bearer <customer apiKey>.

Method & path

Purpose

GET /me

Account info + current status

POST /connect

Start/resume the connection if it's not live

GET /qr

Current status + QR code (if pending)

GET /status

Current status only

POST /send

{ "to": "+1555...", "message": "..." }

GET /chats?limit=20

Recently active chats

GET /messages?chatId=...&limit=30

Recent messages in one chat

POST /logout

Unlink the number and clear its session

Connecting Claude (or any MCP client)

The MCP endpoint is POST {your-server-url}/mcp, authenticated the same way as the REST API: Authorization: Bearer <customer apiKey>.

In Claude: Settings/Customize -> Connectors -> Add custom connector, paste your server's /mcp URL. If Claude's custom-connector UI doesn't prompt for a bearer token directly, front the endpoint with a URL-embedded token variant or an OAuth shim -- the stateless handler in src/mcp/server.ts is the piece to adapt if you want a different auth scheme (e.g. OAuth, like Blueticks' remote connector).

Tools exposed: whatsapp_status, whatsapp_get_qr, whatsapp_send_message, whatsapp_list_chats, whatsapp_read_messages.

Important: ToS and ban risk

Baileys (and every similar tool, including the third-party services we discussed) connects as an unofficial WhatsApp Web client, not Meta's Business API. WhatsApp's terms don't sanction third-party automation like this, so any number connected this way carries some risk of being flagged or restricted -- more so with high message volume or spammy behavior. If you productize this:

  • Say so plainly in your terms of service; don't imply Meta affiliation or endorsement.

  • Rate-limit sends per account and avoid enabling bulk/unsolicited messaging patterns -- that's both the biggest ban trigger and the behavior most likely to draw abuse complaints against your platform.

  • Consider offering (or migrating power users to) the real WhatsApp Business API for anyone sending real volume.

Known MVP limitations (by design, for a first cut)

  • In-memory chat/message store. Restarting the process clears chat history seen so far (linked sessions persist on disk in sessions/ and reconnect automatically -- only the message cache is volatile). Swap in Postgres/SQLite before real usage.

  • No dashboard. Account creation and QR display are API-only; build a small frontend against /admin/* and /qr.

  • No billing/metering. Nothing tracks usage per account yet.

  • Single process. Fine for one server; for horizontal scaling you'd move account runtime state (and the Baileys auth state) into shared storage instead of in-process Maps.

  • Text messages only. Sending/reading media, groups-specific actions, and reactions aren't wired up yet, though Baileys supports all of it.

Turning this into a sellable product

Roughly in priority order:

  1. Swap the JSON-file store and in-memory chat cache for a real database.

  2. Add a minimal dashboard: create account, show QR, show status, revoke.

  3. Add per-account rate limiting and usage metering (for billing + abuse prevention).

  4. Add Stripe (or similar) for subscriptions/usage-based billing.

  5. Decide your auth story for the MCP connector specifically -- API-key Bearer (current, simplest) vs. OAuth (nicer UX, more setup, closer to what Blueticks/Wassenger do).