Email Agent MCP

Conectividad de correo electrónico local para agentes de IA: leer, redactar, enviar y organizar correos de Outlook a través de MCP. Licencia Apache-2.0.

Documentación

Agent Email

npm version npm downloads License: Apache-2.0 CI codecov GitHub stargazers Tests: Vitest OpenSpec Traceability Socket Badge install size

English | Español | 简体中文 | Português (Brasil) | Deutsch

email-agent-mcp por UseJunior -- conectividad de correo local para agentes de IA.

Agent Email es un servidor MCP de TypeScript de código abierto que permite a Claude Code, Cursor, Gemini CLI, OpenClaw y otros entornos compatibles con MCP leer correos, buscar hilos, redactar respuestas, etiquetar mensajes, cambiar el estado de lectura, mover mensajes y enviar correos a través de tu propia bandeja de entrada. Microsoft 365 / Outlook y Gmail son compatibles hoy en día. Los valores predeterminados de seguridad primero significan que los agentes no pueden enviar correos hasta que configures explícitamente una lista de permitidos.

Inicio rápido

npx -y email-agent-mcp

El asistente de configuración interactivo te guía a través de la configuración de OAuth y la selección de la bandeja de entrada.

Qué funciona hoy

  • Acceso a la bandeja de entrada de Microsoft 365 / Outlook a través de MCP stdio
  • list_emails, read_email, search_emails y get_thread
  • create_draft, update_draft, send_draft, send_email y reply_to_email
  • label_email, mark_read y move_to_folder
  • listas de permitidos de envío, eliminación deshabilitada por defecto y errores saneados

La pasada actual de preparación para el lanzamiento se validó contra una bandeja de entrada real de Outlook para los flujos de lectura, borrador, envío, categorización, movimiento y estado de lectura.

Por qué existe esto

Los agentes de IA necesitan leer, responder y actuar sobre el correo electrónico, pero las API de correo son complejas. Flujos de OAuth, consultas delta de Graph, suscripciones push de Gmail, conversión de HTML a markdown, semántica de hilos: cada proveedor tiene sus propias peculiaridades.

Agent Email envuelve esta complejidad en herramientas MCP deterministas con barreras de seguridad:

  • listas de permitidos de envío y recepción que controlan a quién pueden contactar los agentes
  • eliminación deshabilitada por defecto (requiere aceptación explícita)
  • saneamiento de errores que elimina claves de API, rutas de archivos y trazas de pila
  • sandboxing de archivos de cuerpo con protección contra traversal de rutas

Uso con Claude Code

Añade a ~/.claude/settings.json o a tu proyecto .claude/settings.json:

{
  "mcpServers": {
    "email-agent-mcp": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "email-agent-mcp"]
    }
  }
}

Uso con Cursor

// .cursor/mcp.json
{
  "mcpServers": {
    "email-agent-mcp": {
      "command": "npx",
      "args": ["-y", "email-agent-mcp"]
    }
  }
}

Uso con Gemini CLI

gemini extensions install https://github.com/UseJunior/email-agent-mcp

Uso con OpenClaw

Añade un bloque mcp a ~/.openclaw/openclaw.json:

{
  // ... existing config ...
  mcp: {
    servers: {
      email: {
        command: "npx",
        args: ["tsx", "/path/to/email-agent-mcp/packages/email-mcp/src/serve-entry.ts"],
        transport: "stdio"
      }
    }
  }
}

Nota de versión: La clave de configuración mcp requiere OpenClaw app >= 2026.3.24. Si el CLI es más antiguo que la app, puede rechazar esta clave durante la validación aunque la puerta de enlace la acepte. Actualiza el CLI con npm install openclaw@latest en tu directorio NemoClaw, o reinicia la puerta de enlace directamente con launchctl kickstart -k gui/501/ai.openclaw.gateway.

Vigilante de correo

El vigilante consulta tu bandeja de entrada y envía señales de activación a OpenClaw cuando llega correo nuevo:

# Set the hooks token (must match hooks.token in openclaw.json)
export OPENCLAW_HOOKS_TOKEN="your-hooks-token"

# Start the watcher (defaults to http://localhost:18789/hooks/wake)
npm run dev:watch

El vigilante requiere al menos una bandeja de entrada configurada. Ejecuta npx email-agent-mcp o npm run dev:configure primero para completar el flujo de OAuth.

Prueba de humo de preparación para el lanzamiento

Antes de grabar una demostración, ejecuta el script de humo en vivo contra una bandeja de entrada real y una lista de permitidos de envío segura. El script ejercita:

  • get_mailbox_status
  • list_emails + read_email
  • mark_read no leído -> leído -> no leído
  • label_email en un candidato seguro de la bandeja de entrada
  • create_draft
  • reply_to_email solo borrador
  • send_email opcional

Ejemplo:

EMAIL_AGENT_MCP_HOME=/tmp/email-agent-mcp-live \
AGENT_EMAIL_SEND_ALLOWLIST=/tmp/email-agent-mcp-live/send-allowlist.json \
npm run launch:prep:smoke -- --live-write --send-to beta@usejunior.com

La selección predeterminada de candidatos seguros busca notifications@github.com en la bandeja de entrada para que puedas ensayar el flujo de grabación en un mensaje seguro para el público en lugar de correo de clientes. Si el nombre de estado de tu bandeja de entrada no es una dirección de correo electrónico, pasa --reply-sender <email> o establece EMAIL_AGENT_MCP_REPLY_SENDER para que el script pueda encontrar un mensaje autoenviado para la verificación de respuesta de borrador.

Referencia de herramientas

Perfiles de alcance

Establece EMAIL_AGENT_MCP_SCOPE_PROFILE antes de configurar e iniciar el servidor para elegir los permisos expuestos a un agente. El valor predeterminado es full para compatibilidad hacia atrás.

PerfilAlcances delegados de MicrosoftHerramientas expuestas
observeMail.Read, User.Read, offline_accessHerramientas de correo de solo lectura, menos list_inbox_rules (ver abajo)
full (predeterminado)Mail.Read, Mail.ReadWrite, Mail.Send, MailboxSettings.ReadWrite, User.Read, offline_accessTodas las herramientas

Para una implementación de solo observación, establece el perfil tanto para la configuración como para el tiempo de ejecución para que el consentimiento de OAuth y la lista de herramientas MCP coincidan:

export EMAIL_AGENT_MCP_SCOPE_PROFILE=observe
npx email-agent-mcp configure
npx email-agent-mcp serve

Los alcances de observe son un subconjunto estricto deliberado de los de full. Eso es lo que hace adoptable el perfil: un inquilino que ya ha consentido el conjunto completo otorga observe silenciosamente, por lo que cambiar full → observe no necesita nuevo consentimiento. Cambiar observe → full sí requiere un nuevo consentimiento interactivo, porque pide alcances que nunca fueron otorgados.

Por la misma razón, observe no expone list_inbox_rules. Graph limita /mailFolders/inbox/messageRules detrás de MailboxSettings, y Entra trata MailboxSettings.Read como un alcance distinto del MailboxSettings.ReadWrite de full en lugar de implicado por él. Solicitarlo rompería la propiedad de subconjunto y forzaría un nuevo consentimiento en cada implementación de observe — una solicitud de aprobación de administrador en inquilinos que restringen el consentimiento del usuario. Usa full si necesitas visibilidad de reglas de bandeja de entrada.

Un valor de perfil inválido detiene el inicio en lugar de otorgar silenciosamente un acceso más amplio. Si las credenciales en caché no cubren los alcances del perfil, el servidor falla rápidamente con un error procesable en lugar de bloquearse en un inicio de sesión interactivo.

Qué garantiza y qué no garantiza observe

observe siempre elimina las herramientas de escritura de la superficie de herramientas MCP, por lo que un agente no puede invocarlas. Esa parte se cumple en todas partes.

Solo reduce el token de OAuth en una bandeja de entrada que no haya consentido ya los alcances de escritura. Entra emite un token de acceso que lleva cada alcance que el usuario o inquilino ya ha consentido para esa aplicación — no solo el subconjunto solicitado en el momento de la adquisición del token. Entonces, si apuntas observe a una bandeja de entrada configurada previamente como full, el token subyacente aún lleva Mail.ReadWrite y Mail.Send; solo se reduce la superficie de herramientas.

Para un token genuino de privilegio mínimo, consiente observe desde una bandeja de entrada que nunca haya recibido los alcances de escritura — un configure fresco contra un registro de aplicación cuyos permisos delegados se detienen en Mail.Read/User.Read. Trata la reducción de la superficie de herramientas como defensa en profundidad, no como un límite de OAuth, a menos que controles la concesión.

Herramientas

El perfil full expone 26 herramientas MCP; observe omite toda herramienta cuya acción esté marcada como mutación de bandeja de entrada:

HerramientaDescripciónTipo
list_emailsLista correos recientes con filtradolectura
read_emailLee el contenido completo del correo como markdown, o HTML sin procesar con format: "html"lectura
search_emailsBúsqueda de texto completo en las bandejas de entradalectura
list_mailboxesEnumera las bandejas de entrada configuradas, su estado y la predeterminadalectura
get_mailbox_statusEstado de conexión y advertenciaslectura
get_threadContexto completo de la conversaciónlectura
list_attachmentsLista metadatos de adjuntos de un correolectura
download_attachmentDescarga un adjunto de archivo como base64lectura
send_emailEnvía correo nuevo (restringido por lista de permitidos)escritura
reply_to_emailResponde dentro del hilo (restringido por lista de permitidos al enviar)escritura
create_draftCrea un borrador de correoescritura
update_draftActualiza el contenido del borradorescritura
send_draftEnvía un borrador guardadoescritura
list_scheduled_sendsLista envíos programados pendientes retenidos por el proveedor (Microsoft 365)lectura
cancel_scheduled_sendCancela un envío programado pendiente (Microsoft 365)destructiva
label_emailAplica etiquetas/categoríasescritura
flag_emailMarca/desmarca correos con banderaescritura
mark_readMarca como leído/no leídoescritura
move_to_folderMueve entre carpetasescritura
delete_emailElimina (requiere entorno de operador + indicador de llamante)destructiva
list_foldersLista carpetas recursivamente y rutas calculadas (Microsoft 365)lectura
create_folderCrea una carpeta hija personalizada (Microsoft 365)escritura
delete_folderElimina una carpeta personalizada (requiere entorno de operador + indicador de llamante); las carpetas del sistema están protegidas (Microsoft 365)destructiva
list_inbox_rulesLista reglas de bandeja de entrada del lado del servidor (Microsoft 365)lectura
create_inbox_ruleCrea una regla de bandeja de entrada segura persistente; el reenvío, la redirección, la eliminación y el descarte a Elementos eliminados están bloqueados (Microsoft 365)escritura
delete_inbox_ruleElimina una regla de bandeja de entrada del lado del servidor (requiere entorno de operador + indicador de llamante) (Microsoft 365)destructiva

Esta es una superficie de herramientas compartida entre proveedores. Aunque label_email, flag_email, mark_read, move_to_folder y delete_email se anuncian, el adaptador de Gmail intencionalmente no implementa esas capacidades de mutación. Fracasan de forma cerrada antes de cualquier solicitud de mutación de Gmail: las acciones no compatibles devuelven NOT_SUPPORTED, mientras que la eliminación puede detenerse primero en la puerta de operador predeterminada DELETE_DISABLED. El gmail.readonly predeterminado más la concesión gmail.compose no autorizarían esas mutaciones tampoco; no agregues gmail.modify para intentar hacer que las herramientas estén disponibles. Siguen disponibles solo para proveedores cuyos adaptadores y concesiones los admiten.

Cada fila devuelta por list_emails, search_emails y get_thread, y la respuesta de read_email, lleva un booleano isDraft siempre presente. isDraft: true significa que el mensaje es un borrador no enviado: no ha sido enviado, y su receivedAt es metadatos proporcionados por el proveedor en lugar de evidencia de entrega. El campo nunca se omite, por lo que un agente consumidor puede distinguir "no es un borrador" de "estado de borrador no informado" — pero ten en cuenta que false solo afirma que el proveedor no marcó el mensaje como borrador no enviado, no que el propietario de la bandeja de entrada lo envió (el correo recibido también es false). Los borradores aparecen en listados y resultados de búsqueda como antes.

La gestión de carpetas y reglas de bandeja de entrada requiere el consentimiento de Microsoft Graph MailboxSettings.ReadWrite. Las conexiones existentes de bandejas de entrada de Microsoft deben volver a consentir después de la actualización. Gmail usa etiquetas en lugar de carpetas jerárquicas/reglas de Exchange del lado del servidor, por lo que estas seis herramientas devuelven NOT_SUPPORTED para bandejas de entrada de Gmail.

Formatos de cuerpo

Los cuerpos cruzan el cable como markdown por defecto en ambas direcciones. Ese valor predeterminado es deliberado — el markdown es eficiente en tokens, y una lectura rutinaria debería mantenerse económica. Ambas direcciones pueden optar por no usarlo.

Escritura — send_email, reply_to_email, create_draft y update_draft aceptan un format opcional:

formatComportamiento
markdown (predeterminado)Renderizado a HTML a través de marked (GFM, breaks: true). El HTML sin procesar incrustado en el markdown se conserva.
htmlPaso directo — tu HTML se envía tal cual.
textSin renderizado; se envía como texto plano.

html es paso directo sin saneamiento: sin saneador, sin lista de permitidos, sin reescritura de tu marcado. CSS en línea y etiquetas arbitrarias sobreviven hasta el cable — eso es lo que hace posible el correo con estilo, y significa que eres dueño de lo que envías. La única modificación predeterminada es el envoltorio negro forzado a continuación; con force_black: false el cuerpo pasa byte por byte.

Para markdown y html el HTML renderizado se envuelve en un <div style="color: #000000;"> para que el modo oscuro de Outlook no convierta el texto en blanco sobre blanco. Archivos de cuerpo — send_email, create_draft y update_draft también aceptan body_file: una ruta a un archivo .md, .html o .txt (leído relativo a EMAIL_MCP_SAFE_DIR, por defecto el directorio de trabajo del proceso, más cualquier raíz de AGENT_EMAIL_ALLOWED_DIRS) utilizado como cuerpo en lugar de body. Un archivo de cuerpo .md puede abrir con un bloque de frontmatter YAML:

Para update_draft, body y body_file solo se permiten en un borrador que no sea de respuesta y requieren replace_body: true; el cuerpo almacenado se reemplaza entonces por completo. Los cuerpos de borradores de respuesta no se pueden editar de forma segura porque incluyen historial citado ensamblado por el proveedor, así que crea un nuevo borrador en su lugar. Asunto, destinatario y actualizaciones de adjuntos siguen disponibles en borradores de respuesta.

---
to: alex@example.com
subject: Quarterly summary
format: html
force_black: false
---
<p>The body starts after the closing delimiter.</p>

Las claves reconocidas son to, cc, subject, reply_to, draft, format y force_black (draft: true convierte una llamada a send_email en un guardado de borrador). Un valor de frontmatter anula el parámetro de herramienta del mismo nombre. El frontmatter se analiza solo desde archivos .md, y la extensión del archivo nunca selecciona el formato — sin un parámetro de herramienta format explícito, incluso un archivo de cuerpo .html recibe el renderizado markdown por defecto; pasa format: "html" para paso directo.

Lectura — read_email acepta un format opcional de markdown (por defecto) o html:

{ "id": "AAMkAD...", "format": "html" }

format: "html" devuelve el HTML del cuerpo del mensaje sin modificar. Úsalo cuando necesites estilos que markdown no puede transportar — color, background-color, text-decoration, <u> — por ejemplo, para cambiar una frase de un cuerpo formateado y dejar el resto intacto. A través de la ruta markdown, ese estilo se destruye silenciosamente al salir.

Dos campos vuelven con él:

  • bodyFormat — siempre presente: markdown, html o text. Obtienes text cuando pediste html pero el mensaje no tiene parte HTML, en cuyo caso se devuelve el cuerpo de texto plano. Verifica esto antes de escribir un cuerpo de vuelta.
  • bodyTruncated — presente y true solo si el cuerpo superó el presupuesto de respuesta de 256 KB para format: "html" (la ruta markdown no tiene límite, como antes). No escribas un cuerpo truncado de vuelta a un borrador.

El HTML sin procesar cuesta muchos más tokens que su reducción markdown, así que deja el valor por defecto a menos que necesites el estilo. strip_quoted_history y strip_signatures son transformaciones de texto con forma markdown y no se aplican cuando format es html — el HTML sin procesar se devuelve sin tocar.

Escribirlo de vuelta. Pasa format: "html" y force_black: false:

{ "draft_id": "AAMkAD...", "body": "<edited html>", "format": "html", "force_black": false }

force_black por defecto es true, que envuelve cualquier HTML que envíes en un <div style="color: #000000;">. Eso es correcto para HTML que hayas creado tú mismo, pero en un cuerpo que acabas de leer, anida un envoltorio más en cada ciclo — después de quince revisiones tienes quince divs anidados. Con force_black: false los bytes sobreviven al viaje de ida y vuelta sin cambios.

Envío programado

send_email y send_draft aceptan scheduled_send_at, una marca de tiempo ISO 8601 futura con una zona horaria explícita. Microsoft 365 mantiene el mensaje en el servidor, por lo que la entrega sobrevive a la salida de este proceso. Usa el messageId devuelto con cancel_scheduled_send mientras esté pendiente, o inspecciona los elementos pendientes con list_scheduled_sends. Esa lista cubre mensajes que programaste desde Outlook mismo, no solo los programados a través de este servidor — Outlook aparca el mensaje retenido donde le plazca (a menudo en Elementos eliminados), por lo que el descubrimiento escanea el buzón en lugar de una carpeta, y reporta solo envíos cuya hora aún está por llegar.

Microsoft Graph cambia el ID del mensaje cuando el borrador retenido se mueve a Elementos enviados, por lo que el ID devuelto es un identificador de gestión previo a la entrega, no un ID de mensaje enviado permanente. La API pública de Gmail no expone el envío programado; todas las superficies de envío programado devuelven NOT_SUPPORTED para Gmail mientras los envíos inmediatos permanecen sin cambios.

Adjuntos salientes

send_email, reply_to_email, create_draft y update_draft aceptan un array attachments opcional. Cada entrada toma una ruta de archivo path en sandbox (leída relativa a EMAIL_MCP_SAFE_DIR, por defecto el directorio de trabajo del proceso, más cualquier raíz de AGENT_EMAIL_ALLOWED_DIRS — ver abajo) o base64 en línea, más anulaciones opcionales de filename / mimeType:

{
  "to": "alice@example.com",
  "subject": "Signed agreement",
  "body": "Attached as requested.",
  "attachments": [
    { "path": "./out/agreement.pdf" },
    { "base64": "iVBORw0KGgo...", "filename": "screenshot.png" }
  ]
}

Los archivos están limitados a 25MB cada uno; Microsoft Graph además limita los envíos en línea a ~3MB en total (archivos más grandes necesitan una sesión de carga — aún no soportada). Para update_draft, omitir attachments conserva los archivos existentes del borrador; pasar un array (incluso vacío) los reemplaza.

Adjuntar archivos desde fuera del directorio de trabajo — establece AGENT_EMAIL_ALLOWED_DIRS a una lista separada por delimitadores de directorios absolutos (: en macOS/Linux, ; en Windows; un ~ inicial se expande) que attachments[].path y body_file también pueden leer:

{
  "mcpServers": {
    "email-agent-mcp": {
      "command": "npx",
      "args": ["-y", "@usejunior/email-agent-mcp"],
      "env": { "AGENT_EMAIL_ALLOWED_DIRS": "~/Downloads:/Volumes/Shared/Contracts" }
    }
  }
}

Cómo se resuelve la ruta de un llamador — las dos reglas siguientes aparecen ambas como FILE_NOT_FOUND, que es fácil de malinterpretar como un archivo faltante:

Ruta que pasasSe resuelve contra¿Encuentra un archivo en una raíz permitida?
contract.pdfEMAIL_MCP_SAFE_DIR (por defecto: cwd)❌ las rutas relativas nunca buscan en las raíces adicionales
~/Downloads/contract.pdfEMAIL_MCP_SAFE_DIR — el ~ no se expande❌
/Users/you/Downloads/contract.pdfcada raíz por turno✅

La abreviatura ~ se expande en AGENT_EMAIL_ALLOWED_DIRS (la configuración del operador) pero no en attachments[].path o body_file (el argumento del llamador) — pásalas como rutas absolutas. Y una ruta relativa está deliberadamente confinada al directorio seguro: buscar en cada raíz permitida un contract.pdf desnudo adjuntaría silenciosamente la copia que existiera primero.

Cada raíz se canoniza con realpath antes de la verificación de contención: una raíz en la lista permitida que es en sí misma un enlace simbólico se resuelve a su ubicación real, una raíz que no se puede canonizar no autoriza nada, y un enlace simbólico que escapa de cada raíz aún se rechaza. Sin establecer (el valor por defecto) mantiene el directorio de trabajo como la única raíz — por eso un agente que no puede alcanzar un archivo debería pedirte que incluyas su directorio en la lista permitida en lugar de copiar documentos confidenciales en un árbol de trabajo git.

Incluir un directorio en la lista permitida confía en todos los que pueden escribir en él. La validación y la apertura son operaciones separadas en una ruta, por lo que un principal que puede reemplazar un directorio dentro de una raíz permitida entre las dos aún puede redirigir la lectura; solo incluye raíces cuyos ancestros no sean escribibles por usuarios no confiables. El archivo final se abre con O_NOFOLLOW, por lo que el archivo en sí no se puede intercambiar por un enlace simbólico después de la validación.

Soporte de proveedores

ProveedorEstadoPaquete
Microsoft 365 (Graph API)Totalmente soportado@usejunior/provider-microsoft
GmailSoportado vía OAuth CLI interactivo (cliente por defecto o el tuyo propio) o configuración manual de token de actualización@usejunior/provider-gmail

Usa email-agent-mcp configure --provider gmail para ejecutar el flujo OAuth del navegador local, o agrega un archivo de token de buzón manual bajo ~/.email-agent-mcp/tokens/. Consulta Configuración de Gmail abajo y packages/provider-gmail/README.md.

Configuración de Gmail

Gmail tiene dos rutas OAuth soportadas. Ambas terminan con el mismo resultado: un token de actualización en tu máquina, y llamadas a la API de Gmail yendo directamente desde tu máquina a Google.

RutaComando¿Se necesita proyecto de Google Cloud?
Cliente OAuth por defectoemail-agent-mcp configure --provider gmailNo
Trae tu propia clave (BYOK)mismo comando más --client-id / --client-secretSí, el tuyo

Recomendado por ahora: BYOK. Nuestro cliente OAuth por defecto aún está en el estado de publicación "Pruebas" de Google mientras la verificación está en progreso, lo que lo limita a 100 usuarios de prueba registrados y muestra el aviso "Google no ha verificado esta aplicación" durante el consentimiento. La verificación para el alcance restringido de Gmail requiere una evaluación de seguridad CASA y toma varias semanas; el progreso se rastrea en issue #112. BYOK evita el límite compartido de 100 usuarios y pone el estado de publicación de la aplicación bajo tu control; tu propia aplicación aún tiene las restricciones de Pruebas de Google hasta que la publiques.

Otras razones para elegir BYOK: cuota de API dedicada, tu propia política de privacidad y estado de verificación, y sin dependencia del broker alojado en https://oauth.usejunior.com.

Alcances solicitados

Agent Email solicita exactamente estos dos alcances de Gmail por defecto:

https://www.googleapis.com/auth/gmail.readonly
https://www.googleapis.com/auth/gmail.compose

gmail.readonly es requerido para leer mensajes e hilos; gmail.compose gestiona borradores y envío pero no autoriza users.threads.get o users.messages.get. La concesión por defecto no incluye gmail.modify. Por separado, el adaptador de Gmail deja intencionalmente los cambios de etiquetas, cambios de estado de lectura, movimientos, papelera y operaciones de eliminación sin soporte, por lo que esas herramientas fallan cerradas con NOT_SUPPORTED. Agrega ambos alcances a tu pantalla de consentimiento OAuth.

Las implementaciones que establecen explícitamente GMAIL_OAUTH_SCOPES deben actualizarlo a los dos alcances separados por espacios anteriores. Las nuevas autorizaciones y las autorizaciones repetidas deben volver a consentir el nuevo par de alcances. Los archivos de buzón existentes y sus metadatos de token de actualización almacenados permanecen sin cambios.

BYOK: crea tu propio cliente OAuth de Google

  1. Crea un proyecto en la Consola de Google Cloud, o selecciona uno existente.
  2. Habilita la API de Gmail bajo APIs y servicios → Biblioteca → API de Gmail → Habilitar.
  3. Configura la pantalla de consentimiento OAuth bajo APIs y servicios → Pantalla de consentimiento OAuth:
    • Tipo de usuario Externo para una cuenta personal de @gmail.com, o Interno si estás en Google Workspace y solo tu propia organización necesita acceso.
    • Agrega tanto https://www.googleapis.com/auth/gmail.readonly como https://www.googleapis.com/auth/gmail.compose.
    • Mientras la aplicación esté en Pruebas, agrega tu propia dirección de Gmail bajo Usuarios de prueba, o el consentimiento será rechazado.
  4. Crea el cliente OAuth bajo APIs y servicios → Credenciales → Crear credenciales → ID de cliente OAuth. Elige el tipo de aplicación Aplicación de escritorio. Escritorio es requerido: configure inicia un listener desechable en un puerto loopback efímero (http://127.0.0.1:<port>/oauth2callback), y solo los clientes de Escritorio permiten que Google acepte un puerto loopback arbitrario sin pre-registrar la URI de redirección exacta. Un cliente de aplicación web fallará con redirect_uri_mismatch.
  5. Copia el ID de cliente y el secreto de cliente.

BYOK: entrega las credenciales a email-agent-mcp

Pasa ambas mitades como banderas:

npx email-agent-mcp configure \
  --provider gmail \
  --mailbox personal \
  --client-id YOUR_GOOGLE_CLIENT_ID \
  --client-secret YOUR_GOOGLE_CLIENT_SECRET

O establece las dos variables de entorno con espacio de nombres y omite las banderas:

export AGENT_EMAIL_GMAIL_CLIENT_ID=YOUR_GOOGLE_CLIENT_ID
export AGENT_EMAIL_GMAIL_CLIENT_SECRET=YOUR_GOOGLE_CLIENT_SECRET

npx email-agent-mcp configure --provider gmail --mailbox personal

Ambas mitades son requeridas. Proporcionar solo una sale con un error en lugar de volver silenciosamente al cliente por defecto. No proporcionar ninguna selecciona el cliente OAuth por defecto.

Tu navegador abre la pantalla de consentimiento de Google, el CLI captura la devolución de llamada en loopback, intercambia el código con PKCE y escribe el buzón en ~/.email-agent-mcp/tokens/<safe-key>.json con "source": "byok" junto a tu clientId, clientSecret y el refreshToken resultante. Los refrescos de token luego van directamente al endpoint de tokens de Google; no hay broker involucrado.

Volver a ejecutar configure para un buzón que ya está guardado reutiliza las credenciales guardadas, por lo que solo pasas las banderas una vez. Pasar --client-id y --client-secret para un buzón previamente configurado contra el cliente por defecto lo migra a BYOK.

BYOK: evita que la concesión expire después de 7 días

Google caduca los tokens de actualización después de 7 días mientras tu aplicación OAuth esté en estado Testing (Pruebas), lo que se manifiesta como un aviso de re-autenticación aproximadamente una vez por semana. email-agent-mcp status advierte cuando un buzón se acerca a esa ventana. Publicar tu aplicación (Pantalla de consentimiento de OAuth → Publicar aplicación) elimina la caducidad de 7 días. Dado que eres dueño de la aplicación y eres su único usuario, el límite de 100 usuarios de prueba no aplica para ti de todos modos.

Desconectar Gmail y revocar el acceso

Desconectar tiene dos partes independientes:

  1. Detén cualquier proceso email-agent-mcp en ejecución. En Finder, abre ~/.email-agent-mcp/tokens/, identifica el único archivo JSON correspondiente al buzón que deseas desconectar y mueve ese archivo exacto a la Papelera. No abras, pegues ni compartas su contenido, y no elimines todo el directorio ~/.email-agent-mcp si hay otros buzones configurados. Si configuraste EMAIL_AGENT_MCP_HOME, usa la carpeta tokens/ de ese directorio en su lugar.
  2. Abre https://myaccount.google.com/connections, selecciona Email Agent MCP (o el nombre de tu aplicación BYOK) y elimina su acceso.

Eliminar el archivo local evita que esta instalación use la credencial guardada. Eliminar la conexión de la cuenta de Google revoca la concesión en Google. Ejecuta email-agent-mcp status después para confirmar que el buzón ya no está configurado.

Valores de seguridad predeterminados

Agent Email incluye valores restrictivos predeterminados que puedes flexibilizar según sea necesario:

  • Lista de permitidos de envío: vacía por defecto — los agentes no pueden enviar correos hasta que agregues destinatarios
  • Lista de permitidos de recepción: acepta todo por defecto — controla qué remitentes activan el vigilante
  • Eliminación deshabilitada: los agentes no pueden eliminar correos por defecto. Deben cumplirse dos condiciones:
    1. El operador establece AGENT_EMAIL_DELETE_ENABLED=true en el entorno del proceso email-agent-mcp (y AGENT_EMAIL_HARD_DELETE_ENABLED=true para eliminación permanente). Se requiere reinicio después del cambio.
    2. El llamador pasa user_explicitly_requested_deletion: true en la llamada a la herramienta.
  • Sanitización de errores: las claves API, rutas de archivos y trazas de pila se redactan de las respuestas de error
  • Aislamiento de archivos de cuerpo: sin recorrido ../, sin enlaces simbólicos, detección binaria
  • Protección contra envíos duplicados: activada por defecto — un envío, respuesta o borrador idéntico repetido dentro de 15 minutos se rechaza en lugar de entregarse dos veces

Protección contra envíos duplicados

Los endpoints de envío de los proveedores (Graph POST /sendMail, Gmail users.messages.send) no aceptan clave de idempotencia, por lo que Agent Email despacha cada entrega exactamente una vez y nunca reintenta automáticamente. Eso evita que la biblioteca duplique un mensaje. No evita que el llamador lo haga: un agente cuyo resultado de herramienta se perdió — contexto compactado, transporte caído, supervisor reiniciando el turno, o una persona reintentando algo que parecía atascado — reproduce la misma instrucción aprobada, y la segunda llamada se ve exactamente como la primera.

send_email, reply_to_email y send_draft por lo tanto registran cada despacho en un registro en memoria, claveado por un resumen de las entradas efectivas de la acción — buzón, destinatarios, asunto, cuerpo renderizado, contenido de adjuntos y el ID del padre de respuesta o borrador. (Entradas efectivas, no bytes finales en el cable: un proveedor puede transformar un mensaje en el camino, y Graph por ejemplo trunca un asunto a 255 caracteres, por lo que dos entradas diferentes pueden salir como correo idéntico. La protección detecta una llamada de herramienta reproducida, que es lo que realmente ocurre.) Una repetición dentro de la ventana se rechaza antes de que se despache la entrega:

CódigoSignificadoQué hacer
DUPLICATE_SEND_IN_FLIGHTUna entrega idéntica aún no ha regresadoEspera la primera llamada
DUPLICATE_SEND_BLOCKEDUna entrega idéntica ya tuvo éxitoDetente — la respuesta lleva el messageId original
DUPLICATE_SEND_UNRESOLVEDUna entrega idéntica terminó de forma ambiguaRevisa Elementos enviados antes de reenviar

Un fallo que demuestra que nada se entregó — cualquier código derivado de 4xx, un 429 o un fallo de conexión — libera el registro inmediatamente, por lo que reenviar después de un rechazo nunca se bloquea. Cualquier otra cosa, incluido un código de un proveedor que no hemos visto, se mantiene como no resuelto: un mensaje retenido incorrectamente cuesta un reenvío bloqueado; uno liberado incorrectamente cuesta un duplicado en la bandeja de entrada de alguien.

Para enviar el mismo mensaje de nuevo a propósito, pasa allow_duplicate: true. Es un bypass incondicional por solicitud — una reproducción que también lleva la bandera se despacha de nuevo, porque la bandera afirma que una persona decidió este envío. El intento forzado se registra junto al anterior en lugar de reemplazarlo, por lo que si se rechaza la anulación, la entrega original aún rechaza una reproducción ordinaria.

Los registros están limitados a un buzón: mailbox cuando el llamador proporciona uno, de lo contrario la instancia del proveedor. Los operadores pueden cambiar la ventana con AGENT_EMAIL_DUPLICATE_SEND_WINDOW_MS (por defecto 900000); 0 desactiva la protección por completo.

El registro vive en el proceso del servidor y en memoria. Cierra la ventana de reproducción que realmente ocurre — un reintento dentro de un servidor en ejecución — y no sobrevive al reinicio de ese servidor.

Paquetes

PaqueteDescripción
@usejunior/email-coreAcciones de correo principales, motor de contenido, seguridad e interfaces de proveedor
@usejunior/email-mcpAdaptador de servidor MCP, CLI y vigilante
@usejunior/provider-microsoftProveedor de correo de Microsoft Graph API
@usejunior/provider-gmailProveedor de correo de Gmail API
email-agent-mcpEnvoltorio de distribución (npx email-agent-mcp)
@usejunior/email-agent-mcpEnvoltorio de compatibilidad, publicado en sincronía; usa email-agent-mcp para nuevas instalaciones

Señales de calidad y confianza

  • CI se ejecuta en cada pull request y push a main (lint, typecheck, pruebas en Node 20 + 22)
  • Escaneo de seguridad CodeQL y Semgrep
  • Cobertura publicada en Codecov
  • Cumplimiento de trazabilidad OpenSpec mediante npm run check:spec-coverage
  • Más de 300 pruebas en el conjunto
  • Mantenedor: Steven Obiajulu

Arquitectura

email-agent-mcp/
├── packages/
│   ├── email-core          Core actions, content engine, security
│   ├── email-mcp           MCP server adapter, CLI, watcher
│   ├── provider-microsoft  Microsoft Graph provider
│   ├── provider-gmail      Gmail API provider
│   └── email-agent-mcp         Distribution wrapper (npx entry point)
├── openspec/               Spec-driven development
└── scripts/                CI and validation scripts

Publicación

Publicación impulsada por etiquetas mediante GitHub Actions con publicación confiable npm OIDC. Los 6 paquetes se publican en orden de dependencia con --provenance, luego server.json se publica en el Registro MCP oficial con mcp-publisher.

Preguntas frecuentes

¿Funciona esto con Claude Code?

Sí. Ejecuta npx email-agent-mcp para iniciar el servidor MCP, luego configúralo en la configuración de Claude Code.

¿Pueden los agentes enviar correos sin mi permiso?

No. La lista de permitidos de envío está vacía por defecto. Los agentes no pueden enviar ningún correo hasta que configures explícitamente los destinatarios permitidos.

¿Esto almacena mis credenciales de correo?

Los tokens OAuth son gestionados por MSAL (Microsoft) y se almacenan en tu llavero del sistema operativo o archivos de configuración locales bajo ~/.email-agent-mcp/. Agent Email nunca almacena contraseñas en texto plano.

¿Puedo conectar múltiples buzones?

Sí. Puedes configurar Microsoft 365 y Gmail simultáneamente. Las acciones de lectura usan tu buzón principal por defecto; las acciones de escritura requieren especificar un buzón cuando hay múltiples configurados.

El CLI de OpenClaw rechaza mi configuración con "Unrecognized key: mcp"

El CLI de OpenClaw y la aplicación macOS pueden ser versiones diferentes. La aplicación (que ejecuta la puerta de enlace) puede admitir claves de configuración que el CLI aún no reconoce. Actualiza el CLI: cd ~/Projects/NemoClaw && npm install openclaw@latest. Alternativamente, reinicia la puerta de enlace directamente: launchctl kickstart -k gui/501/ai.openclaw.gateway.

El vigilante inicia pero encuentra cero buzones

Las credenciales de buzón se almacenan en ~/.email-agent-mcp/tokens/. Si este directorio está vacío, ejecuta npx email-agent-mcp o npm run dev:configure para autenticarte mediante OAuth. El vigilante saldrá sin buzones que consultar hasta que al menos uno esté configurado.

OpenClaw dice "Demo mode -- run email-agent-mcp configure to connect"

El servidor MCP está en ejecución pero no tiene credenciales de buzón reales. Ejecuta npx email-agent-mcp para completar la configuración OAuth interactiva, luego reinicia la puerta de enlace de OpenClaw para que el servidor MCP se reconecte con tokens válidos.

Token caducado después de una semana aunque acabo de autenticarme

Los tokens de actualización de Microsoft suelen durar 90 días, pero tu inquilino de Azure AD puede imponer duraciones más cortas. El código usa MSAL con persistencia en el llavero del sistema operativo (@azure/identity-cache-persistence), que maneja la renovación silenciosa de tokens automáticamente. Si MSAL informa interaction_required o invalid_grant, vuelve a ejecutar npx email-agent-mcp para re-autenticarte. Causas comunes: políticas de acceso condicional, requisitos de re-verificación MFA o políticas de duración de tokens configuradas por el administrador.

El bot de Telegram de OpenClaw recibe mensajes pero no responde

Verifica que el canal de Telegram esté saludable con openclaw status. Si el canal muestra OK pero no llegan respuestas, verifica que: (1) tu ID de usuario de Telegram esté en channels.telegram.allowFrom en openclaw.json, (2) exista un enlace que coincida con channel: "telegram", y (3) la puerta de enlace se haya reiniciado después de los cambios de configuración. Para bots de un solo propietario, usa dmPolicy: "allowlist" con IDs explícitos de allowFrom en lugar de depender de aprobaciones de emparejamiento.

Desarrollo

npm ci
npm run build
npm run lint --workspaces --if-present
npm run test:run
npm run check:spec-coverage

Ver también

  • Safe DOCX Suite — edición quirúrgica de documentos de Word con agentes de codificación
  • Open Agreements — completa plantillas legales estándar con agentes de codificación

Privacidad

Agent Email se ejecuta completamente en tu máquina local. Las credenciales de correo se almacenan en tu llavero del sistema operativo (MSAL) y archivos de configuración locales. Agent Email no envía contenido de correo a servidores externos por sí mismo.

Gobernanza