MCP server + CLI to send WhatsApp messages (text, image, document, location, contact) from any Claude CLI session, via a Baileys-backed always-on local daemon — multi-number, draft/confirm, no third-party API, no server, no database. macOS, Linux, Windows.
Strong tool design with clear naming (verb_noun pattern), comprehensive schemas, and detailed descriptions. All 12 tools have explicit input schemas with proper JSON Schema structure. Descriptions average ~180 chars, well within the 10-1024 baseline. Key strengths: draft/confirm pattern prevents accidental sends (pattern:confirmation-request); idempotent confirm_send; multi-message-type support. Gaps: no tool annotations (readOnlyHint/destructiveHint/idempotentHint); output schemas not formally documented in code; some parameter descriptions could be more prescriptive about constraints.
Discard a pending draft so it can never be sent. Use this when the user rejects the preview or wants it redrafted. Takes the draftId from draft_message; that id is dead afterwards.
Cancel a scheduled send so it never fires. Takes the scheduledId from schedule_send or list_scheduled. Only a pending one can be cancelled — an already-sent or failed entry is left untouched.
Send a previously drafted message. The ONLY tool that dispatches. Idempotent: replaying a draftId returns the original result instead of re-sending. Runs a pre-send health check and returns a clear error (never a false "sent") if the number is not connected.
Build a preview of a message and store it as a draft. Does NOT send. Supports kind=text|image|document|location|contact. Message text may be written in Markdown — **bold**, _italic_, ~~strike~~, `code`, # headings, [links](url), lists, and newlines are auto-converted to WhatsApp formatting (pass raw:true to send verbatim). For image/document, pass an absolute file `path` (sensitive paths like ~/.ssh or .env are refused) with optional `text` caption. Returns { draftId, preview, expiresInSec }. Show the preview to the user and get explicit confirmation, then call confirm_send.
No tool annotations (readOnlyHint, destructiveHint, idempotentHint) in MCP tool definitions. Current spec (2026-07-28) expects these to guide agent behavior and safety.
Output schemas not formally documented in code. While draft_message and confirm_send describe return structure in text, no JSON Schema is visible for response validation or LLM planning.
draft_message parameter descriptions lack explicit constraints. 'path' accepts 'absolute file path' but does not specify max length, allowed extensions, or size limits. 'latitude'/'longitude' lack range constraints (±90, ±180).
Inferred effective spec: <=2025-11-25.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | C | 68 | <=2025-11-25 | v2 |
Daemon status + the state of all linked WhatsApp numbers (sessions) + pending-scheduled count. Returns JSON.
Can a given number (or the default) send right now? Returns { ok, status, canSend, reason }.
List the WhatsApp groups a number belongs to (jid, subject, participant count).
Recent send history (metadata only — no message bodies): timestamp, session, recipient, kind, message id, status. Optionally filter by session.
List scheduled sends (optionally filtered by status: pending|sent|failed|cancelled).
List all linked WhatsApp numbers with label, phone, status, and which is default.
Resolve a name / phone / group reference to a WhatsApp JID before drafting. Returns a single match, or { candidates } when ambiguous.
Send a previously drafted message at a future time, fired by the always-on daemon (works even if this session is closed). The draft is snapshotted, so it is fine for it to expire afterward. One-time only. Returns { scheduledId, fireAt }.
list_scheduled and list_recent lack pagination parameters (limit, offset, cursor). Large result sets could exhaust context window. Descriptions do not mention result limits or truncation behavior.
Error handling descriptions are minimal. Tools do not document recovery paths (e.g., 'If recipient not found, call resolve_recipient first'). No guidance on retryable vs. fatal errors.