whatsapp-mcp
Allows controlling WhatsApp Desktop via CDP to send messages, read chats, list unread messages, search contacts, manage drafts, and take screenshots.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@whatsapp-mcplist my unread chats"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
whatsapp-mcp
Servidor MCP que permite a opencode (o cualquier cliente MCP) controlar WhatsApp Desktop vía Chrome DevTools Protocol (CDP), usando la sesión real del usuario (misma cuenta, misma ventana, mismos datos) en lugar de una API no oficial. El control se hace sobre el DOM de web.whatsapp.com que renderiza el propio WhatsApp Desktop, con medidas anti-ban (rate limiting y typing simulado) para minimizar el riesgo de bloqueo de la cuenta.
Arquitectura
┌────────────┐ JSON-RPC 2.0 (stdio) ┌──────────────────────────────┐
│ opencode │ ◀──────────────────────▶ │ MCP server │
│ (cliente) │ │ src/server.ts │
└────────────┘ │ (10 tools expuestas) │
└───────────────┬───────────────┘
│ CDP (WebSocket)
▼
┌──────────────────────────────────┐
│ WhatsApp Desktop (Electron) │
│ --remote-debugging-port=9222 │
│ web.whatsapp.com │
└──────────────────────────────────┘Capas del proyecto:
Capa | Archivos | Responsabilidad |
Config |
| Configuración con defaults y override por env ( |
CDP |
| Cliente Chrome DevTools Protocol: descubrimiento de targets (HTTP |
| Operaciones de negocio sobre el DOM de WhatsApp: | |
Rate limit |
|
|
Server |
| Registra las 10 tools MCP, traduce errores a resultados estructurados |
Flujo de una llamada típica: el server recibe tools/call por stdio → la tool correspondiente conecta (si no está cacheada) al target de WhatsApp vía CDP → evalúa JavaScript en la página → devuelve el resultado estructurado al cliente.
Related MCP server: WhatsApp MCP Server
Requisitos
Node.js >= 23 (se usa el type stripping nativo de TypeScript, sin build step; habilitado por defecto desde Node 23.6 — se probó con Node 24).
WhatsApp Desktop instalado (ver Compatibilidad).
Cliente MCP (p. ej. opencode) para consumir las tools.
Compatibilidad (¿cuál WhatsApp funciona?)
El server no usa una API no oficial: controla el DOM de web.whatsapp.com que renderiza la app de escritorio de WhatsApp. Por lo tanto, lo que necesita es una app de escritorio basada en Electron que muestre la web de WhatsApp.
✅ Probado y verificado en vivo: paquete AUR
whatsapp-linux-desktop-bin(la app no oficial de WhatsApp para Linux), versión 1.0.1-1, con binario en/opt/WhatsApp Desktop/whatsapp-linux-desktop. Es la que el wrapperscripts/launch-whatsapp.shlanza por defecto.Los selectores del DOM (
src/whatsapp/dom.ts) y los mecanismos CDP fueron verificados contra el build 2026 de esa app (Electron 32). Si WhatsApp actualiza su web y cambia el DOM, hay que actualizar los selectores (ver Notas / limitaciones).⚠️ Puede funcionar con otras apps de escritorio de WhatsApp (WebCord, Ferdium, etc.) siempre que: (1) sean Electron y expongan
--remote-debugging-port, y (2) rendericenweb.whatsapp.comcon el mismo DOM. No están soportadas ni probadas: ajustaBINenscripts/launch-whatsapp.shy verifica los selectores antes de usarlas.❌ No funciona con WhatsApp Web en un navegador normal (necesitas el flag de CDP de un runtime controlable) ni con el cliente móvil.
Para saber qué tienes instalado:
pacman -Q | grep -i whatsapp # paquete + versión (p. ej. whatsapp-linux-desktop-bin 1.0.1-1)
ls /opt/ | grep -i whatsapp # binario (p. ej. "WhatsApp Desktop")Instalación
npm installNo hay step de compilación: node src/server.ts ejecuta el TypeScript directamente.
Configuración
Todas las variables son opcionales y se leen del entorno con prefijo WA_MCP_. Ver .env.example.
Variable | Default | Descripción |
|
| Puerto TCP donde Chrome/Electron expone el endpoint CDP. |
|
| Interfaz donde escucha el endpoint CDP. |
|
| Directorio donde se escriben los medios descargados (imágenes, documentos, etc.). |
|
| Delay mínimo entre dos |
|
| Delay mínimo entre dos mensajes al mismo chat. |
|
| Tope de mensajes por ventana deslizante de 60s (todos los chats). |
|
| Toggle del typing simulado. |
|
| Delay mínimo entre caracteres al teclear. |
|
| Delay máximo entre caracteres al teclear. |
|
| Pausa extra tras puntuación ( |
|
| Delay aleatorio (jitter 50–150%) entre terminar de teclear y pulsar enviar. |
|
| Mensajes más largos que esto omiten el typing simulado (inserción directa). |
Uso con WhatsApp
Lanzar WhatsApp con CDP
El server solo puede controlar WhatsApp si la app corre con el flag de debugging de CDP. El wrapper scripts/launch-whatsapp.sh lo garantiza de forma idempotente:
scripts/launch-whatsapp.sh # lanza (o reutiliza) WhatsApp con CDP en 127.0.0.1:9222
scripts/launch-whatsapp.sh --check # dry-run: solo informa qué haría, sin tocar nadaTres casos que maneja el wrapper:
Ya corriendo con el flag (
--remote-debugging-port=9222) → no hace nada; solo verifica que el endpoint CDP responda.Corriendo sin el flag → termina esa instancia (SIGTERM → SIGKILL si es necesario), espera a que liberen los procesos hijos y relanza con el flag.
No corriendo → lo lanza directamente con el flag.
El wrapper usa --no-sandbox (la app no tiene chrome-sandbox setuid-root) y comprueba el endpoint CDP durante 15s tras lanzar. El login/sesión persiste en el user-data-dir, así que relanzar es seguro.
IMPORTANTE: el server solo funciona con WhatsApp abierto y con la sesión iniciada. Si WhatsApp no está corriendo, las tools no crashean: devuelven un error estructurado accionable (
whatsapp_not_running) indicando cómo lanzarlo.
Override del .desktop de usuario
Para que WhatsApp siempre arranque con CDP (aunque se lance desde el menú, no solo desde el wrapper), el lanzador de aplicaciones del usuario (~/.local/share/applications/whatsapp-linux-desktop.desktop) apunta al wrapper:
Exec=/home/junior/Projects/whatsapp-mcp/scripts/launch-whatsapp.sh %UEl archivo fuente está en desktop/whatsapp-linux-desktop.desktop. Así cualquier apertura de WhatsApp pasa por el wrapper y garantiza el puerto CDP.
Integración con opencode
Registra el server como MCP local (stdio) en ~/.config/opencode/opencode.json:
{
"mcp": {
"whatsapp": {
"type": "local",
"command": ["node", "/home/junior/Projects/whatsapp-mcp/src/server.ts"],
"enabled": true
}
}
}Después de editar el archivo hay que reiniciar opencode. Y ojo: el server MCP es un proceso stdio long-running, así que cualquier cambio bajo src/*.ts (no solo la config) tampoco se recarga en vivo — npm run verify y los tests spawnean instancias frescas y pasan con el código nuevo, pero el cliente opencode en ejecución conserva el código viejo hasta el reinicio. Para verificar que quedó registrado, revisa que las tools whatsapp_status, list_chats, read_messages, get_unread, search_contacts, read_draft, clear_draft, send_message, take_screenshot y download_media estén disponibles para el agente.
Leer adjuntos de WhatsApp desde opencode:
read_messagesreporta para cada mensaje suidytype. Para leer el contenido de un adjunto, llama adownload_mediacon eseid(la media se escribe en/tmp/opencode, o el directorio deWA_MCP_MEDIA_DIR) y luego lee elpathdevuelto con las herramientas de archivo de opencode (o el agentefile-analyser) para analizar la imagen, PDF, documento, etc.
Nota:
send_messageenvía mensajes reales. Configura los permisos de opencode (o el flujo de aprobación de tools) si quieres que cada envío requiera confirmación.
Tools MCP
Tool | Argumentos | Descripción |
| — | Estado de WhatsApp: |
|
| Lista los chats renderizados en el panel (nombre, último mensaje, hora, no-leídos). |
|
| Abre el chat indicado y lee sus últimos mensajes (autor, texto, hora/fecha, dirección, tipo). |
| — | Chats con al menos un mensaje sin leer (nombre, último mensaje, contador). |
|
| Busca chats usando el buscador real de WhatsApp (no un filtro local). |
|
| Lee el draft (texto a medio escribir) del input del chat. Devuelve |
|
| Limpia el draft del input. DESTRUCTIVO: elimina el texto sin enviar del usuario; devuelve el texto eliminado ( |
|
| Envía un mensaje con typing simulado y pasando por el rate limiter. Si el chat tiene un draft, aborta con |
|
| Descarga la media (imagen, video, audio, documento) del mensaje |
| — | Captura PNG de la ventana de WhatsApp vía CDP y la devuelve como data URL base64. |
Errores: todas las tools (salvo whatsapp_status) devuelven { ok:false, error, message, ... } con isError: true ante fallos, con claves estables (whatsapp_not_running, not_logged_in, chat_not_found, rate_limited con retryAfterMs, draft_conflict, send_not_confirmed, message_not_found, media_unsupported, media_not_loaded, cdp_error, unexpected).
Seguridad anti-ban
Rate limiting (
src/ratelimit.ts): ventana deslizante de 60s con tres políticas evaluadas en orden antes de tocar el DOM — intervalo mínimo global (minIntervalMs), cooldown por chat (cooldownPerChatMs) y tope por minuto (maxPerMinute). Un envío bloqueado devuelverate_limitedconretryAfterMs. Nada se envía sin pasar porcheckSend.Typing simulado: el texto se ingresa carácter a carácter con delays aleatorios configurables y pausas tras puntuación, más un delay de "pensar" antes de pulsar enviar. El indicador "escribiendo…" aparece para el receptor. Se omite (inserción directa, con
console.warn) cuando el mensaje superaWA_MCP_TYPING_MAX_MESSAGE_CHARSo el toggle está apagado.Manejo de drafts (texto sin enviar): si el chat tiene un draft en el input,
send_messageaborta condraft_conflict(incluye el texto del draft en el mensaje) en lugar de borrarlo o concatenarlo con el mensaje. El draft nunca se sobreescribe sin consentimiento: solo se limpia conclear_drafto pasandoclearDraft: trueexplícitamente asend_message.Sin broadcasts ni reenvíos automáticos: no hay código que haga envíos masivos.
Confirmación de permisos:
send_messagees una tool como las demás; puede exigirse aprobación manual desde el cliente MCP (permissions de opencode).
Validación
npm run typecheck # tsc --noEmit (validación de tipos)
npm test # node:test (44 tests: RateLimiter, confirmación de envío, open-chat, media)
node scripts/verify.mjs # batería completa de verificación
npm run verify # alias del anteriorscripts/verify.mjs ejecuta y reporta PASS/FAIL/SKIP por sección:
ENV: versión de Node (>= 23) y alcance del endpoint CDP.
TYPECHECK:
npx tsc --noEmit.MCP: spawn del server, handshake (
initialize+notifications/initialized),tools/list(las 10 tools),whatsapp_status,list_chats,get_unread,take_screenshoty cierre limpio con SIGTERM.send_messagenunca se invoca (solo se comprueba que esté registrada); lo mismo paradownload_media(presencia entools/listes suficiente).
Exit code 0 si no hay FAILs, 1 si algo falla. Si WhatsApp no está corriendo, los checks dependientes de CDP se reportan como SKIP (no FAIL) con un mensaje claro:
WA_MCP_CDP_PORT=9299 node scripts/verify.mjs # simula "WhatsApp caído"Troubleshooting
"Sesión nueva / QR al lanzar"
WhatsApp Desktop no tiene single-instance lock: si arranca una segunda instancia (p. ej. la abres del menú estando ya abierta, o el wrapper relanza), dos procesos compiten por los mismos LevelDB y la segunda cae a un estado vacío (pantalla de QR/sesión nueva).
Solución: cerrar WhatsApp por completo y lanzarlo solo con el wrapper (una única instancia con el flag CDP):
scripts/launch-whatsapp.shEl wrapper ya contempla el caso b (instancia sin flag → la termina y relanza). Evita abrir WhatsApp de cualquier otra forma mientras uses este server.
"Puerto 9222 no responde"
Las tools devuelven whatsapp_not_running. Causa casi siempre: WhatsApp no está corriendo con el flag de CDP. Lánzalo con el wrapper y verifica:
scripts/launch-whatsapp.sh
curl http://127.0.0.1:9222/json/version # debe responder con el "Browser" de WhatsAppErrores comunes de las tools
Error | Significado / solución |
| WhatsApp no está con CDP. Lanza con |
| WhatsApp abierto pero en pantalla de QR/login. Completa el login en la ventana y reintenta. |
| El nombre no coincide con el del chat list. Usa |
| Envío bloqueado por el rate limiter; respeta |
| El chat tiene un draft (texto sin enviar) en el input. Usa |
| El chat no cargó o el mensaje no se confirmó dentro del timeout. Reintenta. |
|
|
|
|
|
|
| Problema de conexión o error inesperado; revisa los logs y verifica que WhatsApp sigue corriendo. |
Notas / limitaciones
Selectores del DOM: las tools dependen de la estructura del DOM de WhatsApp Web, que cambia con las actualizaciones. Los selectores actuales fueron verificados contra el build 2026 (Electron 32); si WhatsApp cambia su DOM, habrá que actualizar
src/whatsapp/dom.ts,src/whatsapp/send.tsysrc/whatsapp/media.ts.Descarga de media:
download_medianecesita que el mensaje esté renderizado en el DOM (WhatsApp virtualiza#main). El blob URL de la media se revoca si el mensaje sale del viewport, así que extrae con el chat abierto y, si falla conmedia_not_loaded/message_not_found, scrollea el mensaje a la vista y reintenta. Los documentos no exponen URL en el DOM en este build, así que no son descargables (media_unsupported/sin fuente).Identificador de chat: la clave es el nombre del chat tal como aparece en el chat list (el DOM no expone un JID estable para todas las operaciones).
send_messageintenta leer el JID del header cuando está disponible, pero la identificación sigue siendo por nombre. Las variantes de emoji del mismo nombre se tratan como el mismo chat: la verificación del header (headerMatchesName, emoji-strip + case-insensitive) hace que un nombre cuya emoji difiera del header abra correctamente el chat.Typing simulado omitido en mensajes largos: por encima de
WA_MCP_TYPING_MAX_MESSAGE_CHARSel texto se inserta de golpe (con aviso en stderr), para no bloquear el envío con un tecleo interminable.Conexión on-demand: el server arranca sin tocar CDP y cada tool conecta/reutiliza la conexión WebSocket cacheada; se re-conecta si WhatsApp se relanza.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseBqualityCmaintenanceAn MCP server that enables interaction with WhatsApp using the Baileys library and Streamable HTTP transport. It supports managing contacts, chats, and messages, while providing a web admin UI for QR code authentication and media handling.285MIT
- AlicenseBqualityDmaintenanceAn MCP server that enables AI to control WhatsApp Web via Puppeteer Stealth for reading and sending messages. It features human-like interaction patterns and anti-ban protections to securely manage chats and communications through natural language.368MIT
- Alicense-qualityBmaintenanceA WhatsApp Web MCP server that enables reading chats, contacts, and messages, as well as sending, replying, reacting, and managing WhatsApp messages via stdio.493MIT
- Alicense-qualityCmaintenanceMCP server that connects AI agents to WhatsApp using the multi-device API, enabling messaging, group management, and more as a regular user.8MIT
Related MCP Connectors
Managed LinkedIn MCP server for AI agents: search, connect, message and enrich on accounts you own.
A comprehensive Model Context Protocol (MCP) server that enables AI assistants to interact with yo…
A paid remote MCP for AI agent browser DevTools MCP, built to return verdicts, receipts, usage logs,
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/moonslayers/whatsapp-cdp-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server