mailwarden
mailwarden
Ein zuverlässiger, nativer Gmail MCP Server – vollständige Mailbox-Triage für KI-Assistenten, mit der Funktion, die kein anderer Gmail MCP Server bietet: Mailbox-seitiges Snooze.
Highlights
Snooze – das einzige Mailbox-seitige Snooze in einem Gmail MCP Server. Archiviere einen Thread jetzt und lasse ihn an einem Datum wieder im Posteingang auftauchen. Basiert auf datierten Labels + einem Sweep, sodass es von jedem Client aus funktioniert, in Gmail selbst sichtbar ist und Neustarts übersteht. (Wenn ein anderer Server ein "Snooze" anbietet, handelt es sich um eine lokale Erinnerungsliste – die E-Mail verlässt den Posteingang nie oder kehrt nicht zurück.)
Suche, der Sie vertrauen können. Gmail's
threads.list– der Aufruf, den jede Thread-Suche durchläuft – kannis:unreadaus einem veralteten Thread-Level-Lesestatus beantworten: In einem echten Postfach gemessen, enthielten 86% der zurückgegebenen Threads überhaupt keine ungelesene Nachricht; in einem zweiten Postfach gab es keinerlei Abweichung. Man kann ohne Nachschauen nicht sagen, in welchem Postfach man sich befindet, daher überprüftsearchjeden Treffer erneut anhand seiner Live-Labels. Paginiert überpageToken/nextPageToken.Massenoperationen, die skalieren.
bulk_modifyarchiviert/beschriftet alles, was einer Abfrage entspricht, mit 1000 Nachrichten pro API-Anfrage – mit teilweiser Erfolgsmeldung pro Block anstatt alles-oder-nichts. Der Snooze-Sweep verwendet denselben Batch-Pfad.Strukturierte Ausgaben. Jedes Werkzeug deklariert ein
outputSchemaund gibt validiertesstructuredContentzusammen mit eingerahmtem JSON-Text zurück – keine Rätselraten beim Parsen für Clients.Kleine Angriffsfläche. Keine Sende-Werkzeuge (kein Exfiltrationspfad für prompt-injizierte E-Mails), optionaler Nur-Lese-Modus, keine Telemetrie, standardmäßig keine offenen Ports, symlink-sichere Download-Begrenzung, injektionsgeschützte Ausgabe. Eine bewusste Ausnahme:
unsubscribe/bulk_unsubscribe(Verwaltungsstufe) kontaktieren den in einem Nachrichten-Header genannten Opt-out-Endpunkt – der einzige Nicht-Google-Host, den mailwarden jemals erreicht, und eineread-Stufen-Bereitstellung sendet überhaupt keine ausgehenden Anfragen. Details unter Sicherheit & Datenschutz und Abbestellen.Korrekt mit echten E-Mails. RFC 2047-Header dekodiert (
=?UTF-8?B?…?=→ lesbarer Text), Body in ihrem deklarierten Zeichensatz dekodiert (kein Mojibake für ISO-8859-1/Shift_JIS-Mail), 429/5xx mit exponentiellem Backoff wiederholt.
Related MCP server: Gmail MCP
Warum
Konnektoren, die Ihre Mailbox synchronisieren oder cachen, können hinterherhinken – und sogar Gmail's eigener Suchindex ist manchmal ungenau (siehe unten). mailwarden spricht direkt mit der Live-Gmail-API (kein zwischengespeicherter Schnappschuss) und überprüft, was der Index zurückgibt, sodass das, was Sie sehen, auch tatsächlich vorhanden ist. Es ist eine generische Gmail-Funktionsebene – behalten Sie Ihre eigenen Regeln/Logik in Ihrem KI-Client, nicht im Server.
search geht einen Schritt weiter als die rohe API: Gmail's threads.list-Index kann Lese-Zustands-Operatoren aus einer veralteten Kopie dieses Zustands beantworten, sodass is:unread Threads zurückgibt, die Sie vor Wochen fertig gelesen haben – in einem gemessenen Postfach die große Mehrheit der zurückgegebenen. Da jeder Treffer ohnehin live abgerufen wird, überprüft search die eindeutigen Prädikate (is:unread/is:read/is:starred/in:inbox/category:…, mit Negation) gegen die tatsächlichen Labels jedes Threads und verwirft die falsch positiven des Index.
Verglichen mit anderen Gmail MCP Servern
Die meisten Gmail MCP Server decken dieselbe Lese-/Label-/Sendeoberfläche ab. Zwei Fähigkeiten sind noch einzigartig für mailwarden (Mailbox-seitiges Snooze, Suche-Neuverifikation), und eine bewusste Auslassung ist ein Sicherheitsmerkmal, keine Lücke. Google's eigener Server ist auch enger als es aussieht: Nur-Entwurf, und kein Papierkorb, keine Filter oder Abbestellen.
Fähigkeit | mailwarden | ||||
Mailbox-seitiges Snooze – jetzt archivieren, zu einem Datum/Uhrzeit oder voreingestellt im Posteingang wieder auftauchen | ✅ | — | — | — | — |
Suchergebnis-Neuverifikation – verwirft die falsch positiven des Thread-Index gegen Live-Labels | ✅ | — | — | — | — |
Sweep / Bulk über eine Abfrage – eine Aktion über jeden Thread, den eine Suche zurückgibt | ✅ 1000/req, partial-success | — | ⚠️ batch by explicit ids | — | ⚠️ batch by explicit ids |
Abbestellen – Übersicht pro Absender + RFC 8058 One-Click-Opt-out, kein Sendebereich erforderlich | ✅ | — | ⚠️ header shown, no action | — | — |
Posteingang-Triage-Übersicht – ein Aufruf, der das Wartende in Eimer einteilt | ✅ sender/label/age + header signals | — | — | ✅ heuristic flags + stats | — |
Serverseitige Filter – Regeln, die ohne Assistenten im Kreislauf weiter triagieren | ✅ never forwarding | — | ✅ | — | ✅ |
Keine Sende-Werkzeuge – mit Absicht – eine prompt-injizierte E-Mail hat keinen Exfiltrationspfad | ✅ no compose at all | ⚠️ draft-only | ❌ sends | ❌ sends | ❌ sends |
Werkzeugstufen mit geringsten Privilegien – OAuth-Bereiche, die von den aktivierten Werkzeugen abgeleitet werden | ✅ | ⚠️ scope split | — | — | ⚠️ inverse: tools gated by granted scopes |
Token-Verschlüsselung im Ruhezustand (optional) | ✅ AES-256-GCM | n/a (hosted) | ✅ | — | — |
Keine Anbieter-Cloud – Sie betreiben den Server | ✅ | ❌ Google-hosted | ✅ | ✅ | ✅ |
Strukturierte Ausgaben – jedes Werkzeug deklariert ein | ✅ | — | — | — | — |
Stand: 16. August 2026, aus den öffentlichen Dokumentationen und Quellen jedes Projekts; — = nicht angeboten / nicht dokumentiert. Die Spalten sind die Server, die ein Leser am ehesten finden wird – Googles Erstanbieter-Server, plus die beiden größten Community-Server – und klodr, der dem eigenen Least-Privilege-Design von mailwarden am nächsten kommt. Die Sendefähigkeit ist als Sicherheitseigenschaft aufgeführt: Das Fehlen bei mailwarden ist beabsichtigt (siehe Sicherheit & Datenschutz). Die letzte Zeile fragt, wer den Server betreibt, nicht wo er zufällig läuft: Self-Hosting ist hier gemeinsamer Nenner, und jeder Community-Server in dieser Tabelle bietet eine Fernbereitstellung außer klodr (nur stdio) – mailwarden über --http, taylorwilsdon über streamable HTTP mit OAuth 2.1, a-bonus auf Cloud Run. Einen von ihnen auf Ihrem eigenen Host zu betreiben, ist keine Cloud-Kopie; ihn auf dem des Anbieters zu betreiben, schon.
Der Burggraben ist keine einzelne Zeile – es ist Snooze + Live-Neuverifikation zusammen: eine tatsächliche Posteingang-Workflow-Ebene, die auf dem aktuellen Zustand der Mailbox handelt, nicht auf einem zwischengespeicherten Schnappschuss. Wo andere aufgeholt haben, ist es oben ehrlich vermerkt: Ruheverschlüsselung (taylorwilsdon), bereichsgesteuertes Werkzeug-Gating (klodr), eine reichhaltige per-Nachricht Triage-Heuristik (a-bonus) und Bulk-Organisation über eine Mailbox (das gehostete mcpemails.com, das auch kein Snooze hat). Was keiner von ihnen tut, ist auf eine Abfrage zu reagieren und die Antwort der Mailbox zu überprüfen, bevor darauf reagiert wird.
Warum Neuverifikation wichtig ist – ein konkretes Beispiel
Bitten Sie einen Assistenten, "die ungelesene Werbepost, die bereits meinen Posteingang umgangen hat, zu archivieren", und er wird nach der offensichtlichen Abfrage greifen, category:updates is:unread -in:inbox. Ein Server, der Gmail's Index vertraut, archiviert nun Threads, die Sie bereits gelesen haben – E-Mails, die Sie nie berühren wollten, weg in einer Massenaktion, die Sie nicht einfach rückgängig machen können.
Gemessen, nicht behauptet. Ein echtes Postfach (~70.000 Nachrichten), 15.08.2026, schreibgeschützt:
Abfrage ( | Zurückgegebene Threads | Mit ungelesener Nachricht | Veraltet |
| 131 | 17 | 87% |
| 128 | 14 | 89% |
| 235 | 99 | 58% |
Der Index ignoriert das Prädikat nicht — dieselbe Abfrage ohne is:unread liefert 800+ Threads, es wird also angewendet. Es wird gegen einen Thread-Lesestatus angewendet, der nicht nachgezogen hat: Threads, deren jede Nachricht gelesen ist, gelten dort immer noch als ungelesen. Ein zurückgegebener Thread trug ein einziges Label, SENT. Und es ist keine Eigenheit exotischer Operator-Kombinationen: Die schlichteste der drei Abfragen zeigt es ebenfalls — mit dem niedrigsten Anteil (58%), aber den meisten falschen Threads in absoluten Zahlen (136).
Es ist speziell der Thread-Index. Dieselbe Abfrage, dasselbe Postfach, dieselbe Minute, über messages.list statt dessen gestellt: 19 Nachrichten, keine veraltet. Das ist also nicht "Gmail-Suche ist unzuverlässig" — sondern dass die Thread-Ansicht des Lesestatus hinterherhinkt, während die Einzelnachricht-Ansicht es nicht tut. search läuft über threads.list, genau deshalb verifiziert es erneut.
Ein zweites Postfach, am selben Tag auf dieselbe Weise gemessen, driftete überhaupt nicht — null Roh-Index-Treffer für is:unread, obwohl es über die API viele Male am Tag als gelesen markiert wird. Das ist also eine Eigenschaft eines Postfachs, nicht von Gmail überall. Was sie unterscheidet, ist offen: Sie unterscheiden sich im Volumen (etwa drei Größenordnungen) und im Alter, und dem zweiten fehlt etwas Grundlegenderes — kein Thread darin wurde jemals archiviert, während er noch ungelesen war, was die einzige Form ist, in der ein veralteter Lesestatus auftreten kann. Es ist also kein Gegenbeispiel zu einer bestimmten Ursache; es ist ein Postfach ohne den Kandidaten.
Genau das ist der Punkt: Ein Server kann nicht wissen, in welcher Art von Postfach er sich befindet. Erneute Verifizierung kostet nichts, wo nichts driftet, und rettet dich, wo es driftet — in der obigen Messung war jeder Thread, den search verworfen hat, wirklich gelesen, und es hat keine wirklich ungelesene Mail verworfen.
Wo es nicht kostenlos ist: die Massenwerkzeuge. search verifiziert erneut, weil es ohnehin jeden Treffer abruft; bulk_modify (und der applyToExisting-Durchlauf von create_filter) ist auf Tausende von Nachrichten ausgelegt, wo ein Abruf pro Treffer eine andere Größenordnung an Kosten darstellt. Diese arbeiten auf dem, was der Index zurückgibt — sie melden daher jetzt unverifiedPredicates, die Bedingungen aus deiner Abfrage, die beim Wort des Index genommen wurden (+UNREAD, -INBOX, …). Leer bedeutet, dass es nichts gab, dem man misstrauen musste. Nicht leer und das Ergebnis muss lesestatus-genau sein? Löse die Menge zuerst mit search auf und arbeite mit diesen Thread-IDs. Ein dryRun schließt diese Lücke nicht: Er liest denselben Index erneut, bestätigt also nur, wie groß die Menge ist, nie, ob sie richtig ist.
mailwarden ruft ohnehin jeden Treffer live ab, also überprüft search die eindeutigen Prädikate (is:unread, is:read, in:inbox, category:…, mit Negation) gegen die echten Labels jedes Threads und verwirft die falsch-positiven des Index, bevor irgendein Werkzeug sie sieht. Die Massenaktion läuft dann genau auf der Menge, die du angefordert hast. Das ist der Unterschied zwischen dem Handeln nach dem, was Gmail indiziert hat, und dem Handeln nach dem, was tatsächlich gerade im Postfach ist — und warum Snooze/Sweep sicher einem Assistenten übergeben werden können: Der Sweep holt nur Threads hervor, deren Snooze wirklich fällig ist, verifiziert gegen Live-Labels zur Laufzeit.
Sieh es selbst — kein Gmail-Konto nötig. Aus einem Klon des Repos (die Demo ist ein reines Repo-Verifizierungsskript, nicht Teil des npm-Pakets):
git clone https://github.com/csitte/mailwarden && cd mailwarden
npm install && npm run build
node scripts/demo-reverify.mjsDaneben gibt es ein zweites Skript, node scripts/probe-reverify.mjs, das dasselbe in deinem Postfach misst statt in einem gefälschten — schreibgeschützt, nur Metadaten (kein Betreff, Absender oder Text wird abgerufen), druckt Zählungen und Labelnamen. So wurden die obigen Zahlen erzeugt, und so kannst du prüfen, ob dein Postfach überhaupt driftet.
Die Demo treibt die echte search() gegen eine gefälschte Gmail-API, deren Index absichtlich veraltet ist (gibt einen gelesenen Thread für eine is:unread-Abfrage zurück, genau wie Gmail) und zeigt, wie mailwarden den falsch-positiven verwirft. Sie bestätigt das Ergebnis, beendet sich also mit einem Fehlercode, wenn das Verhalten je zurückfällt. Derselbe Fall ist durch Unit-Tests in test/gmail.test.ts abgesichert ("verwirft Index-Falschpositive durch Live-Label-Neuverifizierung").
Werkzeuge
Tool | Was es macht |
| Gmail-Abfragesyntax → Thread-Zusammenfassungen (von/Betreff/Datum/Labels/Auszug); Lese-/Kategorie-Prädikate werden gegen die Live-Labels jedes Treffers erneut geprüft; paginiert via |
| Vollständiger Thread: Header, Klartext- + HTML-Bodies, Metadaten zu Anhängen |
| Alle Labels (System + Benutzer) |
| Adresse des verbundenen Kontos + Gesamtzahl der Nachrichten/Threads — bestätigt, welches Postfach verdrahtet ist, bevor gehandelt wird |
| Strukturierte Übersicht über einen Postfachausschnitt für Entscheidungen: Top-Absender (jeweils mit den Signalen, die seine Threads tragen), Label- und Alters-Buckets, ungelesene + Anhänge-Zählungen, und wie viele Threads Newsletter / automatisiert / Kalendereinladungen / Reply-to-Mismatches sind — statt einer rohen Thread-Liste |
| Welche Opt-Out-Optionen ein Thread bewirbt ( |
| Ein Postfachausschnitt gruppiert nach Absender: Thread-/Ungelesen-Zählungen, der Zeitraum, in dem jeder gesehen wurde, und die Opt-Out-Optionen jedes Absenders — ein Header-Fetch pro Absender, kontaktiert niemanden. |
| Ein Benutzerlabel erstellen (idempotent; verschachtelt via |
| Labels nach Name oder ID hinzufügen/entfernen — ein unbekannter Name in |
| Batch-Label-Änderungen für jede Nachricht, die einer Abfrage entspricht — 1000 Nachrichten pro API-Anfrage, Teilerfolg wird pro Chunk gemeldet (Thread-ID-Liste auf 500 begrenzt, |
| Komfort-Wrapper |
| In den Papierkorb verschieben / aus dem Papierkorb wiederherstellen |
| Einen Anhang in einem lokalen Pfad speichern (überschreibt nie — bei Kollisionen wird ein numerischer Suffix angehängt) |
| One-Click-Opt-Out (RFC 8058) unter Verwendung des Endpunkts aus dem eigenen Header der Nachricht — das einzige Tool, das einen nicht-Google-Host kontaktiert (Details) |
| Dasselbe für mehrere Threads, nacheinander und höchstens eine Anfrage pro Absender; Teilerfolg wird pro Thread gemeldet. |
| Jetzt archivieren, an/ab einem Datum ( |
| Eine Schlummerfunktion abbrechen, sofort zurück zum Posteingang |
| Alle schlummernden Threads + Fälligkeitsdaten |
| Threads, deren Schlummerzeit abgelaufen ist, wieder einblenden (auf Abruf, via Cron oder den Daemon); gebündelt mit Teilerfolgsmeldung. |
Alle Tools deklarieren ein outputSchema und geben strukturierten Inhalt (validiert, maschinenlesbar) zusammen mit demselben JSON als eingerahmten Text zurück – Clients müssen nie Prosa parsen.
So funktioniert „Snooze" (es gibt keine Gmail-API-Snooze-Funktion – wir bauen sie)
snooze entfernt INBOX und wendet ein datiertes Label MCP/Snoozed/<key> an, wobei der Schlüssel entweder YYYY-MM-DD (fällig den ganzen Tag) oder YYYY-MM-DDTHHMM (fällig zu dieser lokalen Minute) ist. Das Argument until akzeptiert ein explizites Datum, eine Datums+Uhrzeit (2026-06-20 9am, …T17:00) oder eine serverseitig aufgelöste Voreinstellung – today, tomorrow, weekend (nächster Samstag), next week (nächster Montag), einen Wochentagsnamen (monday–sunday, nächstes Vorkommen), in N days oder in N hours – und eine Datumsvoreinstellung kann eine nachgestellte Uhrzeit tragen (tomorrow 9am, monday 8:30), sodass der Aufrufer nie den Zeitpunkt selbst berechnen muss. sweep_snoozed findet fällige Labels und bringt diese Threads in den Posteingang zurück (als ungelesen markiert); ein zeitgesteuerter Snooze wird beim ersten Sweep zu oder nach seiner Minute aktiviert, sodass die Aufwecklatenz Ihrem Sweep-Intervall entspricht. Führen Sie den Sweep aus:
bedarfsgesteuert (
sweep_snoozed-Tool),via cron:
mailwarden --sweep,oder automatisch: setzen Sie
MAILWARDEN_AUTO_SWEEP=1(stündlicher Sweep, während der Server läuft).
Filter (dauerhafte automatische Triage-Regeln)
create_filter richtet eine serverseitige Gmail-Regel ein: E-Mails, die den Kriterien entsprechen, erhalten automatisch die angegebenen Label-Aktionen – das Postfach triagiert sich selbst, ohne dass ein Assistent eingreift.
Kriterien:
from,to,subject,query(vollständige Gmail-Suchsyntax),negatedQuery,hasAttachment,excludeChatsundsize+sizeComparison(smaller/larger, zusammen angegeben). Mindestens eines ist erforderlich.Aktionen (nur Label):
addLabels/removeLabels, nach Name oder ID (ein unbekannter Name inaddLabelswird automatisch erstellt, verschachtelt via/). Häufige Rezepte: Posteingang überspringen →removeLabels: ["INBOX"]; automatisch als gelesen markieren →removeLabels: ["UNREAD"]; automatisch in den Papierkorb →addLabels: ["TRASH"]; mit Stern markieren →addLabels: ["STARRED"]; nie Spam →removeLabels: ["SPAM"]; unter einem Label ablegen →addLabels: ["Receipts"].Vorhandene E-Mails: Ein Filter wirkt nur auf Nachrichten, die nach seiner Erstellung eingehen. Übergeben Sie
applyToExisting: true, um dieselben Aktionen einmalig auch auf bereits im Postfach vorhandene E-Mails anzuwenden – mailwarden erstellt eine Gmail-Suche aus den Kriterien und führt eine Bulk-Modifikation durch (bis zumaxMessages, Standard 1000; gleicher Vorbehalt bezüglich nicht verifiziertem Index wie beibulk_modify, und der einmalige Durchlauf schließt Spam/Papierkorb aus). Dies erfordert mindestens ein positives Kriterium (from/to/subject/query/hasAttachment:true/size): eine reine Ausschlussregel (negatedQueryoderhasAttachment:false) wird fürapplyToExistingabgelehnt, da sie fast das gesamte Postfach treffen würde – erstellen Sie einen solchen Filter ohne das Flag. Das Ergebnis wird unterappliedzurückgegeben (die verwendetequery, Zählungen vonmatchedMessages/modifiedMessages/modifiedThreadCount,capped, wenn die TreffermengemaxMessageserreicht hat, pro-Chunkfailedund einerror-String, falls der gesamte Durchlauf fehlschlug); es istnull, wennapplyToExistingnicht gesetzt wurde. Der Filter wird zuerst erstellt, sodass ein teilweiser oder fehlgeschlagener Nachhol-Durchlauf imappliedgemeldet, aber nicht als Fehler ausgelöst wird – die Regel bleibt bestehen.Kein Weiterleiten – siehe Security & privacy.
Erfordert den Bereich
gmail.settings.basic; führen Sie--autheinmal erneut aus, wenn Sie eine ältere Version autorisiert haben. Nicht im schreibgeschützten Modus verfügbar.
Abmelden – die eine ausgehende Anfrage
list_unsubscribe (Lese-Ebene) berichtet, was der Absender anbietet, ohne jemanden zu kontaktieren. Es liest die neueste Nachricht, die tatsächlich einen List-Unsubscribe-Header trägt – eine in einen Newsletter eingefügte Antwort steht am Ende und gibt nichts bekannt, was sonst als „Diese Liste hat keine Abmelde-Option" gelesen werden würde. list_subscriptions (Lese-Ebene) macht dasselbe für einen gesamten Ausschnitt, gruppiert nach Absender, sodass Sie sehen können, wer ständig schreibt und welche von ihnen tatsächlich verlassen werden können – ein Header-Abruf pro Absender statt pro Thread. unsubscribe und bulk_unsubscribe (Verwaltungsebene) handeln darauf basierend – und das ist die einzige Stelle, an der mailwarden jemals mit einem Host spricht, der nicht Google ist, daher sind die Regeln streng:
Es gibt keinen URL-Parameter. Der Endpunkt stammt aus dem Header der Nachricht selbst und nirgendwo anders. Ein URL-Argument würde es einer prompt-injizierten E-Mail ermöglichen, das Tool in einen Exfiltrationskanal zu verwandeln (Postfachinhalt in einer Abfragezeichenfolge); der Header kann keine vom Modell gewählten Daten transportieren.
Es wird nur RFC 8058 One-Click ausgeführt – der Absender muss über
List-Unsubscribe-Postzugestimmt haben. Ein einfacherhttps:-Link ist für einen Menschen im Browser gedacht und wird zurückgegeben, nicht abgerufen.mailto:-Abmeldungen werden nie durchgeführt. Sie erfordern das Senden einer E-Mail, was mailwarden nicht kann. Die Adresse wird gemeldet, damit Sie selbst handeln können.Feste Anfrage, verworfene Antwort. Der POST-Text ist immer
List-Unsubscribe=One-Clickund wird nie von etwas abgeleitet; der Antworttext wird ungelesen verworfen. Was an das Modell zurückgegeben wird, sind Statuscode und die tatsächlich aufgerufene URL – kein Inhalt vom Endpunkt, sodass er nicht mit Anweisungen antworten kann. (Eine 301/302/303-Umleitung wird als GET gefolgt, d.h. mit keinem Body.)Eine Anfrage pro Absender, sequenziell, innerhalb eines Budgets.
bulk_unsub scribeakzeptiert Thread-IDs (niemals eine Abfrage – eine abfragegesteuerte Bulk würde eine Anfrage pro passendem Absender auslösen, bevor jemand hinsieht). Threads von einem Absender, dessen Anfrage bereits gesendet wurde, werden alsduplicateOfgemeldet und verursachen keine zweite Anfrage: Zwei Threads einer Liste teilen sich eine Abmeldung, und zweimaliges Aufrufen bestätigt Ihre Adresse nur zweimal. Ein Absender wird erst aufgezeichnet, wenn eine Anfrage tatsächlich einen Endpunkt erreicht hat, sodass eine Ablehnung oder eine unterbrochene Verbindung dem nächsten Thread immer noch seinen eigenen Versuch läßt – und wenn der übersprungenen Thread einen anderren Endpunkt anpreist, sagt der Grund das, da ein Absender meherere Listen betreiben kann. Begrenzt auf 25 Threads und 60 Sekunden pro Aufruf; was das Budget nicht abdeckt, wird alsskippedOutOfTimezurückgegeben, nicht stillschweigend übergangen. Nichts davon kann rückgängig gemacht werden, weshalb alle drei Grenzen existieren.SSRF-Schutz. Nur https, nur Standardport, keine Anmeldedaten in der URL, und jeder Hop – einschließlich Weiterleitungen, maximal 3 – muss ausschließlich zu global erreichbaren Adressen auflösen. Die Prüfung parst jede Adresse in ihre Bytes und vergleicht sie mit dem IANA Special-Purpose-Registry, sodass jede Schreibweise derselben Adresse das gleiche Urteil erhält (
::1und0:0:0:0:0:0:0:1gleichermaßen); eine Adresse, die nicht parst, wird abgelehnt. DNS-Auflösung und alle Hops teilen sich ein 10-Sekunden-Budget. Nicht rebinding-sicher (fetchlöst erneut auf, wenn es verbindet) – siehe SECURITY.md; was diese Lücke überlebt, ist ein blindes POST, dessen Antwort nie gelesen wird.
Überprüfen Sie es an Ihrer eigenen E-Mail, bevor Sie ihm vertrauen. Aus einem Repository-Klon (nur Repository, nicht im npm-Paket), nach npm run build und mailwarden --auth:
node scripts/probe-unsubscribe.mjs --vet # category:promotions, 25 threads
node scripts/probe-unsubscribe.mjs "from:substack.com" --max 50 --vetEs druckt jeden echten List-Unsubscribe-Header neben das, was der Parser daraus gemacht hat, und --vet führt auch den Endpunkt durch die URL-Überprüfung und die Adresssperre – sodass Sie sowohl sehen, ob der Parser den Header verstanden hat, als auch ob die Wächter diese Abmeldung durchgelassen hätten. Streng schreibgeschützt: Es wird nie eine Anfrage an einen Absender gestellt, und nichts im Postfach ändert sich.
Was es nicht rückgängig machen kann: Die Anfrage teilt dem Absender mit, dass Ihre Adresse aktiv ist. Ein Absender, der seine eigene Abmeldeoption ignoriert, ist für jeden Client unerreichbar – kombinieren Sie unsubscribe mit create_filter oder trash für diese. Das Fehlen einer automatisierbaren Option wird als unsubscribed:false mit den Alternativen gemeldet, nicht als Fehler. Eine schreibgeschützte Bereitstellung erhält list_unsubscribe und list_subscriptions und stellt nie eine Anfrage.
Sicherheit & Datenschutz
Für das vollständige Bedrohungsmodell – Vertrauensgrenze, maßgeschneiderte Gegenmaßnahmen, explizite Nicht-Ziele und wie man eine Schwachstelle meldet – siehe SECURITY.md. Die Höhepunkte:
Keine Telemetrie. Es wird nichts nach Hause geschickt – keine Analysen, keine Absturzberichte, kein Tracking.
Standardmäßig keine offenen Ports. Nur stdio. Der optionale
--http-Listener bindet an127.0.0.1(nicht das LAN) und weigert sich zu starten, wenn keinMAILWARDEN_TOKEN-Bearer-Token vorhanden ist – setzen SieMAILWARDEN_ALLOW_NO_TOKEN=1, um dies in einem vertrauenswürdigen, isolierten Netzwerk zu überschreiben. Bei einer Loopback-Bindung wird auch derHost-Header validiert (DNS-Rebinding-Abwehr). Für Remote-Hosting setzen SieMAILWARDEN_HOSTund schützen es mit TLS.Keine Sende-Tools – bewusst so konzipiert. mailwarden kann keine E-Mails verfassen, beantworten oder weiterleiten. Eine prompt-injizierte Anweisung in einer E-Mail hat keinen Exfiltrationspfad über diesen Server.
create_filterfolgt derselben Regel: Es kann E-Mails labeln, archivieren, in den Papierkorb verschieben, mit Stern markieren oder als wichtig markieren, erstellt aber niemals einen Weiterleitungs-Filter (was ein Exfiltrationspfad wäre).list_filterszeigt dennoch alle Weiterleitungsfilter an, die bereits auf dem Konto vorhanden sind, sodass Sie sie erkennen können. Dies gilt, weil kein solches Tool existiert und keines zur Laufzeit registriert werden kann; für die stärkere Variante, bei der Google sich weigert zu senden, anstatt dass mailwarden ablehnt, siehe Read-only-Modus unten.Ein ausgehender Host, keine modellgewählte URL. Das
unsubscribe-Tool ist der einzige Codepfad, der einen Nicht-Google-Host kontaktiert. Sein Endpunkt wird aus demList-Unsubscribe-Header der Nachricht gelesen – niemals aus einem Tool-Argument – der Anfragebody ist fest und der Antwortbody wird verworfen, sodass er kein Datenkanal werden kann. Nur https/Standard-Port, Weiterleitungen werden erneut validiert, und jeder Hop, der zu einer privaten, Loopback-, Link-Local- oder Metadatenadresse auflöst, wird abgelehnt. Siehe Abmelden.Tool-Stufen (progressive Offenlegung + geringste Berechtigung).
MAILWARDEN_TOOLSbewirbt nur die Stufen, die Sie nennen –read(die Lese-Tools),manage(Postfach-Änderungen, Schlummern, Downloads),filters(serverseitige Filter-CRUD, die einzige Stufe, deren Toolsgmail.settings.basicbenötigen). Standardmäßig sind alle drei aktiviert; z. B.read,managebietet eine vollständige Triage-Oberfläche ohne Filterverwaltung. Die angeforderten OAuth-Bereiche bei--authwerden aus den aktivierten Stufen abgeleitet – eineread-Bereitstellung fragt nur nachgmail.readonly, undgmail.settings.basicwird nur angefordert, wenn diefilters-Stufe aktiv ist. Und die Filter-Tools werden automatisch ausgeblendet, wenn das gespeicherte Token keingmail.settings.basicenthält (z. B. ein Token, das autorisiert wurde, bevor Sie die Stufe aktiviert haben) – führen Sie--autherneut aus, um es zu gewähren. Ältere Tokens ohne aufgezeichneten Bereich werden wie zuvor beworben, mit der Laufzeitmeldung „unzureichender Bereich“ als Fallback.Read-only-Modus. Setzen Sie
MAILWARDEN_READONLY=1(Kurzform fürMAILWARDEN_TOOLS=read) und nur die Lese-Tools (search,get_thread,list_labels,list_snoozed,get_profile,triage_digest,list_unsubscribe,list_subscriptions) werden registriert – nichts, was das Postfach ändern oder Dateien schreiben kann, wird den Clients überhaupt angeboten (die Filter-Tools, die den breiterengmail.settings.basic-Bereich benötigen, sind ebenfalls ausgeschlossen). Empfohlen für gemeinsam genutzte/HTTP-Bereitstellungen, die nur Triage durchführen. Es ist auch die einzige Stufe, deren No-Send-Eigenschaft von Google erzwungen wird: sie hält eingmail.readonly-Token, das Gmail-Sende-Endpunkte kategorisch ablehnen.managebenötigtgmail.modify, und Gmail akzeptiert diesen Bereich tatsächlich zum Senden – mailwarden stellt nur kein Tool bereit, das dies tun würde. Eineread-Bereitstellung könnte also nicht senden, selbst wenn diese Binärdatei ersetzt würde; einemanage-Bereitstellung kann nicht senden, weil es nichts gibt, was aufgerufen werden könnte. (Es gibt keinen sendefreien Schreibbereich, zu dem man wechseln könnte – siehe SECURITY.md, Bedrohung 1.)Eingeschränkte Downloads. Mit
MAILWARDEN_DOWNLOAD_DIRwerden Anhänge auf dieses Verzeichnis beschränkt (mit realpath-kanonisiert, symlink-bewusst) und überschreiben niemals eine vorhandene Datei.Eingrenzung nicht vertrauenswürdiger Inhalte. Jedes Tool-Ergebnis wird in
<untrusted-tool-output>-Marker eingeschlossen und von unsichtbaren/BiDi-Override-Zeichen befreit, sodass Clients zitierte E-Mail-Inhalte von Anweisungen unterscheiden können.Live-API, keine Kopie. Es wird kein Postfach-Spiegel oder Suchindex gespeichert. Der einzige lokale Zustand ist Ihr OAuth-Token in
~/.mailwarden/.Optionale Token-Verschlüsselung im Ruhezustand.
token.jsonenthält ein Refresh-Token; auf der Festplatte ist es nur durchModus 0o600geschützt (unter Windows wirkungslos). Setzen SieMAILWARDEN_TOKEN_PASSPHRASEauf eine Passphrase, und das Token wird AES-256-GCM-verschlüsselt (scrypt-abgeleiteter Schlüssel) gespeichert, sodass eine Kopie der Datei – ein Backup, ein synchronisierter Ordner, ein anderer Rechner – ohne die Passphrase nutzlos ist. Führen Sie nach dem Setzen einmalmailwarden --autherneut aus, um das vorhandene Token zu verschlüsseln. Beachten Sie die Grenze: Dies schützt vor Dateidiebstahl, nicht gegen Malware, die als Ihr Benutzer läuft (die die Passphrase ebenfalls aus der Umgebung lesen kann).
Kurzanleitung
claude mcp add mailwarden -- npx -y mailwardenDas ist die gesamte Installation – npx holt und führt das veröffentlichte Paket aus, kein Klonen oder Build-Schritt. Sie benötigen nur einmal Google OAuth-Anmeldeinformationen (weiter unten).
Einrichtung
Erstmalige Einrichtung einer Google OAuth-App? Folgen Sie der Schritt-für-Schritt-Einrichtungsanleitung – sie führt durch die Google Cloud Console mit genauen Klickpfaden, erklärt den Bildschirm „Unverifizierte App“ und behandelt die Falle, die Tokens nach 7 Tagen ungültig macht. Die Kurzfassung:
Google Cloud: Erstellen Sie ein Projekt → aktivieren Sie die Gmail-API → konfigurieren Sie den OAuth-Zustimmungsbildschirm und veröffentlichen Sie ihn als Produktion (im Status Test läuft Googles Refresh-Token nach 7 Tagen ab) → erstellen Sie eine OAuth-Client-ID vom Typ Desktop-App → laden Sie sie als
credentials.jsonherunter.Legen Sie
credentials.jsonin~/.mailwarden/ab (oder setzen SieMAILWARDEN_CREDENTIALS=/pfad/zu/credentials.json).Autorisiert einmal – öffnet einen Browser, speichert ein Refresh-Token in
~/.mailwarden/token.json:npx -y mailwarden --authAngeforderte Bereiche:
gmail.modify(lesen + labeln/archivieren/papierkorb) undgmail.settings.basic(nur Filterverwaltung). Wenn Sie eine Version autorisiert haben, bevor es Filter gab, führen Sie--autheinmal erneut aus, um den hinzugefügten Bereich zu gewähren. Um ein Token zu erhalten, mit dem Gmail selbst sich weigert zu senden, autorisieren Sie mitMAILWARDEN_TOOLS=read– siehe Read-only-Modus oben.Überprüfen Sie die Einrichtung jederzeit mit dem integrierten Doctor:
npx -y mailwarden --checkEs prüft
credentials.json, ob ein Token existiert (und ob es verschlüsselt ist), ob die gewährten Bereiche Ihre aktivierten Stufen abdecken, und führt einen Live-Gmail-Aufruf durch, um zu beweisen, dass das Token noch funktioniert – es gibt eine konkrete Lösung für alles aus, was falsch ist, und beendet sich mit einem Fehlercode (praktisch in CI/Health-Checks). Diagnostiziert die häufigsten Fallen: keine/falschecredentials.json-Datei, nie autorisiert, ein verschlüsseltes Token ohneMAILWARDEN_TOKEN_PASSPHRASE, einen fehlenden Bereich oder den 7-Tage-Ablauf des Tokens im „Test“-Zustimmungsstatus.
Verbinden
Claude Code (lokales stdio):
claude mcp add mailwarden -- npx -y mailwardenClaude Code Plugin – derselbe Server plus eine /mailwarden:setup-Fähigkeit, die Sie durch die OAuth-Einrichtung führt und eine defekte diagnostiziert. Das Repository-Root ist das Plugin (.claude-plugin/plugin.json), also von einem Klon:
claude --plugin-dir /path/to/mailwardenEs ist im Community-Marktplatz von Anthropic eingereicht; sobald es gelistet ist, macht /plugin marketplace add anthropics/claude-plugins-community und dann /plugin install mailwarden@claude-community dasselbe ohne Klon. Das Plugin führt die gesamte Tool-Oberfläche aus – für eine schmalere Stufe (MAILWARDEN_TOOLS=read) oder ein zweites Konto verwenden Sie claude mcp add mit der gewünschten Umgebungsvariablen (siehe Konfiguration (Umgebungsvariablen) und Mehrere Konten).
Claude Desktop – fügen Sie zu claude_desktop_config.json hinzu:
{
"mcpServers": {
"mailwarden": { "command": "npx", "args": ["-y", "mailwarden"] }
}
}Oder installieren Sie das MCPB-Bundle (mailwarden-<version>.mcpb, ab Version 0.10.0 an GitHub-Releases angehängt) als Desktop-Erweiterung – Einstellungen → Erweiterungen → Erweiterung installieren… – derselbe Server, zur Laufzeit eigenständig (kein npx; Claude Desktop bringt die Node-Laufzeitumgebung mit), mit den Tool-Stufen als Einstellung. Das Bundle wird aus dem gepackten npm-Paket erstellt (gleicher Dateisatz wie veröffentlicht; npm run mcpb, in CI verifiziert: validiert, entpackt und gestartet) und ist derselbe Dateisatz, den Smithery ausliefert. Das einmalige npx -y mailwarden --auth gilt weiterhin (Node wird einmal dafür benötigt) – das Bundle liest dasselbe ~/.mailwarden/-Token.
Smithery – gelistet als csitte/mailwarden, das dieses Bundle bereitstellt:
npx -y @smithery/cli install csitte/mailwarden --client claude # local stdio entry in the client's configBeachten Sie, welchen der beiden Smithery-Pfade Sie nehmen. Die obige Installation schreibt einen einfachen lokalen Server-Eintrag: Der Prozess, Ihr Token und Ihre E-Mails bleiben auf Ihrem Rechner, genau wie bei npx. Wenn Sie es stattdessen zu Smitherys Toolbox hinzufügen (smithery mcp add), wird das Bundle ebenfalls lokal ausgeführt, leitet aber den Tool-Datenverkehr über Smitherys Gateway, sodass ein entfernter Client darauf zugreifen kann – der E-Mail-Inhalt in diesen Antworten passiert dann einen Dritten. Das ist eine Eigenschaft des Gateways, nicht von mailwarden; wenn Sie die Garantie ohne Dritte wünschen, verwenden Sie die lokale Installation, das npm-Paket oder die .mcpb von der Release-Seite.
Remote (Streamable HTTP) – für einen VPS / claude.ai benutzerdefinierten Connector:
# Loopback + token required by default. For real hosting, bind outward and keep the token:
MAILWARDEN_TOKEN=<secret> MAILWARDEN_HOST=0.0.0.0 npx -y mailwarden --http # :8787/mcpDann in claude.ai: Einstellungen → Connectors → Benutzerdefinierten Connector hinzufügen → Ihre https://ihr-host/mcp-URL. In Claude Code: claude mcp add --transport http mailwarden https://ihr-host/mcp.
Mehrere Konten
Eine OAuth-App (eine credentials.json) kann mehrere Gmail-Konten autorisieren. Jedes Konto behält sein eigenes Refresh-Token in einer separaten Datei, ausgewählt durch MAILWARDEN_ACCOUNT:
mailwarden --auth --account work # stores token.work.json
mailwarden --auth --account personal # stores token.personal.jsonFühren Sie sie nebeneinander aus, indem Sie den Server einmal pro Konto registrieren, jedes mit seinem eigenen MAILWARDEN_ACCOUNT. Jede Instanz ist vollständig isoliert – ihr eigenes Token, ihre eigenen gewährten Bereiche, ihre eigene Tool-Oberfläche – sodass nichts auf das falsche Postfach zugreifen kann:
{
"mcpServers": {
"gmail-work": { "command": "npx", "args": ["-y", "mailwarden"], "env": { "MAILWARDEN_ACCOUNT": "work" } },
"gmail-personal": { "command": "npx", "args": ["-y", "mailwarden"], "env": { "MAILWARDEN_ACCOUNT": "personal" } }
}
}Kontonamen sind nicht case-sensitive – sie werden zu Dateinamen, daher wären Work und work auf Windows/macOS dieselbe Datei. mailwarden wandelt sie in Kleinbuchstaben um (--account Work → token.work.json), sodass ein Name immer genau einem Postfach zugeordnet ist.
Welche Datei --auth schreibt, hängt nur von --account / MAILWARDEN_ACCOUNT ab – niemals vom Konto, das Sie im Browser auswählen. Das Autorisieren eines zweiten Postfachs ohne --account würde daher direkt auf das Token des ersten zielen, daher prüft --auth zuerst und weigert sich, anstatt das Token eines anderen Postfachs zu ersetzen; --force überschreibt es absichtlich. Die beiden Stellschrauben sind nicht austauschbar: MAILWARDEN_ACCOUNT ist für mehrere Postfächer aus einem Konfigurationsverzeichnis (es wählt token.<name>.json aus), während MAILWARDEN_DIR das gesamte Verzeichnis verschiebt – nützlich, um Setups vollständig getrennt zu halten, gibt Ihnen aber kein zweites Konto innerhalb eines. npm run auth aus einem Repository-Klon übergibt weder das eine noch das andere, d. h. es dient immer dem Standardkonto.
mailwarden --check zeigt das aktive Konto und listet die anderen auf, die es findet. Ohne gesetzten MAILWARDEN_ACCOUNT verwendet alles genau wie zuvor das Standard-token.json – dies ist vollständig abwärtskompatibel.
Aus dem Quellcode
git clone https://github.com/csitte/mailwarden && cd mailwarden
npm install && npm run build
node dist/index.js --authKonfiguration (Umgebungsvariablen)
Var | Bedeutung |
| Konfigurationsverzeichnis (Standard: |
| Pfad zu |
| Wählt ein benanntes Konto aus (dessen Token ist |
| Passphrase → verschlüsselt |
|
|
| Beschränkt |
|
|
| Kommagetrennte Tool-Stufen zur Ankündigung: |
|
|
| HTTP-Port (Standard 8787) |
| HTTP-Bindeadresse (Standard |
| Bearer-Token für den HTTP-Endpunkt – erforderlich für |
|
|
| Zusätzliche kommagetrennte |
Status
Funktionsfähig und wird in der täglichen Mailbox-Automatisierung verwendet. Kern-Gmail-Tools + Snooze implementiert gegen googleapis, abgedeckt durch eine vitest-Suite (789 Tests – npm run coverage). Aktuelle Version: siehe das npm-Abzeichen oben, das Changelog oder die Releases. PRs sind willkommen.
Lizenz
MIT © C.Sitte Softwaretechnik
Maintenance
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceAn MCP server that enables Gmail integration, allowing users to manage emails (send, receive, read, trash, mark as read) directly through MCP clients like Claude Desktop.1MIT
- AlicenseBqualityDmaintenanceManage your emails effortlessly with a standardized interface for drafting, sending, retrieving, and organizing messages. Streamline your email workflow with complete Gmail API coverage, including label and thread management.641,39856MIT
- AlicenseNot gradedqualityAmaintenanceGmail MCP server — scope-gated tools (readonly / send / modify), path jails for attachments + downloads, hardened OAuth credentials, Sigstore-signed releases.20711MIT
- AlicenseAqualityFmaintenanceA Gmail MCP server with native multi-account support, enabling management of multiple Gmail accounts from a single server instance.75MIT
Related MCP Connectors
Read, search, send, organize, draft and schedule email across your inboxes from any MCP client.
Shipmail MCP server for AI agent custom-domain email inboxes with REST API and webhooks.
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/csitte/mailwarden'
If you have feedback or need assistance with the MCP directory API, please join our Discord server