Skip to main content
Glama

mailwarden

npm license Node Website Smithery Available on CodeGuilds

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 – kann is:unread aus 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üft search jeden Treffer erneut anhand seiner Live-Labels. Paginiert über pageToken/nextPageToken.

  • Massenoperationen, die skalieren. bulk_modify archiviert/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 outputSchema und gibt validiertes structuredContent zusammen 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 eine read-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

Google official

taylorwilsdon

a-bonus

klodr

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 outputSchema

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 (threads.list)

Zurückgegebene Threads

Mit ungelesener Nachricht

Veraltet

category:updates is:unread

131

17

87%

category:updates is:unread -in:inbox

128

14

89%

is:unread -in:inbox

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.mjs

Daneben 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

search

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 pageToken/nextPageToken. Jeder Treffer trägt signalsnewsletter (List-Id / List-Unsubscribe / Precedence bulk oder list), automated (Auto-Submitted, Auto-Reply/Suppress-Header, No-Reply-artige Absender), calendar (text/calendar oder .ics-Teil), replyToMismatch (Reply-To auf einer anderen Domain als From; eine Subdomain derselben Domain zählt als gleich) — ausgelesen aus den Headern/MIME der ersten Nachricht, kein zusätzlicher Aufruf

get_thread

Vollständiger Thread: Header, Klartext- + HTML-Bodies, Metadaten zu Anhängen

list_labels

Alle Labels (System + Benutzer)

get_profile

Adresse des verbundenen Kontos + Gesamtzahl der Nachrichten/Threads — bestätigt, welches Postfach verdrahtet ist, bevor gehandelt wird

triage_digest

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

list_unsubscribe

Welche Opt-Out-Optionen ein Thread bewirbt (List-Unsubscribe) — kontaktiert niemanden

list_subscriptions

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. sendersFound meldet, wie viele Absender es gab, bevor topN die Liste abgeschnitten hat

create_label

Ein Benutzerlabel erstellen (idempotent; verschachtelt via Parent/Child) und seine ID zurückgeben

modify_labels

Labels nach Name oder ID hinzufügen/entfernen — ein unbekannter Name in add wird automatisch erstellt (archivieren = INBOX entfernen, gelesen = UNREAD entfernen)

bulk_modify

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, modifiedThreadCount enthält die Gesamtzahl). Arbeitet auf dem rohen Index, daher benennt unverifiedPredicates die Bedingungen, die nicht bestätigt werden konnten (siehe unten). dryRun: true löst die Abfrage auf und meldet die passenden Threads sowie die Labels, die erstellt würden, ohne etwas zu berühren

archive / mark_read / mark_unread

Komfort-Wrapper

trash / untrash

In den Papierkorb verschieben / aus dem Papierkorb wiederherstellen

download_attachment

Einen Anhang in einem lokalen Pfad speichern (überschreibt nie — bei Kollisionen wird ein numerischer Suffix angehängt)

unsubscribe

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)

bulk_unsubscribe

Dasselbe für mehrere Threads, nacheinander und höchstens eine Anfrage pro Absender; Teilerfolg wird pro Thread gemeldet. dryRun: true führt die gleichen Header-Lese- und Deduplizierungsschritte aus und meldet den Endpunkt, den jeder Thread wouldCall — ohne jemanden zu kontaktieren

snooze

Jetzt archivieren, an/ab einem Datum (YYYY-MM-DD), einem Datum+Uhrzeit (2026-06-20 9am) oder einem vordefinierten Wert (tomorrow, tomorrow 9am, weekend, next week, einem Wochentagnamen, in N days, in N hours) wieder einblenden

unsnooze

Eine Schlummerfunktion abbrechen, sofort zurück zum Posteingang

list_snoozed

Alle schlummernden Threads + Fälligkeitsdaten

sweep_snoozed

Threads, deren Schlummerzeit abgelaufen ist, wieder einblenden (auf Abruf, via Cron oder den Daemon); gebündelt mit Teilerfolgsmeldung. dryRun: true beantwortet

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 (mondaysunday, 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, excludeChats und size + sizeComparison (smaller/larger, zusammen angegeben). Mindestens eines ist erforderlich.

  • Aktionen (nur Label): addLabels / removeLabels, nach Name oder ID (ein unbekannter Name in addLabels wird 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 zu maxMessages, Standard 1000; gleicher Vorbehalt bezüglich nicht verifiziertem Index wie bei bulk_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 (negatedQuery oder hasAttachment:false) wird für applyToExisting abgelehnt, da sie fast das gesamte Postfach treffen würde – erstellen Sie einen solchen Filter ohne das Flag. Das Ergebnis wird unter applied zurückgegeben (die verwendete query, Zählungen von matchedMessages/modifiedMessages/modifiedThreadCount, capped, wenn die Treffermenge maxMessages erreicht hat, pro-Chunk failed und ein error-String, falls der gesamte Durchlauf fehlschlug); es ist null, wenn applyToExisting nicht gesetzt wurde. Der Filter wird zuerst erstellt, sodass ein teilweiser oder fehlgeschlagener Nachhol-Durchlauf im applied gemeldet, 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 --auth einmal 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-Post zugestimmt haben. Ein einfacher https:-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-Click und 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 scribe akzeptiert 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 als duplicateOf gemeldet 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 als skippedOutOfTime zurü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 (::1 und 0:0:0:0:0:0:0:1 gleichermaßen); eine Adresse, die nicht parst, wird abgelehnt. DNS-Auflösung und alle Hops teilen sich ein 10-Sekunden-Budget. Nicht rebinding-sicher (fetch lö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 --vet

Es 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 an 127.0.0.1 (nicht das LAN) und weigert sich zu starten, wenn kein MAILWARDEN_TOKEN-Bearer-Token vorhanden ist – setzen Sie MAILWARDEN_ALLOW_NO_TOKEN=1, um dies in einem vertrauenswürdigen, isolierten Netzwerk zu überschreiben. Bei einer Loopback-Bindung wird auch der Host-Header validiert (DNS-Rebinding-Abwehr). Für Remote-Hosting setzen Sie MAILWARDEN_HOST und 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_filter folgt 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_filters zeigt 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 dem List-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_TOOLS bewirbt nur die Stufen, die Sie nennen – read (die Lese-Tools), manage (Postfach-Änderungen, Schlummern, Downloads), filters (serverseitige Filter-CRUD, die einzige Stufe, deren Tools gmail.settings.basic benötigen). Standardmäßig sind alle drei aktiviert; z. B. read,manage bietet eine vollständige Triage-Oberfläche ohne Filterverwaltung. Die angeforderten OAuth-Bereiche bei --auth werden aus den aktivierten Stufen abgeleitet – eine read-Bereitstellung fragt nur nach gmail.readonly, und gmail.settings.basic wird nur angefordert, wenn die filters-Stufe aktiv ist. Und die Filter-Tools werden automatisch ausgeblendet, wenn das gespeicherte Token kein gmail.settings.basic enthält (z. B. ein Token, das autorisiert wurde, bevor Sie die Stufe aktiviert haben) – führen Sie --auth erneut 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ür MAILWARDEN_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 breiteren gmail.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 ein gmail.readonly-Token, das Gmail-Sende-Endpunkte kategorisch ablehnen. manage benötigt gmail.modify, und Gmail akzeptiert diesen Bereich tatsächlich zum Senden – mailwarden stellt nur kein Tool bereit, das dies tun würde. Eine read-Bereitstellung könnte also nicht senden, selbst wenn diese Binärdatei ersetzt würde; eine manage-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_DIR werden 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.json enthält ein Refresh-Token; auf der Festplatte ist es nur durch Modus 0o600 geschützt (unter Windows wirkungslos). Setzen Sie MAILWARDEN_TOKEN_PASSPHRASE auf 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 einmal mailwarden --auth erneut 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 mailwarden

Das 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:

  1. 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.json herunter.

  2. Legen Sie credentials.json in ~/.mailwarden/ ab (oder setzen Sie MAILWARDEN_CREDENTIALS=/pfad/zu/credentials.json).

  3. Autorisiert einmal – öffnet einen Browser, speichert ein Refresh-Token in ~/.mailwarden/token.json:

    npx -y mailwarden --auth

    Angeforderte Bereiche: gmail.modify (lesen + labeln/archivieren/papierkorb) und gmail.settings.basic (nur Filterverwaltung). Wenn Sie eine Version autorisiert haben, bevor es Filter gab, führen Sie --auth einmal 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 mit MAILWARDEN_TOOLS=read – siehe Read-only-Modus oben.

  4. Überprüfen Sie die Einrichtung jederzeit mit dem integrierten Doctor:

    npx -y mailwarden --check

    Es 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/falsche credentials.json-Datei, nie autorisiert, ein verschlüsseltes Token ohne MAILWARDEN_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 mailwarden

Claude 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/mailwarden

Es 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 config

Beachten 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/mcp

Dann 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.json

Fü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 Worktoken.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 --auth

Konfiguration (Umgebungsvariablen)

Var

Bedeutung

MAILWARDEN_DIR

Konfigurationsverzeichnis (Standard: ~/.mailwarden)

MAILWARDEN_CREDENTIALS

Pfad zu credentials.json

MAILWARDEN_ACCOUNT

Wählt ein benanntes Konto aus (dessen Token ist token.<name>.json; Namen werden kleingeschrieben); nicht gesetzt = das Standard-token.json. Siehe Mehrere Konten

MAILWARDEN_TOKEN_PASSPHRASE

Passphrase → verschlüsselt token.json im Ruhezustand (AES-256-GCM); führen Sie --auth nach dem Setzen erneut aus

MAILWARDEN_AUTO_SWEEP

1 → Snooze-Bereinigung beim Start + stündlich während des Betriebs (schreibt Labels – benötigt den manage/gmail.modify-Bereich; eine reine read-Berechtigung kann nicht bereinigen)

MAILWARDEN_DOWNLOAD_DIR

Beschränkt download_attachment auf dieses Verzeichnis (dringend empfohlen für HTTP-Hosting)

MAILWARDEN_READONLY

1 → registriert nur die Lese-Tools (search/get_thread/list_labels/list_snoozed/get_profile/triage_digest/list_unsubscribe/list_subscriptions). Kurzform für MAILWARDEN_TOOLS=read

MAILWARDEN_TOOLS

Kommagetrennte Tool-Stufen zur Ankündigung: read, manage, filters (Standard: alle). Leitet auch die bei --auth angeforderten OAuth-Bereiche ab. Z.B. read,manage entfernt die Filter-Tools und deren gmail.settings.basic-Bereich

MAILWARDEN_DEBUG

1 → gibt vollständige Fehler mit Stack-Traces aus anstatt einer einzeiligen Nachricht (für Fehlerberichte)

PORT

HTTP-Port (Standard 8787)

MAILWARDEN_HOST

HTTP-Bindeadresse (Standard 127.0.0.1; setzen Sie z.B. 0.0.0.0 für Remote-Hosting)

MAILWARDEN_TOKEN

Bearer-Token für den HTTP-Endpunkt – erforderlich für --http, sofern nicht überschrieben

MAILWARDEN_ALLOW_NO_TOKEN

1 → erlaubt --http ohne Token (nur für vertrauenswürdige/isolierte Netzwerke)

MAILWARDEN_ALLOWED_HOSTS

Zusätzliche kommagetrennte host:port-Werte, die von der Loopback-Host-Whitelist akzeptiert werden

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

Install Server
A
license - permissive license
A
quality
A
maintenance

Maintenance

Maintainers
Response time
2dRelease cycle
24Releases (12mo)
Commit activity

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    An 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.
    1
    MIT
  • A
    license
    B
    quality
    D
    maintenance
    Manage 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.
    64
    1,398
    56
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Gmail MCP server — scope-gated tools (readonly / send / modify), path jails for attachments + downloads, hardened OAuth credentials, Sigstore-signed releases.
    207
    11
    MIT
  • A
    license
    A
    quality
    F
    maintenance
    A Gmail MCP server with native multi-account support, enabling management of multiple Gmail accounts from a single server instance.
    7
    5
    MIT

View all related MCP servers

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.

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/csitte/mailwarden'

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