Skip to main content
Glama

mailwarden

npm license Node Website Smithery Available on CodeGuilds

Un servidor nativo y fiable de Gmail MCP — clasificación completa de la bandeja de entrada para asistentes de IA, con la función que ningún otro servidor MCP de Gmail incluye: posponer del lado del buzón.

Destacados

  • Posponer — el único posponer del lado del buzón en un servidor MCP de Gmail. Archiva un hilo ahora, haz que reaparezca en la bandeja de entrada en una fecha. Construido sobre etiquetas con fecha + una limpieza, por lo que funciona desde cualquier cliente, es visible en el propio Gmail y sobrevive a reinicios. (Donde otro servidor ofrece un "posponer", es una lista de recordatorios local — el correo nunca sale ni vuelve a entrar en la bandeja de entrada).

  • Búsqueda en la que puedes confiar. threads.list de Gmail — la llamada por la que pasa cualquier búsqueda de hilos — puede responder a is:unread desde un estado de lectura a nivel de hilo obsoleto: medido en un buzón real, el 86% de los hilos que devolvió no contenía ningún mensaje no leído; en un segundo buzón, ninguna desviación. No puedes saber en qué buzón estás sin mirar, por lo que search vuelve a verificar cada resultado contra sus etiquetas en vivo. Paginado mediante pageToken/nextPageToken.

  • Operaciones masivas que escalan. bulk_modify archiva/etiqueta todo lo que coincide con una consulta a 1000 mensajes por solicitud de API — con informes de éxito parcial por fragmento en lugar de todo o nada. La limpieza de pospuestos utiliza la misma ruta por lotes.

  • Salidas estructuradas. Cada herramienta declara un outputSchema y devuelve structuredContent validado junto con texto JSON delimitado — sin adivinanzas de análisis para los clientes.

  • Superficie de ataque pequeña. Sin herramientas de envío (sin ruta de exfiltración para correo inyectado por prompt), modo de solo lectura opcional, sin telemetría, sin puertos abiertos por defecto, protección de descarga a prueba de enlaces simbólicos, salida protegida contra inyección. Una excepción deliberada: unsubscribe / bulk_unsubscribe (nivel de gestión) contactan con el punto final de exclusión voluntaria nombrado en el propio encabezado de un mensaje — el único host no Google al que mailwarden llega, y un despliegue de nivel read no realiza ninguna solicitud saliente. Detalles en Seguridad y privacidad y Cancelación de suscripción.

  • Correcto con el correo del mundo real. Encabezados RFC 2047 decodificados (=?UTF-8?B?…?= → texto legible), cuerpos decodificados en su juego de caracteres declarado (sin mojibake para correo ISO-8859-1/Shift_JIS), 429/5xx reintentados con retroceso exponencial.

Related MCP server: Gmail MCP

Por qué

Los conectores que sincronizan o almacenan en caché tu buzón pueden retrasarse con respecto a él — e incluso el propio índice de búsqueda de Gmail a veces es impreciso (ver más abajo). mailwarden habla directamente con la API de Gmail en vivo (sin instantánea en caché) y vuelve a verificar lo que devuelve el índice, por lo que lo que ves es lo que realmente hay. Es una capa de capacidad genérica de Gmail — mantén tus propias reglas/lógica en tu cliente de IA, no en el servidor.

search va un paso más allá de la API sin procesar: el índice threads.list de Gmail puede responder a operadores de estado de lectura desde una copia obsoleta de ese estado, por lo que is:unread devuelve hilos que terminaste de leer hace semanas — en un buzón medido, la gran mayoría de lo que devolvió. Dado que cada resultado se obtiene en vivo de todos modos, search vuelve a comprobar los predicados inequívocos (is:unread/is:read/is:starred/in:inbox/category:…, con negación) contra las etiquetas reales de cada hilo y descarta los falsos positivos del índice.

En comparación con otros servidores MCP de Gmail

La mayoría de los servidores MCP de Gmail cubren la misma superficie de lectura/etiquetado/envío. Dos capacidades siguen siendo exclusivas de mailwarden (posponer del lado del buzón, re-verificación de búsqueda), y una omisión deliberada es una característica de seguridad, no una brecha. El propio servidor de Google también es más limitado de lo que parece: solo borradores, y sin papelera, filtros ni cancelación de suscripción.

Capacidad

mailwarden

Google oficial

taylorwilsdon

a-bonus

klodr

Posponer del lado del buzón — archivar ahora, reaparecer en la bandeja de entrada en una fecha/hora o preajuste

Re-verificación de resultados de búsqueda — descarta los falsos positivos del índice de hilos contra etiquetas en vivo

Limpieza / masivo sobre una consulta — una acción en cada hilo que devuelve una búsqueda

✅ 1000/solicitud, éxito parcial

⚠️ lote por ids explícitos

⚠️ lote por ids explícitos

Cancelar suscripción — resumen por remitente + exclusión voluntaria con un clic RFC 8058, sin necesidad de ámbito de envío

⚠️ encabezado mostrado, sin acción

Resumen de clasificación de bandeja de entrada — una llamada que agrupa lo que está esperando

✅ remitente/etiqueta/antigüedad + señales de encabezado

✅ banderas heurísticas + estadísticas

Filtros del lado del servidor — reglas que siguen clasificando sin un asistente en el bucle

✅ nunca reenviando

Sin herramientas de envío — por diseño — un correo inyectado por prompt no tiene ruta de exfiltración

✅ sin redactar en absoluto

⚠️ solo borradores

❌ envía

❌ envía

❌ envía

Niveles de herramientas de privilegio mínimo — ámbitos OAuth derivados de las herramientas que habilitas

⚠️ división de ámbito

⚠️ inverso: herramientas controladas por ámbitos concedidos

Cifrado de token en reposo (opcional)

✅ AES-256-GCM

n/a (alojado)

Sin nube del proveedor — tú operas el servidor

❌ Alojado por Google

Salidas estructuradas — cada herramienta declara un outputSchema

Instantánea al 16 de agosto de 2026, a partir de los documentos públicos y el código fuente de cada proyecto; = no ofrecido / no documentado. Las columnas son los servidores a los que un lector tiene más probabilidades de llegar: el de primera parte de Google, más los dos servidores comunitarios más grandes — y klodr, que es el que más se acerca al diseño de privilegio mínimo de mailwarden. La capacidad de envío se enumera como una propiedad de seguridad: la falta de ella en mailwarden es intencional (ver Seguridad y privacidad). La última fila pregunta quién opera el servidor, no dónde se ejecuta: el autoalojamiento es un terreno común aquí, y cada servidor comunitario en esta tabla ofrece algún despliegue remoto excepto klodr (solo stdio) — mailwarden mediante --http, taylorwilsdon sobre HTTP transmisible con OAuth 2.1, a-bonus en Cloud Run. Ejecutar uno de ellos en tu propio host no es una copia en la nube; ejecutarlo en el del proveedor lo es.

El foso no es ninguna fila individual — es posponer + re-verificación en vivo juntos: una capa real de flujo de trabajo de bandeja de entrada que actúa sobre el estado actual del buzón, no una instantánea en caché. Donde otros se han puesto al día, se señala honestamente arriba: cifrado en reposo (taylorwilsdon), control de herramientas basado en ámbito (klodr), una heurística de clasificación por mensaje más rica (a-bonus), y organización masiva sobre un buzón (el alojado mcpemails.com, que tampoco tiene posponer). Lo que ninguno de ellos hace es actuar sobre una consulta y verificar la respuesta del buzón antes de actuar sobre ella.

Por qué la re-verificación es importante — un caso concreto

Pídele a un asistente que "archive el correo promocional no leído que ya ha saltado mi bandeja de entrada" y recurrirá a la consulta obvia, category:updates is:unread -in:inbox. Un servidor que confía en el índice de Gmail ahora archiva hilos que ya habías leído — correo que nunca quisiste tocar, desaparecido en una acción masiva que no puedes revertir fácilmente.

Medido, no afirmado. Un buzón real (~70,000 mensajes), 15.08.2026, solo lectura:

Consulta (threads.list)

Hilos devueltos

Con un mensaje no leído

Desactualizados

category:updates is:unread

131

17

87%

category:updates is:unread -in:inbox

128

14

89%

is:unread -in:inbox

235

99

58%

El índice no está ignorando el predicado — la misma consulta sin is:unread devuelve más de 800 hilos, por lo que se está aplicando. Se aplica contra un estado de lectura a nivel de hilo que no se ha actualizado: los hilos cuyos mensajes están todos leídos aún cuentan como no leídos allí. Un hilo devuelto llevaba una sola etiqueta, SENT. Y no es una rareza de combinaciones exóticas de operadores: la consulta más simple de las tres también lo muestra — con la proporción más baja (58%) pero la mayor cantidad de hilos incorrectos en términos absolutos (136).

Es específicamente el índice de hilos. La misma consulta, mismo buzón, mismo minuto, solicitada a través de messages.list en su lugar: 19 mensajes, ninguno desactualizado. Así que esto no es "la búsqueda de Gmail no es confiable" — es que la vista de hilo del estado de lectura se retrasa mientras que la vista por mensaje no. search pasa por threads.list, que es exactamente por lo que vuelve a verificar.

Un segundo buzón, medido de la misma manera el mismo día, no se desvió en absoluto — cero aciertos del índice bruto para is:unread, aunque se marca como leído a través de la API muchas veces al día. Así que esto es una propiedad de un buzón, no de Gmail en todas partes. Lo que los separa está abierto: difieren en volumen (aproximadamente tres órdenes de magnitud) y antigüedad, y al segundo le falta algo más básico — ningún hilo en él fue archivado mientras aún estaba no leído, que es la única forma en que puede aparecer un estado de lectura desactualizado. Así que no es un contraejemplo de ninguna causa particular; es un buzón sin el candidato.

Que es el punto central: un servidor no puede saber en qué tipo de buzón se encuentra. La re-verificación no cuesta nada donde nada se desvía, y te salva donde lo hace — en la medición anterior, cada hilo que search descartó estaba genuinamente leído, y descartó ningún correo genuinamente no leído.

Donde no es gratuito: las herramientas masivas. search vuelve a verificar porque obtiene cada acierto de todos modos; bulk_modify (y el barrido applyToExisting de create_filter) está dimensionado en miles de mensajes, donde una obtención por acierto es un orden de costo diferente. Esas actúan sobre lo que devuelve el índice — por lo que ahora reportan unverifiedPredicates, las condiciones de tu consulta que se tomaron según la palabra del índice (+UNREAD, -INBOX, …). Vacío significa que no había nada que desconfiar. ¿No vacío y el resultado debe ser preciso en cuanto al estado de lectura? Resuelve el conjunto con search primero y actúa sobre esos IDs de hilo. Un dryRun no cierra esta brecha: vuelve a leer el mismo índice, por lo que confirma cuán grande es el conjunto, nunca si es correcto.

mailwarden obtiene cada acierto en vivo de todos modos, por lo que search vuelve a verificar los predicados inequívocos (is:unread, is:read, in:inbox, category:…, con negación) contra las etiquetas reales de cada hilo y descarta los falsos positivos del índice antes de que cualquier herramienta los vea. La acción masiva entonces se ejecuta exactamente sobre el conjunto que solicitaste. Esta es la diferencia entre actuar sobre lo que Gmail indexó y actuar sobre lo que realmente está en el buzón ahora mismo — y es por lo que posponer/barrer son seguros para entregar a un asistente: el barrido solo reflota los hilos cuyo posposición está genuinamente vencida, verificados contra etiquetas en vivo en tiempo de ejecución.

Compruébalo tú mismo — no se necesita cuenta de Gmail. Desde un clon del repositorio (la demo es un script de verificación solo del repositorio, no parte del paquete npm):

git clone https://github.com/csitte/mailwarden && cd mailwarden
npm install && npm run build
node scripts/demo-reverify.mjs

Hay un segundo script junto a él, node scripts/probe-reverify.mjs, que mide lo mismo en tu buzón en lugar de uno falso — solo lectura, solo metadatos (no se obtiene asunto, remitente ni cuerpo), imprimiendo conteos y nombres de etiquetas. Es cómo se produjeron los números anteriores, y cómo puedes verificar si tu buzón se desvía en absoluto.

La demo ejecuta el search() real contra una API de Gmail falsa cuyo índice está deliberadamente desactualizado (devuelve un hilo leído para una consulta is:unread, exactamente como lo hace Gmail) y muestra a mailwarden descartando el falso positivo. Afirma el resultado, por lo que sale con código distinto de cero si el comportamiento alguna vez retrocede. El mismo caso está asegurado por pruebas unitarias en test/gmail.test.ts ("descarta falsos positivos del índice mediante re-verificación de etiquetas en vivo").

Herramientas

Herramienta

Qué hace

search

Sintaxis de consulta de Gmail → resúmenes de hilos (de/asunto/fecha/etiquetas/fragmento); los predicados de estado de lectura/categoría se vuelven a verificar con las etiquetas en vivo de cada resultado; paginado mediante pageToken/nextPageToken. Cada resultado lleva signalsnewsletter (List-Id / List-Unsubscribe / Precedence bulk o list), automated (Auto-Submitted, encabezados de auto-respuesta/supresión, remitentes estilo no-reply), calendar (parte text/calendar o .ics), replyToMismatch (Reply-To en otro dominio que From; un subdominio del mismo dominio cuenta como igual) — leído de los encabezados/MIME del primer mensaje, sin llamada adicional

get_thread

Hilo completo: encabezados, cuerpos en texto plano + HTML, metadatos de adjuntos

list_labels

Todas las etiquetas (del sistema + del usuario)

get_profile

Dirección de la cuenta conectada + total de mensajes/hilos — confirma qué buzón está conectado antes de actuar

triage_digest

Resumen estructurado de una porción del buzón para decisiones: principales remitentes (cada uno con las señales que llevan sus hilos), grupos por etiqueta y antigüedad, recuentos de no leídos + adjuntos, y cuántos hilos son boletines / automatizados / invitaciones de calendario / desajustes de respuesta — en lugar de una lista de hilos en bruto

list_unsubscribe

Qué opciones de exclusión anuncia un hilo (List-Unsubscribe) — no contacta a nadie

list_subscriptions

Una porción del buzón agrupada por remitente: recuentos de hilos/no leídos, el intervalo de fechas en que se vio cada uno, y las opciones de exclusión de cada uno — una obtención de encabezado por remitente, no contacta a nadie. sendersFound informa cuántos remitentes había antes de que topN truncara la lista

create_label

Crear una etiqueta de usuario (idempotente; anidada mediante Parent/Child) y devolver su id

modify_labels

Añadir/eliminar etiquetas por nombre o id — un nombre desconocido en add se crea automáticamente (archivar = eliminar INBOX, leer = eliminar UNREAD)

bulk_modify

Cambios de etiquetas por lotes para cada mensaje que coincida con una consulta — 1000 mensajes por solicitud API, éxito parcial informado por fragmento (lista de thread-id limitada a 500, modifiedThreadCount tiene el total). Actúa sobre el índice sin procesar, por lo que unverifiedPredicates nombra las condiciones que no pudo verificar (ver más abajo). dryRun: true resuelve la consulta e informa los hilos coincidentes y las etiquetas que crearía, sin tocar nada

archive / mark_read / mark_unread

Envoltorios de conveniencia

trash / untrash

Mover a / restaurar desde Papelera

download_attachment

Guardar un adjunto en una ruta local (nunca sobrescribe — las colisiones obtienen un sufijo numérico)

unsubscribe

Exclusión con un clic (RFC 8058) usando el endpoint del propio encabezado del mensaje — la única herramienta que contacta a un host que no es de Google (detalles)

bulk_unsubscribe

Lo mismo para varios hilos, secuencialmente y como máximo una solicitud por remitente; éxito parcial informado por hilo. dryRun: true ejecuta las mismas lecturas de encabezados y deduplicación e informa el endpoint que cada hilo wouldCall — sin contactar a nadie

snooze

Archivar ahora, reaparecer en/después de una fecha (YYYY-MM-DD), una fecha+hora (2026-06-20 9am), o un valor predefinido (tomorrow, tomorrow 9am, weekend, next week, un nombre de día de la semana, in N days, in N hours)

unsnooze

Cancelar un aplazamiento, volver a la bandeja de entrada ahora

list_snoozed

Todos los hilos aplazados + fechas de vencimiento

sweep_snoozed

Reaparecer hilos cuyo aplazamiento ha vencido (ejecutar bajo demanda, mediante cron o el daemon); por lotes, con informe de fallos parciales. dryRun: true responde "¿qué está vencido ahora mismo?" (dueLabels/dueThreads) sin despertar nada

list_filters

Todos los filtros de Gmail (criterios + acciones de etiquetas); muestra cualquier dirección forward en filtros existentes para auditoría

create_filter

Crear una regla de clasificación automática del lado del servidor (solo criterios → acciones de etiquetas; sin reenvío — ver más abajo). Opcionalmente applyToExisting para también barrer el correo coincidente ya en el buzón

delete_filter

Eliminar un filtro por id

Todas las herramientas declaran un outputSchema y devuelven contenido estructurado (validado, legible por máquina) junto con el mismo JSON como texto delimitado; los clientes nunca tienen que analizar prosa.

Cómo funciona snooze (no existe una función snooze en la API de Gmail — la construimos)

snooze elimina INBOX y aplica una etiqueta fechada MCP/Snoozed/<key>, donde la clave es YYYY-MM-DD (pendiente todo el día) o YYYY-MM-DDTHHMM (pendiente en ese minuto local). El argumento until acepta una fecha explícita, una fecha+hora (2026-06-20 9am, …T17:00), o un valor predefinido resuelto en el servidor — today, tomorrow, weekend (próximo sábado), next week (próximo lunes), un nombre de día de la semana (mondaysunday, siguiente ocurrencia), in N days, o in N hours — y un valor predefinido de fecha puede llevar una hora al final (tomorrow 9am, monday 8:30), por lo que el llamante nunca tiene que calcular el momento por sí mismo. sweep_snoozed encuentra las etiquetas pendientes y devuelve esos hilos a la bandeja de entrada (marcados como no leídos); un snooze temporizado se activa en el primer barrido en o después de su minuto, por lo que la latencia de activación equivale a tu intervalo de barrido. Ejecuta el barrido:

  • bajo demanda (herramienta sweep_snoozed),

  • mediante cron: mailwarden --sweep,

  • o automáticamente: configura MAILWARDEN_AUTO_SWEEP=1 (barrido cada hora mientras el servidor se ejecuta).

Filtros (reglas persistentes de autotriage)

create_filter establece una regla del lado del servidor de Gmail: el correo que coincida con los criterios recibe automáticamente las acciones de etiqueta dadas — la bandeja de entrada sigue triándose sola sin la intervención del asistente.

  • Criterios: from, to, subject, query (sintaxis completa de búsqueda de Gmail), negatedQuery, hasAttachment, excludeChats, y size + sizeComparison (smaller/larger, proporcionados juntos). Se requiere al menos uno.

  • Acciones (solo etiqueta): addLabels / removeLabels, por nombre o id (un nombre desconocido en addLabels se crea automáticamente, anidado mediante /). Recetas comunes: omitir la bandeja de entrada → removeLabels: ["INBOX"]; marcar como leído automáticamente → removeLabels: ["UNREAD"]; enviar a la papelera automáticamente → addLabels: ["TRASH"]; destacar → addLabels: ["STARRED"]; nunca spam → removeLabels: ["SPAM"]; archivar bajo una etiqueta → addLabels: ["Receipts"].

  • Correo existente: un filtro solo se aplica a los mensajes que llegan después de crearlo. Pasa applyToExisting: true para aplicar también las mismas acciones una vez al correo ya presente en la bandeja de entrada — mailwarden construye una búsqueda de Gmail a partir de los criterios y realiza una modificación masiva (hasta maxMessages, por defecto 1000; misma advertencia de índice no verificado que bulk_modify, y el pase único excluye Spam/Papelera). Esto requiere al menos un criterio positivo (from/to/subject/query/hasAttachment:true/size): una regla de solo exclusión (negatedQuery o hasAttachment:false) se rechaza para applyToExisting porque coincidiría con casi toda la bandeja de entrada — crea dicho filtro sin la bandeja. El resultado se devuelve bajo applied (la query utilizada, recuentos de matchedMessages/modifiedMessages/modifiedThreadCount, capped cuando el conjunto de coincidencias alcanzó maxMessages, failed por fragmento, y una cadena error si todo el pase falló); es null cuando applyToExisting no se estableció. El filtro se crea primero, por lo que un pase parcial o fallido del historial se reporta en applied, nunca se lanza — la regla sigue vigente.

  • Sin reenvío — ver Seguridad y privacidad.

  • Requiere el ámbito gmail.settings.basic; ejecuta --auth una vez si autorizaste una versión anterior. No disponible en modo de solo lectura.

Cancelar suscripción — la única solicitud saliente

list_unsubscribe (nivel de lectura) informa lo que ofrece el remitente, sin contactar a nadie. Lee el mensaje más reciente que realmente lleva un encabezado List-Unsubscribe — una respuesta enhebrada a un boletín se encuentra al final y no anuncia nada, lo que de otro modo se leería como "esta lista no tiene opción de exclusión". list_subscriptions (nivel de lectura) hace lo mismo en un segmento completo, agrupado por remitente, para que puedas ver quién sigue escribiendo y cuáles de ellos pueden ser realmente abandonados — una obtención de encabezado por remitente en lugar de por hilo. unsubscribe y bulk_unsubscribe (nivel de gestión) actúan sobre ello — y ese es el único lugar donde mailwarden se comunica con un host que no sea Google, por lo que las reglas son estrictas:

  • No hay parámetro de URL. El punto final proviene del propio encabezado del mensaje y de ningún otro lugar. Un argumento de URL permitiría que un correo con inyección de prompt convirtiera la herramienta en un canal de exfiltración (contenido de la bandeja de entrada en una cadena de consulta); el encabezado no puede contener datos que el modelo haya elegido.

  • Solo se realiza un clic según RFC 8058 — el remitente debe haber optado por ello mediante List-Unsubscribe-Post. Un enlace https: simple está destinado a un humano en un navegador y se devuelve, no se obtiene.

  • Las opciones de exclusión mailto: nunca se realizan. Requerirían enviar correo, lo cual mailwarden no puede hacer. La dirección se informa para que puedas actuar tú mismo.

  • Solicitud fija, respuesta descartada. El cuerpo POST siempre es List-Unsubscribe=One-Click y nunca se deriva de nada; el cuerpo de la respuesta se cancela sin leer. Lo que regresa al modelo es el código de estado y la URL realmente llamada — sin contenido del punto final, por lo que no puede responder con instrucciones. (Una redirección 301/302/303 se sigue como GET, es decir, sin cuerpo alguno).

  • Una solicitud por remitente, secuencialmente, dentro de un presupuesto. bulk_unsubscribe toma identificadores de hilo (nunca una consulta — una exclusión masiva basada en consultas dispararía una solicitud por remitente coincidente antes de que nadie hubiera mirado). Los hilos de un remitente cuya solicitud ya se envió se reportan con duplicateOf y no cuestan una segunda solicitud: dos hilos de una lista comparten una exclusión, y llamarla dos veces solo confirma tu dirección dos veces. Un remitente solo se registra una vez que una solicitud realmente alcanzó un punto final, por lo que un rechazo o una conexión caída aún deja al siguiente hilo su propio intento — y si el hilo omitido anuncia un punto final diferente, la razón lo dice, ya que un remitente puede gestionar varias listas. Limitado a 25 hilos y 60 segundos por llamada; lo que el presupuesto no cubra regresa como skippedOutOfTime en lugar de deshacerse silenciosamente. Ninguno de ellos se puede revertir, por lo que existen los tres límites.

  • Protecciones SSRF. Solo https, solo puerto predeterminado, sin credenciales en la URL, y cada salto — incluyendo redirecciones, seguidas como máximo 3 veces — debe resolverse exclusivamente a direcciones globalmente accesibles. La verificación analiza cada dirección a sus bytes y la compara con el registro de propósito especial de la IANA, por lo que cada ortografía de la misma dirección obtiene el mismo veredicto (::1 y 0:0:0:0:0:0:0:1 por igual); una dirección que no se analiza se rechaza. La resolución DNS y todos los saltos comparten un presupuesto de 10 segundos. No es a prueba de rebinding (fetch resuelve de nuevo cuando se conecta) — ver SECURITY.md; lo que sobrevive a esa brecha es un POST ciego cuya respuesta nunca se lee.

Compruébalo con tu propio correo antes de confiar en él. Desde un clon del repositorio (solo repositorio, no en el paquete npm), después de npm run build y mailwarden --auth:

node scripts/probe-unsubscribe.mjs --vet          # category:promotions, 25 threads
node scripts/probe-unsubscribe.mjs "from:substack.com" --max 50 --vet

Imprime cada encabezado List-Unsubscribe real junto a lo que el analizador hizo de él, y --vet también ejecuta el punto final a través de la verificación de URL y la protección de direcciones — así ves tanto si el analizador entendió el encabezado como si las protecciones habrían dejado pasar esa exclusión. Estrictamente de solo lectura: nunca se realiza ninguna solicitud a un remitente, y nada en la bandeja de entrada cambia.

Lo que no se puede deshacer: la solicitud le dice al remitente que tu dirección está activa. Un remitente que ignora su propia exclusión está fuera del alcance de cualquier cliente — combina unsubscribe con create_filter o trash para esos casos. No ofrecer una opción automatizable se reporta como unsubscribed:false con las alternativas, no como un error. Una implementación de solo read obtiene list_unsubscribe y list_subscriptions, y nunca realiza la solicitud.

Seguridad y privacidad

Para el modelo de amenazas completo — límite de confianza, mitigaciones por amenaza, no objetivos explícitos, y cómo reportar una vulnerabilidad — consulta SECURITY.md. Los aspectos destacados:

  • Sin telemetría. No se comunica con el exterior — sin análisis, sin informes de fallos, sin seguimiento.

  • Sin puertos abiertos por defecto. Solo stdio. El listener opcional --http se vincula a 127.0.0.1 (no a la LAN) y se niega a iniciar sin un token portador MAILWARDEN_TOKEN — establezca MAILWARDEN_ALLOW_NO_TOKEN=1 para anularlo en una red aislada y de confianza. En un enlace de loopback también valida el encabezado Host (defensa contra DNS-rebinding). Para alojamiento remoto, configure MAILWARDEN_HOST y protéjalo con TLS.

  • Sin herramientas de envío — por diseño. mailwarden no puede redactar, responder ni reenviar. Una instrucción inyectada por prompt dentro de un correo electrónico no tiene ruta de exfiltración a través de este servidor. create_filter sigue la misma regla: puede etiquetar, archivar, enviar a la papelera, destacar o marcar correos, pero nunca crea un filtro de reenvío (que sería una ruta de exfiltración). list_filters sigue mostrando cualquier filtro de reenvío ya existente en la cuenta, para que pueda detectarlo. Esto se cumple porque no existe tal herramienta y ninguna puede registrarse en tiempo de ejecución; para la variante más fuerte, donde Google se niega a enviar en lugar de que mailwarden lo rechace, consulte Modo de solo lectura más abajo.

  • Un único host de salida, sin URL elegida por el modelo. La herramienta unsubscribe es la única ruta de código que contacta con un host que no sea de Google. Su endpoint se lee del encabezado List-Unsubscribe del mensaje — nunca de un argumento de herramienta — el cuerpo de la solicitud es fijo y el cuerpo de la respuesta se descarta, por lo que no puede convertirse en un canal de datos. Solo https/puerto por defecto, las redirecciones se revalidan, y cualquier salto que resuelva a una dirección privada, de loopback, link-local o de metadatos es rechazado. Consulte Cancelar suscripción.

  • Niveles de herramientas (divulgación progresiva + alcance mínimo). MAILWARDEN_TOOLS anuncia solo los niveles que usted nombre — read (las herramientas de lectura), manage (mutaciones de buzón, posponer, descargas), filters (CRUD de filtros del lado del servidor, el único nivel cuyas herramientas necesitan gmail.settings.basic). El valor predeterminado son los tres; por ejemplo, read,manage proporciona una superficie de triaje completa sin gestión de filtros. Los ámbitos de OAuth solicitados en --auth se derivan de los niveles habilitados — una implementación read solicita solo gmail.readonly, y gmail.settings.basic se solicita solo cuando el nivel filters está activo. Y las herramientas de filtro se ocultan automáticamente cuando el token almacenado no incluye gmail.settings.basic (por ejemplo, un token autorizado antes de habilitar el nivel) — vuelva a ejecutar --auth para concederlo. Los tokens antiguos sin un ámbito registrado se anuncian como antes, con el mensaje de alcance insuficiente en tiempo de ejecución como alternativa.

  • Modo de solo lectura. Establezca MAILWARDEN_READONLY=1 (abreviatura de MAILWARDEN_TOOLS=read) y solo se registran las herramientas de lectura (search, get_thread, list_labels, list_snoozed, get_profile, triage_digest, list_unsubscribe, list_subscriptions) — nada que pueda cambiar el buzón o escribir archivos se anuncia siquiera a los clientes (las herramientas de filtro, que necesitan el ámbito más amplio gmail.settings.basic, también se excluyen). Recomendado para implementaciones compartidas/HTTP que solo hacen triaje. También es el único nivel cuya propiedad de no envío Google hace cumplir: posee un token gmail.readonly, que los endpoints de envío de Gmail rechazan de plano. manage necesita gmail.modify, y Gmail acepta ese ámbito para enviar — mailwarden simplemente no expone ninguna herramienta que lo haga. Por lo tanto, una implementación read no podría enviar incluso si este binario fuera reemplazado; una implementación manage no puede enviar porque no hay nada que llamar. (No hay un ámbito de escritura sin envío al que cambiar — consulte SECURITY.md, amenaza 1.)

  • Descargas delimitadas. Con MAILWARDEN_DOWNLOAD_DIR establecido, las escrituras de archivos adjuntos se limitan a ese directorio (canonicalizado con realpath, consciente de enlaces simbólicos) y nunca sobrescriben un archivo existente.

  • Delimitación de contenido no confiable. Cada resultado de herramienta se envuelve en marcadores <untrusted-tool-output> y se eliminan los caracteres invisibles/de anulación bidireccional, para que los clientes puedan distinguir el contenido de correo citado de las instrucciones.

  • API en vivo, sin copia. No se almacena ningún espejo de buzón ni índice de búsqueda en ningún lugar. El único estado local es su token OAuth en ~/.mailwarden/.

  • Cifrado opcional del token en reposo. token.json contiene un token de actualización; en disco está protegido solo por mode 0o600 (sin efecto en Windows). Establezca MAILWARDEN_TOKEN_PASSPHRASE en una frase de contraseña y el token se almacena cifrado con AES-256-GCM (clave derivada de scrypt), por lo que una copia del archivo — una copia de seguridad, una carpeta sincronizada, otra máquina — es inútil sin la frase de contraseña. Vuelva a ejecutar mailwarden --auth una vez después de configurarlo para cifrar el token existente. Tenga en cuenta el límite: esto defiende contra el robo de archivos, no contra malware que se ejecuta como su usuario (que también puede leer la frase de contraseña del entorno).

Quick start

claude mcp add mailwarden -- npx -y mailwarden

Esa es toda la instalación — npx obtiene y ejecuta el paquete publicado, sin clonación ni paso de compilación. Solo necesita las credenciales de OAuth de Google una vez (abajo).

Setup

¿Es la primera vez que configura una aplicación OAuth de Google? Siga la guía de configuración paso a paso — recorre la consola de Google Cloud con rutas de clic exactas, explica la pantalla de "aplicación no verificada" y cubre la trampa que hace que los tokens caduquen después de 7 días. La versión corta:

  1. Google Cloud: cree un proyecto → habilite la API de Gmail → configure la pantalla de consentimiento OAuth y publíquela en Producción (en estado Prueba, Google caduca los tokens de actualización después de 7 días) → cree un ID de cliente OAuth de tipo Aplicación de escritorio → descárguelo como credentials.json.

  2. Coloque credentials.json en ~/.mailwarden/ (o establezca MAILWARDEN_CREDENTIALS=/ruta/a/credentials.json).

  3. Autorice una vez — abre un navegador, almacena un token de actualización en ~/.mailwarden/token.json:

    npx -y mailwarden --auth

    Ámbitos solicitados: gmail.modify (lectura + etiquetar/archivar/papelera) y gmail.settings.basic (solo gestión de filtros). Si autorizó una versión anterior a que existieran los filtros, vuelva a ejecutar --auth una vez para conceder el ámbito añadido. Para tener un token con el que el propio Gmail se niegue a enviar, autorice con MAILWARDEN_TOOLS=read — consulte Modo de solo lectura más arriba.

  4. Verifique la configuración en cualquier momento con el doctor incorporado:

    npx -y mailwarden --check

    Verifica credentials.json, si existe un token (y si está cifrado), si los ámbitos concedidos cubren sus niveles habilitados, y realiza una llamada en vivo a Gmail para demostrar que el token aún funciona — imprime una solución concreta para cualquier cosa que esté mal, y sale con código distinto de cero si es así (útil en verificaciones de CI/salud). Diagnostica las trampas comunes: archivo de credenciales inexistente/incorrecto, nunca autorizado, un token cifrado sin MAILWARDEN_TOKEN_PASSPHRASE, un ámbito faltante, o la caducidad del token de consentimiento de "Prueba" de 7 días.

Connect

Claude Code (stdio local):

claude mcp add mailwarden -- npx -y mailwarden

Plugin de Claude Code — el mismo servidor más una habilidad /mailwarden:setup que le guía a través de la configuración OAuth y diagnostica una configuración rota. La raíz del repositorio es el plugin (.claude-plugin/plugin.json), así que desde un clon:

claude --plugin-dir /path/to/mailwarden

Está enviado al mercado comunitario de Anthropic; una vez listado, /plugin marketplace add anthropics/claude-plugins-community luego /plugin install mailwarden@claude-community hace lo mismo sin un clon. El plugin ejecuta toda la superficie de herramientas — para un nivel más restringido (MAILWARDEN_TOOLS=read) o una segunda cuenta, use claude mcp add con el entorno que desee (consulte Config y Varias cuentas).

Claude Desktop — añada a claude_desktop_config.json:

{
  "mcpServers": {
    "mailwarden": { "command": "npx", "args": ["-y", "mailwarden"] }
  }
}

O instale el paquete MCPB (mailwarden-<version>.mcpb, adjunto a los lanzamientos de GitHub desde 0.10.0) como una extensión de escritorio — Configuración → Extensiones → Instalar extensión… — el mismo servidor, autocontenido en tiempo de ejecución (sin npx; Claude Desktop trae el runtime de Node), con los niveles de herramientas como configuración. El paquete se construye a partir del paquete npm empaquetado (mismo conjunto de archivos que el publicado; npm run mcpb, verificado en CI: validado, desempaquetado e iniciado) y es el mismo conjunto de archivos que distribuye Smithery. El npx -y mailwarden --auth único aún aplica (Node necesario una vez para eso) — el paquete lee el mismo token ~/.mailwarden/.

Smithery — listado como csitte/mailwarden, que sirve ese paquete:

npx -y @smithery/cli install csitte/mailwarden --client claude   # local stdio entry in the client's config

Tenga en cuenta cuál de los dos caminos de Smithery toma. La instalación anterior escribe una entrada de servidor local simple: el proceso, su token y su correo permanecen en su máquina, exactamente como con npx. Agregarlo a la caja de herramientas de Smithery en su lugar (smithery mcp add) también ejecuta el paquete localmente, pero retransmite el tráfico de herramientas a través de la puerta de enlace de Smithery para que un cliente remoto pueda alcanzarlo — el contenido del buzón en esas respuestas pasa entonces a través de un tercero. Eso es una propiedad de la puerta de enlace, no de mailwarden; si desea la garantía de sin terceros, use la instalación local, el paquete npm o el .mcpb de la página de lanzamiento.

Remoto (HTTP transmisible) — para un VPS / conector personalizado de claude.ai:

# Loopback + token required by default. For real hosting, bind outward and keep the token:
MAILWARDEN_TOKEN=<secret> MAILWARDEN_HOST=0.0.0.0 npx -y mailwarden --http   # :8787/mcp

Luego en claude.ai: Configuración → Conectores → Añadir conector personalizado → su URL https://your-host/mcp. En Claude Code: claude mcp add --transport http mailwarden https://your-host/mcp.

Multiple accounts

Una aplicación OAuth (un credentials.json) puede autorizar varias cuentas de Gmail. Cada cuenta mantiene su propio token de actualización en un archivo separado, seleccionado por MAILWARDEN_ACCOUNT:

mailwarden --auth --account work        # stores token.work.json
mailwarden --auth --account personal    # stores token.personal.json

Ejecútelos uno al lado del otro registrando el servidor una vez por cuenta, cada una con su propio MAILWARDEN_ACCOUNT. Cada instancia está completamente aislada — su propio token, sus propios ámbitos concedidos, su propia superficie de herramientas — por lo que nada puede actuar en el buzón equivocado:

{
  "mcpServers": {
    "gmail-work":     { "command": "npx", "args": ["-y", "mailwarden"], "env": { "MAILWARDEN_ACCOUNT": "work" } },
    "gmail-personal": { "command": "npx", "args": ["-y", "mailwarden"], "env": { "MAILWARDEN_ACCOUNT": "personal" } }
  }
}

Los nombres de cuenta no distinguen entre mayúsculas y minúsculas — se convierten en nombres de archivo, por lo que Work y work serían el mismo archivo en Windows/macOS. mailwarden los convierte a minúsculas (--account Worktoken.work.json) para que un nombre siempre se asigne exactamente a un buzón.

El archivo que escribe --auth depende solo de --account / MAILWARDEN_ACCOUNT — nunca de la cuenta que elija en el navegador. Autorizar un segundo buzón sin --account apuntaría directamente al archivo de token del primero, por lo que --auth verifica primero y se niega en lugar de reemplazar el token de otro buzón; --force lo anula deliberadamente. Los dos controles no son intercambiables: MAILWARDEN_ACCOUNT es para varias cuentas desde un directorio de configuración (elige token.<nombre>.json), mientras que MAILWARDEN_DIR mueve el directorio completo — útil para mantener configuraciones completamente separadas, pero no le da una segunda cuenta dentro de una. npm run auth desde un clon del repositorio no pasa ninguno, es decir, siempre sirve la cuenta predeterminada.

mailwarden --check muestra la cuenta activa y enumera las otras que encuentra. Sin MAILWARDEN_ACCOUNT establecido, todo usa el token.json predeterminado exactamente como antes — esto es totalmente compatible hacia atrás.

From source

git clone https://github.com/csitte/mailwarden && cd mailwarden
npm install && npm run build
node dist/index.js --auth

Config (env)

Var

Significado

MAILWARDEN_DIR

directorio de configuración (por defecto ~/.mailwarden)

MAILWARDEN_CREDENTIALS

ruta a credentials.json

MAILWARDEN_ACCOUNT

seleccionar una cuenta con nombre (su token es token.<nombre>.json; los nombres están en minúsculas); sin definir = el token.json por defecto. Ver Múltiples cuentas

MAILWARDEN_TOKEN_PASSPHRASE

frase de contraseña → cifrar token.json en reposo (AES-256-GCM); volver a ejecutar --auth después de configurar

MAILWARDEN_AUTO_SWEEP

1 → barrido de pospuestos al inicio + cada hora mientras se ejecuta (escribe etiquetas — necesita el ámbito manage/gmail.modify; una concesión de solo lectura no puede barrer)

MAILWARDEN_DOWNLOAD_DIR

restringir download_attachment a este directorio (muy recomendado para alojamiento HTTP)

MAILWARDEN_READONLY

1 → registrar solo las herramientas de lectura (search/get_thread/list_labels/list_snoozed/get_profile/triage_digest/list_unsubscribe/list_subscriptions). Abreviatura de MAILWARDEN_TOOLS=read

MAILWARDEN_TOOLS

niveles de herramientas separados por comas para anunciar: read, manage, filters (por defecto: todos). También deriva los ámbitos OAuth solicitados en --auth. Por ejemplo, read,manage elimina las herramientas de filtro y su ámbito gmail.settings.basic

MAILWARDEN_DEBUG

1 → imprimir errores completos con trazas de pila en lugar de un mensaje de una línea (para informes de errores)

PORT

puerto HTTP (por defecto 8787)

MAILWARDEN_HOST

dirección de enlace HTTP (por defecto 127.0.0.1; establecer por ejemplo 0.0.0.0 para alojamiento remoto)

MAILWARDEN_TOKEN

token de portador para el punto final HTTP — requerido para --http a menos que se anule

MAILWARDEN_ALLOW_NO_TOKEN

1 → permitir --http sin token (solo redes de confianza/aisladas)

MAILWARDEN_ALLOWED_HOSTS

valores adicionales host:puerto separados por comas aceptados por la lista blanca de Host de bucle invertido

Estado

En funcionamiento y utilizado en la automatización diaria del buzón. Herramientas principales de Gmail + pospuestos implementados contra googleapis, cubiertos por un conjunto de pruebas vitest (789 pruebas — npm run coverage). Versión actual: ver la insignia npm arriba, el registro de cambios o versiones. Se aceptan PRs.

Licencia

MIT © C.Sitte Softwaretechnik

Install Server
A
license - permissive license
A
quality
A
maintenance

Maintenance

Maintainers
Response time
2dRelease cycle
24Releases (12mo)
Commit activity

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    An MCP server that enables Gmail integration, allowing users to manage emails (send, receive, read, trash, mark as read) directly through MCP clients like Claude Desktop.
    1
    MIT
  • A
    license
    B
    quality
    D
    maintenance
    Manage your emails effortlessly with a standardized interface for drafting, sending, retrieving, and organizing messages. Streamline your email workflow with complete Gmail API coverage, including label and thread management.
    64
    1,398
    56
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Gmail MCP server — scope-gated tools (readonly / send / modify), path jails for attachments + downloads, hardened OAuth credentials, Sigstore-signed releases.
    207
    11
    MIT
  • A
    license
    A
    quality
    F
    maintenance
    A Gmail MCP server with native multi-account support, enabling management of multiple Gmail accounts from a single server instance.
    7
    5
    MIT

View all related MCP servers

Related MCP Connectors

  • Read, search, send, organize, draft and schedule email across your inboxes from any MCP client.

  • Shipmail MCP server for AI agent custom-domain email inboxes with REST API and webhooks.

  • Fully-managed email as MCP tools - register domains, real mailboxes, send and receive mail.

View all MCP Connectors

Latest Blog Posts

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/csitte/mailwarden'

If you have feedback or need assistance with the MCP directory API, please join our Discord server