# io.github.razvangirgiz/wazap (npm · wazap-mcp)

WhatsApp for your agent: chats, messages, media, contacts and groups behind MCP tools.

- Trust score: 94/100 (high trust)
- Change this week: +15
- Registry status: active
- Liveness: live
- Owner verified: no
- Last scored: 2026-09-20

## Components

- npm · `wazap-mcp`: 94/100 (this document), [markdown](https://verifymcp.io/servers/razvangirgiz-wazap/wazap-mcp.md), [page](https://verifymcp.io/servers/razvangirgiz-wazap/wazap-mcp)

## Channel facts

- Registry: `npm`
- Package: `wazap-mcp`
- Version: `0.23.1`
- Transport: `stdio`

## Trust breakdown

How this component scores in each security and reliability category. Every signal is checked automatically from public evidence about the published package, including repeated runs of it in an isolated sandbox, and we only credit what we can confirm. Scores are 0–100 per category. Scoring method: https://verifymcp.io/docs/scoring (what has changed: https://verifymcp.io/docs/scoring/changelog)

Scored 2026-09-20.

- **Supply Chain Security**: 98/100
  - No malware found by supply-chain analysis.
  - No known CVEs affecting this package version or its production dependencies.
  - No install/post-install scripts declared.
  - 83 of 268 dependencies flagged as unhealthy.
- **Provenance & Transparency**: 97/100
  - Source repository is publicly reachable at the declared URL.
  - Cryptographically verified build provenance (signed, bound to razvangirgiz/wazap).
  - Clear OSI-approved license (MIT).
  - Actively maintained (last published 3 days ago).
  - Disclosure check failed: no security disclosure policy was found in the source repository.
- **Schema Quality & AI Usability**: 79/100
  - 100% of prompts and resources have a non-trivial description (not blank, and not just the item's name).
  - AI-judged instruction clarity (excellent).
  - Context-footprint check failed: tool/resource definitions use about 9733 tokens (~256/item across 38 items; 38 tools + 0 resources), over budget; trim descriptions and params.
  - Usage-examples check failed: none of the tools include examples.
- **Stability & Change Management**: 91/100
  - Stability check failed: the tool surface changed between 0.10.0 and 0.23.1: 0 tool removals, 1 breaking changes, 14 additions.
- **Tool Coverage**: 100/100
  - 100% of tools have a non-trivial description (not blank, and not just the tool's name).
  - 100% of tool parameters carry a description.
- **Tool Safety**: 100/100
  - No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.
  - All 7 tool(s) whose name or description implies an irreversible operation declare an MCP destructiveHint annotation.
  - An AI judge read all 39 captured unit(s) of tool text and found none that tries to manipulate the model reading it.
- **Capabilities**: 100/100
  - Implements a supported MCP spec version (2025-11-25); the latest is 2026-07-28.

## Install

### How do I install the io.github.razvangirgiz/wazap MCP server?

io.github.razvangirgiz/wazap runs locally as an npm package, launched with npx -y wazap-mcp. Ready-made configuration for Claude, Cursor, VS Code, Codex and 5 more is on this page, copied from each client's own documentation.

### Claude

```bash
claude mcp add razvangirgiz-wazap -- npx -y wazap-mcp
```

### Cursor

```json
{
  "mcpServers": {
    "razvangirgiz-wazap": {
      "command": "npx",
      "args": [
        "-y",
        "wazap-mcp"
      ]
    }
  }
}
```

### VS Code

```json
{
  "servers": {
    "razvangirgiz-wazap": {
      "command": "npx",
      "args": [
        "-y",
        "wazap-mcp"
      ]
    }
  }
}
```

### Codex

```bash
codex mcp add razvangirgiz-wazap -- npx -y wazap-mcp
```

### opencode

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "razvangirgiz-wazap": {
      "type": "local",
      "command": [
        "npx",
        "-y",
        "wazap-mcp"
      ],
      "enabled": true
    }
  }
}
```

### OpenClaw

```bash
openclaw mcp add razvangirgiz-wazap --command npx --arg -y --arg wazap-mcp
```

### Hermes

```yaml
mcp_servers:
  razvangirgiz-wazap:
    command: "npx"
    args: ["-y", "wazap-mcp"]
```

### Netclaw

```json
{
  "McpServers": {
    "razvangirgiz-wazap": {
      "Transport": "stdio",
      "Command": "npx",
      "Arguments": [
        "-y",
        "wazap-mcp"
      ]
    }
  }
}
```

### Vellum

```bash
assistant mcp add razvangirgiz-wazap -t stdio -c npx -a -y wazap-mcp
```

### Other

```json
{
  "mcpServers": {
    "razvangirgiz-wazap": {
      "command": "npx",
      "args": [
        "-y",
        "wazap-mcp"
      ]
    }
  }
}
```

## Changelog

Every change recorded for this component, newest first. Days that predate change tracking, or that we cannot explain, say so: "we were watching and nothing happened" and "we were not watching" are different claims.

### 2026-09-20 (score 94, +1)

No change was recorded against any check on this day. Stability & Change Management went from 88 to 91.

### 2026-09-18 (score 93, +1)

No change was recorded against any check on this day. Stability & Change Management went from 81 to 85.

### 2026-09-17 (score 92, 0)

- [security regression] Stability: fail → unverified
- [security regression] Known CVEs: pass → unverified
- [security regression] Tool safety: pass → unverified
- [functional regression] Capabilities: pass → unverified
- [functional regression] Dependency health: 0.85 → unverified
- [functional regression] Tool coverage: 100 → unverified
- [functional regression] Schema quality: 100 → unverified
- [functional] Package version: 0.22.0 → 0.23.1
- [functional] Package version: 0.22.0 → 0.23.0

### 2026-09-16 (score 92, +26)

- [security regression] Stability: fail → unverified
- [security regression] Tool safety: pass → unverified
- [security improvement] CVE-2026-82417 no longer affects this package
- [security improvement] CVE-2026-82562 no longer affects this package
- [security improvement] Malware scan: unverified → pass
- [security improvement] Known CVEs: unverified → pass
- [functional regression] Capabilities: pass → unverified
- [functional regression] Tool coverage: 100 → unverified
- [functional regression] Schema quality: 100 → unverified
- [functional improvement] Dependency health: unverified → 0.85
- [functional] Package version: 0.20.2 → 0.22.0
- [functional] Package version: 0.20.2 → 0.21.0

### 2026-09-15 (score 66, −14)

- [security regression] Known CVEs: fail → unverified
- [security regression] Malware scan: pass → unverified
- [security regression] Stability: 0.73 → fail
- [security improvement] CVE-2026-82562 no longer affects this package
- [security improvement] CVE-2026-82417 no longer affects this package
- [security improvement] Provenance: fail → pass
- [security improvement] Known CVEs: fail → pass
- [security] The attested source repository moved: razvangirgiz/wazap
- [functional regression] Schema quality: 113 → 248
- [functional regression] Schema quality: 113 → 228
- [functional regression] Schema quality: 113 → 227
- [functional regression] Dependency health: 0.84 → unverified
- [functional] First check of Schema quality: 100
- [functional] Package version: 0.10.0 → 0.20.2
- [functional] Package version: 0.10.0 → 0.20.1
- [functional] Package version: 0.10.0 → 0.20.0
- [functional] Package version: 0.10.0 → 0.19.1
- [functional] Package version: 0.10.0 → 0.18.6

### 2026-09-14 (score 80, +1)

No change was recorded against any check on this day. Stability & Change Management went from 70 to 73. That category is still filling its 30-day observation window: 21 days of observed history at the previous scan, 22 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-09-12 (score 79, +1)

No change was recorded against any check on this day. Stability & Change Management went from 63 to 67. That category is still filling its 30-day observation window: 19 days of observed history at the previous scan, 20 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-09-10 (score 78, +1)

No change was recorded against any check on this day. Stability & Change Management went from 57 to 60. That category is still filling its 30-day observation window: 17 days of observed history at the previous scan, 18 at this one. The score rises as the window fills, whether or not the server changes.

## MCP tools (38)

### `learn` (~90 tokens)

Learn how to use the WhatsApp tools

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.

Input parameters:

- `account_id` (string): Registry account id (default, work, …). Omit to resolve from chat_id or message_id, or the default account.

### `get_status` (~309 tokens)

Get the WhatsApp connection status

Check the session: connection status ("connected" means the tools work,
"not_linked" means the user must run `npx wazap-mcp login`), whether the initial
history sync has finished, which account is linked, how fresh the local history
is — `history` shows when a message last arrived and flags `stale` when the
phone has been quiet for a day while connected — the versions and data
directory in use, and how many contacts carry a name from
the phone's address book (contacts_named: 0 means it never arrived).
\`webhook.delivery` counts the account's webhook events: delivered (last 7
days), failed and cancelled (last 30), pending; a non-zero
\`consecutive_failures` or `retrying` means the receiver is refusing or
unreachable right now, and `last_failure` says how.

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
\`accounts` listing every live one. While a link is in progress the status
is "linking" and `pairing` carries the code the user still has to type
into their phone.

Input parameters:

- `account_id` (string): Registry account id (default, work, …). Omit to resolve from chat_id or message_id, or the default account.

### `list_accounts` (~105 tokens)

List WhatsApp accounts on this server

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).

Input parameters:

- `account_id` (string): Registry account id (default, work, …). Omit to resolve from chat_id or message_id, or the default account.

### `link_account` (~217 tokens)

Link a WhatsApp account

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 (`wazap account add`). Pass account_id when
more than one is configured. An unknown id is ACCOUNT_NOT_FOUND.
Never call this when the account is already linked.

Input parameters:

- `account_id` (string): Registry account id (default, work, …). Omit to resolve from chat_id or message_id, or the default account.
- `phone` (string, required): International format, e.g. +15550100

### `list_chats` (~141 tokens)

List WhatsApp chats

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).

Input parameters:

- `account_id` (string): Registry account id (default, work, …). Omit to resolve from chat_id or message_id, or the default account.
- `filter` (string): Which chats to list; "all" (default) excludes archived ones
- `limit` (integer): Maximum number of chats (1-100)

### `read_messages` (~274 tokens)

Read messages from a WhatsApp chat

Read messages from one chat, oldest to newest.

Without `before` you get the most recent messages. Pass `before` (the oldest
message_id you already have) to page further back; wazap asks the phone for
older history when the local store runs out, which takes a few seconds.

Input parameters:

- `account_id` (string): Registry account id (default, work, …). Omit to resolve from chat_id or message_id, or the default account.
- `before` (string): Return the messages immediately older than this message_id
- `chat_id` (string, required): Chat id as returned by another tool ("<digits>@s.whatsapp.net" or "<id>@g.us"), or a phone number in international format
- `include_previews` (boolean): Attach a small JPEG of each photo, newest first, up to 12 per call, so you can see what was sent: the preview WhatsApp shipped when there is one, otherwise the photo is downloaded once and shrunk on…
- `limit` (integer): Maximum number of messages (1-200)
- `types` (array): Keep only these message types; omit for every type. The limit counts matching messages, so ["call"] returns that many calls, not that many messages of which some are calls.

### `get_recent_messages` (~343 tokens)

Get every WhatsApp conversation from the last N hours

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. A chat
lists at most its newest 2,000 messages of the window.

Input parameters:

- `account_id` (string): Registry account id (default, work, …). Omit to resolve from chat_id or message_id, or the default account.
- `compact` (boolean): Leave out media without a caption and messages with no words in them, fold what one person sent in a row into one line, and say per chat what was left out. About half the size; use it for a routine c…
- `filter` (string): Restrict to unread chats, groups, or one-to-one chats
- `hours` (integer): Look-back window in hours (1-168)
- `include_previews` (boolean): Attach a small JPEG of each photo, newest first, up to 12 per call, so you can see what was sent: the preview WhatsApp shipped when there is one, otherwise the photo is downloaded once and shrunk on…
- `include_system` (boolean): Include WhatsApp's own system notices, which are excluded from the bodies and the counts by default
- `types` (array): Keep only these message types; omit for every type. The limit counts matching messages, so ["call"] returns that many calls, not that many messages of which some are calls.

### `get_unanswered` (~307 tokens)

Find who is waiting on the user

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.

Input parameters:

- `account_id` (string): Registry account id (default, work, …). Omit to resolve from chat_id or message_id, or the default account.
- `limit` (integer): Maximum number of chats (1-50)
- `max_age_hours` (number): Ignore asks older than this; an ask left for two weeks (the default) is abandoned, not waiting
- `min_age_hours` (number): Only asks at least this old, e.g. 48 for people the user forgot for two days

### `get_stories` (~219 tokens)

See the stories people posted

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.

Input parameters:

- `account_id` (string): Registry account id (default, work, …). Omit to resolve from chat_id or message_id, or the default account.
- `hours` (integer): Look-back window in hours (1-24)
- `include_previews` (boolean): Attach a small JPEG of each photo, newest first, up to 12 per call, so you can see what was sent: the preview WhatsApp shipped when there is one, otherwise the photo is downloaded once and shrunk on…

### `set_contact_note` (~149 tokens)

Note something about a contact

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.

Input parameters:

- `account_id` (string): Registry account id (default, work, …). Omit to resolve from chat_id or message_id, or the default account.
- `contact_id` (string, required): Contact id or phone number
- `note` (string, required): What to remember, or "" to remove the note

### `update_contact_details` (~262 tokens)

Tag and annotate a contact

File local, searchable details on a person: tags like "client" or "echipa",
and key-value fields like {"role": "contabil", "oras": "Cluj"}. This is how
"the accountant" or "all suppliers" resolve later — search_contacts matches
tag and field text, and its tag filter lists everyone carrying one. Nothing
is sent to WhatsApp: the contact never sees it and the phone is unchanged.
A field set to "" is deleted; remove_tags / remove_fields take keys away.
Pass at least one of the four edits. The person need not be a saved contact.

Input parameters:

- `account_id` (string): Registry account id (default, work, …). Omit to resolve from chat_id or message_id, or the default account.
- `add_tags` (array): Tags to file under, e.g. ["client", "echipa"]; lowercase tokens, "#" optional
- `contact_id` (string, required): Contact id or phone number
- `fields` (object): Details to set, e.g. {"role": "contabil"}; an empty value deletes the key
- `remove_fields` (array): Detail keys to delete
- `remove_tags` (array): Tags to take off

### `mark_handled` (~169 tokens)

Take a chat off the waiting list

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.

Input parameters:

- `account_id` (string): Registry account id (default, work, …). Omit to resolve from chat_id or message_id, or the default account.
- `chat_id` (string, required): Chat id as returned by another tool ("<digits>@s.whatsapp.net" or "<id>@g.us"), or a phone number in international format

### `wait_for_messages` (~292 tokens)

Wait for new WhatsApp messages

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.

Input parameters:

- `account_id` (string): Registry account id (default, work, …). Omit to resolve from chat_id or message_id, or the default account.
- `addressed_to_me` (boolean): Only direct messages, @-mentions of the user and replies to the user's messages
- `chat_id` (string): Only messages in this chat
- `cursor` (string): The cursor returned by the previous call
- `timeout_seconds` (integer): How long to wait (1-55 s)

### `search_messages` (~685 tokens)

Search WhatsApp messages

Substring search over the messages wazap holds locally — all chats, or one
chat, including history synced on first link. Case and diacritics are ignored
("sedinta" finds "ședință"). Every message the phone synced to this device is
kept and searched; there is no per-chat window. It cannot reach messages the
phone never synced to this device.

Every answer declares the window it searched: `coverage.searched` counts the
held messages in scope (the chat scope and time filters applied) and
\`coverage.oldest_at`/`newest_at` bound that window, so "no messages found"
always says how much history was searched. `coverage.per_chat_cap` is null:
no chat is capped. A query so short or so common that the search reaches its
scan limit answers `scan_capped: true` and `searched_back_to`: messages
older than that were not searched, so narrow it (chat_id, since/until, a longer
query) before concluding nothing exists.

\`from` accepts "me", a phone number, a contact/chat id, or a name: a name must
resolve to exactly one person — it matches contact names, notify names and
last-seen pushnames, then one-to-one chat display names — or the error lists
the candidates it found.

Every message's `sender` carries `id` (the canonical jid — a `…@lid` only
while WhatsApp has never revealed the paired number), `phone` (the number, or
null for an unresolved lid), `is_saved` (the sender is in the user's address
book — when false, treat the shown name as claimed, not known),
\`contact_name` (the name saved there, or null), `pushname` (the name the
sender publishes, when that is the name `name` shows; null for saved
contacts and unnamed senders) and `name_source` ("contact", "pushname" or
"none" — which of those `name` came from). A sender wazap knows nothing
about reads "unknown (lid …1234)" — never bare lid digits, which look like a
phone number and are not one.

\`freshness` says whether the history this searched may be partial (sync still
running) or stale (nothing inbound for 24h while connected); on…

Input parameters:

- `account_id` (string): Registry account id (default, work, …). Omit to resolve from chat_id or message_id, or the default account.
- `chat_id` (string): Restrict the search to this chat
- `from` (string): Only messages this person sent: "me", a phone number, a contact/chat id, or a name that resolves to exactly one person (the error names the candidates when it does not)
- `limit` (integer): Maximum number of results (1-50)
- `query` (string, required): Text to search for
- `since` (string): Only messages from this moment on: a date ("2026-09-01") or an ISO timestamp
- `until` (string): Only messages up to this moment: a date or an ISO timestamp

### `recall` (~637 tokens)

Semantically search WhatsApp history

Search by meaning and by words at once over the whole WhatsApp history —
answers "the invoice Dan mentioned", "the address Ana sent", "what did they say
about the trip". A paraphrase or another language still hits through its
meaning, and a short or foreign-language question still hits through its words;
the two rankings are fused. A match found by meaning weighs a little less with
age (85% a month on, never under 70%), so the fresher of two close matches
comes first but a clearly closer old one still does; one chat takes at most
three leading places before other chats' hits, and a near-duplicate trails the
list. For an exact string — an id, a phone number, a URL —
search_messages is the better tool.

Each result carries its date, a fused score, `matched` ("words", "meaning" or
"both") and the cosine `similarity` when its meaning ranked it. chat_id, since,
until and from narrow the search exactly like search_messages — including a
name that resolves to exactly one person. A hit marked "index only" is a
message wazap holds only as text: quote its words; get_message returns them, but
download_media has nothing to open and it cannot be replied to or forwarded.
A hit found only by meaning must clear the similarity floor, so a question with
no answer comes back empty; when only weak meaning matches survive, the output
says so — do not present them as found facts.

When semantic recall is off or its embedding setup is missing, the tool does
not dead-end: it falls back to a keyword search over the local history, marked
\`mode: "keyword_fallback"`, and `recall_unavailable.fix` names the command
that turns semantic recall on. An error remains only when even the fallback
cannot run.

Each hit's `sender` carries the same identity fields as search_messages, and
\`freshness` says whether the local history may be partial or stale. Every
answer also declares its window: the semantic path reports how many messages
the index covers, and a keyword fallback carries the same `coverage`…

Input parameters:

- `account_id` (string): Registry account id (default, work, …). Omit to resolve from chat_id or message_id, or the default account.
- `chat_id` (string): Restrict the search to this chat
- `from` (string): Only messages this person sent: "me", a phone number, a contact/chat id, or a name that resolves to exactly one person (the error names the candidates when it does not)
- `limit` (integer): Maximum number of results (1-50)
- `query` (string, required): What to find, said any way — the meaning is what matches
- `since` (string): Only messages from this moment on: a date ("2026-09-01") or an ISO timestamp
- `until` (string): Only messages up to this moment: a date or an ISO timestamp

### `get_message` (~472 tokens)

Get one WhatsApp message in full

The complete message behind a message_id, including the quoted message it
replies to, each reaction with who left it, who chose each option of a poll or
answered an event, and its media metadata. Use it after search_messages
or read_messages when you need the context around a single message.

On the user's own messages, `delivery.status` says how far it got ("sent",
"delivered", "read", "played", "pending" or "error"), and in a group
\`read_by` and `delivered_to` name who, with the time. It stops at "delivered"
or is missing when read receipts are off on either side, and large groups may
send none.

The id also resolves in its raw form: `false_<lid>@lid_<stanza>` works even
when the chat's number was never learned, and an id that names the same
message under the lid or the paired number finds it either way. With several
accounts linked and no `account_id`, an id the resolved account cannot find
is tried on each of the others in turn before MESSAGE_NOT_FOUND comes back,
and the answer's `account_id` names the one that had it; pass `account_id`
to keep the lookup on one account.

The `sender` carries the same identity fields as search_messages: `id` (the
canonical jid — a `…@lid` only while WhatsApp has never revealed the paired
number), `phone` (the number, or null for an unresolved lid), `is_saved`
(whether the sender is in the user's address book), `contact_name` (the name
saved there, or null), `pushname` (the name the sender publishes, when that
is the name `name` shows) and `name_source` ("contact", "pushname" or
"none" — which of those `name` came from).

Input parameters:

- `account_id` (string): Registry account id (default, work, …). Omit to resolve from chat_id or message_id, or the default account.
- `message_id` (string, required): Message id from read_messages / search_messages / get_message, e.g. "false_4072...@s.whatsapp.net_3EB0..."

### `search_contacts` (~183 tokens)

Search WhatsApp contacts

Find contacts by name, phone number, tag or detail: a substring match on the
name, a digit match on the number, or a word from a local tag or detail — so
"contabil" finds the person filed under role: contabil by update_contact_details.
With only tag it lists everyone carrying that tag. Returns contact_id values
usable as chat_id.

Input parameters:

- `account_id` (string): Registry account id (default, work, …). Omit to resolve from chat_id or message_id, or the default account.
- `limit` (integer): Maximum number of results (1-50)
- `query` (string): Name fragment, phone number, or tag/detail text (at least 2 characters). Omit with tag to list everyone carrying it.
- `tag` (string): Only contacts filed under this tag ("client"); "#" optional

### `sync_contacts` (~149 tokens)

Fetch the phone's address book again

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.

Input parameters:

- `account_id` (string): Registry account id (default, work, …). Omit to resolve from chat_id or message_id, or the default account.

### `get_contact` (~148 tokens)

Get WhatsApp contact details

Full details for one contact: name, number, about text, profile picture URL,
whether they are a saved contact, a business, or blocked. `name_source` says
where the shown name comes from — "contact" when it is the saved address-book
name (is_my_contact), "pushname" when it is a name the person publishes, or
"none" when there is no usable name.

Input parameters:

- `account_id` (string): Registry account id (default, work, …). Omit to resolve from chat_id or message_id, or the default account.
- `contact_id` (string, required): Contact id from search_contacts / list_chats, or a phone number

### `save_contact` (~206 tokens)

Add or rename a WhatsApp contact

Save a person in the account's WhatsApp contacts: a new entry for a phone
number, or a new name for an existing one. The name syncs to every linked
device, and with save_on_phone (default) also into the phone's own address
book. WhatsApp keeps no other fields — email, "my accountant" and the like go
to set_contact_note, which stays on this machine.

Input parameters:

- `account_id` (string): Registry account id (default, work, …). Omit to resolve from chat_id or message_id, or the default account.
- `contact_id` (string, required): Contact id from search_contacts / get_contact, or a phone number
- `first_name` (string): First name, when it differs from the full name
- `name` (string, required): Full name to save the contact under
- `save_on_phone` (boolean): Also write the contact into the phone's address book; false keeps it inside WhatsApp

### `remove_contact` (~97 tokens)

Remove a WhatsApp contact

Take a person out of the account's WhatsApp contacts: the saved entry and its
name go, the chat and its history stay. Nothing is sent to the contact.

Input parameters:

- `account_id` (string): Registry account id (default, work, …). Omit to resolve from chat_id or message_id, or the default account.
- `contact_id` (string, required): Contact id from search_contacts / get_contact, or a phone number

### `get_group_info` (~198 tokens)

Get WhatsApp group info

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. The settings come too: info_locked
(only admins edit the name, description and photo), member_add_mode ("admins"
or "all"), join_approval, disappearing_seconds (0 when off), and community
({is_community, parent_group_id}) when the group is a community or belongs to one.

Call this before manage_group: most group actions need admin rights.

Input parameters:

- `account_id` (string): Registry account id (default, work, …). Omit to resolve from chat_id or message_id, or the default account.
- `group_id` (string, required): Group chat id ("<id>@g.us")

### `download_media` (~384 tokens)

Download media from a WhatsApp message

Download the photo/video/audio/document attached to a message and save it to
disk on the machine running wazap. The file at `path` is already decrypted —
open or process it as is; nothing else is needed. Images of 1 MB or less are
also returned inline so you can look at them.

The structured result carries: `path` (the saved file), `mime`, `size`
(bytes), `filename` (the name it was saved under — a timestamped name wazap
made, not the sender's), `original_filename` (the name the sender's file had,
or null when the envelope carried none), `caption` (the text the sender wrote
under the media, or null — audio and voice notes cannot carry one),
\`message_id` and `sender` (the same identity fields as search_messages —
is_saved, contact_name, pushname, name_source — or null when even the message
can no longer be read back).

The id resolves in its raw form too: `false_<lid>@lid_<stanza>` works whether
or not the chat's number was ever learned — when it was, the lid spelling finds
the same message filed under the paired number. An unresolved sender never
blocks the file. Without `account_id` the same lookup walks every linked
account's store in turn before failing, and the result's `account_id` names
the one that served the file.

Fails with MEDIA_UNAVAILABLE when WhatsApp has expired the file.

Input parameters:

- `account_id` (string): Registry account id (default, work, …). Omit to resolve from chat_id or message_id, or the default account.
- `message_id` (string, required): A message with has_media=true
- `save_to` (string): Absolute directory to save into (default: <data-dir>/media)

### `transcribe_audio` (~260 tokens)

Transcribe a WhatsApp voice message

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.

Input parameters:

- `account_id` (string): Registry account id (default, work, …). Omit to resolve from chat_id or message_id, or the default account.
- `language` (string): ISO 639-1 code of what is spoken, e.g. "ro"; "auto" detects it. Omit to use the configured default.
- `message_id` (string, required): A message whose type is voice or audio

### `send_message` (~247 tokens)

Draft a WhatsApp text message

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.

To @-mention people, pass mention_ids and write @<number> in the text where
each mention belongs, the digits of their id (@40722123456). A mention the
text lacks gets its @<number> added at the end, so the preview is the text
that goes out.

Input parameters:

- `account_id` (string): Registry account id (default, work, …). Omit to resolve from chat_id or message_id, or the default account.
- `chat_id` (string, required): Chat id as returned by another tool ("<digits>@s.whatsapp.net" or "<id>@g.us"), or a phone number in international format
- `mention_ids` (array): Chat ids to @-mention; write @<number> in the text for each, or wazap adds it at the end
- `reply_to` (string): Quote-reply to this message
- `text` (string, required): The message text

### `send_media` (~286 tokens)

Draft a WhatsApp media message

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).

Input parameters:

- `account_id` (string): Registry account id (default, work, …). Omit to resolve from chat_id or message_id, or the default account.
- `as_document` (boolean): Send as a plain document instead of rendered media
- `as_gif` (boolean): Send a .gif or an mp4 as a looping GIF, the way WhatsApp plays them
- `as_voice` (boolean): Send an audio file as a voice note (push-to-talk)
- `caption` (string): Text shown under the media
- `chat_id` (string, required): Chat id as returned by another tool ("<digits>@s.whatsapp.net" or "<id>@g.us"), or a phone number in international format
- `file_path` (string): Absolute path of a local file to send
- `url` (string): Public http(s) URL to fetch and send

### `send_poll` (~171 tokens)

Draft a WhatsApp poll

Draft a poll. Does not send. Participants vote in WhatsApp, and their votes
show on the poll message: read_messages counts them and get_message says who
chose each option. Show the preview; after the user says yes, call confirm_send.

Input parameters:

- `account_id` (string): Registry account id (default, work, …). Omit to resolve from chat_id or message_id, or the default account.
- `chat_id` (string, required): Chat id as returned by another tool ("<digits>@s.whatsapp.net" or "<id>@g.us"), or a phone number in international format
- `multi_select` (boolean): Allow voters to pick more than one option
- `options` (array, required): Answer options (2-12)
- `question` (string, required): The poll question

### `send_location` (~160 tokens)

Draft a WhatsApp location

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.

Input parameters:

- `account_id` (string): Registry account id (default, work, …). Omit to resolve from chat_id or message_id, or the default account.
- `address` (string): Street address shown under the name
- `chat_id` (string, required): Chat id as returned by another tool ("<digits>@s.whatsapp.net" or "<id>@g.us"), or a phone number in international format
- `latitude` (number, required): Latitude in decimal degrees
- `longitude` (number, required): Longitude in decimal degrees
- `name` (string): Place name shown on the pin

### `edit_message` (~96 tokens)

Edit a WhatsApp message you sent

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.

Input parameters:

- `account_id` (string): Registry account id (default, work, …). Omit to resolve from chat_id or message_id, or the default account.
- `message_id` (string, required): A message the linked account sent
- `text` (string, required): The replacement text

### `react_to_message` (~119 tokens)

React to a WhatsApp message

Add an emoji reaction to a message, or pass an empty string to remove your reaction.

Input parameters:

- `account_id` (string): Registry account id (default, work, …). Omit to resolve from chat_id or message_id, or the default account.
- `emoji` (string, required): A single emoji such as "👍", or "" to remove your reaction
- `message_id` (string, required): Message id from read_messages / search_messages / get_message, e.g. "false_4072...@s.whatsapp.net_3EB0..."

### `forward_message` (~131 tokens)

Draft a forwarded WhatsApp message

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.

Input parameters:

- `account_id` (string): Registry account id (default, work, …). Omit to resolve from chat_id or message_id, or the default account.
- `message_id` (string, required): Message id from read_messages / search_messages / get_message, e.g. "false_4072...@s.whatsapp.net_3EB0..."
- `to_chat_id` (string, required): Destination chat

### `confirm_send` (~199 tokens)

Send a drafted WhatsApp message

Send a draft created by send_message, send_media, send_poll, send_location or
forward_message. This is the only call that reaches WhatsApp, and it sends a
draft at most once: confirming it again answers the same receipt with
already_sent: true. Only the MCP session that created the draft may confirm it.
After reinitializing or reconnecting with a new session, draft again and obtain
fresh user approval. A missing or expired draft_id also means draft again and
show the new preview before calling this. SEND_OUTCOME_UNKNOWN means WhatsApp
may have the message: check the chat with read_messages before anything else,
and never draft it again without asking the user.

Input parameters:

- `account_id` (string): Registry account id (default, work, …). Omit to resolve from chat_id or message_id, or the default account.
- `draft_id` (string, required): The draft_id returned by a send_* tool

### `delete_message` (~243 tokens)

Delete a WhatsApp message

Delete a message. DESTRUCTIVE — confirm with the user first. for_everyone is
required, and picks one of two different deletes; tell the user which:
  \- for_everyone: true retracts it for everyone in the chat. Works on messages
    the linked account sent, within 2 days of sending. In a group where the
    linked account is an admin it also takes someone else's message, deleted
    as an admin; anywhere else that is NOT_OWN_MESSAGE.
  \- for_everyone: false removes it from the linked account's own devices only:
    anyone's message, at any age. Nobody else sees a change.

Input parameters:

- `account_id` (string): Registry account id (default, work, …). Omit to resolve from chat_id or message_id, or the default account.
- `for_everyone` (boolean, required): Required. true retracts it for everyone in the chat; false deletes it for the linked account only
- `message_id` (string, required): Message id from read_messages / search_messages / get_message, e.g. "false_4072...@s.whatsapp.net_3EB0..."

### `set_profile_picture` (~160 tokens)

Set the linked WhatsApp profile picture

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.

Input parameters:

- `account_id` (string): Registry account id (default, work, …). Omit to resolve from chat_id or message_id, or the default account.
- `file_path` (string): Absolute path of a local JPEG, PNG or WebP
- `url` (string): Public http(s) URL to fetch and use as the photo

### `manage_chat` (~385 tokens)

Manage a WhatsApp chat

Change a chat, or a message in it. Actions:
  \- archive / unarchive, pin / unpin (the chat), mute / unmute (mute_hours
    defaults to 8), mark_read (sends read receipts) / mark_unread
  \- pin_message / unpin_message — need message_id; pins it for everyone in the
    chat, for pin_hours 24, 168 (default) or 720
  \- star_message / unstar_message — need message_id; the star is the linked
    account's own
  \- clear — DESTRUCTIVE, empties the chat for the linked account only
  \- delete — DESTRUCTIVE, deletes the chat for the linked account only
  \- block / unblock — a person's chat only; a blocked person can no longer
    message or call the account

A message_id must belong to chat_id. Every action hits WhatsApp at once and
there is no draft: say what will change and wait for a yes before calling it.

Input parameters:

- `account_id` (string): Registry account id (default, work, …). Omit to resolve from chat_id or message_id, or the default account.
- `action` (string, required): What to do with the chat
- `chat_id` (string, required): Chat id as returned by another tool ("<digits>@s.whatsapp.net" or "<id>@g.us"), or a phone number in international format
- `message_id` (string): The message for pin_message, unpin_message, star_message and unstar_message; it must be in chat_id
- `mute_hours` (integer): Hours to mute, default 8; only used by "mute"
- `pin_hours` (number): How long pin_message keeps the message pinned: 24, 168 (default) or 720 hours

### `create_group` (~112 tokens)

Create a WhatsApp group

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.

Input parameters:

- `account_id` (string): Registry account id (default, work, …). Omit to resolve from chat_id or message_id, or the default account.
- `name` (string, required): Group name
- `participant_ids` (array, required): Chat ids or phone numbers to add (1-256)

### `join_group` (~239 tokens)

Join a WhatsApp group from an invite

Join a group from an invite: a link (https://chat.whatsapp.com/<code>, or the
code alone) as invite, or the message_id of an invite message someone sent
(type "invite"). Exactly one of invite / message_id.

Without confirm: true it joins nothing and returns the group's name,
description, member count and whether an admin must approve new members.
Show that to the user and wait for an explicit yes, then call again with the
same invite or message_id and confirm: true. Every member sees the account
join. The answer is the group's chat_id, or pending_approval when an admin
must let the account in first.

Input parameters:

- `account_id` (string): Registry account id (default, work, …). Omit to resolve from chat_id or message_id, or the default account.
- `confirm` (boolean): true joins, only after the user said yes to the preview; omit to preview
- `invite` (string): A https://chat.whatsapp.com/ link, or its code
- `message_id` (string): An invite message (type "invite") from read_messages

### `manage_group` (~501 tokens)

Manage a WhatsApp group

Administer a group. Actions:
  \- add / remove / promote / demote — need participant_ids; each participant
    comes back with status ok, invite_needed or failed
  \- leave — DESTRUCTIVE, rejoining needs an invite
  \- set_subject / set_description — need value
  \- set_picture — needs exactly one of file_path / url: JPEG, PNG or WebP, at
    most 10 MB. Every member sees it at once and there is no draft: show the
    image and wait for a yes first
  \- remove_picture — takes the group photo down; ask first the same way
  \- get_invite_link / revoke_invite_link
  \- list_join_requests — who is waiting for approval, with when and how they asked
  \- approve_join_requests / reject_join_requests — need participant_ids from
    list_join_requests; each comes back with status ok or failed
  \- set_announcement_only / set_info_locked / set_join_approval — value "on" or "off"
  \- set_add_mode — value "admins" or "all"
  \- set_disappearing — value "off", "24h", "7d" or "90d"

Everything except leave requires the linked account to be a group admin; call
get_group_info first to check. Every change is visible to all members at once:
say what will change and wait for a yes before calling it.

Input parameters:

- `account_id` (string): Registry account id (default, work, …). Omit to resolve from chat_id or message_id, or the default account.
- `action` (string, required): Group action to perform
- `file_path` (string): set_picture: absolute path of a local JPEG, PNG or WebP
- `group_id` (string, required): Group chat id ("<id>@g.us")
- `participant_ids` (array): Targets of add/remove/promote/demote/approve_join_requests/reject_join_requests
- `url` (string): set_picture: public http(s) URL to fetch and use as the photo
- `value` (string): New subject or description; "on"/"off" for set_announcement_only, set_info_locked, set_join_approval; "admins"/"all" for set_add_mode; "off"/"24h"/"7d"/"90d" for set_disappearing

## Diagnostics

Captured diagnostic sections: Provenance, Dependencies. The full working is on the page: https://verifymcp.io/servers/razvangirgiz-wazap/wazap-mcp#diagnostics

## Score history

- 2026-09-20: 94
- 2026-09-19: 93
- 2026-09-18: 93
- 2026-09-17: 92
- 2026-09-16: 92
- 2026-09-15: 66
- 2026-09-14: 80
- 2026-09-13: 79
- 2026-09-12: 79
- 2026-09-11: 78
- 2026-09-10: 78
- 2026-09-09: 77
- 2026-09-08: 77
- 2026-09-07: 76
- 2026-09-06: 76
- 2026-09-05: 76
- 2026-09-04: 75
- 2026-09-03: 75
- 2026-09-02: 76
- 2026-09-01: 75
- 2026-08-31: 75
- 2026-08-30: 74
- 2026-08-29: 71
- 2026-08-28: 71
- 2026-08-27: 71
- 2026-08-26: 71
- 2026-08-25: 70
- 2026-08-24: 70
- 2026-08-23: 59

## Common questions

### What is the io.github.razvangirgiz/wazap MCP server?

io.github.razvangirgiz/wazap is an MCP server listed in the public MCP registry as io.github.razvangirgiz/wazap. WhatsApp for your agent: chats, messages, media, contacts and groups behind MCP tools. This page covers its npm package (wazap-mcp).

### Is the io.github.razvangirgiz/wazap MCP server safe to use?

io.github.razvangirgiz/wazap scores 94 out of 100 on VerifyMCP. We found no known CVEs affecting it as of 20 September 2026. It declares no install or post-install scripts. Its build provenance is signed and verified. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.

### What tools does the io.github.razvangirgiz/wazap MCP server expose?

io.github.razvangirgiz/wazap exposes 38 tools: learn, get_status, list_accounts, link_account, list_chats, and 33 more. Their descriptions and schemas cost roughly 9,345 tokens of context every time the server is loaded.

### Is the io.github.razvangirgiz/wazap MCP server still maintained?

io.github.razvangirgiz/wazap is still listed as active in the MCP registry. We last reached this channel on 20 September 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.

### What licence is the io.github.razvangirgiz/wazap MCP server under?

io.github.razvangirgiz/wazap declares the MIT licence, which is OSI-approved. That covers the source only, and says nothing about the cost of any service it calls.

## Links

- npm package: https://www.npmjs.com/package/wazap-mcp
- Socket report: https://socket.dev/npm/package/wazap-mcp
- Repository: https://github.com/razvangirgiz/wazap
- Changelog RSS feed: https://verifymcp.io/servers/razvangirgiz-wazap/wazap-mcp.xml
- Changelog JSON feed: https://verifymcp.io/servers/razvangirgiz-wazap/wazap-mcp.json
- HTML version of this page: https://verifymcp.io/servers/razvangirgiz-wazap/wazap-mcp
