mcp-whatsapp

Servidor MCP local para una cuenta personal de WhatsApp. Binario único en Go que envuelve whatsmeow. Añade resolución de LID, almacenamiento de mensajes enviados, temporizadores de mensajes que desaparecen y sincronización de historial dirigida. Uso personal; se aplican los Términos de Servicio de Meta.

Documentación

Servidor MCP de WhatsApp

License: MIT CI Go Report Card Go 1.26+ MCP 42 tools whatsmeow Sealjay/mcp-whatsapp MCP server GitHub issues

Un servidor MCP de un solo binario en Go que envuelve whatsmeow para exponer una cuenta personal de WhatsApp a los LLMs. whatsapp-mcp serve se ejecuta como un daemon HTTP ligero en 127.0.0.1:8765; los clientes MCP (Claude Desktop, Cursor, Claude Code, etc.) se conectan a él vía HTTP — sin creación de procesos, sin manejo de stdin/stdout. Los mensajes se almacenan en caché en SQLite local y solo viajan al modelo cuando el agente llama a una herramienta.

Sin afiliación. Este es un proyecto independiente de código abierto. No está afiliado, respaldado ni asociado de ninguna manera con Meta Platforms, Inc., WhatsApp o whatsmeow. "WhatsApp" es una marca comercial de Meta Platforms, Inc., utilizada aquí de forma nominativa para describir interoperabilidad.

Esto comenzó como un fork de lharries/whatsapp-mcp y desde entonces se ha reescrito como un único binario en Go. Lo que añade sobre el original:

  • Resolución de LID — normaliza los JIDs de @lid a números de teléfono reales para una coincidencia precisa de contactos.
  • Almacenamiento de mensajes enviados — los mensajes salientes se persisten localmente para que el historial de conversaciones permanezca completo.
  • Temporizadores de mensajes efímeros — los mensajes salientes heredan automáticamente el temporizador efímero del chat grupal.
  • Sincronización de historial dirigida — relleno bajo demanda por chat mediante la herramienta request_sync.
  • Superficie de herramientas ampliada — 42 herramientas (ver más abajo): reacciones, respuestas, ediciones, revocar, marcar como leído, escribiendo, está-en-whatsapp, administración completa de grupos, lista de bloqueados, encuestas (crear + votar + recuento), tarjetas de contacto, indicador de ver-una-vez, presencia, ajustes de privacidad y el texto "Acerca de" del perfil.
  • Aplicación de instancia única — un flock(2) en store/.lock evita que dos procesos de serve compitan por los mismos archivos SQLite.

Configuración

Requisitos previos

  • Go 1.26+ (solo en tiempo de compilación; el tiempo de ejecución solo necesita el binario compilado).
  • Un cliente MCP que hable HTTP (Claude Desktop, Cursor, Claude Code, etc.).
  • FFmpeg (opcional) — requerido solo para send_audio_message cuando la entrada no es ya .ogg Opus. Sin él, usa send_file para enviar audio sin procesar.
  • Windows: CGO debe estar habilitado — ver docs/windows.md.

Instalación

git clone https://github.com/Sealjay/mcp-whatsapp.git
cd mcp-whatsapp
make build    # writes ./bin/whatsapp-mcp

Vincular tu teléfono (solo la primera ejecución)

Inicia el daemon y luego abre la página de vinculación en un navegador:

./bin/whatsapp-mcp serve          # starts on 127.0.0.1:8765
open http://127.0.0.1:8765/pair   # macOS; or visit the URL manually

Escanea el código QR con WhatsApp (Configuración → Dispositivos vinculados → Vincular un dispositivo). La sesión persiste en ./store/whatsapp.db; WhatsApp la rota aproximadamente cada 20 días, cuando /pair muestra un nuevo código QR.

  • Sin navegador: ./bin/whatsapp-mcp login renderiza el código QR en la terminal.
  • Sin escaneo de QR: usa el formulario de número de teléfono en /pair (o login -phone +<international number>), luego escribe el código en Vincular un dispositivo → Vincular con número de teléfono en su lugar.
  • Los endpoints de /pair/* están protegidos contra CSRF y limitados en velocidad (15 GET/min, 1 restablecimiento/min, 3 solicitudes de código/min).

Conecta tu cliente MCP

serve escucha en 127.0.0.1:8765 (o $WHATSAPP_MCP_ADDR) y sirve MCP en /mcp. Es sin estado (sin Mcp-Session-Id) y habla MCP 2026-07-28 así como revisiones anteriores.

// Claude Code (.claude/mcp.json or ~/.claude/mcp.json) and Cursor (~/.cursor/mcp.json)
{ "mcpServers": { "whatsapp": { "type": "http", "url": "http://127.0.0.1:8765/mcp" } } }

// Claude Desktop only launches stdio servers, so bridge with mcp-remote
// (~/Library/Application Support/Claude/claude_desktop_config.json)
{ "mcpServers": { "whatsapp": { "command": "npx", "args": ["-y", "mcp-remote", "http://127.0.0.1:8765/mcp"] } } }

Reinicia el cliente. El daemon sobrevive a los reinicios del cliente.

Envío y recepción de archivos

WHATSAPP_MCP_MEDIA_ROOT (por defecto <store>/uploads/, creado en la primera ejecución) es el único directorio que las herramientas de archivos pueden tocar:

  • Envío: send_file y send_audio_message toman un media_path bajo la raíz.
  • Recepción: download_media almacena en caché en <store>/<chat_jid>/<hex(message_id)>/<safe_filename> (verificado contra el tamaño almacenado y SHA-256). Un output_path opcional también coloca el archivo bajo la raíz; un archivo existente allí hace que la llamada sea una operación sin efecto.

Las rutas fuera de la raíz, incluidos los enlaces simbólicos que resuelven fuera de ella, se rechazan. Cualquier cosa dentro de la raíz es legible y escribible por las herramientas, así que mantén los secretos fuera.

Clientes en sandbox (Claude Cowork y similares) no pueden leer la caché del daemon. Apunta WHATSAPP_MCP_MEDIA_ROOT a un directorio que el sandbox pueda leer y dale al cliente esta instrucción:

Al llamar al download_media del MCP de WhatsApp, pasa siempre output_path establecido en una ruta bajo tu espacio de trabajo compartido. Sin él, el archivo descifrado aterriza solo en la caché local del daemon, que está fuera de tu sandbox y no es legible. output_path debe vivir bajo WHATSAPP_MCP_MEDIA_ROOT en el lado del daemon; el nombre base es tuyo para elegir.

Banderas de servicio y entorno

  • -addr host:port (env WHATSAPP_MCP_ADDR, por defecto 127.0.0.1:8765).
  • -allow-remote: vincula una dirección que no sea de loopback. Requiere WHATSAPP_MCP_TOKEN, un token portador para /mcp y /pair/*; serve sale sin él.
  • WHATSAPP_MCP_MEDIA_ROOT: la raíz de la lista blanca de archivos anterior.
  • WHATSAPP_MCP_DEBUG=1 o -debug: registros verbosos (ver Registro de depuración).

Ejecución del daemon

Los eventos llegan a SQLite solo mientras serve se ejecuta. Después de un tiempo de inactividad, whatsmeow rellena mediante HistorySync dentro de la ventana de retención propia de WhatsApp; cualquier cosa más antigua se pierde, y request_sync rellena un chat bajo demanda. Un flock(2) en store/.lock permite un serve (o login) por almacén.

OpciónCómo
macOS launchdCopia docs/launchd/com.sealjay.whatsapp-mcp.plist a ~/Library/LaunchAgents/, completa {{PATH_TO_REPO}} / {{STORE_DIR}}, launchctl load
Linux systemdCopia docs/systemd/whatsapp-mcp.service a ~/.config/systemd/user/, completa los marcadores de posición, systemctl --user enable --now whatsapp-mcp
Gancho de Claude CodePon docs/hooks/setup.sh en .claude/hooks/ y llámalo desde settings.json (idempotente)
Manual./bin/whatsapp-mcp serve

Docker. La imagen (debian:bookworm-slim más ffmpeg, unos cientos de MB) ejecuta -store /home/app/store; persiste ese volumen. Vincula primero con un contenedor único de login, porque login y un serve en ejecución no pueden compartir el bloqueo del almacén:

docker build -t whatsapp-mcp --build-arg VERSION=$(git describe --tags --always) .
docker run --rm -it -v whatsapp-mcp-store:/home/app/store whatsapp-mcp login
TOKEN=$(openssl rand -hex 32); echo "$TOKEN"
docker run -d --name whatsapp-mcp -v whatsapp-mcp-store:/home/app/store \
  -p 127.0.0.1:8765:8765 -e WHATSAPP_MCP_TOKEN="$TOKEN" \
  whatsapp-mcp serve -addr 0.0.0.0:8765 -allow-remote

Los clientes luego envían Authorization: Bearer $TOKEN. El contenedor vincula loopback por defecto, así que -allow-remote más un token es lo que hace que -p funcione. Para re-vincular después de que WhatsApp rote la sesión (aproximadamente cada 20 días), detén el contenedor, vuelve a ejecutar el contenedor de login e inícialo de nuevo.

Arquitectura

cmd/whatsapp-mcp/       login / serve / smoke subcommands
internal/client/        whatsmeow client wrapper (send, download, events, history, features)
internal/daemon/        HTTP server, pairing state machine, /pair endpoints
internal/mcp/           mark3labs/mcp-go v1 server + tool registrations
internal/media/         ogg parsing, waveform synthesis, ffmpeg shell-out
internal/security/      path allowlisting, filename sanitisation, log redaction
internal/store/         SQLite cache, LID resolution, query layer

Los datos viven bajo ./store/ (-store DIR): messages.db (caché de mensajes), whatsapp.db (sesión de whatsmeow) y .lock. Las herramientas de consulta leen SQLite; las herramientas de envío y acción llaman a whatsmeow; los eventos entrantes se escriben en SQLite por una goroutine en el mismo proceso.

Herramientas

42 herramientas, agrupadas por propósito.

Lectura / consulta

HerramientaPropósito
search_contactsBúsqueda de subcadenas en nombres de contactos y números de teléfono en caché
list_messagesConsulta + filtro de mensajes; devuelve texto formateado con ventanas de contexto
list_chatsLista de chats con vista previa del último mensaje; ordena por actividad o nombre
get_chatMetadatos del chat por JID
get_message_contextVentana antes/después alrededor de un mensaje específico
download_mediaDescarga medios persistidos a una ruta local
request_syncPide a WhatsApp que rellene el historial de un chat

Envío

HerramientaPropósito
send_messageEnvía un mensaje de texto a un número de teléfono o JID
send_fileEnvía imagen/video/documento/audio sin procesar con subtítulo opcional; view_once: bool marca los submensajes de imagen/video/audio como ver-una-vez (ignorado para documentos)
send_audio_messageEnvía una nota de voz (auto-convierte vía ffmpeg si no es .ogg Opus); soporta view_once: bool
send_pollEnvía una encuesta con una pregunta y 2+ opciones; selectable_count controla cuántas opciones puede elegir un votante. Genera el MessageSecret de 32 bytes requerido para que los votos se descifren
send_poll_voteEmite un voto en una encuesta vista previamente; options debe coincidir exactamente con los nombres de las opciones
get_poll_resultsDevuelve el recuento de una encuesta que tenemos en caché (incluye opciones con 0 votos)
send_contact_cardEnvía una tarjeta de contacto; sintetiza una vCard 3.0 desde name + phone, o pasa un vcard sin procesar para omitir la síntesis

Acciones de mensajes

HerramientaPropósito
mark_readMarca IDs de mensajes específicos como leídos
mark_chat_readConfirma los mensajes entrantes más recientes en un chat para limpiar la insignia de no leídos
send_reactionReacciona a un mensaje (emoji vacío limpia una reacción existente)
send_replyRespuesta de texto que cita un mensaje anterior
edit_messageEdita un mensaje enviado previamente
delete_messageRevoca (elimina para todos) un mensaje
send_typingEstablece presencia de escribiendo / grabando por chat

Grupos

HerramientaPropósito
create_groupCrea un grupo con un nombre y participantes iniciales
leave_groupAbandona un grupo
list_groupsLista todos los grupos de los que el usuario es miembro
get_group_infoMetadatos completos del grupo (participantes, ajustes, configuración de invitación)
update_group_participantsAñadir / eliminar / ascender / degradar participantes (action: add|remove|promote|demote). Comprueba primero que eres administrador; las personas cuyos ajustes de privacidad bloquean una adición directa reciben una tarjeta de invitación al grupo en su chat 1:1 (send_invite, por defecto true; invite_message de subtítulo opcional)
set_group_nameCambia el asunto del grupo
set_group_topicCambia la descripción del grupo; cadena vacía la limpia
set_group_announceAlterna el modo solo-anuncios (solo los administradores pueden enviar)
set_group_lockedAlterna el modo bloqueado (solo los administradores pueden editar los metadatos del grupo)
get_group_invite_linkObtiene el enlace de invitación; reset: true revoca el enlace anterior primero
join_group_with_linkÚnete a un grupo mediante una URL de chat.whatsapp.com o código de invitación simple

Lista de bloqueados

HerramientaPropósito
get_blocklistDevuelve la lista de bloqueados actual (JIDs, usualmente @lid, más números de teléfono donde se conocen)
block_contactBloquea un contacto por número de teléfono o JID
unblock_contactDesbloquea un contacto

Privacidad / presencia / estado

HerramientaPropósito
send_presenceEstablece la disponibilidad propia (available o unavailable) — distinta del send_typing por chat
get_privacy_settingsAjustes de privacidad actuales como JSON
set_privacy_settingCambia un ajuste de privacidad por name + value (validación estricta de enumeraciones; las combinaciones inválidas se rechazan)
set_status_messageActualiza el texto "Acerca de" del perfil; cadena vacía lo limpia

Administración

HerramientaPropósito
is_on_whatsappVerificación por lotes de qué números de teléfono están registrados en WhatsApp
get_statusInforma si el puente está conectado y con qué cuenta está vinculado
pairing_statusInforma el estado de vinculación del dispositivo como un sobre setup_state estructurado (ready / awaiting_qr + qr_payload / error) para supervisores programáticos que muestran el QR de vinculación

Diferido

Intencionalmente aún no expuesto:

  • subscribe_presence — sin capa de persistencia para eventos de presencia, omitido para evitar una herramienta colgante.
  • Configurador de foto de perfil — whatsmeow aguas arriba no expone un configurador a nivel de usuario.
  • Participantes en modo aprobación, comunidades, boletines — superficie de bajo uso, diferido.

Limitaciones

  • Riesgo de inyección de prompts: como muchos servidores MCP, este está sujeto a la tríada letal. La inyección de prompts en mensajes entrantes podría provocar la exfiltración de datos privados: trate la superficie de herramientas en consecuencia.
  • Lapsos de mensajes: consulte Ejecutando el daemon.
  • Instancia única por almacén: solo un whatsapp-mcp serve puede mantener el bloqueo del almacén. Los clientes MCP paralelos deben apuntar a directorios -store diferentes (y, por lo tanto, a sesiones emparejadas diferentes).
  • Windows: requiere CGO y un compilador de C — consulte docs/windows.md.
  • Límites ascendentes: la obtención/envío de mensajes está limitado por lo que whatsmeow soporta contra la API multidispositivo web de WhatsApp.

Desarrollo

make test          # unit tests
make test-race     # with -race
make vet           # go vet
make e2e           # build + JSON-RPC smoke over HTTP (requires -tags=e2e)
make smoke         # boot-test the server without connecting to WhatsApp

Actualización de whatsmeow

El CI semanal ejecuta una sonda de actualización ascendente. Para hacerlo manualmente:

make upgrade-check

Esto actualiza go.mau.fi/whatsmeow@main, reordena, compila y prueba. Si todo está en verde, confirme los cambios de go.mod / go.sum.

scripts/mdtest-parity.sh en CI falla la compilación temprano si el upstream elimina o renombra cualquier método de whatsmeow que llamamos: es el canario para la deriva de la API.

Solución de problemas

  • connect failed … en serve — el daemon no está emparejado. Abra http://127.0.0.1:8765/pair en un navegador y escanee el código QR. Alternativamente, ejecute ./bin/whatsapp-mcp login en una terminal.
  • another whatsapp-mcp instance is already running — solo un serve puede mantener el bloqueo del almacén. Verifique si hay un proceso suelto (ps aux | grep whatsapp-mcp) u otro cliente MCP apuntando al mismo directorio -store.
  • El QR no se muestra — la terminal no renderiza Unicode de medio bloque. Pruebe con iTerm2, Windows Terminal o similar.
  • Límite de dispositivos alcanzado — WhatsApp limita los dispositivos vinculados. Elimine uno desde Configuración → Dispositivos vinculados en su teléfono.
  • No se cargan mensajes — después de la autenticación inicial, puede tomar varios minutos que el historial se rellene. Use request_sync para apuntar a un chat específico.
  • WhatsApp desincronizado — elimine ambos archivos de base de datos (store/messages.db y store/whatsapp.db) y vuelva a ejecutar login.
  • ffmpeg not found — send_audio_message necesita ffmpeg en PATH para convertir audio que no sea Opus. Use send_file para audio sin procesar en su lugar.

Registro de depuración

Por defecto, los registros de stderr redactan los JID a …<last-4>, resumen los cuerpos como [<length>B: text|url|command] y colapsan las URL de medios a <scheme>://<host>/…. Para ver el contenido mientras depura, ejecute ./bin/whatsapp-mcp -debug serve o establezca WHATSAPP_MCP_DEBUG=1. Incluso entonces, los dígitos con forma de número de teléfono muestran solo sus últimos 5 (+15551234567 → ****34567). Eso es ofuscación, no anonimización: alguien que conozca sus contactos aún puede correlacionarlos.

Para problemas de integración con Claude Desktop, consulte la documentación de MCP.

Contribuciones

Las contribuciones son bienvenidas mediante solicitudes de extracción. Consulte CONTRIBUTING.md.

Licencia

Licencia MIT — consulte LICENSE.