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.
| Field | Value |
|---|---|
| Name | ping |
| Scope | none — 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.
| Field | Value |
|---|---|
| Name | send_message |
| Scope | tools: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 namingsend_template(deferred to Phase 8) as the recovery path. The DB row is persisted withstatus='failed'anderror_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.
| Field | Value |
|---|---|
| Name | get_messages |
| Scope | tools: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).sinceis the server-sidemessages.created_atof the last row you’ve seen;sinceIdis the optionalidtiebreaker for rows with identicalcreated_at. - Never pass a
wa_message_idassince. The zod schema rejects non-ISO strings. - To get the next page, copy
next.sinceandnext.sinceIdfrom 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.
| Field | Value |
|---|---|
| Name | mark_read |
| Scope | tools:mark_read |
Input
{ id: string } // internal messages.id (UUID) returned by get_messagesOutput
{ "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.