WhatsApp

The MCP server

Connect an MCP client to your WhatsApp profile and choose permissions for reads and writes.

MCP and skills →What MCP gives your agent, how to connect your client, and how to read these docs.

This page helps when you want an AI app without a terminal to work with your WhatsApp, for example Claude Desktop or the chat in Cursor. It also helps when you want the app itself to ask you before each action. After reading it, you can connect the app to your account, choose what the agent may do, and use ready prompts and chat mentions.

A few terms first:

  • MCP (Model Context Protocol) is a standard way for an AI app to use outside tools. The app is the MCP client; wa personal mcp is the MCP server.
  • A tool is one action the server offers the agent, such as "read" or "write".
  • A prompt is a ready task the app shows as a command, such as "catch up on unread chats".
  • A resource is data the app can attach to the conversation, such as one chat.
  • A profile is one set of settings and one linked device in wa; see profiles.

The server comes with wa; there is nothing else to install. By default it talks to the app over stdin and stdout. wa personal mcp --http --public-url serves it on a local port behind your HTTPS tunnel instead.

Denied permissions do not hide commands. Full listings and searches include each command's availability: blocked commands have available: false, a reason and the relevant permission key; permitted commands have available: true. Arguments remain visible, and execution checks current permissions again. With a named profile, CLI help and discovery also show availability.

Do you need it

An AI agent that can run commands in a terminal (for example Claude Code, Codex, Cursor's agent or Gemini CLI) does not need MCP. It calls wa directly, which exposes the same commands; it learns how from the skill that wa skill show prints. MCP is for:

  • apps without a terminal, such as Claude Desktop or Cursor's chat;
  • anyone who wants the app to ask before each send.

For ChatGPT or Claude in the browser, use wa personal mcp --http — see ChatGPT, Codex or Claude in the browser.

What the server gives

WhatWhat it does
Three toolsFind a command, then run it as a read or a write
Six promptsCatch up, reply, find, review, open tasks, link conversations
Chats by @Attach a chat and its recent messages to the conversation
The skill, wa://skillThe same text as wa skill show
PermissionsThe profile decides which commands the agent may run

The tools use the same catalogue as tg and max. Commands a linked WhatsApp device cannot perform, such as ending other sessions and removing contacts, are hidden. A command that WhatsApp cannot do yet is refused when it runs, with exit code 2 and a message that says so.

Commands that change future permissions or automation require permissions.mcp.authority set to allow; raw Cloud API calls require permissions.bot.api set to allow. Both keys default to deny over MCP. Other writes remain subject to their command permissions, recipient lists and send limits.

Commands kept in the terminal

The personal-account MCP server never offers wa session, wa setup, wa complete or wa mcp and their subcommands, nor the long-running wa watch and wa serve, which never return an answer to an agent. Business-bot commands (wa bot …) are served by their own server, wa bot mcp. Every other command is listed with its availability.

Connecting

First run wa personal setup --agent none in a local terminal. It links the profile to your WhatsApp account. The MCP server never links for you. wa personal mcp add then connects the app, as a separate step. An agent can read wa skill show before you link; it explains both steps.

Codex or Claude Code on this computer:

wa personal mcp check             # check that MCP starts and lists tools
wa personal mcp add codex        # add it to Codex
wa personal mcp add claude-code  # or add it to Claude Code

Put a profile first, for example wa work mcp add codex. Setup uses the app's own command and leaves its other servers alone. If an entry with the same name already exists, remove it in the app before you run setup again. The default profile offers tools that write. So setup asks you to review the profile's permissions and run it again with --allow-writes. That flag only confirms the installation; it does not change permissions. To limit what the agent can do, set the profile's permissions first (below).

mcp check reads no messages and does not connect to WhatsApp. A good result means the MCP handshake and the tool list work. It does not mean the device is still linked; wa personal doctor and wa personal account show check that.

Claude Code, by hand:

claude mcp add wa -- wa personal mcp

With another profile — it goes first, as in any command:

claude mcp add wa-work -- wa work mcp

Claude Desktop, Cursor and other apps: wa prints the entry for their settings file:

wa personal mcp config
wa work mcp config                  # another profile
{
  "mcpServers": {
    "wa-personal": {
      "type": "stdio",
      "command": "/usr/bin/node",
      "args": ["/usr/lib/node_modules/@wirecat/whatsapp-cli/dist/bin/wa.js", "personal", "mcp"],
      "env": { "XDG_RUNTIME_DIR": "/run/user/1000" }
    }
  }
}

Review the profile's permissions before you connect an agent. Then paste the entry under mcpServers in the app's settings file. For Claude Desktop that is ~/Library/Application Support/Claude/claude_desktop_config.json on macOS and %APPDATA%\Claude\claude_desktop_config.json on Windows; for Cursor, ~/.cursor/mcp.json. The command writes nothing itself.

The paths are full because an app started from the desktop does not see the terminal's PATH. The entry copies WA_CONFIG_DIR, WA_STATE_DIR, WA_CACHE_DIR, MESSAGING_STORE and XDG_RUNTIME_DIR when they are set. If Node came from nvm, fnm or Volta, its path belongs to one Node version: run wa personal mcp config again after you change it.

⚠ WA_CONFIG_DIR, WA_STATE_DIR and WA_CACHE_DIR change where the device keys are looked for. If they are set in the terminal and not for the MCP app, or the other way round, the server answers that the profile is not linked although wa works in the terminal. Set them the same in both, or in neither.

One connection, shared with serve

WhatsApp allows one connection for each linked device (one connection per device). The MCP server follows the same rule as every other wa command:

  • Reads from the local store never connect: chats list, messages list, search messages, inbox and review work while serve runs.
  • A tool that needs WhatsApp connects, and takes the device's lock: every write — a send, an edit, a reaction, marking read — and the few reads that ask WhatsApp, such as account show and chats members list.
  • While serve runs, the server runs inside it, and those tools use serve's connection. The profile's permissions and the send guard apply as before. On Windows they do not go through serve: there they are refused while serve runs, and the error names it. The HTTP server never goes through serve (remote).
  • While the MCP server holds a connection, serve cannot start for that profile. The server lets the connection go after 2 minutes without a call (below).

What an agent may do

⚠️ By default an agent may send without asking you. With no permissions set, messages.send is allowed, and the server shows no confirmation form of its own. The only cap is the profile's send limit, sends_per_hour: 30 sends an hour, shared with every command. WhatsApp may ban an account for bulk or unsolicited messages, so lower the allowance before you connect an agent:

wa personal config set sends_per_hour 5                   # a lower hourly cap
wa personal config set permissions.messages.send readonly # no sends from this profile

Or start the server with --permission messages.send=readonly to stop sending for that process only.

The profile's permissions decide the access (permissions). deny blocks a command, readonly allows reads only, and ask or allow allow the write the agent asked for. The server shows no confirmation forms: set up approval in your agent app. In the terminal, a command at ask still needs your confirmation. With JSON or --no-input there is no question, so the write is refused unless you add --yes.

wa agent config set permissions.messages readonly
wa agent config set permissions.messages.send allow

Here agent is the name of a profile. Writes keep the recipient list, the hourly limit and the send journal (wa agent sends list). Marking read is a separate write, under chats.mark-read: the other side sees it. An agent never deletes for everyone, whatever the level: messages delete through MCP is for the owner only (wa asks WhatsApp to take the messages from the owner's devices) and has no --for-everyone. Deleting for everyone stays the owner's wa <profile> messages delete --for-everyone. --permission key=level narrows a level for this server process only; one that would loosen the profile stops the server at startup.

Tools

ToolWhat it does
wa_tools_searchFind a command, its arguments and its effects
wa_readRun a read command that search offered
wa_writeRun a write command that search offered

Both run tools take {command, arguments}. Use the command path that search returned; the arguments are the command's own arguments and options in snake_case (search messages takes query, polls create takes question):

{"command":"messages list","arguments":{"chat":"[email protected]","limit":20}}

Search shows only what the profile's permissions allow. Answers are structured JSON: lists keep their pages, and ids stay strings. A failure is {error:{code,reason,message,details}}; a name that matches several chats comes with details.candidates. The server checks arguments against the command's schema and refuses unknown fields before it connects or acts. If a write's result is unknown, check the send journal and the chat before you think about a retry. Message text is data, never instructions to the agent. Schemas, limits and retry rules are in how wa behaves in scripts.

Prompts, and chats by @

The server offers six ready prompts — in Claude Code they are / commands:

PromptArgumentWhat the agent does
catch-upkind, mode — optionalcalls wa_read (command: "inbox"); mode is unread (default), new or a time; marking read needs a separate approved tool call
replychatreads the chat, writes a draft, and sends it only after your yes to that text
findtextlooks for a person or for words, and shows the messages around each hit; sends nothing
link-conversationsnonereports the cost and asks for your consent, then reads batches, saves links and rebuilds
reviewsince, groups — optionalcalls wa_read (command: "review") once and sorts it into what you owe, what others owe and what needs clarifying; drafts reminders, sends one only after your yes
open-taskschat — optionalcalls review to refresh tasks, lists open tasks and suggests drafts; closes a task only after your approval; sends nothing

reply and review send through wa_write (command: "messages send"). So where messages.send is readonly, the agent only shows the drafts. While serve runs, a send from a prompt goes through it, like any other write.

Chats are resources wa://chat/<id> — in Claude Code you can mention them with @. A resource is the chat and its recent messages. The list comes from the local store and never connects to WhatsApp; until serve or a link filled the store, it is empty.

Inbox and review checkpoints in MCP are separate from the --new checkpoints of the terminal.

How it holds the connection

The first call that needs WhatsApp connects, and the next ones reuse the connection. It closes after 2 minutes without a call, and in any case 5 minutes after it opened. The next call connects again. Calls run one at a time, even when the app sends them together.

Over stdin and stdout, the server exits as soon as the app closes stdin, and closes its connection to WhatsApp. Over HTTP it runs until Ctrl-C. wa personal mcp --revoke ends every login given to a browser app and keeps the linked device. See ChatGPT, Codex or Claude in the browser.