mailwarden
mailwarden is a native Gmail MCP server providing full mailbox triage for AI assistants, including reliable search, bulk operations, and a unique snooze system.
Search & Retrieval
search– Query Gmail using native syntax (e.g.from:foo is:unread); results are paginated and read-state predicates are re-verified against live labels to eliminate false positives.get_thread– Fetch full thread content: headers, plaintext/HTML bodies, and attachment metadata.list_labels– View all system and user-defined Gmail labels.
Label & Inbox Management
modify_labels– Add or remove labels on a thread.bulk_modify– Apply label changes to all messages matching a query (batched at 1000/call, with partial-failure reporting).archive,mark_read,mark_unread– Convenience wrappers for common triage actions.trash/untrash– Move a thread to Trash or restore it.
Snooze (unique feature)
snooze– Archives a thread and applies a dated label (MCP/Snoozed/YYYY-MM-DD) to resurface it in the inbox on a specified date.list_snoozed– See all snoozed threads and their due dates.unsnooze– Cancel a snooze and immediately return the thread to the inbox.sweep_snoozed– Resurface all due snoozed threads; safe to run repeatedly or on a schedule.
Attachments
download_attachment– Save attachments to a local path; never overwrites existing files; can be restricted to a safe download directory.
Security & Robustness
Read-only mode available; no send capability, no telemetry, no open ports by default.
Correctly decodes RFC 2047 headers and various charsets; API errors handled with exponential backoff retries.
Provides tools for searching, reading, labeling, archiving, trashing, downloading attachments, and snoozing threads in Gmail via the live Gmail API.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@mailwardensearch for recent emails from john@example.com"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
mailwarden
A reliable, native Gmail MCP server — full mailbox triage for AI assistants, with the feature nobody else ships: snooze.
Highlights
Snooze — the feature nobody else ships. Archive a thread now, have it resurface in the inbox on a date. Built on dated labels + a sweep, so it works from any client and survives restarts.
Search you can trust. Gmail's own search index silently drops
is:unreadin some operator combinations —searchre-verifies every hit against its live labels and discards the index's false positives. Paginated viapageToken/nextPageToken.Bulk operations that scale.
bulk_modifyarchives/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.Structured outputs. Every tool declares an
outputSchemaand returns validatedstructuredContentalongside fenced JSON text — no parsing guesswork for clients.Small attack surface. No send tools (no exfiltration path for prompt-injected mail), optional read-only mode, no telemetry, no open ports by default, symlink-safe download fencing, injection-fenced output. Details under Security & privacy.
Correct with real-world mail. RFC 2047 headers decoded (
=?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.
Related MCP server: Gmail MCP
Why
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.
Tools
Tool | What it does |
| 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 |
| Full thread: headers, plaintext + HTML bodies, attachment metadata |
| All labels (system + user) |
| Create a user label (idempotent; nested via |
| Add/remove labels by name or id — an unknown name in |
| 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, |
| Convenience wrappers |
| Move to / restore from Trash |
| Save an attachment to a local path (never overwrites — collisions get a numeric suffix) |
| Archive now, resurface on/after a date ( |
| Cancel a snooze, return to inbox now |
| All snoozed threads + due dates |
| Resurface threads whose snooze is due (run on demand, via cron, or the daemon); batched, with partial-failure reporting |
| All Gmail filters (criteria + label actions); surfaces any |
| Create a server-side auto-triage rule (criteria → label actions only; no forwarding — see below). Optionally |
| 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.
How snooze works (no Gmail API snooze exists — we build it)
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:
on demand (
sweep_snoozedtool),via cron:
mailwarden --sweep,or automatically: set
MAILWARDEN_AUTO_SWEEP=1(hourly sweep while the server runs).
Filters (persistent auto-triage rules)
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.
Criteria:
from,to,subject,query(full Gmail search syntax),negatedQuery,hasAttachment,excludeChats, andsize+sizeComparison(smaller/larger, given together). At least one is required.Actions (label only):
addLabels/removeLabels, by name or id (an unknown name inaddLabelsis 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"].Existing mail: a filter only runs on messages arriving after it's created. Pass
applyToExisting: trueto 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 tomaxMessages, default 1000; same loose-index caveat asbulk_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 (negatedQueryorhasAttachment:false) is refused forapplyToExistingbecause it would match almost the whole mailbox — create such a filter without the flag. The outcome comes back underapplied(thequeryused,matchedMessages/modifiedMessages/modifiedThreadCountcounts,cappedwhen the match set hitmaxMessages, per-chunkfailed, and anerrorstring if the whole pass failed); it'snullwhenapplyToExistingwas not set. The filter is created first, so a partial or failed backlog pass is reported inapplied, never raised — the rule still stands.No forwarding — see Security & privacy.
Requires the
gmail.settings.basicscope; re-run--authonce if you authorized an older version. Not available in read-only mode.
Security & privacy
No telemetry. Nothing phones home — no analytics, no crash reporting, no tracking.
No open ports by default. stdio only. The optional
--httplistener binds to127.0.0.1(not the LAN) and refuses to start without aMAILWARDEN_TOKENbearer token — setMAILWARDEN_ALLOW_NO_TOKEN=1to override on a trusted, isolated network. On a loopback bind it also validates theHostheader (DNS-rebinding defense). For remote hosting, setMAILWARDEN_HOSTand front it with TLS.No send tools — by design. mailwarden cannot compose, reply, or forward. A prompt-injected instruction inside an email has no exfiltration path through this server.
create_filterfollows 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_filtersstill surfaces any forwarding filter already on the account, so you can spot one.Read-only mode. Set
MAILWARDEN_READONLY=1and only the read tools (search,get_thread,list_labels,list_snoozed) are registered — nothing that can change the mailbox or write files is even advertised to clients (the filter tools, which need the broadergmail.settings.basicscope, are excluded too). Recommended for shared/HTTP deployments that only triage.Fenced downloads. With
MAILWARDEN_DOWNLOAD_DIRset, attachment writes are confined to that directory (realpath-canonicalized, symlink-aware) and never overwrite an existing file.Untrusted-content fencing. Every tool result is wrapped in
<untrusted-tool-output>markers and stripped of invisible/BiDi-override characters, so clients can tell quoted mail content from instructions.Live API, no copy. No mailbox mirror or search index is stored anywhere. The only local state is your OAuth token in
~/.mailwarden/.
Quick start
claude mcp add mailwarden -- npx -y mailwardenThat's the whole install — npx fetches and runs the published package, no clone or build step. You only need Google OAuth credentials once (below).
Setup
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:
Google Cloud: create a project → enable the Gmail API → configure the OAuth consent screen and publish it to Production (in Testing status, Google expires refresh tokens after 7 days) → create an OAuth client ID of type Desktop app → download it as
credentials.json.Put
credentials.jsonin~/.mailwarden/(or setMAILWARDEN_CREDENTIALS=/path/to/credentials.json).Authorize once — opens a browser, stores a refresh token in
~/.mailwarden/token.json:npx -y mailwarden --authScopes requested:
gmail.modify(read + label/archive/trash) andgmail.settings.basic(filter management — grants no send capability). If you authorized a version before filters existed, re-run--authonce to grant the added scope.
Connect
Claude Code (local stdio):
claude mcp add mailwarden -- npx -y mailwardenClaude 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/mcpThen 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.
From source
git clone https://github.com/csitte/mailwarden && cd mailwarden
npm install && npm run build
node dist/index.js --authConfig (env)
Var | Meaning |
| config dir (default |
| path to |
|
|
| restrict |
|
|
| HTTP port (default 8787) |
| HTTP bind address (default |
| bearer token for the HTTP endpoint — required for |
|
|
| extra comma-separated |
Status
Working and used in daily mailbox automation. Core Gmail tools + snooze implemented against googleapis, covered by a vitest suite (168 tests — npm run coverage). Current version: see the npm badge above, the changelog, or releases. PRs welcome.
License
MIT © C.Sitte Softwaretechnik
Maintenance
Related MCP Servers
- Alicense-qualityDmaintenanceAn 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.Last updated1MIT
- 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.Last updated641,71956MIT
- Alicense-qualityAmaintenanceGmail MCP server — scope-gated tools (readonly / send / modify), path jails for attachments + downloads, hardened OAuth credentials, Sigstore-signed releases.Last updated20310MIT
- AlicenseAqualityFmaintenanceA Gmail MCP server with native multi-account support, enabling management of multiple Gmail accounts from a single server instance.Last updated73MIT
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.
Hosted email MCP for AI agents with inboxes, send/receive, memory, recovery, and credits.
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