postbus-mcp
postbus-mcp
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) sendingInhalt
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 startDer 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 |
| Erstellt einen Benutzer und gibt das Token aus (einmalig) |
| Zeigt Benutzer, Postfach-Anzahl und Status |
| Neues Token; das alte funktioniert sofort nicht mehr |
| 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 WvDnhafdM5yQGespeichert 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 | Erfordert 2FA auf dem Konto | |
Outlook / Microsoft 365 | 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 |
| Listet deine Postfächer mit Alias und E-Mail-Adresse auf |
| Verknüpft ein IMAP/SMTP-Postfach mit einem App-Passwort (testet zuerst die Verbindung) |
| Hebt die Verknüpfung eines Postfachs auf und löscht das gespeicherte App-Passwort |
| Durchsucht mit Gmail-ähnlicher Syntax; liefert pro Nachricht eine |
| Voller Inhalt einer Nachricht: Header, Textkörper, Metadaten zu Anhängen |
| Jede Nachricht einer Konversation, älteste zuerst |
| 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 |
| ✅ | ✅ |
| ✅ | ✅ |
| ✅ | ✅ |
| ✅ | ✅ |
| ✅ | ✅ |
| ✅ | ✅ (danach gefiltert) |
| ✅ | ✅ (über SPECIAL-USE) |
| ✅ | ✅ |
| ✅ | ⚠️ ein kombinierter Textbegriff |
| ✅ | ❌ ignoriert |
Beispiele:
from:boss@company.com is:unread newer_than:7d
subject:"march invoice" has:attachment
in:sent to:client@example.com older_than:1mEine 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 diethreadIdsieht dann wiesrv:1829384756aus.Alle anderen IMAP-Server kennen keine Threads. Dort rekonstruieren wir die Konversation aus den Standard-Headern
Message-ID,In-Reply-ToundReferences: Die erste ID in dieser Kette ist die Wurzel des Threads. DiesethreadId-Werte beginnen mitref:.
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_HOSTin.envauf deinen eigenen Hostnamen; nur dort taucht die Domain auf, die Compose-Datei selbst bleibt also unangetastet.Das Netzwerk
proxymuss 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=truelässt Express denX-Forwarded-*-Headern vertrauen.Terminiere TLS an Traefik. Tokens reisen als Bearer-Zugangsdaten; ohne HTTPS sind sie im Klartext.
Das Volume
postbus-dataenthält die Datenbank mit jedem verschlüsselten App-Passwort. Sichere es zusammen mit demMASTER_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:
Erweitere
ProviderIdund dieMailAccount-Union insrc/types.ts.Schreibe
src/providers/<name>/provider.tsmit einer Klasse, die das Interface implementiert.Füge der Map in
src/providers/registry.tseine Zeile hinzu.Stelle sicher, dass ein Konto dieses Typs die Datenbank erreichen kann: ein
save<Name>Account()insrc/db/accounts.ts(Geheimnisse laufen überencryptSecret), plus eine Möglichkeit, eines zu verknüpfen — ein zusätzliches Tool nebenadd_mail_accountoder 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.
Erstelle ein Projekt unter https://console.cloud.google.com.
APIs & Dienste → Bibliothek → suche nach „Gmail API“ → Aktivieren.
APIs & Dienste → OAuth-Zustimmungsbildschirm → wähle Extern aus → trage einen Namen und eine Support-E-Mail ein.
Füge die Adressen, die du verknüpfen möchtest, unter Testnutzer hinzu.
Anmeldedaten → Anmeldedaten erstellen → OAuth-Client-ID → wähle Desktop-App aus.
Trage die Client-ID und das Secret in
.envein:
GOOGLE_CLIENT_ID=xxxxxxxxxxxx.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=GOCSPX-xxxxxxxxxxxxxxxxxxxx
OAUTH_CALLBACK_PORT=53682Verknüpfe ein Postfach. Dies läuft auf dem Rechner des Administrators, weil Google den Callback an
localhostsendet:
npm run list-users # look up the user id
npm run link-gmail -- <user-id> workVerwendete 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.0Verknü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-RAWundX-GM-THRIDverwendet, 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.
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