postbus-mcp
postbus-mcp
Un servidor MCP autoalojado que te permite a ti y a un puñado de personas a tu alrededor trabajar con vuestros buzones desde Claude o cualquier otro cliente MCP: buscar, leer conversaciones completas y enviar correo.
Funciona con cualquier proveedor IMAP/SMTP — Gmail, Outlook, Fastmail, tu propio servidor de correo — usando una simple contraseña de aplicación. Sin proyecto de Google Cloud, sin verificación OAuth, sin límite de usuarios de prueba.
Una instancia sirve a múltiples usuarios. Cada uno tiene su propio token de API y solo ve sus propios buzones. Lo alojas tú, repartes los tokens; no hay registro abierto.
Claude / MCP client
│ Authorization: Bearer <token>
▼
POST /mcp ──► postbus-mcp ──► SQLite (users + encrypted app passwords)
│
├──► IMAP (imapflow) search, read, threads
└──► SMTP (nodemailer) sendingContenido
Related MCP server: simple-email-mcp
Cómo funciona
Multiusuario, pero pequeño. Un solo archivo SQLite con dos tablas: users (id más un hash del token de API) y mail_accounts (los buzones de cada usuario, con la contraseña de aplicación cifrada). No hay ningún servicio de base de datos separado que ejecutar.
El aislamiento vive en la consulta, no en una comprobación posterior. Cada sesión de MCP pertenece exactamente a un usuario, decidido por el token bearer. El servidor MCP se construye por petición en torno a ese usuario, y cada consulta a la base de datos lleva el user_id en su WHERE. El alias de otra persona simplemente no existe en tu sesión.
Interfaz de proveedor. La capa de herramientas habla con un MailProvider genérico y no sabe nada de IMAP ni de Gmail. ImapSmtpProvider es la implementación principal, con un GmailApiProvider opcional a su lado. Añadir un tercero no requiere cambios en las herramientas; consulta Añadir un proveedor.
Inicio rápido
Con Docker (recomendado)
git clone https://github.com/HalloSouf/postbus-mcp.git
cd postbus-mcp
cp .env.example .env
openssl rand -hex 32 # put the result in .env as MASTER_KEY
docker compose up -d --build
docker compose exec postbus node dist/cli/add-user.js "Soufiane"Ese último comando imprime un token de API exactamente una vez. Guárdalo de inmediato.
Localmente con Node (22 o superior)
npm install
cp .env.example .env
openssl rand -hex 32 # put the result in .env as MASTER_KEY
npm run build
npm run add-user -- "Soufiane"
npm startEl servidor escucha en http://localhost:3000/mcp. Una GET /health devuelve {"status":"ok"}, lo que resulta útil para una comprobación de disponibilidad.
Usuarios y tokens
Los tokens los repartes tú mismo; no hay registro de autoservicio.
Comando | Qué hace |
| Crea un usuario e imprime el token (una vez) |
| Muestra usuarios, número de buzones y estado |
| Nuevo token; el anterior deja de funcionar de inmediato |
| Elimina el usuario y todos sus buzones |
En Docker, ejecuta los mismos scripts como node dist/cli/<script>.js:
docker compose exec postbus node dist/cli/list-users.js
docker compose exec postbus node dist/cli/rotate-token.js WvDnhafdM5yQSolo se almacena un hash SHA-256 de cada token, por lo que un token perdido no se puede recuperar; en su lugar, rótalo.
Conexión de tu cliente
Claude Desktop
Claude Desktop habla stdio, así que pon mcp-remote en medio. En claude_desktop_config.json:
{
"mcpServers": {
"postbus": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://mcp.example.com/mcp",
"--header",
"Authorization: Bearer pb_YOUR_TOKEN_HERE"
]
}
}
}Ese archivo se encuentra en ~/Library/Application Support/Claude/claude_desktop_config.json en macOS y en %APPDATA%\Claude\claude_desktop_config.json en Windows. Reinicia Claude Desktop después de editarlo.
Claude Code
claude mcp add --transport http postbus https://mcp.example.com/mcp \
--header "Authorization: Bearer pb_YOUR_TOKEN_HERE"Otros clientes
Cualquier cosa que hable Streamable HTTP funciona: endpoint POST /mcp, token como Authorization: Bearer <token>. El servidor funciona sin estado — sin ids de sesión, sin stream en el servidor — por lo que una GET /mcp devuelve deliberadamente 405.
Vincular un buzón
Esto se hace en la conversación, con tu propio token. No necesitas terminal:
Vincula mi Gmail como "personal", dirección souf@gmail.com, contraseña de aplicación abcd efgh ijkl mnop
Claude entonces llama a add_mail_account. La conexión se prueba primero (tanto IMAP como SMTP); no se almacena nada hasta que ambas funcionan.
Crear una contraseña de aplicación
Proveedor | Dónde | Nota |
Gmail / Workspace | Requiere 2FA en la cuenta | |
Outlook / Microsoft 365 | Requiere 2FA; un administrador puede bloquear IMAP | |
Fastmail | Ajustes → Privacidad y seguridad → Contraseñas de aplicación | Elige "Mail (IMAP/SMTP)" |
iCloud | https://account.apple.com → Contraseñas específicas de la aplicación | Requiere 2FA |
Servidor propio | n/a | Tu contraseña de correo, o una cuenta dedicada |
Nunca uses tu contraseña normal cuando el proveedor ofrezca contraseñas de aplicación.
Host y puerto
Para proveedores conocidos, postbus-mcp los rellena automáticamente: solo tienes que proporcionar alias, correo electrónico y contraseña de aplicación:
Gmail, Google Workspace, Outlook, Hotmail, Microsoft 365, Fastmail, iCloud, Yahoo, Zoho, Proton (mediante Bridge).
Para cualquier otro, indícalos tú:
imap_host: imap.yourdomain.com imap_port: 993 (TLS)
smtp_host: smtp.yourdomain.com smtp_port: 465 (TLS) or 587 (STARTTLS)Los puertos 993 y 465 usan TLS desde el primer byte; en otros puertos se usa STARTTLS cuando el servidor lo ofrece. Si esa suposición no es válida para tu servidor, pasa imap_secure o smtp_secure explícitamente.
Herramientas disponibles
Herramienta | Qué hace |
| Lista tus buzones con alias y dirección de correo electrónico |
| Vincula un buzón IMAP/SMTP con una contraseña de aplicación (prueba la conexión primero) |
| Desvincula un buzón y elimina la contraseña de aplicación almacenada |
| Busca con sintaxis estilo Gmail; devuelve un |
| Contenido completo de un mensaje: cabeceras, cuerpo, metadatos de los adjuntos |
| Todos los mensajes de una conversación, del más antiguo al más reciente |
| Envía un nuevo mensaje de inmediato (cc, bcc, reply-to, html) |
Cada herramienta solo toca los buzones del usuario que hay detrás del token.
Sintaxis de búsqueda
search_emails utiliza sintaxis estilo Gmail. Para buzones de Gmail, tu consulta va a Gmail sin cambios (mediante X-GM-RAW), por lo que todo lo que funciona en la barra de búsqueda de Gmail funciona aquí. Para otros servidores IMAP, se traduce:
Término | Gmail | Otro IMAP |
| ✅ | ✅ |
| ✅ | ✅ |
| ✅ | ✅ |
| ✅ | ✅ |
| ✅ | ✅ |
| ✅ | ✅ (filtrado después) |
| ✅ | ✅ (mediante SPECIAL-USE) |
| ✅ | ✅ |
| ✅ | ⚠️ un único término de texto combinado |
| ✅ | ❌ ignorado |
Ejemplos:
from:boss@company.com is:unread newer_than:7d
subject:"march invoice" has:attachment
in:sent to:client@example.com older_than:1mUna consulta vacía devuelve los mensajes más recientes de la bandeja de entrada.
Hilos
Cada resultado de search_emails lleva un threadId, y get_thread lo usa para recuperar toda la conversación — cronológica, con remitente, asunto, fecha y cuerpo por mensaje.
Esto ocurre de dos maneras, según lo que el servidor pueda hacer:
Los servidores Gmail (
X-GM-THRID) y RFC 8474 (OBJECTID) otorgan ellos mismos un id de hilo estable. Lo usamos directamente, y elthreadIdtiene este aspecto:srv:1829384756.Cualquier otro servidor IMAP no tiene concepto de hilos. Allí reconstruimos la conversación a partir de las cabeceras estándar
Message-ID,In-Reply-ToyReferences: el primer id de esa cadena es la raíz del hilo. EsosthreadIdempiezan porref:.
Al recuperar, buscamos en la carpeta "all mail" si el servidor la tiene y, si no, en Inbox, Sent y Archive; así tus propias respuestas también acaban en la conversación.
Despliegue detrás de Traefik
El docker-compose.yml de este repositorio es un ejemplo funcional. Su núcleo:
services:
postbus:
build: .
restart: unless-stopped
environment:
MASTER_KEY: ${MASTER_KEY:?set MASTER_KEY in .env}
DATABASE_PATH: /data/postbus.db
TRUST_PROXY: "true"
volumes:
- postbus-data:/data
networks: [proxy]
labels:
traefik.enable: "true"
traefik.docker.network: proxy
traefik.http.routers.postbus.rule: Host(`${PUBLIC_HOST:-mcp.example.com}`)
traefik.http.routers.postbus.entrypoints: websecure
traefik.http.routers.postbus.tls.certresolver: letsencrypt
traefik.http.services.postbus.loadbalancer.server.port: "3000"Cosas a tener en cuenta:
Establece
PUBLIC_HOSTen.envcon tu propio hostname; ese es el único sitio donde aparece el dominio, así que el archivo compose permanece intacto.La red
proxydebe existir (docker network create proxy) y Traefik debe estar en ella.El contenedor no publica ningún puerto propio: solo Traefik puede alcanzarlo.
TRUST_PROXY=truepermite que Express confíe en las cabecerasX-Forwarded-*.Termina TLS en Traefik. Los tokens viajan como credenciales bearer; sin HTTPS van en claro.
El volumen
postbus-datacontiene la base de datos con todas las contraseñas de aplicación cifradas. Haz una copia de seguridad junto conMASTER_KEY, almacenándolos por separado.
Seguridad
MASTER_KEY. Las contraseñas de aplicación y los tokens de actualización se almacenan con AES-256-GCM, cada uno con su propio IV. El servidor se niega a arrancar sin la clave. Si la pierdes, todos tendrán que volver a vincular sus buzones, así que mantenla aparte de la copia de seguridad de la base de datos.
Tokens. Solo se almacena el hash SHA-256. Compártelos por un canal de confianza y rótalos ante cualquier duda (npm run rotate-token).
Aislamiento. Cada consulta sobre mail_accounts filtra por user_id, y el servidor MCP se construye por petición en torno a un único usuario, por lo que no hay almacén de sesiones que pueda mezclar a las personas.
Lo que no es. Sin limitación de tasa, sin registro de auditoría, sin permisos granulares. Está pensado para un puñado de personas que conoces, detrás de TLS. No lo expongas a un público desconocido.
Añadir un proveedor
La capa de herramientas solo habla con MailProvider de src/types.ts:
interface MailProvider<A extends MailAccount = MailAccount> {
readonly id: ProviderId;
verify(account: A): Promise<void>;
search(account: A, query: string, maxResults: number): Promise<MessageSummary[]>;
getMessage(account: A, messageId: string): Promise<MessageDetail>;
getThread(account: A, threadId: string): Promise<MessageDetail[]>;
send(
account: A,
to: string,
subject: string,
body: string,
options?: SendOptions,
): Promise<string>;
}Un proveedor recibe una cuenta completamente resuelta, con las credenciales descifradas. La búsqueda del alias ocurre en la capa de herramientas, por lo que un proveedor no puede salir del usuario de la sesión.
Para añadir uno:
Amplía
ProviderIdy la uniónMailAccountensrc/types.ts.Escribe
src/providers/<name>/provider.tscon una clase que implemente la interfaz.Añade una línea al mapa en
src/providers/registry.ts.Asegúrate de que una cuenta de ese tipo pueda llegar a la base de datos: un
save<Name>Account()ensrc/db/accounts.ts(los secretos pasan porencryptSecret), además de una forma de vincularla: una herramienta adicional junto aadd_mail_account, o un script CLI.
Las herramientas existentes (search_emails, get_message, get_thread, send_email) no requieren cambios. Consulta también CONTRIBUTING.md.
Opcional: Gmail mediante la API en lugar de IMAP
El repositorio incluye un segundo proveedor que accede a Gmail a través de la Gmail API en lugar de IMAP/SMTP. Casi nunca lo necesitarás: IMAP con una contraseña de aplicación hace el mismo trabajo con mucho menos trámite. Solo es útil cuando tu organización bloquea IMAP pero permite la API.
Crea un proyecto en https://console.cloud.google.com.
APIs & Services → Library → busca "Gmail API" → Enable.
APIs & Services → OAuth consent screen → selecciona External → rellena un nombre y un correo de soporte.
Añade las direcciones que planeas vincular en Test users.
Credentials → Create credentials → OAuth client ID → selecciona Desktop app.
Pon el client id y el secret en
.env:GOOGLE_CLIENT_ID=xxxxxxxxxxxx.apps.googleusercontent.com GOOGLE_CLIENT_SECRET=GOCSPX-xxxxxxxxxxxxxxxxxxxx OAUTH_CALLBACK_PORT=53682Vincula un buzón. Esto se ejecuta en la máquina del administrador, porque Google envía el callback a
localhost:npm run list-users # look up the user id npm run link-gmail -- <user-id> work
Alcances utilizados: gmail.readonly, gmail.send, gmail.compose, gmail.labels.
Nota: mientras la pantalla de consentimiento de OAuth esté en Testing, los refresh tokens expiran a los 7 días y tienes que vincular otra vez. Eso solo se detiene cuando la pantalla de consentimiento pasa a In production, lo que para estos alcances requiere verificación de Google. Esto es exactamente por lo que IMAP con una contraseña de aplicación es la ruta principal.
Desarrollo
npm install
npm run dev # server with hot reload (tsx watch)
npm test # unit tests (vitest)
npm run typecheck # src + tests
npm run format # prettier across the repo
npm run build # into dist/Las pruebas en tests/ se ejecutan en medio segundo y no tocan nada fuera del
proceso: SQLite corre en memoria y ninguna conexión sale de la máquina. Cubren
la lógica que puede fallar silenciosamente: traducción de consultas de búsqueda,
codificación de ids de mensajes y de hilos, análisis y composición de MIME,
almacenamiento cifrado, la separación entre usuarios y el middleware bearer.
Lo que no cubren es hablar con un servidor de correo real. Para eso, ejecuta GreenMail localmente:
docker run -d --rm --name greenmail -p 3143:3143 -p 3025:3025 \
-e GREENMAIL_OPTS='-Dgreenmail.setup.test.imap -Dgreenmail.setup.test.smtp -Dgreenmail.users=souf:secret@postbus.test -Dgreenmail.hostname=0.0.0.0' \
greenmail/standalone:2.1.0Luego vincula un buzón con imap_host: 127.0.0.1, imap_port: 3143,
smtp_host: 127.0.0.1, smtp_port: 3025, username: souf,
app_password: secret.
GreenMail no soporta extensiones de Gmail. La rama del código que usa
X-GM-RAWyX-GM-THRIDsolo se puede probar contra un buzón de Gmail real.
GitHub Actions ejecuta las mismas comprobaciones en cada push y pull request:
formato, tipos, npm audit sobre las dependencias de producción, las pruebas,
y un docker build que arranca el contenedor y verifica que /health responde
y que /mcp devuelve 401 sin un token. Tanto CI como el contenedor ejecutan
Node 24, el LTS actual.
Estructura del proyecto
src/
├── index.ts startup: check MASTER_KEY, open the db, listen
├── config.ts environment configuration
├── crypto.ts AES-256-GCM for secrets, hashing for tokens
├── types.ts MailProvider plus every shared type
├── db/ SQLite: migrations, users, mail_accounts
├── http/ Express app, bearer auth, MCP transport per request
├── providers/
│ ├── registry.ts account -> provider
│ ├── imap/ IMAP/SMTP: connections, search, threading, sending
│ └── gmail/ optional Gmail API provider (OAuth)
├── tools/ the MCP tools (they know no provider)
└── cli/ admin scripts: users and tokens
tests/ unit tests (vitest), mirroring the layout of src/Licencia
MIT — consulta LICENSE.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables reading and sending emails via IMAP and SMTP through the MCP protocol. Supports multiple email accounts and configuration via UI or environment variables.BSD 3-Clause
- AlicenseBqualityBmaintenanceEnables users to manage email accounts via IMAP/SMTP, including reading, searching, sending emails with attachments and calendar invites, all through natural language interactions with MCP-compatible clients.14MIT
- AlicenseAqualityBmaintenanceMCP server that enables email management (send, read, search, delete, etc.) via IMAP/SMTP, compatible with Gmail, Outlook, Yahoo, iCloud, and other standard mail servers.11MIT
- AlicenseNot gradedqualityAmaintenanceExposes any IMAP mailbox and SMTP relay as MCP tools, enabling email management (read, search, send, delete) through MCP-compatible agents.MIT
Related MCP Connectors
Read, search, send, organize, draft and schedule email across your inboxes from any MCP client.
Hosted email MCP for AI agents with inboxes, send/receive, memory, recovery, and credits.
Fully-managed email as MCP tools - register domains, real mailboxes, send and receive mail.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/HalloSouf/postbus-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server