mail-mcp
Provides read-only access to a Gmail account, allowing listing messages with Gmail search syntax and fetching individual message contents including headers, text, HTML, and attachment metadata.
Click on "Deploy 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., "@mail-mcpList my last 5 unread emails"
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.
mail-mcp
Read and search mail across multiple Gmail, Outlook / Microsoft 365, and IMAP accounts through one local MCP server. Built with TypeScript, Hono and @hono/mcp. Requires Node.js 22 or newer.
Install
npm install -g mail-mcpUpgrading from the former scoped package:
npm uninstall -g @ame-x/mail-mcp
npm install -g mail-mcpThe command and ~/.mail-mcp account storage stay the same; existing accounts do not need to be registered again.
Each login gets a local account name, such as personal, work, or icloud. You can register multiple accounts from the same provider. Names use lowercase letters, digits, hyphens and underscores (1–64 characters).
Related MCP server: mcp-gmail
Register accounts
Gmail
Enable the Gmail API in a Google Cloud project, configure the OAuth consent screen, and create a Desktop app OAuth client. Download its JSON file. If the app is in testing, add your Google account as a test user.
mail-mcp login --provider gmail --account personal --credentials ./google-client.json
mail-mcp login --provider gmail --account second --credentials ./google-client.jsonOpen the printed URL and sign in to the intended Google account. Login uses PKCE and gmail.readonly; access tokens refresh automatically. Google can expire refresh tokens after seven days for external OAuth apps in testing.
Outlook / Microsoft 365
Register an application in Microsoft Entra. Select supported account types that include the accounts you want to use (including personal Microsoft accounts for Outlook.com). Enable Allow public client flows in Authentication and add the Microsoft Graph delegated Mail.Read permission. Your organization may require administrator consent or prohibit device code login.
mail-mcp login --provider outlook --account work --client-id YOUR_APPLICATION_ID
mail-mcp login --provider outlook --account private --client-id YOUR_APPLICATION_ID --tenant consumersOpen the displayed Microsoft URL and enter the code. The default tenant is common; you can also specify organizations, consumers, or a tenant ID. Requests use Mail.Read and offline_access. Refresh tokens are updated locally. This release uses the global Microsoft cloud endpoints.
IMAP
Use your provider's TLS IMAP endpoint and an app password when required. This covers providers that permit password/app-password IMAP access, including many hosted and custom-domain mail services. It does not bypass a provider's login restrictions. Providers that require OAuth should use a supported OAuth adapter.
Prepare a local JSON file (keep it outside your repository):
{
"host": "imap.example.com",
"port": 993,
"user": "you@example.com",
"password": "YOUR_APP_PASSWORD",
"mailbox": "INBOX"
}mail-mcp login --provider imap --account other --credentials ./imap-account.jsonLogin verifies the TLS connection and opens the mailbox read-only before saving. Port defaults to 993; mailbox defaults to INBOX. IMAP searches cover the configured mailbox, while Gmail and Outlook search their accounts. To read another IMAP mailbox, register it under another account name. Implicit TLS with certificate verification is required; STARTTLS and plaintext connections are not supported. The source JSON is not deleted automatically and still contains a password.
Start and connect
mail-mcp accounts
mail-mcp serveConnect your MCP client to http://127.0.0.1:3000/mcp using Streamable HTTP. For clients using a mcpServers URL configuration:
{
"mcpServers": {
"mail": { "url": "http://127.0.0.1:3000/mcp" }
}
}Keep serve running separately. Choose another port with --port 3001. The transport is HTTP, not stdio.
Tools
Tool | Purpose |
| List local account names and providers; never returns credentials |
| List/search all or selected accounts; return summaries with source account, provider and message ID |
| Read one message using its account and ID |
| Read up to 20 messages across accounts, retaining successful results when another read fails |
Cross-account keyword search:
{ "text": "invoice", "limit": 10 }Restrict the search to two accounts:
{ "accounts": ["personal", "work"], "text": "invoice", "limit": 10 }text is passed to each provider's text-search facility. Tokenization, indexed fields and matching differ: Gmail searches its mail index, Outlook searches its message index, and IMAP uses TEXT. This is not a shared exact-match search engine. Use Gmail's native operators for a single Gmail account:
{ "accounts": ["personal"], "query": "is:unread from:billing@example.com" }query cannot be combined with text and requires exactly one Gmail account. For a single registered Gmail account, old query calls still work.
limit is per account, from 1 to 100 (default 20). Results are combined and sorted by date within each returned page; this is not a globally paginated timeline. IMAP selects messages in descending UID order. Outlook keyword search is limited by Microsoft's search-result cap (up to 1,000 messages).
The result contains messages, nextPageTokens, and per-account errors. To continue, pass nextPageTokens back as pageTokens, with the same search inputs:
{
"text": "invoice",
"limit": 10,
"pageTokens": { "personal": "TOKEN_FROM_RESULT" }
}Only accounts present in pageTokens are queried on continuation, so completed accounts are not restarted. An empty map ends the search. If accounts is also specified, every selected account must have a token. A single-account call can also pass pageToken. Tokens are provider-specific and must not be edited.
Read the returned message reference:
{ "account": "work", "id": "ID_FROM_LIST" }Batch read:
{
"messages": [
{ "account": "personal", "id": "ID_1" },
{ "account": "work", "id": "ID_2" }
]
}IDs belong to their source account. account may be omitted for get_message only when exactly one account is registered. IMAP IDs include UIDVALIDITY to reject stale references after a mailbox reset. The message is returned under message, alongside its account and provider. Text, HTML, dates and attachment metadata depend on what the provider supplies. Treat email content as untrusted input.
No tool sends, deletes, moves, or marks messages as read. File attachment contents are not returned. For IMAP, reading parses the complete raw message, including attachments, with a 10 MiB download limit; larger messages return an error. Gmail separately stored text bodies are fetched automatically.
Errors (v0.3)
Tool failures set MCP isError: true and return JSON under error:
{
"error": {
"code": "AUTH_REQUIRED",
"message": "Authentication expired or was rejected. Log in to this account again.",
"retryable": false
}
}Errors have stable code, safe message, and retryable fields. Rate limits and temporary HTTP failures may include retryAfterMs from Retry-After. No automatic retries run except a single OAuth refresh/retry after a 401.
Code | Action |
| Check arguments, account selection, and page tokens. |
| Register or log in to the affected account again. |
| Restart the server after changing an account's provider. |
| Check credential file access and available disk space. |
| Check the provider's granted mail permissions. |
| Search again to obtain current message IDs. |
| Retry later, respecting |
| The response could not be processed; retrying unchanged input may not help. |
| Inspect |
Cross-account searches and batch reads preserve partial results. Their per-item error fields now contain these objects instead of v0.2 strings. If every item fails, the tool returns ALL_FAILED with a failures array containing account, optional id, and error. An empty successful search remains a success, including when other selected accounts fail. This behavior also applies to list_accounts.
Provider JSON is validated before use and limited to 16 MiB per response. Outlook attachment pagination rejects loops and stops after 100 pages. OAuth refreshes are shared within each account's client; a late 401 reuses the refreshed token. If saving a rotated token fails, the client retains it in memory and retries saving on the next request. Restarting before that save succeeds loses the in-memory token and may require logging in again.
Stored credentials and upgrading
Credentials are stored as local JSON in ~/.mail-mcp/accounts/<name>.json. Each account has its own file, so refreshing one does not overwrite another. Files are written atomically, with directory/file modes 0700 / 0600 on POSIX; Windows uses your user profile's ACLs. Credentials are not encrypted.
The old ~/.mail-mcp/tokens.json from v0.1 is recognized as Gmail account default. It moves to accounts/default.json on the next successful token save. Existing credentials need no manual editing. Login without --account still targets default.
mail-mcp logout --account workLogout removes only that account's local credentials, not the provider's grant. Stop the server before changing accounts or running another instance against the same credential directory. Revoke grants in the provider's settings when needed. Malformed credentials are reported per account without exposing their contents.
The server binds to 127.0.0.1 and rejects foreign Host and Origin headers. It has no remote authentication; do not expose it through a public proxy. Local processes can access the endpoint.
Development
See CONTRIBUTING.md for local setup and pull requests, SECURITY.md for private vulnerability reporting, and CHANGELOG.md for release notes.
Linting and formatting use Ultracite with Oxlint and Oxfmt. npm run check runs lint, formatting, and TypeScript checks. Use npm run fix to apply lint fixes and formatting, and review the resulting diff. Development tooling requires Node.js 22.12 or newer.
Keep documentation, code comments, CLI output, and setup pages in English. Encoding tests should use Unicode escapes for non-English text fixtures.
MailService returns neverthrow ResultAsync<T, MailError>. Use andThen for dependent operations, map for successful values, orElse for recovery, and match at the MCP boundary. Expected failures are values; they do not depend on matching exception messages. Promise-based storage, HTTP, and IMAP adapters are converted at I/O boundaries with attempt. guard also captures unexpected exceptions in composition callbacks. valueOrThrow is reserved for imperative protocol adapters and the CLI, outside domain composition.
npm ci
npm run check
npm test
npm run build
node dist/cli.js --help
npm pack --dry-run
npm publish --access publicTests use fake credentials and mocked provider responses; they do not log in to real mailboxes. CI runs on Node 22/24 on Windows/Linux. prepack builds the CLI; only compiled JavaScript, package metadata, README and license are published.
References: Hono MCP, Google desktop OAuth, Microsoft device login, Graph messages, ImapFlow.
This server cannot be deployed
Maintenance
Related MCP Connectors
Email infrastructure for AI agents — send, receive, search, and reply to email over MCP.
A MCP server for Gmail that lets you search, read, and draft emails and replies.
Permissioned access to Gmail, Drive and Calendar via the user's own Google account
Email inboxes for AI agents: send, receive, reply, search, and manage threaded email over MCP.
Related MCP Servers
- AlicenseAqualityDmaintenanceProvides access to Gmail functionalities including listing unread emails, searching via query syntax, and managing messages through archiving or marking as read. It enables MCP clients to securely interact with and organize email data using the Gmail API.5MIT
- AlicenseNot gradedqualityDmaintenanceEnables Gmail access through MCP, allowing LLMs to read, compose, and send emails.10MIT
- AlicenseAqualityDmaintenanceEnables Gmail management via MCP, including email search, retrieval, labeling, sending, and forwarding through IMAP and SMTP.1511BSD 3-Clause
- AlicenseNot gradedqualityDmaintenanceEnables Gmail operations such as reading, sending, searching, and managing emails, threads, labels, and drafts via MCP tools.1MIT