Skip to Content
WhatsApp MCPPlan & PhasesWhatsApp MCP Server — Plan

WhatsApp MCP Server — Plan

A private, multi-tenant-ready MCP server exposing WhatsApp messaging via the Meta Cloud API. Built for the owner’s own projects first, then for explicitly-approved external clients. Security and internal use are the top priority.

Supersedes the single-tenant SQLite sketch in ../whatsapp-mcp-plan.md with Postgres, multi-tenant API-key auth, remote MCP transport, Inngest Cloud, and Docker Compose deployment — while keeping a clean local-stdio dev mode for the owner.

Project status

Current phase: Phase 1 — Skeleton (stdio). Implementation is in place and CI is green on main. Two DoD items remain open: registering the wamcp_ custom pattern with GitHub Secret Scanning, and the manual end-to-end check of Claude Code calling ping over stdio.

Each phase file has a Definition of Done checklist at the bottom — open the phase file, tick boxes as work lands, the phase is considered done when every box (including the final signoff) is ticked.

PhaseStatusDone when
Phase 0 — Meta setup⬜ Not startedMeta App + WABA + token + DNS + host baseline + Inngest Cloud all green
Phase 1 — Skeleton (stdio)🟡 In progresspnpm dev boots stdio MCP; Claude lists tools + calls ping; CI green
Phase 2 — Outbound + webhook⬜ Not startedsend_message + reply round-trip with rows in messages
Phase 3 — Inngest⬜ Not startedWebhook returns 200 in < 50ms; sends queued + durable across restarts
Phase 4 — Auth + audit⬜ Not startedSecond client cannot exceed scope/grant/rate; every call audited
Phase 5 — Remote MCP⬜ Not startedRemote Claude receives notifications/resources/updated within ~1s
Phase 6 — Media + interactive⬜ Not startedImage + button round-trips work; signed URLs gated by auth + tenancy
Phase 7 — Production deploy⬜ Not startedsystemctl restart whatsapp-mcp brings full stack back; /health 200 from open internet
Phase 8 — Future⬜ DeferredOut of scope for v1 — sketches only

Status legend: ⬜ Not started · 🟡 In progress · ✅ Done · ⏸ Blocked

To update: edit the Status column above as phases advance. Single source of truth for “where are we?”. The detailed checkboxes inside each phase file tell you what’s left; this table tells you which phase to look at.


Locked architectural decisions

AreaDecision
WhatsApp APIMeta Cloud API, Graph v23.0 (bumped quarterly)
Tenant modelSingle shared WABA number in v1; multi-tenant schema from day one
MCP transportRemote Streamable HTTP/SSE + local stdio
Client authPer-client API keys (wamcp_live_...) with tool + number scopes
Background workInngest Cloud (orchestrator); functions execute inside the Node app
DatastorePostgreSQL 16 via Docker Compose
DeploymentDocker Compose on a single Ubuntu host
Reverse proxyNginx + certbot (Let’s Encrypt)
Inbound → MCPnotifications/resources/updated push + get_messages pull backfill
v1 message typesText (24h window), media (image/doc/audio), interactive (buttons/list). Templates deferred.
Media storageLocal disk /var/lib/whatsapp-mcp/media, signed-URL tool only
Audit + limitsPer-client audit log + per-key RPM + per-client daily message cap from v1
RuntimeNode.js 20+, TypeScript strict, ESM, Drizzle ORM
TestingVitest (unit) + Vitest + testcontainers (integration); coverage gates per file group
DocsHand-written architecture + auto-generated TSDoc reference + per-component docs

File index

Cross-cutting

FilePurpose
architecture.mdSchema spine, auth pipeline, scope model, Inngest events, notification routing, secrets, env vars, threat model
standards.mdEngineering standards: mandatory tests, code commenting, TSDoc → markdown reference, docs structure

Phases

FileEffortGoal
phase-0-meta-setup.mdSExternal accounts, secrets, DNS, host baseline
phase-1-skeleton.mdMTypeScript MCP server skeleton on stdio with Postgres + Drizzle
phase-2-outbound-and-webhook.mdLEnd-to-end send + receive single-tenant
phase-3-inngest.mdLMove side effects behind Inngest functions
phase-4-auth-audit.mdLAPI keys, scopes, grants, rate limits, audit log
phase-5-remote-mcp.mdMStreamable HTTP/SSE transport + notifications
phase-6-media-and-interactive.mdLMedia + interactive messages
phase-7-production-deploy.mdLDocker Compose + Nginx + SOPS + backups
phase-8-future.mddeferredTemplates, multi-number ops, portal, scale-out, OpenTelemetry

Ops runbook stubs

FilePurpose
ops/client-onboarding.mdOnboard / offboard an MCP client
ops/phone-number-onboarding.mdAdd a new WABA number to the server
ops/incident-runbook.mdToken rotation, DB restore, cert failure, pepper rotation
ops/upgrade.mdGraph API version bump procedure
ops/backups.mdBackup, restore, off-host sync verification

Effort summary

v1 (Phases 0–7): roughly 4–6 focused engineering weeks for one experienced TypeScript developer. Meta verification waits overlap Phase 0–1.

Verification milestones (high level)

  • After Phase 2 — stdio send_message + reply round-trip with rows in messages.
  • After Phase 4 — second client with scoped key cannot exceed scope/grant/rate; every call audited.
  • After Phase 5 — remote Claude on another machine receives notifications/resources/updated within ~1s; offline backfill via get_messages works.
  • After Phase 7systemctl restart whatsapp-mcp brings the whole stack back; https://wa.<yourdomain>/health reachable; restore from yesterday’s pg_dump succeeds in staging compose.

How to use this plan

  1. Read architecture.md and standards.md before starting any phase. They are the contract everything else honours.
  2. Execute phases in order. Each phase file has explicit Tests and Acceptance sections — a phase is not done until both are green.
  3. Phase docs reference each other freely. Cross-cutting concerns (auth, Inngest events, schema) live in architecture.md; phases describe what each one builds on top.