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
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
@lida 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, revocación, marcar como leído, escritura, is-on-whatsapp, administración completa de grupos, lista de bloqueados, encuestas (crear + votar + recuento), tarjetas de contacto, indicador de vista única, presencia, ajustes de privacidad y el texto "Acerca de" del perfil.
- Aplicación de instancia única — un
flock(2)enstore/.lockevita que dos procesosservecompitan por los mismos archivos SQLite.
Configuración
Requisitos previos
- Go 1.25+ (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_messagecuando la entrada no es ya.oggOpus. Sin él, usasend_filepara 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 en tu teléfono (Ajustes → Dispositivos vinculados → Vincular un dispositivo). La vinculación persiste en ./store/whatsapp.db. Cuando WhatsApp invalida la sesión (aproximadamente cada 20 días), visita /pair de nuevo y vuelve a escanear.
Alternativa (sin interfaz gráfica / CI): ./bin/whatsapp-mcp login renderiza el QR en la terminal. Úsalo cuando no haya un navegador disponible.
Conecta tu cliente MCP
whatsapp-mcp serve es un daemon HTTP en 127.0.0.1:8765 (o $WHATSAPP_MCP_ADDR). Los clientes MCP se conectan a él por HTTP:
// Claude Desktop — ~/Library/Application Support/Claude/claude_desktop_config.json
{
"mcpServers": {
"whatsapp": { "url": "http://127.0.0.1:8765/mcp" }
}
}
// Claude Code — .claude/mcp.json (project) or ~/.claude/mcp.json (user)
{
"mcpServers": {
"whatsapp": { "type": "http", "url": "http://127.0.0.1:8765/mcp" }
}
}
// Cursor — ~/.cursor/mcp.json
{
"mcpServers": {
"whatsapp": { "type": "http", "url": "http://127.0.0.1:8765/mcp" }
}
}
Reinicia el cliente. WhatsApp aparece como una integración disponible. Cerrar y reabrir el cliente se reconecta al daemon — sin creación de procesos, sin handshake por sesión, sin manejo de stdin/stdout.
Envío y recepción de archivos
WHATSAPP_MCP_MEDIA_ROOT controla ambas direcciones del movimiento de archivos:
- Envío —
send_fileysend_audio_messageaceptan un argumentomedia_pathque apunta al archivo a enviar. La ruta debe estar bajo la raíz permitida. - Recepción —
download_mediaescribe los medios descifrados en la caché del daemon en<store>/<chat_jid>/. Pasar el argumento opcionaloutput_pathademás coloca el archivo en una ubicación elegida por el llamante, que también debe estar bajo la raíz permitida. Si ya existe un archivo enoutput_path, la llamada es un no-op.
Por defecto, la raíz permitida es ./store/uploads/ (resuelta en relación con tu directorio -store). En la primera ejecución, serve la crea automáticamente; coloca los archivos que quieras enviar en ella y apunta output_path aquí si quieres leer los medios entrantes desde el mismo lugar.
Para permitir un directorio diferente, establece WHATSAPP_MCP_MEDIA_ROOT (ruta absoluta) al iniciar el daemon:
WHATSAPP_MCP_MEDIA_ROOT=/Users/me/whatsapp-shared ./bin/whatsapp-mcp serve
O añádelo a tu plist de launchd / unidad de systemd / perfil de shell para que persista entre reinicios.
Las rutas fuera de la raíz permitida se rechazan con un error claro para que Claude pueda pedirte que muevas el archivo o actualices la variable de entorno. Los enlaces simbólicos dentro de la raíz se resuelven antes de la comprobación, por lo que un enlace simbólico que apunte fuera de la raíz también se rechaza. No coloques secretos dentro de la raíz permitida — la lista de permitidos limita lo que la herramienta puede leer o escribir, pero cualquier cosa dentro está en juego.
Clientes en sandbox (Claude.ai con Cowork, etc.)
Los clientes MCP en sandbox no pueden leer la caché local del daemon. Para que los medios descargados sean visibles para ellos, apunta WHATSAPP_MCP_MEDIA_ROOT a un directorio que el sandbox del cliente también pueda leer (un montaje del espacio de trabajo de Cowork, un volumen compartido, etc.), y dile al cliente que pase output_path en cada llamada a download_media dentro de esa raíz. Una instrucción de sistema copiable y pegable:
Al llamar al
download_mediadel MCP de WhatsApp, pasa siempreoutput_pathestablecido 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_pathdebe estar bajoWHATSAPP_MCP_MEDIA_ROOTen el lado del daemon; el nombre base es tuyo para elegir.
Arquitectura
Un binario, siete paquetes internos:
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 endpoint
internal/mcp/ mark3labs/mcp-go 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
Ciclo de vida del proceso
serve se ejecuta como un daemon HTTP de larga duración. Los clientes MCP se conectan y desconectan libremente; el daemon permanece activo y continúa recibiendo eventos de WhatsApp. Un flock(2) en store/.lock evita que dos instancias compitan por el mismo almacén (WhatsApp expulsaría de todos modos una de las dos conexiones de dispositivo vinculado).
La compensación: los eventos se persisten en SQLite solo mientras serve está en ejecución. Si el daemon se detiene, la conexión de WhatsApp se cierra. En el siguiente inicio, whatsmeow emite eventos events.HistorySync que rellenan las conversaciones en SQLite, pero la ventana de recuperación está gobernada por la retención del lado del servidor de WhatsApp para clientes multidispositivo — no por este código. Los mensajes que llegan durante una brecha lo suficientemente larga como para superar la retención de WhatsApp no son recuperables. Para brechas conocidas más cortas, la herramienta request_sync activa un relleno por chat bajo demanda.
Almacenamiento de datos
Todo vive bajo ./store/ (anula con -store DIR):
store/messages.db— caché local de chats/mensajes, indexada para búsqueda.store/whatsapp.db— estado de dispositivo/sesión propio de whatsmeow.store/.lock— bloqueo de asesoramiento efímero para instancia única deserve.
Flujo de datos
- El cliente envía un
tools/callJSON-RPC aservepor HTTP. - La capa MCP despacha a un manejador interno.
- El manejador consulta el almacén SQLite local o llama a whatsmeow directamente (enviar, descargar, reacciones, etc.).
- Los eventos entrantes de WhatsApp se persisten en el almacén en una goroutine en segundo plano dentro del mismo proceso, por lo que las herramientas de consulta siempre ven el estado actual.
Ejecución del daemon
El daemon está diseñado para ejecutarse independientemente de cualquier cliente MCP. Tres modelos de ciclo de vida compatibles:
macOS — launchd. Plantilla en docs/launchd/com.sealjay.whatsapp-mcp.plist. Copia a ~/Library/LaunchAgents/, reemplaza los marcadores de posición {{PATH_TO_REPO}} / {{STORE_DIR}}, launchctl load. El daemon se ejecuta desde el inicio de sesión en adelante.
Linux — unidad de usuario de systemd. Plantilla en docs/systemd/whatsapp-mcp.service. Copia a ~/.config/systemd/user/, reemplaza los marcadores de posición, systemctl --user enable --now whatsapp-mcp.
Hook de SessionStart de Claude Code. Para ciclos de vida con alcance de proyecto, coloca docs/hooks/setup.sh en el .claude/hooks/ de tu proyecto y configura settings.json para invocarlo. El hook es idempotente — seguro de ejecutar junto con launchd/systemd.
Manual. ./bin/whatsapp-mcp serve -addr 127.0.0.1:8765 en cualquier terminal. Ctrl-C para detener.
La vinculación inicial ocurre en un navegador: inicia el daemon, abre http://127.0.0.1:8765/pair, escanea el QR con tu teléfono. No se requiere terminal. El protocolo multidispositivo de WhatsApp rota la sesión del dispositivo vinculado aproximadamente cada 20 días; cuando eso ocurre, la página /pair sirve un QR nuevo automáticamente — visítala de nuevo y vuelve a vincular. Los endpoints /pair/* tienen límite de velocidad (5 GET/min, 1 POST/min en /pair/reset) y están protegidos contra CSRF.
Banderas y variables de entorno para serve:
-addr host:port(envWHATSAPP_MCP_ADDR, predeterminado127.0.0.1:8765).-allow-remote(opt-in explícito para vincular una dirección que no sea de loopback; requiereWHATSAPP_MCP_TOKEN).WHATSAPP_MCP_TOKEN— token de portador para/mcpy/pair/*cuando-allow-remoteestá establecido. Requerido;servesale si falta.WHATSAPP_MCP_MEDIA_ROOT— raíz permitida parasend_file/send_audio_messagemedia_pathydownload_mediaoutput_path.WHATSAPP_MCP_DEBUG=1— habilita el registro verboso con redacción parcial de números de teléfono (últimos 5 dígitos visibles).
Herramientas
42 herramientas, agrupadas por propósito.
Leer / consultar
| Herramienta | Propósito |
|---|---|
search_contacts | Búsqueda de subcadenas en nombres de contactos y números de teléfono en caché |
list_messages | Consultar + filtrar mensajes; devuelve texto formateado con ventanas de contexto |
list_chats | Listar chats con vista previa del último mensaje; ordenar por actividad o nombre |
get_chat | Metadatos del chat por JID |
get_message_context | Ventana antes/después alrededor de un mensaje específico |
download_media | Descargar medios persistidos a una ruta local |
request_sync | Pedir a WhatsApp que rellene el historial de un chat |
Enviar
| Herramienta | Propósito |
|---|---|
send_message | Enviar un mensaje de texto a un número de teléfono o JID |
send_file | Enviar imagen/video/documento/audio sin procesar con subtítulo opcional; view_once: bool marca los submensajes de imagen/video/audio como de vista única (ignorado para documentos) |
send_audio_message | Enviar una nota de voz (auto-convierte vía ffmpeg si no es .ogg Opus); admite view_once: bool |
send_poll | Enviar 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_vote | Emitir un voto en una encuesta vista previamente; options debe coincidir exactamente con los nombres de las opciones |
get_poll_results | Devolver el recuento de una encuesta que tenemos en caché (incluye opciones con 0 votos) |
send_contact_card | Enviar una tarjeta de contacto; sintetiza una vCard 3.0 a partir de name + phone, o pasa un vcard sin procesar para omitir la síntesis |
Acciones de mensajes
| Herramienta | Propósito |
|---|---|
mark_read | Marcar IDs de mensajes específicos como leídos |
mark_chat_read | Confirmar los mensajes entrantes más recientes en un chat para limpiar la insignia de no leídos |
send_reaction | Reaccionar a un mensaje (emoji vacío limpia una reacción existente) |
send_reply | Respuesta de texto que cita un mensaje anterior |
edit_message | Editar un mensaje enviado previamente |
delete_message | Revocar (eliminar para todos) un mensaje |
send_typing | Establecer presencia de redacción / grabación por chat |
Grupos
| Herramienta | Propósito |
|---|---|
create_group | Crear un grupo con un nombre y participantes iniciales |
leave_group | Abandonar un grupo |
list_groups | Listar todos los grupos de los que el usuario es miembro |
get_group_info | Metadatos completos del grupo (participantes, ajustes, configuración de invitación) |
update_group_participants | Añadir / eliminar / ascender / degradar participantes (action: add|remove|promote|demote) |
set_group_name | Cambiar el asunto del grupo |
set_group_topic | Cambiar la descripción del grupo; una cadena vacía la borra |
set_group_announce | Alternar el modo solo anuncios (solo los administradores pueden enviar) |
set_group_locked | Alternar el modo bloqueado (solo los administradores pueden editar los metadatos del grupo) |
get_group_invite_link | Obtener el enlace de invitación; reset: true revoca el enlace anterior primero |
join_group_with_link | Unirse a un grupo mediante una URL chat.whatsapp.com o un código de invitación simple |
Lista de bloqueados
| Herramienta | Propósito |
|---|---|
get_blocklist | Devolver la lista de bloqueados actual |
block_contact | Bloquear un contacto por número de teléfono o JID |
unblock_contact | Desbloquear un contacto |
Privacidad / presencia / estado
| Herramienta | Propósito |
|---|---|
send_presence | Establecer la propia disponibilidad (available o unavailable) — distinto del send_typing por chat |
get_privacy_settings | Ajustes de privacidad actuales como JSON |
set_privacy_setting | Cambiar un ajuste de privacidad mediante name + value (validación estricta de enumerados; las combinaciones inválidas se rechazan) |
set_status_message | Actualizar el texto "Acerca de" del perfil; una cadena vacía lo borra |
Administración
| Herramienta | Propósito |
|---|---|
is_on_whatsapp | Comprobar por lotes qué números de teléfono están registrados en WhatsApp |
get_status | Informar si el puente está conectado y con qué cuenta está emparejado |
pairing_status | Informar del estado de emparejamiento 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
Intencionadamente aún no expuesto:
subscribe_presence— no hay capa de persistencia para eventos de presencia, se omite para evitar una herramienta colgante.- Establecedor de foto de perfil — whatsmeow aguas arriba no expone un establecedor a nivel de usuario.
- Participantes en modo aprobación, comunidades, boletines — superficie de bajo uso, diferido.
Limitaciones
- Riesgo de inyección de instrucciones: como en muchos servidores MCP, este está sujeto a la tríada letal. La inyección de instrucciones en mensajes entrantes podría provocar la exfiltración de datos privados — trate la superficie de herramientas en consecuencia.
- Reautenticación: WhatsApp puede invalidar la sesión del dispositivo vinculado periódicamente; vuelva a ejecutar
./bin/whatsapp-mcp logincuando ocurra. - Lagunas de mensajes cuando
serveno está en ejecución: los eventos solo fluyen a SQLite mientras el binario está activo. Los mensajes enviados durante una ventana sin conexión se recuperan en la siguiente reconexión solo si la retención de multidispositivo de WhatsApp aún los conserva; para lagunas más largas userequest_syncpor chat, o acepte la pérdida. - Instancia única por almacén: solo un
whatsapp-mcp servepuede mantener el bloqueo del almacén. Los clientes MCP paralelos deben apuntar a directorios-storediferentes (y por tanto a sesiones emparejadas diferentes). - Windows: requiere CGO y un compilador de C — consulte docs/windows.md.
- Límites aguas arriba: la obtención/envío de mensajes está limitado por lo que whatsmeow soporta contra la API de multidispositivo web de WhatsApp.
- La redacción de registros es ofuscación, no anonimización. El conocimiento parcial de sus contactos permite la correlación a partir de los últimos 5 dígitos visibles. Los enlaces simbólicos dentro de
./store/uploads/se resuelven antes de la comprobación de ruta para que no puedan escapar, pero la raíz misma es un límite de confianza — solo coloque archivos que tenga intención de enviar dentro de ella.
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
Actualizar whatsmeow
El CI semanal ejecuta una sonda de actualización aguas arriba. 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 aguas arriba 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 …enserve— el daemon no está emparejado. Abrahttp://127.0.0.1:8765/pairen un navegador y escanee el QR. Alternativamente, ejecute./bin/whatsapp-mcp loginen una terminal.another whatsapp-mcp instance is already running— solo unservepuede mantener el bloqueo del almacén. Compruebe 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 Ajustes → Dispositivos vinculados en su teléfono.
- No se cargan mensajes — tras la autenticación inicial, puede tardar varios minutos en rellenarse el historial. Use
request_syncpara apuntar a un chat específico. - WhatsApp desincronizado — elimine ambos archivos de base de datos (
store/messages.dbystore/whatsapp.db) y vuelva a ejecutarlogin. ffmpeg not found—send_audio_messagenecesita ffmpeg enPATHpara convertir audio no Opus. Usesend_filepara audio sin procesar en su lugar.
Registro de depuración
Por defecto, los JID en los registros de stderr se redactan a …<last-4-chars-of-user-part> y los cuerpos de mensajes se resumen como [<length>B: text|url|command]. Las URL de CDN de medios se colapsan a <scheme>://<host>/…. Para ver el contenido de los mensajes mientras depura activamente:
-
Como bandera:
./bin/whatsapp-mcp -debug serve -
Como variable de entorno en la configuración de su cliente MCP:
"env": { "WHATSAPP_MCP_DEBUG": "1" }
Incluso con el modo de depuración activado, las secuencias de dígitos con forma de número de teléfono en cuerpos y JID están parcialmente enmascaradas — solo los últimos 5 dígitos son visibles (p. ej. +15551234567 → ****34567). Esto significa que los registros de depuración son seguros para compartir en informes de errores sin filtrar números de teléfono completos.
Aviso de honestidad. El esquema de redacción parcial es ofuscación para la comodidad del lector de registros, no anonimización. Alguien con conocimiento independiente de sus contactos aún puede correlacionar los últimos 5 dígitos con un número de teléfono específico. Trate los registros redactados como "probablemente seguros para pegar en un issue de GitHub", no como "anonimizados".
Para problemas de integración con Claude Desktop, consulte la documentación de MCP.
Contribuciones
Las contribuciones son bienvenidas mediante pull request. Consulte CONTRIBUTING.md.
Licencia
Licencia MIT — consulte LICENSE.