Skip to main content
Glama
HalloSouf

postbus-mcp

by HalloSouf

postbus-mcp

Code quality Docker License: MIT

Ein selbst gehosteter MCP-Server, der dir und einer Handvoll Leute in deinem Umfeld ermöglicht, von Claude oder einem anderen MCP-Client aus mit euren Postfächern zu arbeiten: durchsuchen, ganze Konversationen lesen und E-Mails senden.

Funktioniert mit jedem IMAP/SMTP-Anbieter — Gmail, Outlook, Fastmail, dem eigenen Mailserver — und einem einfachen App-Passwort. Kein Google-Cloud-Projekt, keine OAuth-Verifizierung, keine Begrenzung auf Testnutzer.

Eine Instanz bedient mehrere Benutzer. Jeder bekommt ein eigenes API-Token und sieht nur die eigenen Postfächer. Du hostest sie, du gibst die Tokens aus; eine offene Registrierung gibt es nicht.

Claude / MCP client
        │  Authorization: Bearer <token>
        ▼
   POST /mcp  ──►  postbus-mcp  ──►  SQLite (users + encrypted app passwords)
                        │
                        ├──►  IMAP  (imapflow)      search, read, threads
                        └──►  SMTP  (nodemailer)    sending

Inhalt


Related MCP server: simple-email-mcp

Funktionsweise

Multi-Tenant, aber klein. Eine SQLite-Datei mit zwei Tabellen: users (ID plus ein Hash des API-Tokens) und mail_accounts (die Postfächer jedes Benutzers, mit verschlüsseltem App-Passwort). Kein separater Datenbankdienst nötig.

Die Isolation steckt in der Abfrage, nicht in einer anschließenden Prüfung. Jede MCP-Sitzung gehört zu genau einem Benutzer, bestimmt durch das Bearer-Token. Der MCP-Server wird pro Anfrage um diesen Benutzer herum aufgebaut, und jede Datenbankabfrage führt das user_id in ihrem WHERE mit. Das Alias eines anderen existiert in deiner Sitzung schlicht nicht.

Provider-Schnittstelle. Die Tool-Ebene spricht mit einem generischen MailProvider und weiß nichts über IMAP oder Gmail. ImapSmtpProvider ist die Hauptimplementierung, optional ergänzt durch einen GmailApiProvider. Einen dritten Anbieter hinzuzufügen erfordert keine Änderungen an den Tools — siehe Einen Provider hinzufügen.


Schnellstart

Mit Docker (empfohlen)

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"

Der letzte Befehl gibt ein API-Token genau einmal aus. Speichere es sofort.

Lokal mit Node (22 oder neuer)

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

Der Server lauscht auf http://localhost:3000/mcp. Ein GET /health liefert {"status":"ok"} — praktisch für einen Uptime-Check.


Benutzer und Tokens

Tokens gibst du selbst aus; eine Selbstregistrierung gibt es nicht.

Befehl

Was er tut

npm run add-user -- "Name"

Erstellt einen Benutzer und gibt das Token aus (einmalig)

npm run list-users

Zeigt Benutzer, Postfach-Anzahl und Status

npm run rotate-token -- <id>

Neues Token; das alte funktioniert sofort nicht mehr

npm run remove-user -- <id>

Löscht den Benutzer und alle seine Postfächer

In Docker führst du dieselben Skripte als node dist/cli/<script>.js aus:

docker compose exec postbus node dist/cli/list-users.js
docker compose exec postbus node dist/cli/rotate-token.js WvDnhafdM5yQ

Gespeichert wird nur ein SHA-256-Hash jedes Tokens; ein verlorenes Token kann also nicht nachgeschlagen werden — rotiere es stattdessen.


Deinen Client verbinden

Claude Desktop

Claude Desktop spricht stdio, also setze mcp-remote dazwischen. In claude_desktop_config.json:

{
  "mcpServers": {
    "postbus": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://mcp.example.com/mcp",
        "--header",
        "Authorization: Bearer pb_YOUR_TOKEN_HERE"
      ]
    }
  }
}

Die Datei liegt unter ~/Library/Application Support/Claude/claude_desktop_config.json auf macOS und %APPDATA%\Claude\claude_desktop_config.json auf Windows. Starte Claude Desktop nach dem Bearbeiten neu.

Claude Code

claude mcp add --transport http postbus https://mcp.example.com/mcp \
  --header "Authorization: Bearer pb_YOUR_TOKEN_HERE"

Andere Clients

Alles, was Streamable HTTP spricht, funktioniert: Endpunkt POST /mcp, Token als Authorization: Bearer <token>. Der Server arbeitet zustandslos — keine Session-IDs, kein serverseitiger Stream — daher liefert ein GET /mcp absichtlich 405.


Ein Postfach verknüpfen

Das machst du im Gespräch, mit deinem eigenen Token. Kein Terminal nötig:

Verknüpfe mein Gmail als "personal", Adresse souf@gmail.com, App-Passwort abcd efgh ijkl mnop

Claude ruft dann add_mail_account auf. Die Verbindung wird zuerst getestet (sowohl IMAP als auch SMTP); gespeichert wird erst, wenn beides funktioniert.

Ein App-Passwort erstellen

Anbieter

Wo

Hinweis

Gmail / Workspace

https://myaccount.google.com/apppasswords

Erfordert 2FA auf dem Konto

Outlook / Microsoft 365

https://account.microsoft.com/security

Erfordert 2FA; ein Admin kann IMAP blockieren

Fastmail

Settings → Privacy & Security → App passwords

Wähle "Mail (IMAP/SMTP)"

iCloud

https://account.apple.com → App-specific passwords

Erfordert 2FA

Eigener Server

n/a

Dein Mail-Passwort oder ein dediziertes Konto

Verwende niemals dein normales Passwort, wenn der Anbieter App-Passwörter anbietet.

Host und Port

Bei bekannten Anbietern trägt postbus-mcp diese Angaben selbst ein — du lieferst nur Alias, E-Mail und App-Passwort:

Gmail, Google Workspace, Outlook, Hotmail, Microsoft 365, Fastmail, iCloud, Yahoo, Zoho, Proton (via Bridge).

Für alles andere gibst du sie selbst an:

imap_host: imap.yourdomain.com    imap_port: 993   (TLS)
smtp_host: smtp.yourdomain.com    smtp_port: 465   (TLS) or 587 (STARTTLS)

Die Ports 993 und 465 verwenden TLS ab dem ersten Byte; auf anderen Ports wird STARTTLS verwendet, wenn der Server es anbietet. Wenn diese Annahme für deinen Server nicht zutrifft, übergib imap_secure oder smtp_secure ausdrücklich.


Verfügbare Tools

Tool

Was es tut

list_accounts

Listet deine Postfächer mit Alias und E-Mail-Adresse auf

add_mail_account

Verknüpft ein IMAP/SMTP-Postfach mit einem App-Passwort (testet zuerst die Verbindung)

remove_mail_account

Hebt die Verknüpfung eines Postfachs auf und löscht das gespeicherte App-Passwort

search_emails

Durchsucht mit Gmail-ähnlicher Syntax; liefert pro Nachricht eine id und eine threadId

get_message

Voller Inhalt einer Nachricht: Header, Textkörper, Metadaten zu Anhängen

get_thread

Jede Nachricht einer Konversation, älteste zuerst

send_email

Sendet sofort eine neue Nachricht (cc, bcc, reply-to, html)

Jedes Tool greift ausschließlich auf Postfächer zu, die zum Benutzer hinter dem Token gehören.


Suchsyntax

search_emails verwendet Gmail-ähnliche Syntax. Bei Gmail-Postfächern geht deine Abfrage unverändert an Gmail (über X-GM-RAW); was also in der Gmail-Suchleiste funktioniert, funktioniert auch hier. Bei anderen IMAP-Servern wird sie übersetzt:

Begriff

Gmail

Anderes 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

✅ (danach gefiltert)

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

✅ (über SPECIAL-USE)

-from:someone (ausschließen)

"exact phrase" und lose Wörter

⚠️ ein kombinierter Textbegriff

label:, filename:, category:

❌ ignoriert

Beispiele:

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

Eine leere Abfrage liefert die neuesten Nachrichten im Posteingang.


Threads

Jedes search_emails-Ergebnis trägt eine threadId, und get_thread nutzt sie, um die gesamte Konversation zu holen — chronologisch, mit Absender, Betreff, Datum und Textkörper pro Nachricht.

Wie das geschieht, hängt davon ab, was der Server kann; es gibt zwei Wege:

  • Gmail (X-GM-THRID) und RFC 8474-Server (OBJECTID) vergeben selbst eine stabile Thread-ID. Wir nutzen sie direkt, und die threadId sieht dann wie srv:1829384756 aus.

  • Alle anderen IMAP-Server kennen keine Threads. Dort rekonstruieren wir die Konversation aus den Standard-Headern Message-ID, In-Reply-To und References: Die erste ID in dieser Kette ist die Wurzel des Threads. Diese threadId-Werte beginnen mit ref:.

Beim Abrufen schauen wir in den Ordner "all mail", falls der Server einen hat, andernfalls in Inbox, Gesendet und Archiv — so landen auch deine eigenen Antworten in der Konversation.


Bereitstellung hinter Traefik

docker-compose.yml in diesem Repository ist ein funktionierendes Beispiel. Der Kern daraus:

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"

Zu beachten:

  • Setze PUBLIC_HOST in .env auf deinen eigenen Hostnamen; nur dort taucht die Domain auf, die Compose-Datei selbst bleibt also unangetastet.

  • Das Netzwerk proxy muss existieren (docker network create proxy), und Traefik muss daran angeschlossen sein.

  • Der Container gibt selbst keinen Port frei: Nur Traefik kann ihn erreichen.

  • TRUST_PROXY=true lässt Express den X-Forwarded-*-Headern vertrauen.

  • Terminiere TLS an Traefik. Tokens reisen als Bearer-Zugangsdaten; ohne HTTPS sind sie im Klartext.

  • Das Volume postbus-data enthält die Datenbank mit jedem verschlüsselten App-Passwort. Sichere es zusammen mit dem MASTER_KEY — getrennt aufbewahrt.


Sicherheit

MASTER_KEY. App-Passwörter und Refresh-Tokens werden mit AES-256-GCM gespeichert, jedes mit einem eigenen IV. Ohne den Schlüssel weigert sich der Server zu starten. Verlierst du ihn, müssen alle ihre Postfächer neu verknüpfen; bewahre ihn daher getrennt vom Datenbank-Backup auf.

Tokens. Gespeichert wird nur der SHA-256-Hash. Teile sie über einen Kanal, dem du vertraust, und rotiere sie im Zweifel (npm run rotate-token).

Isolation. Jede Abfrage auf mail_accounts filtert nach user_id, und der MCP-Server wird pro Anfrage um einen einzelnen Benutzer herum aufgebaut. Es gibt also keinen Session-Speicher, der Personen vermischen könnte.

Was das nicht ist. Kein Rate-Limiting, kein Audit-Log, keine feingranularen Berechtigungen. Das ist für eine Handvoll Leute gebaut, die du kennst, hinter TLS. Öffne es nicht für ein unbekanntes Publikum.


Einen Provider hinzufügen

Die Tool-Ebene spricht ausschließlich mit MailProvider aus 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>;
}

Ein Provider erhält ein vollständig aufgelöstes Konto; die Zugangsdaten sind entschlüsselt. Die Alias-Auflösung geschieht in der Tool-Ebene, sodass ein Provider nicht über den Benutzer der Sitzung hinausgreifen kann.

So fügst du einen hinzu:

  1. Erweitere ProviderId und die MailAccount-Union in src/types.ts.

  2. Schreibe src/providers/<name>/provider.ts mit einer Klasse, die das Interface implementiert.

  3. Füge der Map in src/providers/registry.ts eine Zeile hinzu.

  4. Stelle sicher, dass ein Konto dieses Typs die Datenbank erreichen kann: ein save<Name>Account() in src/db/accounts.ts (Geheimnisse laufen über encryptSecret), plus eine Möglichkeit, eines zu verknüpfen — ein zusätzliches Tool neben add_mail_account oder ein CLI-Skript.

Die vorhandenen Tools (search_emails, get_message, get_thread, send_email) benötigen keine Änderungen. Siehe auch CONTRIBUTING.md.


Optional: Gmail über die API statt IMAP

Das Repository enthält einen zweiten Provider, der über die Gmail API auf Gmail zugreift, statt über IMAP/SMTP. Du wirst ihn fast nie brauchen — IMAP mit einem App-Passwort erledigt dieselbe Aufgabe mit deutlich weniger Aufwand. Er ist nur nützlich, wenn deine Organisation IMAP blockiert, die API aber erlaubt.

  1. Erstelle ein Projekt unter https://console.cloud.google.com.

  2. APIs & Dienste → Bibliothek → suche nach „Gmail API“ → Aktivieren.

  3. APIs & Dienste → OAuth-Zustimmungsbildschirm → wähle Extern aus → trage einen Namen und eine Support-E-Mail ein.

  4. Füge die Adressen, die du verknüpfen möchtest, unter Testnutzer hinzu.

  5. Anmeldedaten → Anmeldedaten erstellen → OAuth-Client-ID → wähle Desktop-App aus.

  6. Trage die Client-ID und das Secret in .env ein:

GOOGLE_CLIENT_ID=xxxxxxxxxxxx.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=GOCSPX-xxxxxxxxxxxxxxxxxxxx
OAUTH_CALLBACK_PORT=53682
  1. Verknüpfe ein Postfach. Dies läuft auf dem Rechner des Administrators, weil Google den Callback an localhost sendet:

npm run list-users                       # look up the user id
npm run link-gmail -- <user-id> work

Verwendete Scopes: gmail.readonly, gmail.send, gmail.compose, gmail.labels.

Hinweis: Solange der OAuth-Zustimmungsbildschirm auf Testmodus gesetzt ist, laufen Refresh-Tokens nach 7 Tagen ab und du musst das Konto erneut verknüpfen. Das endet erst, wenn der Zustimmungsbildschirm auf In Produktion umgestellt wird, was für diese Scopes eine Google-Verifizierung erfordert. Genau deshalb ist IMAP mit einem App-Passwort der Hauptweg.


Entwicklung

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/

Die Tests in tests/ laufen in einer halben Sekunde und berühren nichts außerhalb des Prozesses: SQLite läuft im Speicher und keine Verbindung verlässt die Maschine. Sie decken die Logik ab, die unbemerkt schiefgehen kann — Übersetzung von Suchabfragen, Kodierung von Nachrichten- und Thread-IDs, Parsen und Zusammenstellen von MIME, verschlüsselte Speicherung, die Trennung zwischen Benutzern und die Bearer-Middleware.

Was sie nicht abdecken, ist die Kommunikation mit einem echten Mailserver. Führe dazu GreenMail lokal aus:

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

Verknüpfe dann ein Postfach mit imap_host: 127.0.0.1, imap_port: 3143, smtp_host: 127.0.0.1, smtp_port: 3025, username: souf, app_password: secret.

GreenMail unterstützt keine Gmail-Erweiterungen. Der Codezweig, der X-GM-RAW und X-GM-THRID verwendet, kann nur gegen ein echtes Gmail-Postfach getestet werden.

GitHub Actions führt bei jedem Push und Pull-Request dieselben Prüfungen aus: Formatierung, Typen, npm audit über die Produktionsabhängigkeiten, die Tests und ein Docker-Build, der den Container startet und überprüft, dass /health antwortet und /mcp ohne Token 401 zurückgibt. CI und der Container laufen beide mit Node 24, dem aktuellen LTS.

Projektstruktur

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/

Lizenz

MIT — siehe 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