wazap-mcp
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| WAZAP_HOST | No | HTTP bind address. | 127.0.0.1 |
| WAZAP_PORT | No | HTTP bind port. | 8766 |
| OPENAI_API_KEY | No | Fallback API key for OpenAI-compatible transcription when WAZAP_TRANSCRIBE_API_KEY is not set. | |
| WAZAP_DATA_DIR | No | Where everything is stored. | ~/.wazap |
| WAZAP_NO_SHARE | No | Set to 1 to opt out of sharing a WhatsApp session between multiple wazap processes. | 0 |
| WAZAP_READ_ONLY | No | Do not register the write tools. Set to 1 to enable read-only mode. | 0 |
| WAZAP_TRANSPORT | No | stdio or http. | stdio |
| WAZAP_PUBLIC_URL | No | The https address agents reach the server at. Must be a bare origin with no path. With WAZAP_OAUTH_PASSWORD, turns OAuth on. | |
| WAZAP_RATE_LIMIT | No | Write tool calls per minute; 0 disables. | 20 |
| WAZAP_READ_TOKEN | No | HTTP bearer token that grants read-only access. Required to bind a non-loopback address without OAuth. | |
| WAZAP_TRANSCRIBE | No | local, openai or off. | off |
| WAZAP_WHISPER_BIN | No | Path to a whisper.cpp binary that is not on PATH. | |
| WAZAP_WRITE_TOKEN | No | HTTP bearer token that also unlocks write tools. | |
| WAZAP_WHISPER_MODEL | No | turbo, large-v3 or medium. | turbo |
| WAZAP_OAUTH_PASSWORD | No | What the consent page asks for. At least 8 characters. With WAZAP_PUBLIC_URL, turns OAuth on. | |
| WAZAP_TRANSCRIBE_URL | No | OpenAI-compatible base URL. | https://api.openai.com/v1 |
| WAZAP_NO_UPDATE_CHECK | No | Set to 1 to stop status asking npm for a newer version. | 0 |
| WAZAP_PERSIST_HISTORY | No | Keep chats and messages across restarts. | 1 |
| WAZAP_TRANSCRIBE_AUTO | No | Transcribe incoming voice notes in the background. Set to 0 to keep the tool and stop background work. | 1 |
| WAZAP_TRANSCRIBE_MODEL | No | Model at that URL. | gpt-4o-mini-transcribe |
| WAZAP_SYNC_FULL_HISTORY | No | Ask WhatsApp for a fuller history sync. | 0 |
| WAZAP_TRANSCRIBE_API_KEY | No | API key for OpenAI-compatible transcription. Never a flag; stored in .env. OPENAI_API_KEY is the fallback. | |
| WAZAP_TRANSCRIBE_LANGUAGE | No | Spoken language, e.g. ro. | auto |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": true
} |
| prompts | {
"listChanged": true
} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| learnA | Read this FIRST, before any other WhatsApp tool. Returns the guide to the tools, the id formats, the recommended workflows, the message shape and every error code with what to do about it. Takes no arguments and never touches WhatsApp. |
| get_statusA | Check the session: connection status ("connected" means the tools work,
"not_linked" means the user must run Call this whenever another tool reports NOT_CONNECTED, NOT_LINKED or
SYNC_IN_PROGRESS, or to confirm which account you are about to send from.
Without account_id the top-level fields are the default account, plus
|
| list_accountsA | List every configured WhatsApp account: id, name, connection status, masked phone, owner name, and whether that account allows writes. Call this first when more than one account is linked, then pass account_id on the other tools. Takes no arguments besides the optional account_id (ignored for the listing). |
| link_accountA | Pair this wazap with the user's WhatsApp when get_status says not_linked, logged_out,
session_corrupt or auth_failure. Ask the user for their phone number in international format,
call this, and show them the code it returns with these exact steps:
WhatsApp → Settings → Linked devices → Link a device → Link with phone number instead → enter the code.
Then call get_status every 10 seconds until it says connected (up to 3 minutes). The code expires;
call this again for a fresh one if get_status goes back to not_linked with an error.
The account must already exist ( |
| list_chatsA | List conversations, most recently active first. Use it to discover the chat_id values the other tools need. Each chat has: chat_id, name, type, unread_count, last_message {text, timestamp, from_me}, archived, pinned, muted_until, and left (groups you are no longer in). |
| read_messagesA | Read messages from one chat, oldest to newest. Without |
| get_recent_messagesA | Everything that happened recently, grouped by chat. This is the catch-up tool: one call instead of list_chats plus a read_messages per chat. WhatsApp's own notices — device linking, group membership changes, encryption notices — are left out so the counts are conversation; pass include_system to see them. |
| get_unansweredA | Chats where the last word is theirs and it asks for something: a question, a request ("poți", "te rog", "can you", "when"…), or a voice note nobody has heard yet. A conversation that ended in "ok, thanks" is not listed, and neither is an ask older than max_age_hours (two weeks by default): that one was abandoned, not left waiting. Groups count only when the user was @-mentioned or replied to after their own last message. A [business] account's ask is often an automatic reply; weigh it accordingly. People come first, then the oldest wait. Each entry quotes the ask, says how many of their messages arrived since the user's last one, and how long they have been waiting. This is the follow-up half of an inbox triage; use get_recent_messages for what happened, and this for who is still waiting. |
| get_storiesA | The stories (status updates) the linked account has received in the last N hours, newest first, each with its author, its text or caption and its time. WhatsApp keeps a story for a day and so does wazap; nothing older is held. With include_previews the photos come as small images, and download_media works on a story's message_id like on any message. Stories never appear in chats, catch-ups or waits; this is the only place they show. |
| set_contact_noteA | Remember something about a person, on this machine only: "Hermi, my own agent", "the accountant", "always answers late". The note then rides along wherever the contact shows: list_chats, search_contacts, get_contact, get_recent_messages and get_unanswered. Nothing is sent to WhatsApp and the contact never sees it. An empty note removes it. |
| mark_handledA | The user dealt with what this chat was asking, outside WhatsApp or by a reply wazap did not see: a phone call, a meeting, a decision. The open ask is remembered as handled and the chat leaves get_unanswered. The next message from the other side makes a new ask and the chat comes back on its own. Kept on this machine only; nothing is sent or marked read on WhatsApp. |
| wait_for_messagesA | Block until a message arrives, then return it, or return empty when the timeout passes. This is how an agent stays on the line without polling: call it in a loop, and pass the cursor it returns into the next call so nothing that landed between two calls is missed. The first matching message starts a one-second settle so a burst comes back together. Only messages from other people are returned, never the user's own, and never WhatsApp's system notices. With addressed_to_me, only direct messages, group messages that @-mention the user, and replies to the user's own messages wake the wait; everything else in a group is ignored. A cursor from a previous run of wazap cannot be honoured: the wait then starts from now and says cursor_reset. The timeout is capped at 55 seconds because MCP clients give up at 60. |
| search_messagesA | Case-insensitive text search over the messages wazap holds locally — all chats, or one chat. It cannot reach messages the phone never synced to this device. |
| get_messageA | The complete message behind a message_id, including the quoted message it replies to, its reactions, and its media metadata. Use it after search_messages or read_messages when you need the context around a single message. |
| search_contactsA | Find contacts by name or phone number (substring match on the name, digit match on the number). Returns contact_id values usable as chat_id. |
| sync_contactsA | Ask WhatsApp to send the linked phone's address book from scratch, and wait up to 15 seconds for it. Nothing on WhatsApp changes: this only refills wazap's own contact list. Use it when get_status reports contacts_named: 0, or when senders in a group read as phone numbers for people you know are saved on the phone. Returns named_before and named_after so you can tell whether it helped; if both are 0 the phone has no saved contacts for these people. |
| get_contactA | Full details for one contact: name, number, about text, profile picture URL, whether they are a saved contact, a business, or blocked. |
| get_group_infoA | Details of a group: name, description, owner, creation date, whether only admins may post, whether the linked account is an admin, and the participant list (up to 500; participant_count is always the true total). The invite link is included only when the linked account is an admin. Call this before manage_group: most group actions need admin rights. |
| download_mediaA | Download the photo/video/audio/document attached to a message and save it to disk on the machine running wazap. Images of 1 MB or less are also returned inline so you can look at them. Fails with MEDIA_UNAVAILABLE when WhatsApp has expired the file. |
| transcribe_audioA | Turn a voice note or an audio message into text. The transcript is cached, so a second call on the same message costs nothing, and from then on the message reads as [voice message · 0:42] "what was said" in read_messages, get_recent_messages and get_message, and its words become searchable through search_messages. What it costs depends on how the user set transcription up: the local provider (whisper.cpp) is free and the audio never leaves the machine, while the API provider uploads the audio to a third-party service and is billed per minute. Either way this is capped at 10 calls a minute. TRANSCRIBE_UNAVAILABLE means transcription is off or unfinished on this machine; the fix names the command the user has to run. Do not retry it. |
| send_messageA | Draft a text message. Does not send. Returns a draft_id and a preview of the recipient and exact text. Show that preview to the user; after they say yes, call confirm_send. A draft lasts 15 minutes. |
| send_mediaA | Draft an image, video, audio file, document or GIF, from a local path on the machine running wazap or from a public URL. Does not send. Exactly one of file_path / url. Maximum 100 MB. Show the preview; after the user says yes, call confirm_send. A GIF is sent with as_gif: an mp4 goes out looping, a .gif is converted to mp4 first (needs ffmpeg on the machine running wazap). |
| send_pollA | Draft a poll. Does not send. Participants vote in WhatsApp; wazap cannot read the votes back. Show the preview; after the user says yes, call confirm_send. |
| send_locationA | Draft a map pin, optionally labelled with a place name and address. Does not send. Show the preview; after the user says yes, call confirm_send. |
| edit_messageA | Replace the text of a message the linked account sent. WhatsApp only allows this within 15 minutes of sending; after that send a correction instead. |
| react_to_messageA | Add an emoji reaction to a message, or pass an empty string to remove your reaction. |
| forward_messageA | Draft a forward of an existing message to another chat. Does not send. The recipient will see it marked as forwarded. Show the preview; after the user says yes, call confirm_send. |
| confirm_sendA | Send a draft created by send_message, send_media, send_poll, send_location or forward_message. This is the only call that reaches WhatsApp. The draft is consumed. A missing or expired draft_id means draft again and show the new preview before calling this. |
| delete_messageA | Retract a message. DESTRUCTIVE and visible to everyone in the chat — confirm with the user first. Only works on messages the linked account sent, and only within 2 days of sending. |
| set_profile_pictureA | Set the linked WhatsApp account's own profile picture from a local path on the machine running wazap or from a public URL. Exactly one of file_path / url. JPEG, PNG or WebP only, at most 10 MB. DESTRUCTIVE and visible to every contact. Show the image and wait for a yes first. This call hits WhatsApp immediately; there is no draft. |
| manage_chatA | Change the state of a chat: archive/unarchive, pin/unpin, mute/unmute (mute_hours defaults to 8), mark_read (sends read receipts) or mark_unread. |
| create_groupA | Create a group with the given name and participants; the linked account becomes the owner. Each participant comes back with a status: ok, invite_needed (their privacy settings require an invite link) or failed. |
| manage_groupA | Administer a group. Actions:
Everything except leave requires the linked account to be a group admin; call get_group_info first to check. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
| wazap-setup | Install, link or repair the WhatsApp connection. Use when the user wants to connect WhatsApp to Claude, when a WhatsApp tool returns NOT_LINKED, SESSION_EXPIRED or SESSION_CORRUPT, when `get_status` is anything but connected, or when the user asks how wazap works with Claude Desktop, Claude Code or HTTP. |
| whatsapp-groups | Catch up on a busy WhatsApp group. Use when the user asks what happened in a group, what was decided, what they are being asked to do there, or for a summary of a thread with many messages. Read-only. |
| whatsapp-inbox | Triage the user's WhatsApp. Use when they ask what they missed, what's unread, who is waiting on a reply, what needs attention today, or whom they forgot to answer. Read-only; it sends nothing and marks nothing read. |
| whatsapp-recall | Find something in the user's WhatsApp history. Use when they ask for an address, invoice, photo, document, link, phone number, date or a thing someone said or sent, or "what did X say about Y". Read-only. |
| whatsapp-send | Send, reply, forward, share a file, react, create a poll, or change the linked account's profile picture on WhatsApp on the user's behalf. Use for any request that results in an outgoing WhatsApp message or a new profile photo. Messages draft first; a profile picture has no draft. Show the image, wait for a yes, then call set_profile_picture. |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
TDQS
Scored across 33 tools
Most tools have clearly distinct purposes, but the set includes several message-reading and account-related tools (read_messages, get_recent_messages, get_unanswered, wait_for_messages; get_status, list_accounts) that could cause hesitation despite descriptive guidance. The draft/send pattern is well differentiated.
Nearly all tools use snake_case with a verb-first pattern, but a few names (learn, get_unanswered, mark_handled, wait_for_messages, confirm_send) deviate from strict verb_noun structure. Still predictable overall.
33 tools is well above the typical 3–15 range for a focused server, and many granular operations (five send_* drafts, multiple get_* variants, manage_group bundling many actions) could be consolidated. This increases selection overhead and cognitive load.
The surface covers core messaging, contacts, groups, media, account linking, and chat management with CRUD-like operations, leaving few dead ends. Minor gaps exist (e.g., no unlink/logout, no block/unblock contacts, no WhatsApp-side contact editing), but agents can work around most.