rubit-mcp-mail
Provides read-only access to Gmail mailboxes via IMAP, including listing folders, searching messages, reading email content, and downloading attachments.
Provides read-only access to iCloud Mail via IMAP, including listing folders, searching messages, reading email content, and downloading attachments.
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., "@rubit-mcp-mailSearch my inbox for unread emails from Sarah and show the latest one."
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.
rubit-mcp-mail
A read-only MCP server for reading your mail. Provider-agnostic: it speaks IMAP, so it works with Outlook.com, Gmail, Fastmail, iCloud, or a self-hosted server — the provider is a line of config, not a code change.
Read-only by construction. Folders are opened with EXAMINE, never SELECT,
and bodies are fetched with BODY.PEEK, so reading a message does not even mark
it as read. There are no send, move, delete, or flag code paths, and a test
asserts none are ever added.
Tools
Tool | What it does |
| Configured accounts and whether each is authenticated |
| Folders with normalized roles and unread counts |
| Browse a folder, newest first, paginated |
| Server-side search by text, sender, subject, date range, unread |
| Full headers, body text, attachment metadata |
| Save one attachment into the download directory |
Folders are addressed by role — inbox, sent, drafts, junk, trash,
archive — so you never need to know that Outlook calls it Junk Email while
Gmail calls it [Gmail]/Spam. Raw folder names work too.
Roles come from the server's SPECIAL-USE flags where available. Outlook.com does
not advertise them, so there names are matched instead — accent-insensitively,
and including the localizations Outlook ships (Spanish, Catalan, French, German,
Italian, Portuguese), so a mailbox with Elementos enviados still resolves to
sent. Run rubit-mcp-mail doctor to see exactly how your folders were classified;
any folder that comes out as other can still be addressed by its raw name.
list_messages and search_messages return opaque handles. Pass one to
read_message or get_attachment. A handle encodes the folder's UIDVALIDITY,
so if the mailbox is renumbered you get a clear "re-run the search" error rather
than the wrong message.
Related MCP server: mcp-imap-server
Install
cd /path/to/rubit-mcp-mail
conda create -p ./.venv python=3.12 pip -y # this machine has no python3-venv
./.venv/bin/python -m pip install -e .Setting up Outlook
Microsoft has permanently retired basic auth for Outlook.com, so app passwords no longer work — OAuth2 is the only way in. That needs a free Azure app registration, done once.
1. Register the app
Go to portal.azure.com → Microsoft Entra ID → App registrations → New registration.
Name it anything (
rubit-mcp-mail).Supported account types: "Accounts in any organizational directory and personal Microsoft accounts". Required for an outlook.com/hotmail address — the default single-tenant option will reject your sign-in.
Leave Redirect URI empty. Click Register.
Authentication → Advanced settings → Allow public client flows: set to Yes. Without this the device-code flow fails immediately.
API permissions → Add a permission → APIs my organization uses tab → search Office 365 Exchange Online → Delegated permissions → check
IMAP.AccessAsUser.All→ Add permissions.Copy the Application (client) ID from the Overview page.
Those two bolded steps are the ones people miss; both produce confusing errors. If registering an app is blocked by your organization's policy, sign in to portal.azure.com with the personal Microsoft account itself (not a work account) — a personal outlook.com/hotmail account has its own directory that org policies don't govern.
2. Configure
mkdir -p ~/.config/rubit-mcp-mail
cat > ~/.config/rubit-mcp-mail/config.toml <<'EOF'
download_dir = "~/Downloads/rubit-mcp-mail"
[accounts.outlook]
provider = "outlook"
email = "you@outlook.com"
client_id = "<Application (client) ID from step 7>"
EOFNo secrets go in this file. Tokens live in your OS keyring (with a 0600 file
fallback on headless machines).
3. Sign in
./.venv/bin/rubit-mcp-mail auth outlookThis prints a code and a URL; approve in your browser. The refresh token is cached, so this is a one-time step — the server itself only ever refreshes silently and never prompts.
4. Verify
./.venv/bin/rubit-mcp-mail doctorThis checks config, credentials, connectivity, and authentication, then lists your folders with their detected roles. If this works, the server will too.
Can't register your own app?
If your Microsoft account genuinely can't register an Azure app (and it isn't
just a work-tenant policy — see the note in step 1), you can use a public
client ID that other open-source mail tools already share for exactly this
purpose instead of registering your own: Thunderbird's,
9e5f94bc-e8a4-4e73-b8be-63364c29d753. It's multi-tenant and already has the
IMAP.AccessAsUser.All permission granted, so device-code sign-in works
immediately — just paste it in as client_id:
[accounts.outlook]
provider = "outlook"
email = "you@outlook.com"
client_id = "9e5f94bc-e8a4-4e73-b8be-63364c29d753"Two things to know: the Microsoft consent screen will say "Thunderbird" is requesting access (cosmetic only — the token it grants works the same), and because this ID is outside our control, Microsoft could disable or rotate it in the future (it's happened to Thunderbird's client IDs before) — if sign-in suddenly starts failing, that's the first thing to suspect. Switch to your own registration above if that happens.
If Microsoft changes their endpoints
The Outlook IMAP host, OAuth authority URL, and IMAP scope are compiled into
providers.py as of when this was built. If Microsoft ever changes one of
them, you don't need to wait for a new release — add a [providers.outlook]
table to your own config.toml (never tracked by git) with the corrected
value, and it takes effect immediately:
[providers.outlook]
host = "outlook.office365.com" # current default, shown for reference
[providers.outlook.oauth]
authority = "https://login.microsoftonline.com/common"
scopes = ["https://outlook.office.com/IMAP.AccessAsUser.All"]Only include the keys you actually need to change. rubit-mcp-mail doctor
prints a line naming any overrides currently in effect. This works for any
provider, not just Outlook — port and ssl are overridable too.
Other providers
Anything that speaks IMAP with an app password:
[accounts.personal]
provider = "generic"
email = "you@fastmail.com"
host = "imap.fastmail.com"
# port = 993 (default)Then rubit-mcp-mail auth personal and paste the app password. Known hosts:
Gmail imap.gmail.com, Fastmail imap.fastmail.com,
iCloud imap.mail.me.com, Yahoo imap.mail.yahoo.com.
Gmail and iCloud require an app-specific password, not your login password.
To avoid storing the password at all, set
RUBIT_MCP_MAIL_PASSWORD_<ACCOUNTNAME> in the environment instead.
Register with Claude Code
Run this from the repo root (it resolves the venv path for you):
claude mcp add rubit-mail --scope user -- "$(pwd)/.venv/bin/rubit-mcp-mail" serveIf you'd rather type the path by hand, use one line and no trailing
whitespace — a stray space before the path makes posix_spawn look for a
file that doesn't exist:
claude mcp add rubit-mail --scope user -- /absolute/path/to/rubit-mcp-mail/.venv/bin/rubit-mcp-mail serveRegister with Claude Desktop
Claude Desktop launches MCP servers from the desktop process with a stripped
environment — no DBUS_SESSION_BUS_ADDRESS, no XDG_RUNTIME_DIR. Without
those, keyring cannot reach the desktop Secret Service, so the OAuth token
that rubit-mcp-mail auth stored in your keyring is invisible to the server and
every account reports "not authenticated" — even though doctor in a terminal
says auth ok. Pass the session variables explicitly in
~/.config/Claude/claude_desktop_config.json:
{
"mcpServers": {
"rubit-mail": {
"command": "/absolute/path/to/rubit-mcp-mail/.venv/bin/rubit-mcp-mail",
"args": ["serve"],
"env": {
"HOME": "/home/you",
"USER": "you",
"PATH": "/usr/local/bin:/usr/bin:/bin",
"XDG_RUNTIME_DIR": "/run/user/1000",
"DBUS_SESSION_BUS_ADDRESS": "unix:path=/run/user/1000/bus"
}
}
}
}Use your own uid (id -u) in the two /run/user/... paths. HOME, USER and
PATH are repeated because some clients replace the default environment rather
than merging with it.
The alternative, if you would rather not depend on the keyring at all, is to set
RUBIT_MCP_MAIL_NO_KEYRING=1 — in the env block and in the shell you run
rubit-mcp-mail auth from, so both sides use
~/.config/rubit-mcp-mail/secrets.json (mode 0600). That puts the refresh token
on disk in plain text; the keyring is the better default.
Troubleshooting
serve logs a startup summary to stderr, which Claude Desktop captures in
~/.config/Claude/logs/mcp-server-<name>.log and Claude Code shows with
claude --mcp-debug. It names the config path, the secret backend in use, and
whether each account's credential was found:
config: /home/you/.config/rubit-mcp-mail/config.toml (found)
secrets: keyring (keyring.backends.SecretService)
account outlook <you@outlook.com> via outlook: credential present (key 'msal-cache:outlook')For more, set RUBIT_MCP_MAIL_LOG_LEVEL=DEBUG (and optionally
RUBIT_MCP_MAIL_LOG_FILE=/tmp/rubit-mail.log) in the env block; at DEBUG the
server also reports each account's live auth status, including why a silent
token refresh failed.
To watch it by hand, run the server in the foreground with the same environment the client uses:
env -i HOME="$HOME" USER="$USER" PATH=/usr/local/bin:/usr/bin:/bin \
RUBIT_MCP_MAIL_LOG_LEVEL=DEBUG ./.venv/bin/rubit-mcp-mail serveIf that prints credential NOT FOUND while rubit-mcp-mail doctor in your
terminal prints auth ok, the mismatch is the environment, not the token —
compare the secrets: line from each.
Adding a provider
src/rubit_mcp_mail/providers.py is a dict of profiles — host, port, and which auth
strategy to use. Adding a preset is one entry. A provider that needs a non-IMAP
API instead implements the MailBackend protocol in
src/rubit_mcp_mail/backends/base.py; the tool layer does not change.
Development
./.venv/bin/python -m pytest -qLayout
src/rubit_mcp_mail/
server.py MCP tool definitions
__main__.py CLI: serve | auth | doctor
session.py wires config + auth + backend; attachment path safety
config.py TOML config -> Account models
providers.py provider profile registry
secrets.py keyring with 0600-file fallback
models.py pydantic models + message handles
mime.py BODYSTRUCTURE walking, decoding, HTML->text
backends/
base.py MailBackend protocol
imap.py the IMAP implementation
auth/
password.py app passwords
oauth_microsoft.py MSAL device-code flowNotes and limits
IMAP search is server-side and fairly basic. It matches substrings, not fuzzy relevance, and there is no "has attachment" criterion in IMAP itself — the
has_attachmentsfield on results comes from the message structure.Bodies are truncated at 20,000 characters by default to keep long newsletters from flooding the context. Raise
max_charswhen you need more.read_messageonly downloads the text parts, located viaBODYSTRUCTURE, so reading a mail with a 20 MB attachment still costs a few kilobytes. It preferstext/plainbut falls through totext/htmlwhen the plain part is missing or empty — the shape many newsletters take.body_formatsays which one you got. HTML is converted to Markdown with reference-style links, so a newsletter that repeats the same tracking URL on every headline lists it once at the foot instead of inline on every line.Microsoft is actively tightening third-party mail access. IMAP+OAuth is documented and working, but if it were ever withdrawn for consumer accounts, the fix is a Graph backend behind the existing
MailBackendprotocol.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Read, send, file and search email in any IMAP or Microsoft 365 mailbox, and check it arrived.
Read, search, send, organize, draft and schedule email across your inboxes from any MCP client.
Stateful email for AI agents — read inboxes, reply in-thread, draft with approval.
Read, arrange, and build agents on your Hedwig email board from any AI client. Nothing ever sends.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceProvides read-only access to Gmail, enabling email search, unread listing, message and thread retrieval, and draft reply preparation without sending.205MIT
- FlicenseNot gradedqualityDmaintenanceEnables LLMs to read, search, and manage emails via IMAP with secure, read-only access to email accounts.6
- AlicenseNot gradedqualityCmaintenanceRead-only MCP server for IMAP email access, enabling AI agents to read, search, and monitor email without sending or deleting messages.77MIT
- AlicenseAqualityBmaintenanceRead-only MCP server that connects to multiple IMAP accounts, enabling cross-account email listing, search, and retrieval without modifying mailboxes.4MIT
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/bgalmes/rubit-mcp-mail'
If you have feedback or need assistance with the MCP directory API, please join our Discord server