Turbo Notify: WhatsApp for AI agents
com.turbonotify/whatsappturbonotify.comstreamable-httpv0.1.4Scanned
Send and receive WhatsApp messages from your AI agent, on your own number, with Turbo Notify.
36 tools: 6 can delete or cancel things, 8 do something that can't be undone, 4 change things and 18 only read.
- Reads private data · 13
- Reads text others write · 36
- Can send data out · 10
- 4 findings
Sign-in required, OAuth metadata published. Remote posture
Anlyon alternative
18 of these tools change something. Run those as governed actions.
Declare what an action changes, and Anlyon puts it under a shared limit, binds every approval to the exact request, and grades the outcome. It works on any HTTPS API you can describe as an action.
Anlyon does not run, proxy or govern this MCP server. A tool your own code calls directly is not routed through Anlyon and is not governed by it.
Import an OpenAPI spec →Tools
Grouped by what each tool does, most consequential first. Open one for its reasons and full description; descriptions are the publisher's own words.
36 tools
Deletes or cancels 6Claude Code: deny
cancel_calendar_eventCancel a calendar event this number already sent.Reads text others writeNo findings
Why it is in this group
- The name "cancel_calendar_event" says "cancel": a word for removing or ending something.
- The server marks it destructive (destructiveHint: true).
What it can reach
- untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.
Description, as the server wrote it Cancel a calendar event this number already sent. The event stays in the chat marked as canceled, and WhatsApp tells the guests. Send the same `name` and `start_at` the event has, plus the details you want the canceled card to still show (`description`, `end_at`, `join_link`, `location`, `has_reminder` with `reminder_offset_sec`, `extra_guests_allowed`): the canceled event is shown with what you send here, and anything you leave out disappears from it. Cancel when the date, time or place change and the guests must accept again: cancel the old event here, then send the new one with `send_calendar_event`, because the old answer does not carry over to a new event. To correct a mistake (a typo, the description, a link) use `edit_calendar_event` instead, and the guests keep their answer. A cancel does not change the entry a guest already added to their own calendar with the card's "Add to calendar" button. It stays on the old date, and adding again creates a second entry, which only the guest can delete. Only a calendar event can be canceled. Using this tool on any other message answers `invalid_request: message_not_editable`; to take back a message of another kind, use `delete_message`. Only a message sent THROUGH Turbo Notify can be canceled. One typed on the phone itself belongs to the same number but cannot be canceled here, and neither can a message received from someone else. The 15-minute limit for a text or a caption does not apply to a calendar event. The cancel is accepted at once and the outcome arrives as an event: `message.edited` when it worked, `message.edit.failed` when WhatsApp or the platform could not apply it. Read them with `list_events`. The number first appears online and its unread messages in that conversation are marked as read, then the cancel happens. Opening the conversation this way is automatic and uses no quota of its own.
Annotations the server declares
- destructiveHint: true
- idempotentHint: false
- openWorldHint: true
- readOnlyHint: false
- title: Cancel a calendar event already sent
delete_messageRevoke a message for everyone in the conversation.Reads text others writeNo findings
Why it is in this group
- The name "delete_message" says "delete": a word for removing or ending something.
- The server marks it destructive (destructiveHint: true).
What it can reach
- untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.
Description, as the server wrote it Revoke a message for everyone in the conversation. This is WhatsApp's "delete for everyone". It is not reversible, and recipients see a tombstone where the message was, so they can tell something was deleted. Confirm with a human before using it on anything a person has plausibly already read. The number first appears online and its unread messages in that conversation are marked as read, then the message is deleted. Opening the conversation this way is automatic and uses no quota of its own.
Annotations the server declares
- destructiveHint: true
- idempotentHint: false
- openWorldHint: true
- readOnlyHint: false
- title: Delete a message for everyone
edit_calendar_eventCorrect a calendar event this number already sent.Reads text others writeNo findings
Why it is in this group
- The name "edit_calendar_event" says "edit": a word for a change.
- The server marks it destructive (destructiveHint: true).
What it can reach
- untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.
Description, as the server wrote it Correct a calendar event this number already sent. `name` and `start_at` are always required, plus whatever else the event has: `description`, `end_at`, `join_link`, `location`, `has_reminder` with `reminder_offset_sec`, and `extra_guests_allowed`. This REPLACES the whole event: anything you leave out is removed from it, including its location, reminder and guest setting, so send again every detail you want to keep. The event keeps its place in the chat and the guests keep the answer they already gave. Use an edit to fix a mistake (a typo, the description, a link). When the date, time or place change and the guests must accept again, do not edit: cancel the event with `cancel_calendar_event` and send a new one with `send_calendar_event`, because the old answer does not carry over to a new event. Neither an edit nor a cancel changes the entry a guest already added to their own calendar with the card's "Add to calendar" button. It stays on the old date, and adding again creates a second entry, which only the guest can delete. Only a calendar event can be edited here. Using this tool on any other message answers `invalid_request: message_not_editable`. Only a message sent THROUGH Turbo Notify can be edited. One typed on the phone itself belongs to the same number but cannot be edited here, and neither can a message received from someone else. The 15-minute limit does not apply to a calendar event. The edit is accepted at once and the outcome arrives as an event: `message.edited` when it worked, `message.edit.failed` when WhatsApp or the platform could not apply it. Read them with `list_events`. The number first appears online and its unread messages in that conversation are marked as read, then the edit happens. Opening the conversation this way is automatic and uses no quota of its own.
Annotations the server declares
- destructiveHint: true
- idempotentHint: false
- openWorldHint: true
- readOnlyHint: false
- title: Edit a calendar event already sent
edit_media_captionReplace the caption of an image, video or document this number already sent.Reads text others writeNo findings
Why it is in this group
- The name "edit_media_caption" says "edit": a word for a change.
- The server marks it destructive (destructiveHint: true).
What it can reach
- untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.
Description, as the server wrote it Replace the caption of an image, video or document this number already sent. The recipient sees the new caption marked as edited. The file itself stays exactly as it was and the message keeps its id: only the caption changes. There is no way to replace the file; to send a different one, send a new message with `send_media`. Only an image, video or document can be edited this way. Audio and stickers have no caption, and a location, contact card or link button cannot be edited at all: `delete_message` is the only change for them. For the text of a text message use `edit_text_message`. Using this tool on any other message answers `invalid_request: message_not_editable`. Only a message sent THROUGH Turbo Notify can be edited. One typed on the phone itself belongs to the same number but cannot be edited here, and neither can a message received from someone else. WhatsApp only accepts a change to a caption within 15 minutes of sending it. A later edit is refused up front with `invalid_request: edit_window_expired`; send a new message instead. The edit is accepted at once and the outcome arrives as an event: `message.edited` when it worked, `message.edit.failed` when WhatsApp or the platform could not apply it. Read them with `list_events`. The number first appears online and its unread messages in that conversation are marked as read, then the edit happens. Opening the conversation this way is automatic and uses no quota of its own.
Annotations the server declares
- destructiveHint: true
- idempotentHint: false
- openWorldHint: true
- readOnlyHint: false
- title: Edit the caption of an image, video or document
edit_text_messageReplace the text of a text message this number already sent.Reads text others writeCan send data outNo findings
Why it is in this group
- The name "edit_text_message" says "edit": a word for a change.
- The server marks it destructive (destructiveHint: true).
What it can reach
- untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.
- outboundThe name "edit_text_message" says "edit" and "message": it writes something other people read.
Description, as the server wrote it Replace the text of a text message this number already sent. The recipient sees the new text marked as edited. The message keeps its id and its place in the conversation; only the text changes. Only a text message can be edited here. For the caption of an image, video or document use `edit_media_caption`, and for a calendar event `edit_calendar_event` or `cancel_calendar_event`. A location, contact card, link button, sticker or audio message cannot be edited at all: `delete_message` is the only change for them. Using this tool on any message that is not text answers `invalid_request: message_not_editable`. Only a message sent THROUGH Turbo Notify can be edited. One typed on the phone itself belongs to the same number but cannot be edited here, and neither can a message received from someone else. WhatsApp only accepts a change to a text within 15 minutes of sending it. A later edit is refused up front with `invalid_request: edit_window_expired`; send a new message instead. The edit is accepted at once and the outcome arrives as an event: `message.edited` when it worked, `message.edit.failed` when WhatsApp or the platform could not apply it. Read them with `list_events`. The number first appears online and its unread messages in that conversation are marked as read, then the edit happens. Opening the conversation this way is automatic and uses no quota of its own.
Annotations the server declares
- destructiveHint: true
- idempotentHint: false
- openWorldHint: true
- readOnlyHint: false
- title: Edit the text of a message already sent
react_to_messageAttach an emoji reaction to a message, or remove one.Reads text others writeCan send data outNo findings
Why it is in this group
- The name "react_to_message" says "react": a word for a change.
- The server marks it destructive (destructiveHint: true).
What it can reach
- untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.
- outboundThe name "react_to_message" says "react" and "message": it writes something other people read.
Description, as the server wrote it Attach an emoji reaction to a message, or remove one. Pass a single emoji character, e.g. `👍`. Passing an empty string removes the reaction this number previously left. A new emoji replaces this number's previous reaction rather than adding beside it, so a repeated call overwrites something the recipient can already see. Works on messages in either direction: reacting to something a customer sent is often a cheaper acknowledgement than a reply. The number first appears online and its unread messages in that conversation are marked as read, then the reaction is sent. Opening the conversation this way is automatic and uses no quota of its own. Needs a plan that includes reactions. On a plan that does not, this answers `payment_required: feature_not_available`, and the answer will not change by retrying.
Annotations the server declares
- destructiveHint: true
- idempotentHint: false
- openWorldHint: true
- readOnlyHint: false
- title: React to a message with an emoji
Can't be undone 8Claude Code: ask
reply_to_messageReply to an EARLIER message, quoting it, without naming a recipient.Reads text others writeCan send data outNo findings
Why it is in this group
- The name "reply_to_message" says "reply": a word for sending, and what is sent cannot be taken back.
What it can reach
- untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.
- outboundThe name "reply_to_message" says "reply": it sends or publishes something to other people.
Description, as the server wrote it Reply to an EARLIER message, quoting it, without naming a recipient. Use it to bring context into the conversation: when the conversation has moved on and your answer depends on something said a while ago (by the contact, by you or, in a group, by any participant). To answer the message you just received, do not use this tool: send a normal message with `send_text` and let the chat carry the context, as a person would. The recipient is worked out from the original: an inbound message is answered to its sender, an outbound one to whoever received it, and a group message to the group. The message must belong to the inbox named by `number_alias`; a `message_id` from a different number returns not found. Like a person opening the chat, the number first appears online and its unread messages in that conversation are marked as read, then the reply is sent. Opening the conversation this way is automatic and uses no quota of its own.
Annotations the server declares
- destructiveHint: false
- idempotentHint: false
- openWorldHint: true
- readOnlyHint: false
- title: Reply to a message in the same conversation
send_calendar_eventSend a calendar event invitation.Reads text others writeCan send data outNo findings
Why it is in this group
- The name "send_calendar_event" says "send": a word for sending, and what is sent cannot be taken back.
What it can reach
- untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.
- outboundThe name "send_calendar_event" says "send": it sends or publishes something to other people.
Description, as the server wrote it Send a calendar event invitation. Set exactly one recipient: `number`, `group_id`, or `contact_id`. `start_at` and `end_at` are ISO 8601 **with an offset**, for example `2026-09-10T14:00:00Z`. A timestamp without one is rejected: the recipient's phone renders the event in their own timezone, so an ambiguous instant would show the wrong hour on their screen. When set, `end_at` must be after `start_at`; an event that ends before (or exactly when) it starts is rejected. `location` pins where it happens (`latitude` and `longitude`, with an optional `name` and `address`). `has_reminder` reminds guests, and `reminder_offset_sec` says how many seconds before the event; `extra_guests_allowed` lets guests bring extra people. To fix a mistake later (a typo, the description, a link), use `edit_calendar_event`: guests keep their answer. When the date, time or place change and guests must accept again, cancel the event with `cancel_calendar_event` and send a new one with this tool. The entry a guest added to their own calendar with the card's "Add to calendar" button never follows an edit or a cancel: it stays on the old date and adding again creates a second entry, so tell guests to delete the old one. To see who said they are coming, use `get_message_responses`.
Annotations the server declares
- destructiveHint: false
- idempotentHint: false
- openWorldHint: true
- readOnlyHint: false
- title: Send a calendar event
send_contact_cardSend a contact card the recipient can save to their phone.Reads text others writeCan send data outNo findings
Why it is in this group
- The name "send_contact_card" says "send": a word for sending, and what is sent cannot be taken back.
What it can reach
- untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.
- outboundThe name "send_contact_card" says "send": it sends or publishes something to other people.
- outboundThe input takes a destination (input.phones): what the tool writes goes there.
Description, as the server wrote it Send a contact card the recipient can save to their phone. Set exactly one recipient: `number`, `group_id`, or `contact_id`. `phones` takes plain E.164 numbers, 1 to 10 of them. `emails`, `organization`, `title` and `address` are optional extra fields on the card; `emails` allows up to 10 as well. A contact card cannot be edited after it is sent: `delete_message` is the only change for it.
Annotations the server declares
- destructiveHint: false
- idempotentHint: false
- openWorldHint: true
- readOnlyHint: false
- title: Send a contact card
send_cta_buttonSend an interactive message carrying one link button.Reads text others writeCan send data outNo findings
Why it is in this group
- The name "send_cta_button" says "send": a word for sending, and what is sent cannot be taken back.
What it can reach
- untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.
- outboundThe name "send_cta_button" says "send": it sends or publishes something to other people.
Description, as the server wrote it Send an interactive message carrying one link button. Set exactly one recipient: `number`, `group_id`, or `contact_id`. A link button is the only interactive type WhatsApp accepts on this kind of connection. Reply buttons and list pickers are not available and will not become available, so do not try to emulate them. `body` is the text above the button (up to 1024 characters), `button_text` its label (up to 20), `header` and `footer` optional short lines above and below (up to 60 each). A message with a link button cannot be edited after it is sent: `delete_message` is the only change for it.
Annotations the server declares
- destructiveHint: false
- idempotentHint: false
- openWorldHint: true
- readOnlyHint: false
- title: Send a message with a call-to-action link button
send_locationSend a location pin.Reads text others writeCan send data outNo findings
Why it is in this group
- The name "send_location" says "send": a word for sending, and what is sent cannot be taken back.
What it can reach
- untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.
- outboundThe name "send_location" says "send": it sends or publishes something to other people.
- outboundThe input takes a URL (input.url): a request to a URL can carry data to whoever runs it.
Description, as the server wrote it Send a location pin. Set exactly one recipient: `number`, `group_id`, or `contact_id`. `latitude` is between -90 and 90, `longitude` between -180 and 180. `name` and `address` label the pin; `url` adds a link beside it. A location cannot be edited after it is sent: `delete_message` is the only change for it.
Annotations the server declares
- destructiveHint: false
- idempotentHint: false
- openWorldHint: true
- readOnlyHint: false
- title: Send a map location
send_mediaSend media by URL: an image, video, audio clip, document or sticker.Reads text others writeCan send data outNo findings
Why it is in this group
- The name "send_media" says "send": a word for sending, and what is sent cannot be taken back.
What it can reach
- untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.
- outboundThe name "send_media" says "send": it sends or publishes something to other people.
- outboundThe input takes a URL (input.url): a request to a URL can carry data to whoever runs it.
Description, as the server wrote it Send media by URL: an image, video, audio clip, document or sticker. Set exactly one recipient: `number`, `group_id`, or `contact_id`. `url` must still be reachable when the send happens: Turbo Notify fetches the bytes from it. A copy is kept in the organization's own object storage afterwards when that is configured, which never blocks the send and is not required for one. `caption` applies to `image`, `video` and `document` only; audio and stickers have nowhere to render one. `filename` applies to `document` only. Pairing either with a kind that cannot carry it is rejected here, before anything is sent. After sending, only the caption of an image, video or document can be changed, with `edit_media_caption`. The file itself, an audio clip and a sticker cannot be edited: `delete_message` is the only change for them. `mime_type` is optional and does not change how the message is sent. The type WhatsApp receives comes from what the URL's own server serves, so point `url` at a file that server labels correctly.
Annotations the server declares
- destructiveHint: false
- idempotentHint: false
- openWorldHint: true
- readOnlyHint: false
- title: Send an image, video, audio, document or sticker
send_textSend a WhatsApp text message.Reads text others writeCan send data outNo findings
Why it is in this group
- The name "send_text" says "send": a word for sending, and what is sent cannot be taken back.
What it can reach
- untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.
- outboundThe name "send_text" says "send": it sends or publishes something to other people.
Description, as the server wrote it Send a WhatsApp text message. Set exactly one recipient: `number`, `group_id`, or `contact_id`. Writing `@<number>` in the text mentions that person, for example `@5511999999999`. The platform detects it automatically; there is no separate mentions argument. This is also how to answer the message you just received: a normal message to its sender, as a person does in a chat, with no quote. Quote (`quoted_message_id`, or `reply_to_message`) only an earlier message that your answer depends on. A text message you sent can be corrected later with `edit_text_message`. Like a person opening the chat, the number first appears online and its unread messages in that conversation are marked as read, then the message is sent. Opening the conversation this way is automatic and uses no quota of its own. Returns the new `message_id` and its initial status. Delivery happens asynchronously; poll `get_message_status` or watch `list_events` for the outcome.
Annotations the server declares
- destructiveHint: false
- idempotentHint: false
- openWorldHint: true
- readOnlyHint: false
- title: Send a text message
send_typing_indicatorShow "typing…" to a recipient while you prepare an answer.Reads text others writeCan send data outNo findings
Why it is in this group
- The name "send_typing_indicator" says "send": a word for sending, and what is sent cannot be taken back.
What it can reach
- untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.
- outboundThe name "send_typing_indicator" says "send": it sends or publishes something to other people.
Description, as the server wrote it Show "typing…" to a recipient while you prepare an answer. Set exactly one recipient: `number`, `group_id`, or `contact_id`. Useful before a reply that takes a few seconds to compose, so the person on the other end sees something is happening. It expires on its own, so clearing it is optional: pass `is_typing=false` only if you decide not to answer after all and want the indicator gone at once. There is no duration argument. WhatsApp decides how long it lives. Starting to type also opens the conversation: the number appears online, its unread messages in that conversation are marked as read, and after a short pause "typing…" appears, so it never shows over unread messages. Stopping (`is_typing=false`) only clears the indicator and reads nothing. Opening the conversation this way is automatic and uses no quota of its own. Needs a plan that includes the typing indicator, and showing it in a group needs one that includes group messaging as well. Without either, this answers `payment_required: feature_not_available`, and the answer will not change by retrying.
Annotations the server declares
- destructiveHint: false
- idempotentHint: false
- openWorldHint: true
- readOnlyHint: false
- title: Show the typing indicator in a conversation
Changes things 4Claude Code: ask
connect_numberReconnect a number that is currently offline.Reads text others write1 finding
Why it is in this group
- Nothing in its name or description says what it does, so it is counted as a write.
What it can reach
- untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.
Findings
- Info
The description mentions "credentials".
What this check looks forMatched text
description: "...er that is currently offline. Uses the credentials already stored for the number, so no QR..."
Description, as the server wrote it Reconnect a number that is currently offline. Uses the credentials already stored for the number, so no QR code and no human are involved. Use it when `get_number` reports `disconnected` and you want to send. Returns immediately, before the connection is established. The answer has the number's `alias`, `status` set to `connecting` and `at`, the moment the request was accepted. The number reaches `connected` a few seconds later: poll `get_number`, which stays the source of the current status and of its own `at`, rather than assuming it worked. It works the same on every number and never charges. On an extra number scheduled for removal it does not change the removal: the number keeps working until its date. Cancelling a removal is not available to agents. This cannot rescue a `revoked` number: that one was unlinked from the phone and needs a person to pair it again. Calling it again changes nothing further. A number that is already `connected` or `connecting` answers with an `already_connected` or `already_connecting` error, which means the goal is met, not that the call failed.
Annotations the server declares
- destructiveHint: false
- idempotentHint: true
- openWorldHint: true
- readOnlyHint: false
- title: Bring a number back online
disconnect_numberTake a number offline deliberately, keeping its link to the phone.Reads text others write1 finding
Why it is in this group
- Nothing in its name or description says what it does, so it is counted as a write.
What it can reach
- untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.
Findings
- Info
The description mentions "credentials".
What this check looks forMatched text
description: "...ping its link to the phone. The stored credentials survive, so `connect_number` brings it..."
Description, as the server wrote it Take a number offline deliberately, keeping its link to the phone. The stored credentials survive, so `connect_number` brings it straight back with no pairing. Nothing is deleted. Disconnecting does not stop billing: an extra number is billed every cycle until it is removed. Returns immediately, before the number is offline. The answer has the number's `alias`, `status` set to `disconnecting` and `at`, the moment the request was accepted. `disconnecting` is not a status that `get_number` reports: it keeps showing the previous status and its own `at` until the number is offline. While disconnected the number cannot send or receive, and messages addressed to it are refused rather than queued. Deliberate: a message held for an unknown period and delivered later is worse than one that failed clearly. Calling it again changes nothing further. A number that is already offline answers with an `already_disconnected` error, which means the goal is met, not that the call failed.
Annotations the server declares
- destructiveHint: false
- idempotentHint: true
- openWorldHint: true
- readOnlyHint: false
- title: Take a number offline without unlinking it
mark_as_readMark a conversation's unread messages as read, without sending anything.Reads text others writeNo findings
Why it is in this group
- The name "mark_as_read" says "mark": a word for a change.
What it can reach
- untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.
Description, as the server wrote it Mark a conversation's unread messages as read, without sending anything. Set exactly one recipient: `number`, `group_id`, or `contact_id`. This is independent of the automatic mark-as-read that already happens when you send, reply, react, edit, delete or start typing (the number also appears online first): use it to clear an unread badge on its own, for example when handing a conversation to a human who has already seen it. No message is sent and no `message.*` event is produced, but the sender sees the messages as read (blue ticks) on WhatsApp. Needs a plan that includes read receipts (BUSINESS and above). Without it this answers `payment_required: feature_not_available`, and the answer will not change by retrying.
Annotations the server declares
- destructiveHint: false
- idempotentHint: false
- openWorldHint: true
- readOnlyHint: false
- title: Clear a conversation's unread backlog
refresh_contactAsk WhatsApp for this contact's current name.Reads text others writeNo findings
Why it is in this group
- Nothing in its name or description says what it does, so it is counted as a write.
What it can reach
- untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.
Description, as the server wrote it Ask WhatsApp for this contact's current name. Only the name is refreshed. The profile picture updates on its own the next time this number exchanges a message with the contact, and this does not change how messages reach them. It answers right away with `requested_at`; the refresh then runs in the background. Call `get_contact` and read `refresh`: when `completed_at` is set, `outcome` says what happened (`updated` means the name changed, `unchanged` means it was already current, `number_not_connected` means the number was offline, `unavailable` means WhatsApp did not answer). This is the one contact tool that is not read-only: it queries WhatsApp over the live connection and spends the organization's per-minute refresh budget, so it needs a key with contacts write access. Do not call it in a loop over a list. Refresh the one contact whose name looks stale.
Annotations the server declares
- destructiveHint: false
- idempotentHint: false
- openWorldHint: true
- readOnlyHint: false
- title: Refresh a contact's WhatsApp name
Only reads 18Show 18 read-only tools
get_contactFetch one contact: name, phone number and activity timestamps.Reads private dataReads text others writeNo findings
Why it is in this group
- The name "get_contact" says "get": a word for reading.
What it can reach
- private-dataThe name "get_contact" says "get" and "contact": it reads contacts.
- private-dataThe description's first sentence says "fetch" and "contact": it reads contacts.
- untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.
Description, as the server wrote it Fetch one contact: name, phone number and activity timestamps. `first_messaged_at` is when a message from this number first reached them on WhatsApp, and `last_interaction_at` is the last message that passed in EITHER direction, so it moves when they write to you as well. A message that failed before reaching WhatsApp moves neither. `refresh` shows the last `refresh_contact` you asked for: when it was requested, when it finished, and its `outcome` (`updated`, `unchanged`, `number_not_connected` or `unavailable`). While `completed_at` is null it is still running. The id must belong to the inbox named by `number_alias`. An id from a different number returns not found even though the person exists.
Annotations the server declares
- idempotentHint: true
- openWorldHint: true
- readOnlyHint: true
- title: Get one contact
get_contact_profile_pictureGet a contact's profile picture, or the reason there is none.Reads private dataReads text others writeNo findings
Why it is in this group
- The name "get_contact_profile_picture" says "get": a word for reading.
What it can reach
- private-dataThe name "get_contact_profile_picture" says "get" and "contact": it reads contacts.
- private-dataThe description's first sentence says "get" and "contact": it reads contacts.
- untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.
Description, as the server wrote it Get a contact's profile picture, or the reason there is none. The answer carries `kind`: `available` with a temporary URL, or `unavailable` with a reason. `no_storage` means the organization has not configured object storage, which is a setup step rather than a fault with this contact. The person may also simply have no picture, or have hidden it in their privacy settings.
Annotations the server declares
- idempotentHint: true
- openWorldHint: true
- readOnlyHint: true
- title: Get a contact's profile picture
get_contact_quotaCheck how much of this number's contact allowance is left.Reads private dataReads text others writeNo findings
Why it is in this group
- The name "get_contact_quota" says "get": a word for reading.
What it can reach
- private-dataThe name "get_contact_quota" says "get" and "contact": it reads contacts.
- private-dataThe description's first sentence says "contact": it reads contacts.
- untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.
Description, as the server wrote it Check how much of this number's contact allowance is left. A contact counts once per billing cycle when this number messages them through Turbo Notify, new **or** returning. Messaging the same person again in a later cycle counts again. Like the message quota, this is per number rather than per organization, and it resets at `cycle_end`.
Annotations the server declares
- idempotentHint: true
- openWorldHint: true
- readOnlyHint: true
- title: Get a number's contact quota for this billing cycle
get_groupFetch a group: its name, description, picture and settings.Reads text others writeNo findings
Why it is in this group
- The name "get_group" says "get": a word for reading.
What it can reach
- untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.
Description, as the server wrote it Fetch a group: its name, description, picture and settings. Returns how many participants it has, not who they are. Membership is deliberately not readable: it arrives as it changes, on the `group.participants_added` / `_removed` / `_promoted` / `_demoted` events, which `list_events` can read.
Annotations the server declares
- idempotentHint: true
- openWorldHint: true
- readOnlyHint: true
- title: Get one group
get_group_pictureGet a group's picture, or the reason there is none.Reads text others writeNo findings
Why it is in this group
- The name "get_group_picture" says "get": a word for reading.
What it can reach
- untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.
Description, as the server wrote it Get a group's picture, or the reason there is none. Same shape as a contact's picture. The answer carries `kind`: `available` with a temporary URL, or `unavailable` with a reason. `no_storage` means the organization has not configured object storage.
Annotations the server declares
- idempotentHint: true
- openWorldHint: true
- readOnlyHint: true
- title: Get a group's picture
get_messageFetch one message with its full content, parties and mentions.Reads private dataReads text others writeNo findings
Why it is in this group
- The name "get_message" says "get": a word for reading.
What it can reach
- private-dataThe name "get_message" says "get" and "message": it reads messages.
- private-dataThe description's first sentence says "fetch" and "message": it reads messages.
- untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.
- untrusted-inputThe name "get_message" says "get" and "message": it reads messages, which other people can write.
- untrusted-inputThe description's first sentence says "fetch" and "message": it reads messages, which other people can write.
Description, as the server wrote it Fetch one message with its full content, parties and mentions. The answer includes the number's stable `number_id` (`num_…`) next to `number_alias`. `at` is when the message actually happened on WhatsApp; `status.at` is when it reached its current delivery status and keeps moving as receipts arrive. They answer different questions and are not interchangeable. Returns not found if the id belongs to a different number. Past the organization's content retention window (1 hour by default, up to 72 hours depending on the plan) the message is still returned, with `content_expired` true and its content empty.
Annotations the server declares
- idempotentHint: true
- openWorldHint: true
- readOnlyHint: true
- title: Get one message in full
get_message_quotaCheck how much of this number's message allowance is left.Reads private dataReads text others writeNo findings
Why it is in this group
- The name "get_message_quota" says "get": a word for reading.
What it can reach
- private-dataThe name "get_message_quota" says "get" and "message": it reads messages.
- private-dataThe description's first sentence says "message": it reads messages.
- untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.
- untrusted-inputThe name "get_message_quota" says "get" and "message": it reads messages, which other people can write.
- untrusted-inputThe description's first sentence says "message": it reads messages, which other people can write.
Description, as the server wrote it Check how much of this number's message allowance is left. The quota is **per number**, not per organization: an organization with three numbers has three separate allowances. It resets at `cycle_end`, which is the organization's real billing boundary rather than a rolling window, so `resets_at` is a date you can plan around. Only messages sent through Turbo Notify consume it. Messages someone sends from the phone itself do not.
Annotations the server declares
- idempotentHint: true
- openWorldHint: true
- readOnlyHint: true
- title: Get a number's message quota for this billing cycle
get_message_reactionsList the emoji reactions on a message, and who left each one.Reads private dataReads text others writeNo findings
Why it is in this group
- The name "get_message_reactions" says "get": a word for reading.
What it can reach
- private-dataThe name "get_message_reactions" says "get" and "message": it reads messages.
- private-dataThe description's first sentence says "list" and "message": it reads messages.
- untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.
- untrusted-inputThe name "get_message_reactions" says "get" and "message": it reads messages, which other people can write.
- untrusted-inputThe description's first sentence says "list" and "message": it reads messages, which other people can write.
Description, as the server wrote it List the emoji reactions on a message, and who left each one. Each entry names the reactor and the emoji. Reactions are replaced rather than accumulated per person: someone who reacts twice leaves one entry showing their latest emoji, so this is a current snapshot and not a history. An empty list is the normal answer for most messages, and is not an error.
Annotations the server declares
- idempotentHint: true
- openWorldHint: true
- readOnlyHint: true
- title: Get the reactions on a message
get_message_receiptsSee who received and who read a message.Reads private dataReads text others writeNo findings
Why it is in this group
- The name "get_message_receipts" says "get": a word for reading.
What it can reach
- private-dataThe name "get_message_receipts" says "get" and "message": it reads messages.
- private-dataThe description's first sentence says "message": it reads messages.
- untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.
- untrusted-inputThe name "get_message_receipts" says "get" and "message": it reads messages, which other people can write.
- untrusted-inputThe description's first sentence says "message": it reads messages, which other people can write.
Description, as the server wrote it See who received and who read a message. Most useful for group messages, where the single status on the message cannot express that some members read it and others have not. Requires a plan that includes read receipts; on plans without it the call is refused with `feature_not_available`, which no retry will fix.
Annotations the server declares
- idempotentHint: true
- openWorldHint: true
- readOnlyHint: true
- title: Get per-recipient delivery and read receipts
get_message_responsesSee how the guests answered a calendar event: going, not going or maybe.Reads private dataReads text others writeNo findings
Why it is in this group
- The name "get_message_responses" says "get": a word for reading.
What it can reach
- private-dataThe name "get_message_responses" says "get" and "message": it reads messages.
- private-dataThe description's first sentence says "calendar": it reads calendars.
- untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.
- untrusted-inputThe name "get_message_responses" says "get" and "message": it reads messages, which other people can write.
Description, as the server wrote it See how the guests answered a calendar event: going, not going or maybe. Only works on a calendar event; any other message answers `invalid_request: message_not_calendar`. The answer has `counts` (how many guests are going, not going or maybe, and how many extra guests those who are going or maybe are bringing) and `responses`, one entry per guest with who they are, their answer, any extra guests they bring and when they answered. A guest who changes their mind leaves one entry showing the latest answer, so this is a current snapshot and not a history. To be told as answers come in, watch for `message.calendar_responded` with `list_events`. An event nobody has answered yet returns zero counts and an empty list, which is the normal answer and not an error.
Annotations the server declares
- idempotentHint: true
- openWorldHint: true
- readOnlyHint: true
- title: Get the guests' answers to a calendar event
get_message_statusCheck where a message got to: queued, sent, delivered, read, failed.Reads private dataReads text others writeNo findings
Why it is in this group
- The name "get_message_status" says "get": a word for reading.
What it can reach
- private-dataThe name "get_message_status" says "get" and "message": it reads messages.
- private-dataThe description's first sentence says "message": it reads messages.
- untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.
- untrusted-inputThe name "get_message_status" says "get" and "message": it reads messages, which other people can write.
- untrusted-inputThe description's first sentence says "message": it reads messages, which other people can write.
Description, as the server wrote it Check where a message got to: queued, sent, delivered, read, failed. Status only moves forward. A failed status always carries an error code, and for a few media and reaction problems it also carries a hint naming the field to change; most failures give the code alone. Read the code before retrying, because most failures repeat identically.
Annotations the server declares
- idempotentHint: true
- openWorldHint: true
- readOnlyHint: true
- title: Get a message's delivery status
get_numberCheck one number: its phone number, status, and when that status began.Reads text others writeNo findings
Why it is in this group
- The name "get_number" says "get": a word for reading.
What it can reach
- untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.
Description, as the server wrote it Check one number: its phone number, status, and when that status began. `id` (`num_…`) is the number's permanent identifier. The alias can be renamed, the `id` cannot. Either one works as `number_alias`. `connected` means it can send right now. `connecting` means a connection was just requested and is being set up; check again in a few seconds. `reconnecting` means the number lost its connection and Turbo Notify is bringing it back by itself: there is nothing to do but wait and check again. Until it is `connected` again, sends are refused with `number_not_connected`. `disconnected` means the link to the phone still exists but the number is offline, so `connect_number` brings it back. `revoked` means the phone unlinked the device, or another connection took it over. `failed` means a pairing did not complete, and `reason` says why (for example `pairing_timeout` when the code or QR code was not used in time, `paired_phone_mismatch` when another WhatsApp used it). Either one has to be paired again by a person holding that phone: from the dashboard (main or extra number) or, for an extra number, through the API's pairing endpoint, by code or by QR code. No tool here pairs a number: it needs a person with the phone. `pairing` means a person is pairing the number right now. The code or QR code they use is not part of this answer: it belongs to that person. Check again later; the status moves to `connected`, or to `failed` with a `reason`.
Annotations the server declares
- idempotentHint: true
- openWorldHint: true
- readOnlyHint: true
- title: Get one number and its connection status
get_usageGet message and contact usage for the whole organization.Reads private dataReads text others writeNo findings
Why it is in this group
- The name "get_usage" says "get": a word for reading.
What it can reach
- private-dataThe description's first sentence says "get" and "message": it reads messages.
- untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.
- untrusted-inputThe description's first sentence says "get" and "message": it reads messages, which other people can write.
Description, as the server wrote it Get message and contact usage for the whole organization. The top-level totals are the **sum across every live number**: an organization with two numbers on a 50,000-message plan reports a quota of 100,000, not 50,000. The `numbers[]` breakdown shows each number separately, which is what you need to find the one that is close to its own ceiling, so a healthy-looking total can hide a number at 99%. For one number, `get_message_quota` and `get_contact_quota` are more direct.
Annotations the server declares
- idempotentHint: true
- openWorldHint: true
- readOnlyHint: true
- title: Get the organization's usage across all numbers
list_contactsList the people this number has exchanged messages with.Reads private dataReads text others writeNo findings
Why it is in this group
- The name "list_contacts" says "list": a word for reading.
What it can reach
- private-dataThe name "list_contacts" says "list" and "contact": it reads contacts.
- private-dataThe description's first sentence says "list" and "message": it reads messages.
- untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.
- untrusted-inputThe description's first sentence says "list" and "message": it reads messages, which other people can write.
Description, as the server wrote it List the people this number has exchanged messages with. Filter by `phone` or by `name`. They are separate filters, not one search box: `name` matches the contact's name, `phone` matches the number. `source` restricts to how the contact first appeared: `inbound` (they messaged this number), `outbound` (this number messaged them), `mention` (named in a group message) or `self` (the number's own contact card). `contacted_in_cycle=true` narrows the list to exactly the people counting against this number's contact quota this billing cycle: those messaged through Turbo Notify inside the cycle, whether newly or again. Use it to see what the quota gauge is actually counting.
Annotations the server declares
- idempotentHint: true
- openWorldHint: true
- readOnlyHint: true
- title: List an inbox's contacts
list_eventsPoll everything that happened: messages, contacts, groups, numbers, quotas.Reads private dataReads text others write1 finding
Why it is in this group
- The name "list_events" says "list": a word for reading.
What it can reach
- private-dataThe description's first sentence says "message": it reads messages.
- untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.
- untrusted-inputThe description's first sentence says "message": it reads messages, which other people can write.
Findings
- Info
The description is 2,025 characters long, over the 2,000 this check looks for.
What this check looks forMatched text
description ends: "...ave no single number to name, so on those the keys are absent rather than empty."
Description, as the server wrote it Poll everything that happened: messages, contacts, groups, numbers, quotas. This is how an agent learns about inbound messages without hosting a webhook endpoint. **Events come back newest first, so polling for new ones uses `ending_before`, not `starting_after`.** Typical loop: call with no cursor and remember the id of the FIRST event in the page (the newest one); next time, pass that id as `ending_before` to get only what has happened since. An empty page means you are caught up. `starting_after` does the opposite: it walks BACKWARD into history, which is what you want for reading a backlog, and never what you want for noticing something new. Passing the last id you handled to `starting_after` in a loop returns steadily older events and never reaches the present. `request_id` returns everything that came of one earlier call. A write tool's result carries `request_id` when the platform issued one; keep it and you can ask later what became of that specific send. `number_id` restricts to one number and keeps matching its events after the number is renamed. `number_alias` restricts to one inbox by the alias it had when the event happened, so a rename hides the older events; the two can be combined. Either one excludes `storage.*` and `media.unavailable` events, which belong to the organization rather than to any one number. Most events carry a `number_alias` telling you which inbox they belong to, and `number_id` (`num_…`), the same number's permanent identifier. The alias is the one at the time of the event and can be renamed later; the `number_id` cannot. You need one of them: message, contact and group ids are unique but not self-addressing, so an id alone does not tell you which number's tools to use with it, and both work as `number_alias` in the other tools. Read the fields defensively rather than assuming they are there: `storage.*` and `media.unavailable` belong to the organization as a whole and have no single number to name, so on those the keys are absent rather than empty.
Annotations the server declares
- idempotentHint: true
- openWorldHint: true
- readOnlyHint: true
- title: Poll the organization's event log
list_groupsList the WhatsApp groups this number is a member of.Reads text others writeNo findings
Why it is in this group
- The name "list_groups" says "list": a word for reading.
What it can reach
- untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.
Description, as the server wrote it List the WhatsApp groups this number is a member of. The list is built when the number connects: every group it belongs to is picked up then, whether or not anything has been said in it. A group joined since then is picked up from the join itself, without waiting for anyone to speak in it. `name` filters by group name. `include_left=true` also returns groups this number has left, which are otherwise hidden.
Annotations the server declares
- idempotentHint: true
- openWorldHint: true
- readOnlyHint: true
- title: List the groups a number belongs to
list_messagesList messages for one number, newest first, received and sent.Reads private dataReads text others writeNo findings
Why it is in this group
- The name "list_messages" says "list": a word for reading.
What it can reach
- private-dataThe name "list_messages" says "list" and "message": it reads messages.
- private-dataThe description's first sentence says "list" and "message": it reads messages.
- untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.
- untrusted-inputThe name "list_messages" says "list" and "message": it reads messages, which other people can write.
- untrusted-inputThe description's first sentence says "list" and "message": it reads messages, which other people can write.
Description, as the server wrote it List messages for one number, newest first, received and sent. Each item says who it is from and to, which number it belongs to, its `direction`, `content_type`, `origin`, a short `preview`, `at`, `status` and `content_expired`. Mentions, transcription and full media detail are only on `get_message`, so fetch the one message you care about rather than paging for detail. To read a group, find its `grp_` id with `list_groups` and pass it as `group_id`. `contact_id` returns what that contact sent or received, which in a group includes what they said there. `direction` keeps only `inbound` or `outbound`. Filters combine: `group_id` with `contact_id` answers what one person said in one group. Ids belong to one number, so use the same `number_alias` you got them from: an id from another number answers not found. For the next page, pass back the `cursor` from the previous response together with the same filters. Content older than the plan's retention window comes back with `preview` empty and `content_expired` true. The message, its sender and its group stay listed for 12 months.
Annotations the server declares
- idempotentHint: true
- openWorldHint: true
- readOnlyHint: true
- title: List messages in an inbox
list_numbersList every WhatsApp number this organization has, with its status.Reads text others write1 finding
Why it is in this group
- The name "list_numbers" says "list": a word for reading.
What it can reach
- untrusted-inputThe server marks it openWorldHint: true: it reaches things outside the server's own data.
Findings
- Warn
The description contains "every other tool", wording about other tools or servers.
What this check looks forMatched text
description: "...customer chose, and that alias is what every other tool's `number_alias` expects. Each number..."
Description, as the server wrote it List every WhatsApp number this organization has, with its status. Start here when you do not know which aliases exist. The main number is always `main`; extras carry the alias the customer chose, and that alias is what every other tool's `number_alias` expects. Each number also carries `id` (`num_…`), which never changes, even when the alias is renamed. Keep it if you need to recognise a number later: events carry the same value as `number_id`, and the other tools accept either one as `number_alias`. To find the numbers that need attention, pass `status`, for example `disconnected`. Only `connected` numbers can send. `status.at` is when the number entered its current state, which is how you tell a fresh disconnection from one that has been down for hours.
Annotations the server declares
- idempotentHint: true
- openWorldHint: true
- readOnlyHint: true
- title: List the organization's WhatsApp numbers
This is not a safety rating, and no finding is not a safety guarantee. The groups and findings come from fixed checks of the tool definitions the server returned on the date shown, not from its code or what it does when called. How the checks work.
Exfil surface
Which tools read private data, which read text someone else can write, and which can send data out. One of each is a path a prompt injection can use.
4680 paths in this server alone from private data, through text someone else wrote, to a way out. Denying 10 outbound tools breaks every one.
- Reads private data13
- Reads files, mail, messages, databases, secrets, calendars or repositories.
- Reads text others write36
- Brings in text someone else can write: web pages, inbound email, messages, issues and comments.
- Can send data out10
- Can carry data to people or systems: it sends, posts or publishes, or takes a recipient, channel or URL.
{
"permissions": {
"deny": [
"mcp__whatsapp__edit_text_message",
"mcp__whatsapp__react_to_message",
"mcp__whatsapp__reply_to_message",
"mcp__whatsapp__send_calendar_event",
"mcp__whatsapp__send_contact_card",
"mcp__whatsapp__send_cta_button",
"mcp__whatsapp__send_location",
"mcp__whatsapp__send_media",
"mcp__whatsapp__send_text",
"mcp__whatsapp__send_typing_indicator"
]
}
}Other servers in the same agent add their own tools to these paths. Audit your whole setup to see them together.
Remote posture
What anyone on the internet sees at the remote endpoint: an anonymous initialize and tools/list, and the public OAuth metadata.
Looked at Oct 28, 2026, 20:19 UTC, unchanged since Oct 28, 2026. No credentials were sent and no tool was called. What is looked at.
- Reachable
- yes
- HTTP status of an anonymous initialize
- 401
- tools/list served without credentials
- no
- Tools that change things, listed without credentials
- 0
- Protocol version
- not observed
- WWW-Authenticate on 401
- yes
- resource_metadata in WWW-Authenticate
- https://mcp.turbonotify.com/.well-known/oauth-protected-resource
- RFC 9728 protected resource metadata
- https://mcp.turbonotify.com/.well-known/oauth-protected-resource
- Authorization servers
- https://auth.turbonotify.com
- Legacy HTTP+SSE transport
- no
- Dynamic client registration
- yes
- Client ID metadata documents
- yes
Findings
Matches of fixed checks, with the text they matched.
- Warnlist_numbers
The description contains "every other tool", wording about other tools or servers.
What this check looks forMatched text
description: "...customer chose, and that alias is what every other tool's `number_alias` expects. Each number..."
- Infoconnect_number
The description mentions "credentials".
What this check looks forMatched text
description: "...er that is currently offline. Uses the credentials already stored for the number, so no QR..."
The description mentions "credentials".
What this check looks forMatched text
description: "...ping its link to the phone. The stored credentials survive, so `connect_number` brings it..."
- Infolist_events
The description is 2,025 characters long, over the 2,000 this check looks for.
What this check looks forMatched text
description ends: "...ave no single number to name, so on those the keys are absent rather than empty."
History
Changes to its tool definitions and to its remote endpoint, newest first.
No change seen yet. Its tools are rescanned every day, and a remote endpoint is looked at once a week.
Install
Pinned to version 0.1.4. Values in angle brackets are yours to fill in.
claude mcp add --transport http whatsapp https://mcp.turbonotify.comclaude mcp add --transport http whatsapp https://mcp.turbonotify.comRun in a terminal.
Claude Code permissions
Allow the reads, ask before a change or a way out, deny the deletes. Paste into .claude/settings.json.
{
"permissions": {
"allow": [
"mcp__whatsapp__list_messages",
"mcp__whatsapp__get_message",
"mcp__whatsapp__get_message_status",
"mcp__whatsapp__get_message_receipts",
"mcp__whatsapp__get_message_reactions",
"mcp__whatsapp__get_message_responses",
"mcp__whatsapp__get_message_quota",
"mcp__whatsapp__list_contacts",
"mcp__whatsapp__get_contact",
"mcp__whatsapp__get_contact_profile_picture",
"mcp__whatsapp__get_contact_quota",
"mcp__whatsapp__list_groups",
"mcp__whatsapp__get_group",
"mcp__whatsapp__get_group_picture",
"mcp__whatsapp__list_numbers",
"mcp__whatsapp__get_number",
"mcp__whatsapp__get_usage",
"mcp__whatsapp__list_events"
],
"ask": [
"mcp__whatsapp__send_text",
"mcp__whatsapp__send_media",
"mcp__whatsapp__send_location",
"mcp__whatsapp__send_contact_card",
"mcp__whatsapp__send_calendar_event",
"mcp__whatsapp__send_cta_button",
"mcp__whatsapp__reply_to_message",
"mcp__whatsapp__send_typing_indicator",
"mcp__whatsapp__mark_as_read",
"mcp__whatsapp__refresh_contact",
"mcp__whatsapp__connect_number",
"mcp__whatsapp__disconnect_number"
],
"deny": [
"mcp__whatsapp__edit_text_message",
"mcp__whatsapp__edit_media_caption",
"mcp__whatsapp__edit_calendar_event",
"mcp__whatsapp__cancel_calendar_event",
"mcp__whatsapp__delete_message",
"mcp__whatsapp__react_to_message"
]
}
}Tools that only read are allowed, tools that change things ask first, tools that delete or cancel are denied. A read that can send data out (it takes a URL, say) asks first too. It assumes the server is installed as whatsapp. Review it before you use it.
Capability tags behind 36 rules
- mcp__whatsapp__send_text untrusted-input, outbound
- mcp__whatsapp__send_media untrusted-input, outbound
- mcp__whatsapp__send_location untrusted-input, outbound
- mcp__whatsapp__send_contact_card untrusted-input, outbound
- mcp__whatsapp__send_calendar_event untrusted-input, outbound
- mcp__whatsapp__send_cta_button untrusted-input, outbound
- mcp__whatsapp__reply_to_message untrusted-input, outbound
- mcp__whatsapp__edit_text_message untrusted-input, outbound
- mcp__whatsapp__edit_media_caption untrusted-input
- mcp__whatsapp__edit_calendar_event untrusted-input
- mcp__whatsapp__cancel_calendar_event untrusted-input
- mcp__whatsapp__delete_message untrusted-input
- mcp__whatsapp__react_to_message untrusted-input, outbound
- mcp__whatsapp__send_typing_indicator untrusted-input, outbound
- mcp__whatsapp__mark_as_read untrusted-input
- mcp__whatsapp__list_messages private-data, untrusted-input
- mcp__whatsapp__get_message private-data, untrusted-input
- mcp__whatsapp__get_message_status private-data, untrusted-input
- mcp__whatsapp__get_message_receipts private-data, untrusted-input
- mcp__whatsapp__get_message_reactions private-data, untrusted-input
- mcp__whatsapp__get_message_responses private-data, untrusted-input
- mcp__whatsapp__get_message_quota private-data, untrusted-input
- mcp__whatsapp__list_contacts private-data, untrusted-input
- mcp__whatsapp__get_contact private-data, untrusted-input
- mcp__whatsapp__get_contact_profile_picture private-data, untrusted-input
- mcp__whatsapp__refresh_contact untrusted-input
- mcp__whatsapp__get_contact_quota private-data, untrusted-input
- mcp__whatsapp__list_groups untrusted-input
- mcp__whatsapp__get_group untrusted-input
- mcp__whatsapp__get_group_picture untrusted-input
- mcp__whatsapp__list_numbers untrusted-input
- mcp__whatsapp__get_number untrusted-input
- mcp__whatsapp__connect_number untrusted-input
- mcp__whatsapp__disconnect_number untrusted-input
- mcp__whatsapp__get_usage private-data, untrusted-input
- mcp__whatsapp__list_events private-data, untrusted-input
Pin
Keep the decision: fail CI when the tools change, follow every change, or show a badge.
Pin in CI
Fail a build when the tools you reviewed are not the tools the server serves today.
curl -s "https://anlyon.dev/api/v1/servers/com.turbonotify/whatsapp/verify?hash=0fcc990d50c2cf2e8d013da810dd32e0e95b2d73547ea071f0d5e56a0dd86cb1" | jq -e '.changed == false'Pinned hash 0fcc990d50c2, from Nov 2, 2026, 16:18 UTC.
Watch
Get told when its tools change: a new tool, a new description, a new class.
https://anlyon.dev/servers/com.turbonotify/whatsapp/changes.xmlAn Atom feed. Works in any feed reader or Slack's RSS app.
Badge
[](https://anlyon.dev/servers/com.turbonotify/whatsapp)Source: MCP Registry · repository. Synced Nov 2, 2026, 16:17 UTC.