Skip to main content
Glama
HalloSouf

postbus-mcp

by HalloSouf

postbus-mcp

Code quality Docker License: MIT

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)    sending

Contenido


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 start

El 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

npm run add-user -- "Name"

Crea un usuario e imprime el token (una vez)

npm run list-users

Muestra usuarios, número de buzones y estado

npm run rotate-token -- <id>

Nuevo token; el anterior deja de funcionar de inmediato

npm run remove-user -- <id>

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 WvDnhafdM5yQ

Solo 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

https://myaccount.google.com/apppasswords

Requiere 2FA en la cuenta

Outlook / Microsoft 365

https://account.microsoft.com/security

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

list_accounts

Lista tus buzones con alias y dirección de correo electrónico

add_mail_account

Vincula un buzón IMAP/SMTP con una contraseña de aplicación (prueba la conexión primero)

remove_mail_account

Desvincula un buzón y elimina la contraseña de aplicación almacenada

search_emails

Busca con sintaxis estilo Gmail; devuelve un id y un threadId por mensaje

get_message

Contenido completo de un mensaje: cabeceras, cuerpo, metadatos de los adjuntos

get_thread

Todos los mensajes de una conversación, del más antiguo al más reciente

send_email

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

from:, to:, cc:, bcc:, subject:

is:unread, is:read, is:starred, is:answered

newer_than:7d, older_than:2w (d/w/m/y)

after:2026-01-01, before:2026/03/01

larger:5M, smaller:100k

has:attachment

✅ (filtrado después)

in:inbox, in:sent, in:archive, in:all, in:trash

✅ (mediante SPECIAL-USE)

-from:someone (excluir)

"exact phrase" y palabras sueltas

⚠️ un único término de texto combinado

label:, filename:, category:

❌ ignorado

Ejemplos:

from:boss@company.com is:unread newer_than:7d
subject:"march invoice" has:attachment
in:sent to:client@example.com older_than:1m

Una 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 el threadId tiene 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-To y References: el primer id de esa cadena es la raíz del hilo. Esos threadId empiezan por ref:.

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_HOST en .env con tu propio hostname; ese es el único sitio donde aparece el dominio, así que el archivo compose permanece intacto.

  • La red proxy debe 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=true permite que Express confíe en las cabeceras X-Forwarded-*.

  • Termina TLS en Traefik. Los tokens viajan como credenciales bearer; sin HTTPS van en claro.

  • El volumen postbus-data contiene la base de datos con todas las contraseñas de aplicación cifradas. Haz una copia de seguridad junto con MASTER_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:

  1. Amplía ProviderId y la unión MailAccount en src/types.ts.

  2. Escribe src/providers/<name>/provider.ts con una clase que implemente la interfaz.

  3. Añade una línea al mapa en src/providers/registry.ts.

  4. Asegúrate de que una cuenta de ese tipo pueda llegar a la base de datos: un save<Name>Account() en src/db/accounts.ts (los secretos pasan por encryptSecret), además de una forma de vincularla: una herramienta adicional junto a add_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.

  1. Crea un proyecto en https://console.cloud.google.com.

  2. APIs & Services → Library → busca "Gmail API" → Enable.

  3. APIs & Services → OAuth consent screen → selecciona External → rellena un nombre y un correo de soporte.

  4. Añade las direcciones que planeas vincular en Test users.

  5. Credentials → Create credentials → OAuth client ID → selecciona Desktop app.

  6. Pon el client id y el secret en .env:

    GOOGLE_CLIENT_ID=xxxxxxxxxxxx.apps.googleusercontent.com
    GOOGLE_CLIENT_SECRET=GOCSPX-xxxxxxxxxxxxxxxxxxxx
    OAUTH_CALLBACK_PORT=53682
  7. Vincula 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.0

Luego 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-RAW y X-GM-THRID solo 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.

A
license - permissive license
Not graded
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

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

  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables 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
  • A
    license
    B
    quality
    B
    maintenance
    Enables 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.
    1
    4
    MIT
  • A
    license
    A
    quality
    B
    maintenance
    MCP server that enables email management (send, read, search, delete, etc.) via IMAP/SMTP, compatible with Gmail, Outlook, Yahoo, iCloud, and other standard mail servers.
    11
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Exposes any IMAP mailbox and SMTP relay as MCP tools, enabling email management (read, search, send, delete) through MCP-compatible agents.
    MIT

View all related MCP servers

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.

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/HalloSouf/postbus-mcp'

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