
Product
Socket Now Protects the Firefox Extension Ecosystem
Socket is bringing experimental protection to Firefox, scanning 97,000+ extensions in Mozilla's official directory for malware and risky updates.
mailwarden
Advanced tools
A reliable, native Gmail MCP server with full mailbox control — search, labels, archive, trash, attachments, and snooze.
A reliable, native Gmail MCP server — full mailbox triage for AI assistants, with the feature nobody else ships: snooze.
is:unread in some operator
combinations — search re-verifies every hit against its live labels and discards the index's
false positives. Paginated via pageToken/nextPageToken.bulk_modify archives/labels everything matching a query at
1000 messages per API request — with per-chunk partial-success reporting instead of
all-or-nothing. The snooze sweep uses the same batch path.outputSchema and returns validated
structuredContent alongside fenced JSON text — no parsing guesswork for clients.=?UTF-8?B?…?= → readable text),
bodies decoded in their declared charset (no mojibake for ISO-8859-1/Shift_JIS mail),
429/5xx retried with exponential backoff.Connectors that sync or cache your mailbox can lag behind it — and even Gmail's own search index is sometimes loose (see below). mailwarden talks straight to the live Gmail API (no cached snapshot) and re-verifies what the index returns, so what you see is what's actually there. It's a generic Gmail capability layer — keep your own rules/logic in your AI client, not in the server.
search goes one step further than the raw API: Gmail's threads.list index is sometimes loose for read-state operators — is:unread is silently dropped in some operator combinations (e.g. category:updates is:unread -in:inbox returns read mail too). Since every hit is fetched live anyway, search re-checks the unambiguous predicates (is:unread/is:read/is:starred/in:inbox/category:…, with negation) against each thread's true labels and drops the index's false positives.
| Tool | What it does |
|---|---|
search | Gmail query syntax → thread summaries (from/subject/date/labels/snippet); read-state/category predicates are re-verified against each hit's live labels; paginated via pageToken/nextPageToken |
get_thread | Full thread: headers, plaintext + HTML bodies, attachment metadata |
list_labels | All labels (system + user) |
get_profile | Connected account's address + total message/thread counts — confirm which mailbox is wired up before acting |
create_label | Create a user label (idempotent; nested via Parent/Child) and return its id |
modify_labels | Add/remove labels by name or id — an unknown name in add is auto-created (archive = remove INBOX, read = remove UNREAD) |
bulk_modify | Batch label changes for every message matching a query — 1000 messages per API request, partial success reported per chunk (thread-id list capped at 500, modifiedThreadCount has the total) |
archive / mark_read / mark_unread | Convenience wrappers |
trash / untrash | Move to / restore from Trash |
download_attachment | Save an attachment to a local path (never overwrites — collisions get a numeric suffix) |
snooze | Archive now, resurface on/after a date (YYYY-MM-DD) |
unsnooze | Cancel a snooze, return to inbox now |
list_snoozed | All snoozed threads + due dates |
sweep_snoozed | Resurface threads whose snooze is due (run on demand, via cron, or the daemon); batched, with partial-failure reporting |
list_filters | All Gmail filters (criteria + label actions); surfaces any forward address on existing filters for auditing |
create_filter | Create a server-side auto-triage rule (criteria → label actions only; no forwarding — see below). Optionally applyToExisting to also sweep matching mail already in the mailbox |
delete_filter | Delete a filter by id |
All tools declare an outputSchema and return structured content (validated, machine-readable)
alongside the same JSON as fenced text — clients never have to parse prose.
snooze removes INBOX and applies a dated label MCP/Snoozed/<YYYY-MM-DD>. sweep_snoozed finds due labels and returns those threads to the inbox (marked unread). Run the sweep:
sweep_snoozed tool),mailwarden --sweep,MAILWARDEN_AUTO_SWEEP=1 (hourly sweep while the server runs).create_filter sets up a Gmail server-side rule: mail matching the criteria automatically gets the
given label actions — the mailbox keeps triaging itself with no assistant in the loop.
from, to, subject, query (full Gmail search syntax), negatedQuery,
hasAttachment, excludeChats, and size + sizeComparison (smaller/larger, given together).
At least one is required.addLabels / removeLabels, by name or id (an unknown name in
addLabels is auto-created, nested via /). Common recipes: skip the inbox → removeLabels: ["INBOX"];
auto-mark-read → removeLabels: ["UNREAD"]; auto-trash → addLabels: ["TRASH"];
star → addLabels: ["STARRED"]; never-spam → removeLabels: ["SPAM"]; file under a label → addLabels: ["Receipts"].applyToExisting: true to also apply the same actions once to mail already in the mailbox —
mailwarden builds a Gmail search from the criteria and runs a bulk modify (up to maxMessages,
default 1000; same loose-index caveat as bulk_modify, and the one-off pass excludes Spam/Trash).
This requires at least one positive criterion (from/to/subject/query/hasAttachment:true/size):
an exclusion-only rule (negatedQuery or hasAttachment:false) is refused for applyToExisting
because it would match almost the whole mailbox — create such a filter without the flag.
The outcome comes back under applied (the query used, matchedMessages/modifiedMessages/modifiedThreadCount
counts, capped when the match set hit maxMessages, per-chunk failed, and an error string if the whole
pass failed); it's null when applyToExisting was not set. The filter is created first, so a partial or
failed backlog pass is reported in applied, never raised — the rule still stands.gmail.settings.basic scope; re-run --auth once if you authorized an older version.
Not available in read-only mode.--http listener binds to 127.0.0.1
(not the LAN) and refuses to start without a MAILWARDEN_TOKEN bearer token — set
MAILWARDEN_ALLOW_NO_TOKEN=1 to override on a trusted, isolated network. On a loopback bind it
also validates the Host header (DNS-rebinding defense). For remote hosting, set MAILWARDEN_HOST
and front it with TLS.create_filter follows
the same rule: it can label, archive, trash, star or mark mail, but never creates a forwarding
filter (which would be an exfiltration path). list_filters still surfaces any forwarding filter
already on the account, so you can spot one.MAILWARDEN_READONLY=1 and only the read tools (search, get_thread,
list_labels, list_snoozed, get_profile) are registered — nothing that can change the mailbox or write
files is even advertised to clients (the filter tools, which need the broader gmail.settings.basic
scope, are excluded too). Recommended for shared/HTTP deployments that only triage.MAILWARDEN_DOWNLOAD_DIR set, attachment writes are confined to that
directory (realpath-canonicalized, symlink-aware) and never overwrite an existing file.<untrusted-tool-output> markers
and stripped of invisible/BiDi-override characters, so clients can tell quoted mail content from
instructions.~/.mailwarden/.token.json holds a refresh token; on disk it is protected
only by mode 0o600 (a no-op on Windows). Set MAILWARDEN_TOKEN_PASSPHRASE to a passphrase and the token
is stored AES-256-GCM-encrypted (scrypt-derived key), so a copy of the file — a backup, a synced
folder, another machine — is useless without the passphrase. Re-run mailwarden --auth once after
setting it to encrypt the existing token. Note the boundary: this defends against file theft, not
against malware running as your user (which can read the passphrase from the environment too).claude mcp add mailwarden -- npx -y mailwarden
That's the whole install — npx fetches and runs the published package, no clone or build step. You only need Google OAuth credentials once (below).
First time setting up a Google OAuth app? Follow the step-by-step setup guide — it walks through the Google Cloud Console with exact click paths, explains the "unverified app" screen, and covers the trap that makes tokens die after 7 days. The short version:
credentials.json.credentials.json in ~/.mailwarden/ (or set MAILWARDEN_CREDENTIALS=/path/to/credentials.json).~/.mailwarden/token.json:
npx -y mailwarden --auth
Scopes requested: gmail.modify (read + label/archive/trash) and gmail.settings.basic
(filter management — grants no send capability). If you authorized a version before filters
existed, re-run --auth once to grant the added scope.Claude Code (local stdio):
claude mcp add mailwarden -- npx -y mailwarden
Claude Desktop — add to claude_desktop_config.json:
{
"mcpServers": {
"mailwarden": { "command": "npx", "args": ["-y", "mailwarden"] }
}
}
Remote (Streamable HTTP) — for a VPS / claude.ai custom 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
Then in claude.ai: Settings → Connectors → Add custom connector → your https://your-host/mcp URL. In Claude Code: claude mcp add --transport http mailwarden https://your-host/mcp.
git clone https://github.com/csitte/mailwarden && cd mailwarden
npm install && npm run build
node dist/index.js --auth
| Var | Meaning |
|---|---|
MAILWARDEN_DIR | config dir (default ~/.mailwarden) |
MAILWARDEN_CREDENTIALS | path to credentials.json |
MAILWARDEN_TOKEN_PASSPHRASE | passphrase → encrypt token.json at rest (AES-256-GCM); re-run --auth after setting |
MAILWARDEN_AUTO_SWEEP | 1 → snooze sweep at startup + hourly while running |
MAILWARDEN_DOWNLOAD_DIR | restrict download_attachment to this directory (strongly recommended for HTTP hosting) |
MAILWARDEN_READONLY | 1 → register only the read tools (search/get_thread/list_labels/list_snoozed/get_profile) |
PORT | HTTP port (default 8787) |
MAILWARDEN_HOST | HTTP bind address (default 127.0.0.1; set e.g. 0.0.0.0 for remote hosting) |
MAILWARDEN_TOKEN | bearer token for the HTTP endpoint — required for --http unless overridden |
MAILWARDEN_ALLOW_NO_TOKEN | 1 → allow --http without a token (trusted/isolated networks only) |
MAILWARDEN_ALLOWED_HOSTS | extra comma-separated host:port values accepted by the loopback Host allowlist |
Working and used in daily mailbox automation. Core Gmail tools + snooze implemented against googleapis, covered by a vitest suite (169 tests — npm run coverage). Current version: see the npm badge above, the changelog, or releases. PRs welcome.
MIT © C.Sitte Softwaretechnik
FAQs
A reliable, native Gmail MCP server with full mailbox control — search, labels, archive, trash, attachments, and snooze.
The npm package mailwarden receives a total of 657 weekly downloads. As such, mailwarden popularity was classified as not popular.
We found that mailwarden demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Product
Socket is bringing experimental protection to Firefox, scanning 97,000+ extensions in Mozilla's official directory for malware and risky updates.

Research
/Security News
Three compromised Rust crates pulled in a malicious dependency that downloaded and executed cross-platform malware during Cargo builds.

Research
/Security News
Socket uncovered 77 linked Firefox extensions, including 40 that steal wallet secrets or credentials and 37 deceptive sports-score shells.