Skip to Content

MCP tools — reference

Every tool the server exposes, with its name, required scope, input schema, and a sample response. Scopes apply from Phase 4 onwards; in Phase 2 the stdio transport runs as the owner with implicit access.

ping

Health probe. Returns the server’s package version and current timestamp.

FieldValue
Nameping
Scopenone — available to any authenticated context

Input

{}

Output

{ "ok": true, "ts": "2026-05-12T10:21:55.184Z", "version": "0.1.0" }

send_message

Send a plain text WhatsApp message to a single recipient.

FieldValue
Namesend_message
Scopetools:send_message

Input

{ to: string, // phone in any plausible shape — normalised to E.164-no-plus body: string, // message body, UTF-8 phoneNumberId?: string // Meta numeric wa_phone_number_id; defaults to WA_DEFAULT_PHONE_NUMBER_ID }

Output (success)

{ "id": "<our internal UUID>", "wamid": "wamid.XYZ", "status": "sent" }

Failure modes

  • Meta 131047 — the 24h customer-service window has elapsed. The tool throws with a human-readable message naming send_template (deferred to Phase 8) as the recovery path. The DB row is persisted with status='failed' and error_code=131047.
  • Meta 131026 — invalid recipient format. Usually a normalisation bug; check the input.
  • Meta 5xx / 429 — automatically retried up to 3 times with exponential backoff (250 ms → 500 ms → 1 s). Final failure surfaces as a typed MetaError.

get_messages

Cursor-paginated read of inbound + outbound messages.

FieldValue
Nameget_messages
Scopetools:get_messages

Input

{ since?: string, // ISO-8601 timestamp from last response's `next.since`. Wamid strings are REJECTED. sinceId?: string, // UUID tiebreaker, from last response's `next.sinceId` limit?: number, // 1-100, default 20 direction?: 'inbound' | 'outbound' | 'all', // default 'all' contact?: string, // optional wa_id filter (E.164-no-plus) }

Cursor semantics

  • The cursor is (created_at, id). since is the server-side messages.created_at of the last row you’ve seen; sinceId is the optional id tiebreaker for rows with identical created_at.
  • Never pass a wa_message_id as since. The zod schema rejects non-ISO strings.
  • To get the next page, copy next.since and next.sinceId from the previous response.

Output

{ items: Array<{ id: string, // our internal UUID direction: 'inbound' | 'outbound', waMessageId: string | null, contactId: string | null, messageType: string, // 'text', 'image', 'document', 'audio', 'video', 'sticker', 'reaction', 'interactive', 'unknown' body: string | null, status: string, errorCode: number | null, replyToWamid: string | null, payload: object | null, ts: string | null, // ISO; Meta event time createdAt: string, // ISO; pagination cursor // ... }>, next: { since: string, sinceId: string } | null }

mark_read

Send a read receipt to Meta for an inbound message.

FieldValue
Namemark_read
Scopetools:mark_read

Input

{ id: string } // internal messages.id (UUID) returned by get_messages

Output

{ "id": "<our internal UUID>", "status": "read" }

Failure modes

  • The message is outbound — rejected with a clear error.
  • The message has no wa_message_id (shouldn’t happen for inbound) — rejected.
  • Meta failures pass through as typed errors.

Later phases add: send_template, send_media, send_interactive_buttons, send_interactive_list, get_media_url, list_chats, get_contact.