AI

WhatsApp MCP server: how to build it and use it in Claude and Cursor

A WhatsApp MCP server is a program that exposes API actions (send text, check a number, see instance status, read messages) as tools that Claude, Cursor and other MCP clients can call. An honest note: ZapZap does not yet publish its own MCP server. This guide is an example of how to build yours in about 80 lines of TypeScript on top of the API that already exists, and it does not describe a released product.

Published on · 5 min read

This article is also available in: Português · Español

What MCP is and why connect WhatsApp to an AI assistant

MCP (Model Context Protocol) is an open protocol that defines how an AI client (a chat app, a code editor, an agent) discovers and calls tools exposed by a server. Instead of every integration being custom, the server declares tools, parameters and descriptions, and the client decides when to call them during the conversation.

For WhatsApp this opens concrete uses: asking the assistant to confirm whether a list of numbers has WhatsApp, to summarize a customer's latest messages, to check the instance is connected before a campaign, or to draft and, with your approval, send a message. The hard part is not technical, it is control: an assistant with send access needs locks.

What exists today and what does not

Status on 10/27/2026. What exists: ZapZap's REST API, with a per-endpoint reference and an OpenAPI file published at OpenAPI; the n8n-nodes-zapzap 0.1.3 package on npm, for people who use n8n; and the official MCP TypeScript SDK (@modelcontextprotocol/sdk, version 1.32.2 on npm on this date), which we use in the example. What does not exist: a ZapZap MCP server published and maintained by us.

That is why the code below is yours: you host it, you maintain it and you answer for what the assistant can do with it. The advantage is full control over which tools exist. If an official server ever appears, it will be announced on the documentation pages and this guide will be updated.

Which tools to expose (start read-only)

ToolAPI endpointRiskNote
instance_statusGET /{instanceId}/instance/statusLowShows whether the instance is connected
check_numbersPOST /{instanceId}/chat/checkLowTakes numbers and returns isInWhatsapp and jid
find_messagesPOST /{instanceId}/message/findMediumReads conversations; personal data
send_textPOST /{instanceId}/send/textHighActually sends a message; needs a lock

Start with the first three and add send_text after testing the locks in the security section. Whoever reads customer messages must treat them as personal data and as untrusted input, since a customer's text can try to instruct the assistant (prompt injection).

Code: a TypeScript MCP server on top of the ZapZap API

The server below uses the stdio transport, the simplest for running locally: the client starts the process and talks over standard input and output. Each tool has a zod input schema and returns text. Credentials come from the process's environment variables, never from the assistant.

The send_text tool has three locks: it only sends to numbers on an allowed list (ZAPZAP_ALLOWED), it caps text length and it stays off unless ZAPZAP_ENABLE_SEND is true. Install with npm install @modelcontextprotocol/sdk zod and compile with tsc or run with tsx.

TypeScript (server.ts)

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

const BASE = "https://api.zapzapapi.com/api/v1/" + process.env.ZAPZAP_INSTANCE_ID;
const headers = {
  "Content-Type": "application/json",
  "x-api-key": process.env.ZAPZAP_API_KEY ?? "",
  "x-api-secret": process.env.ZAPZAP_API_SECRET ?? "",
};
const allowed = new Set((process.env.ZAPZAP_ALLOWED ?? "").split(",").filter(Boolean));
const sendEnabled = process.env.ZAPZAP_ENABLE_SEND === "true";

async function call(method: "GET" | "POST", path: string, body?: unknown) {
  const res = await fetch(BASE + path, { method, headers, body: body ? JSON.stringify(body) : undefined });
  const text = await res.text();
  if (!res.ok) throw new Error("ZapZap " + res.status + ": " + text.slice(0, 300));
  return text;
}
const ok = (text: string) => ({ content: [{ type: "text" as const, text }] });

const server = new McpServer({ name: "whatsapp-zapzap-exemplo", version: "0.1.0" });

server.registerTool(
  "instance_status",
  { description: "Mostra se a instancia de WhatsApp esta conectada." },
  async () => ok(await call("GET", "/instance/status")),
);

server.registerTool(
  "check_numbers",
  {
    description: "Verifica quais numeros (com DDI, so digitos) tem WhatsApp.",
    inputSchema: { numbers: z.array(z.string().regex(/^\d{10,15}$/)).min(1).max(50) },
  },
  async ({ numbers }) => ok(await call("POST", "/chat/check", { numbers })),
);

server.registerTool(
  "find_messages",
  {
    description: "Lista mensagens recentes de uma conversa (chatid, ex: 5511999999999@s.whatsapp.net).",
    inputSchema: { chatid: z.string(), limit: z.number().int().min(1).max(30).default(10) },
  },
  async ({ chatid, limit }) => ok(await call("POST", "/message/find", { chatid, limit, offset: 0 })),
);

if (sendEnabled) {
  server.registerTool(
    "send_text",
    {
      description: "Envia uma mensagem de texto. So funciona para numeros da lista permitida.",
      inputSchema: { number: z.string().regex(/^\d{10,15}$/), text: z.string().min(1).max(1000) },
    },
    async ({ number, text }) => {
      if (!allowed.has(number)) throw new Error("number not in the allowed list");
      return ok(await call("POST", "/send/text", { number, text }));
    },
  );
}

await server.connect(new StdioServerTransport());

How to connect the server to Claude and Cursor

MCP clients read a JSON config with the command that starts each server and its environment variables. In Claude Desktop the file is claude_desktop_config.json; in Cursor, .cursor/mcp.json in the project (or the global equivalent). In Claude Code, the claude mcp add command registers the server. The JSON format is the same in the first two, but file locations and menus change between versions: check each client's documentation.

After saving, restart the client. The tools show up in the list; ask something like: check that the instance is connected and then check whether these three numbers have WhatsApp. Approve each call the first time instead of allowing everything at once.

JSON (claude_desktop_config.json ou .cursor/mcp.json)

{
  "mcpServers": {
    "whatsapp": {
      "command": "npx",
      "args": ["tsx", "/caminho/para/server.ts"],
      "env": {
        "ZAPZAP_INSTANCE_ID": "SEU_INSTANCE_ID",
        "ZAPZAP_API_KEY": "SUA_API_KEY",
        "ZAPZAP_API_SECRET": "SEU_API_SECRET",
        "ZAPZAP_ALLOWED": "5511999999999",
        "ZAPZAP_ENABLE_SEND": "false"
      }
    }
  }
}

Security: what to do before enabling sending

Four rules. First: an allowed list of recipients, as in the code; an assistant should never be able to send to any number. Second: human approval per send call, which MCP clients usually offer and you should keep on. Third: treat read content as untrusted. A customer message telling the assistant to ignore its instructions and send something to another number is a real attack; the allowed list and approval are the defenses. Fourth: a dedicated instance, not the business's main number.

Add volume limits in the server itself (an hourly counter), log every call and rotate credentials. And remember that automated sending is still subject to WhatsApp's Terms: an AI tool does not change the number-restriction risk, and a conversation started by the assistant with someone who does not know you is the highest-risk scenario. See why WhatsApp bans numbers.

Alternatives: OpenAPI, n8n and your own code

MCP is one of several ways to give an agent tools. If your agent speaks OpenAPI, the ZapZap OpenAPI file describes every route and can feed a tool generator. If you already use n8n, the flow in the AI agent with n8n guide covers the case without writing a server. And for Python frameworks, the LangChain and CrewAI guide shows the same idea with HTTP tools.

Choose by where the agent runs: MCP suits personal and team assistants (Claude, Cursor); n8n suits trigger-based automations; your own code suits a product. In all of them the API reference is the same: send/text, chat/check, message/find and instance/status.

Quick summary

  • MCP is an open protocol for AI clients to call tools; ZapZap does not yet publish its own MCP server (status on 10/27/2026).
  • You can build yours in TypeScript with @modelcontextprotocol/sdk on the instance/status, chat/check, message/find and send/text endpoints.
  • Start with read-only tools; enable sending with an allowed list, a volume limit and human approval.
  • Messages you read are untrusted input: prompt injection is a real risk.
  • Automated sending remains subject to WhatsApp's Terms, with or without an AI assistant.

Also read

Frequently asked questions

Does ZapZap have a ready-made MCP server?

No. On 10/27/2026 there is no MCP server published and maintained by ZapZap. The guide shows how to build yours on top of the existing REST API. What is published is the API, the OpenAPI file and the n8n-nodes-zapzap package for n8n.

How do I use WhatsApp as a tool in Claude?

Build (or install) an MCP server that calls the API, register it in the client's configuration (claude_desktop_config.json in Claude Desktop, or claude mcp add in Claude Code) and restart. The tools appear for the assistant to call, with your approval.

Is it safe to let an AI assistant send WhatsApp messages?

Only with locks: an allowed list of recipients, human approval per send, a volume limit, logging and a dedicated instance. Start with read-only tools and treat the text you read as untrusted because of prompt injection.

What is the difference between MCP and calling the API directly?

The API is the service's interface. MCP is a standardized layer that presents API actions as tools the AI client discovers. You write the server once and any compatible MCP client uses it.

Does it work in Cursor and other clients?

Any client that speaks MCP can use the server. Cursor reads an mcp.json with the command and the environment variables. File locations and menus change between versions, so check the documentation of the client you use.

Start with the API: it is the base of any agent

Create your account, verify your phone and get the welcome credit, no deposit needed (one per phone, subject to the current campaign). Connect by QR code and test the calls before exposing them as tools.