mailhub-mcp
Connects one owner's Gmail mailboxes to provide inbox triage: searching and reading messages and threads across accounts, summarizing mail with an inbox overview, and applying reversible organization such as labels, archiving, marking read and starring, plus creating reply drafts. All changes are journaled and undoable, and the server never sends, trashes or deletes mail.
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
mailhub-mcp
An MCP server that exposes one person's mail accounts (say personal, work and a side project) to an AI assistant through any MCP client that supports remote servers with OAuth. The tested client is Claude, as a custom connector in claude.ai; see docs/tutorial.md for other clients. Gmail is supported today; other providers plug in through a small provider interface (see docs/architecture.md).
It is built for inbox triage: the assistant can search, read and summarize mail across all your accounts, then tidy up with labels, archiving and drafts. Every organizing change is reversible, and the server has no way to send, trash or delete mail.
One instance serves one owner: ALLOWED_EMAILS holds only your own Google login, and every mailbox linked to the instance is yours. Someone else who wants the same setup runs their own instance (see More than one person).
Status: beta (0.x). It runs daily against real mailboxes, but only a handful of people have set it up so far. Expect rough edges in the setup, and pin a release (MAILHUB_VERSION) so updates happen when you choose.
Contents: Features · Admin page · Safety model · Quickstart · More than one person · Privacy · Development · License
Features
The server exposes 18 tools (11 read-only, 7 that change something); see docs/architecture.md for the list.
Read and triage
Multi-account: each mailbox gets an alias. Read tools take
accounts=Noneto mean "all accounts" and return{results, errors}, with at most one error per account.Search, read and triage signals:
search_emails,read_email,read_emails,read_thread,list_labels,list_drafts. Messages carry unread, starred, importance, category and newsletter flags.Inbox overview:
inbox_overviewgives per-account totals, unread counts, newsletters, categories and top senders.count_messagescounts the matches of a query per account from message ids only (about 5 quota units per 500 messages), to size a bulk change before running it.Unsubscribe info: messages carry
unsubscribe(url,mailto,one_click) parsed fromList-Unsubscribe, andinbox_overviewtop senders carry their most recent one. It is sender-controlled text: shown to you, kept in the untrusted block, never visited by the assistant.Content defenses: hidden text is removed from HTML bodies, invisible characters are removed from message text fields (sender, recipients, subject, date, snippet, body, attachment names), email text is delimited as untrusted, and likely injection attempts are flagged (see the safety model).
Organize, reversibly
Reversible organization:
create_label,modify_emails,modify_by_query(labels, archive, mark read, star), each recorded in a journal,undo_changeto revert a batch,list_changesto find earlier changes and their change_ids across sessions, andundo_sinceas a panic button that undoes everything since a time.Change history: every label change and every preference change is journaled with its motivating request, listed by
list_changesand undoable.Drafts:
create_draftleaves a reply draft in the mailbox for you to review and send yourself. Replies only, addressed to people already in the thread.
Remember and automate
Triage preferences:
get_preferencesandset_preferenceskeep rules such as important and ignored senders or keywords, and the time of the last triage, so scheduled triage is consistent from one run to the next. Rules that raise attention apply immediately; rules that lower attention wait for your approval outside the chat (mailhub-admin).Prompts:
daily_triage,clean_newslettersandorganize_inboxare MCP prompts (reusable workflows you pick in your MCP client). The two that change mail preview first and ask for your explicit confirmation; none of them trashes or deletes. WithTOOLS=read-onlyonly a read-onlydaily_triageis offered.
Run it
Admin page:
<PUBLIC_BASE_URL>/adminlists your mailboxes and their state, and links, re-links and removes them through Google's web sign-in, without SSH, file copying or a restart. See Admin page.Billing data: read results carry a
billingfield with payment codes (bank slip line, collection line, PIX copy-and-paste) whose check digits match, with amount and due date, read from the body and from the PDFs of messages that look like bills, including password-protected ones (CPF and CNPJ files insecrets/billing). Only numbers, dates and codes reach the model, never text from the PDF.Attachment download links: with
ADMIN=on(the default), each listed attachment gets a 14-day link that opens only after you sign in to/admin.Health:
GET /healthreturns{"status":"ok"}(no auth, nothing else), the Docker image has aHEALTHCHECK, andmailhub-admin accountschecks that every linked mailbox still answers.
Related MCP server: Gmail MCP Server
Admin page
Sign in at <PUBLIC_BASE_URL>/admin with your own Google login (the one in ALLOWED_EMAILS) to see every linked mailbox with its address and state (ok, credential expired, different mailbox, error). From there you link a new mailbox, re-link an expired one and remove one you no longer want; changes take effect at once, without a restart. It is server-rendered HTML with no JavaScript and works on a phone. The safety rules are in the safety model; setup is in docs/tutorial.md. mailhub-authorize on a workstation remains as a command-line alternative, and ADMIN=off turns the page off.
Safety model
Nothing destructive
The code never calls
messages.trash,messages.delete,batchDelete,messages.sendordrafts.send. A test scanssrc/to enforce this.The system labels
TRASH,SPAM,DRAFT,SENTandCHATare refused inadd_labelsandremove_labels, by name or by ID.
Every change is reversible and capped
modify_by_querypreviews by default (confirm=false: match count plus a sample). Applying (confirm=true) touches at most 500 messages per call.Every change is journaled to
changes.jsonlwith the labels each message had before.undo_changereverts exactly that batch and never removes labels a message already had. Messages whose touched labels were changed again since (for example by hand) are left alone and reported inchanged_since.list_changesshows the journal (newest first, with label names).undo_sincereverts every change since a time, newest first so overlapping changes restore exactly; it previews by default (confirm=false) and handles at most 50 changes per call.Every message the server changes gets the organized label (
mailhub/organizedby default, set withORG_LABEL; existing installs that usedclaude/organizedshould set it to that value; created automatically in each account), so you can always find what the assistant touched.modify_emailsandmodify_by_queryrefuse to remove it; onlyundo_changeandundo_sincetake it off, together with the rest of the reverted change.A daily cap (
MAX_CHANGES_PER_DAY, default 2000) limits how many messagesmodify_emailsandmodify_by_querycan change per account in a rolling 24 hours. Undo is never capped.TOOLS=read-onlystarts an instance that registers only the read tools, for routines that should report but never change anything.
Email is untrusted
Email content is treated as untrusted third-party text. Before the model sees a message, hidden HTML (for example
display:noneor zero-size text, comments, scripts) and invisible or bidirectional control characters are removed. Untrusted text (body,snippet, unsubscribe links, preference values, recorded requests) is never inline: each response puts all of it in oneuntrustedblock,[untrusted:<nonce>]...[/untrusted:<nonce>]with a fresh random nonce per response (marker lookalikes inside are defanged), and messages point into it withcontent_ref. Tool descriptions tell the model that nothing inside is an instruction.Each message carries
suspiciousandsuspicious_reasons, from heuristics for injection attempts (mentions of tool names, "ignore previous instructions", addressing an assistant by name, requests to forward or draft, large amounts of hidden text). The model is told to report flagged emails and not to act on them. This is an aid, not a guarantee.Drafts are replies only:
create_draftrequires a message to reply to, andtoandccmay only contain people already in that thread (plus your own address). A Reply-To that differs from the sender is never used as the default recipient, and a draft addressed to someone seen only in a Reply-To comes back with a warning. Drafting is refused for a thread that contains a suspicious message.These defenses have limits (heuristics can be evaded, the server cannot verify who motivated a call); see Limitations.
Out-of-band approval
Preferences follow a direction rule: anything that raises attention (marking a sender important, un-ignoring) applies immediately, anything that lowers attention (ignoring a sender, un-marking important) becomes a proposal that no MCP tool can approve. You approve it with
mailhub-admin preferences approve <id>inside the container. As long as you only approve proposals you recognize, injected text cannot silently hide future alerts. Raising-attention changes apply without review, but are visible inlist_changesand can be undone; since undoing them lowers attention, that undo is itself a proposal you approve the same way. Undo entries cannot be undone (make the change again instead). Proposals expire after 14 days.
Access
Access to the server requires a Google login, restricted to the addresses in
ALLOWED_EMAILS, and checked on every request. Only the OAuth redirect URIs inALLOWED_REDIRECT_URIS(by default Claude's own) can register as clients. Every allowed login sees every linked mailbox, soALLOWED_EMAILSshould hold only the owner's login; the server logs a warning at startup when it lists more than one address.The admin page has its own sign-in: a separate Google OpenID Connect login on the same Web client, accepted only for a verified email in
ALLOWED_EMAILS. The session is a signed cookie (HttpOnly,Secure,SameSite=Strict) that lasts 8 hours. Linking, re-linking and removing a mailbox require a Google sign-in from the last 5 minutes, a CSRF token and a same-origin request. A re-link only accepts the mailbox already recorded for that alias, and a mailbox already linked under another alias is refused. Credentials are written only by the admin code path; no MCP tool can reach it (a test enforces this). Every response carries a strict Content-Security-Policy and no-framing, no-cross-site-referrer and no-store headers, and every sign-in, link and removal is logged. Keepingsecrets/accountsread-only disables linking, andADMIN=offremoves the page.Attachment links need the owner's
/adminsession and are forced downloads, never shown in the browser. Attachments on spam, those flagged suspicious and Gmail-blocked file types get no link (hidden text alone does not block them, since it never reaches the model).Bill PDFs are parsed in a separate process with memory, CPU and time limits. Only codes that pass their check digits, amounts and dates come back; the PIX code and payee are fenced as untrusted text. Spam and suspicious messages get no billing data.
The lethal trifecta
An agent becomes dangerous when it combines three things: access to private data, exposure to untrusted content, and a way to send data out (egress). Your mailbox is the first two by design. mailhub removes the obvious egress: it cannot send mail or trash it, drafts are replies only, addressed to people already in the thread, and unsubscribe links are never visited. But any other tool in the same assistant session that can reach the internet (web fetch or search, browser automation, another connector that posts messages, files or tickets) reopens the third leg: injected text in an email could ask the model to put your private mail into a URL or a message. The server cannot see or control those tools, so be deliberate about which tools share a session or a routine with mailhub; for unattended routines, prefer TOOLS=read-only and no internet-capable tools alongside.
Note on scope: Gmail's gmail.modify scope technically also permits sending and trashing, and it is the minimum that covers label changes and drafts. The protections above are in this code, not in the token. Review src/ if that distinction matters to you.
Quickstart
Follow docs/tutorial.md for the Google Cloud setup, configuration, account linking on the admin page and the claude.ai connector (or another client, step 15).
Copy
.env.exampleto.env, fill it in, and rundocker compose up -d. This pulls the published imageghcr.io/trainedloop/mailhub-mcp(linux/amd64 and linux/arm64); to build from source instead, add-f docker-compose.build.yml(see the comment in that file). The server listens on127.0.0.1:8742(setHOST_PORTin.envto change the host port); it needs HTTPS at a public URL in front of it.examples/tailscale-funnel/ shows one complete way to publish it, including a firewall that isolates the container. examples/tailscale-sidecar/ is the same with a Tailscale sidecar container: its own hostname on port 443 and no port published on the host.
More than one person
Run a separate instance per person: its own directory, .env, linked accounts, data/, container name, local port and Compose project name, and its own public URL. Each person adds their own custom connector in their MCP client (for example claude.ai) with their instance's URL and logs in with their own Google account. See docs/tutorial.md for the steps and examples/tailscale-funnel/ for a second instance on the same host.
Privacy
Anything the assistant reads through this server (message text, headers, search results) becomes part of your conversation and is sent to the provider of the model you use (for Claude, Anthropic; see its privacy policy).
The server stores locally, in
data/andsecrets/: OAuth tokens, the change journal (including thequeryanduser_requestof each change), your preferences and the account book (each alias's mailbox address).Logs record each tool call with its arguments: tool names, accounts, message ids, search queries, label names, draft recipients and subject, and preference values. Draft bodies are replaced by their length and
user_requestis cut to 80 characters; every other value is capped at 200 characters. Admin page events are logged with the owner email, alias, provider and mailbox address; tokens, codes and cookies never are. The HTTP access log is off, so request URLs, which carry OAuth codes on the sign-in callbacks, are not logged. Set log retention on the host accordingly.Apart from the Google APIs, the server sends nothing anywhere else.
Development
uv sync
uv run pytest -qLayout and extension points are described in docs/architecture.md.
License
MIT, see LICENSE.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
This server cannot be deployed
Maintenance
Related MCP Connectors
Connect any IMAP/SMTP mailbox to Claude & ChatGPT: search, read, sort, draft. Sends only with OK.
Connect any mailbox to Claude, ChatGPT & AI: read, send, reply, schedule & search emails.
Your mailboxes in ChatGPT and Claude: Gmail, iCloud, Fastmail, any IMAP. Passwords stay yours.
Manage Gmail end-to-end: search, read, send, draft, label, and organize threads. Automate workflow…
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables reading, sending, searching, and managing Gmail through Claude using the official Google Gmail API.123 npm3MIT
- AlicenseNot gradedqualityCmaintenanceEnables Claude to read, search, send, label, and trash emails in any Gmail account via Google's Gmail API, using OAuth2 authentication with automatic token refresh.67 npmMIT
- AlicenseNot gradedqualityDmaintenanceEnables Claude to read, search, send, and manage Gmail messages and threads through natural language.302 npmMIT
- FlicenseNot gradedqualityDmaintenanceEnables Claude to read and manage your Gmail account, including listing, searching, sending, drafting, replying to, archiving, labeling, and trashing emails via the Gmail API.-