trackmcp
Back to directory

Secure IMAP/SMTP/EWS/Graph API MCP server in Rust — Microsoft 365, Hotmail, Gmail, Zoho, OAuth2, multi-account

73 stars RustOthers Updated Sep 3, 2026

Documentation

mail-mcp

Production-ready email MCP server for AI agents

IMAP + SMTP + EWS + Microsoft Graph API — built in Rust


Most email MCP servers only do IMAP reads. This one does everything: read, search, send, reply, forward, bulk operations, Microsoft Graph API, and Exchange Web Services — with real OAuth2, multi-account, and multi-provider support. Written in Rust for speed and safety.

What's New in v0.4.10

Community release — all three changes came from external contributors. Thank you!

  • NetEase IMAP compatibility (126.com / 163.com / yeah.net) by

@pep-27 in

#21. NetEase servers

reject mailbox access from clients that don't identify themselves. mail-mcp

now sends the RFC 2971 `ID` command after authentication whenever the server

advertises the `ID` capability. Includes mock-server regression tests and

NetEase setup docs in `docs/account-setup.md`.

  • `MAIL_SMTP__FROM_EMAIL` — sender address override by

@arwack in

#19. For shared/group

mailboxes where SMTP authenticates with a personal account but the From

address should be the group address. Applies to send, reply (including

reply-all self-address detection) and forward; falls back to `_USER` when

unset.

  • Sent-mail copies are now marked `\Seen` by

@ray-of-darkness in

#9. Copies the MCP

appends to the Sent folder after SMTP send no longer show up as unread.

What's New in v0.4.9

  • New tool `imap_get_attachment` — download a single attachment to disk.

Until now the only ways to reach attachment bytes were `imap_get_message`

(which returns attachment *metadata* and optional extracted PDF *text*, never

the binary) and `imap_get_message_raw` (capped at 1 MB and base64-encoded

into the response). A 7 MB email with X-ray images could not be retrieved at

all — over the cap, and dumping it into the response would blow up the model's

context anyway.

  • How it works: call `imap_get_attachment` with the `message_id` plus a

selector — either `part_id` (the value `imap_get_message` reports for each

attachment) or `filename`. The server fetches the full message (no size cap on

the server side), extracts and decodes just that one part, and **writes it to

disk**, returning `{ file_path, filename, content_type, part_id, size_bytes }`.

The binary never enters the response, so context stays small. The saved path

feeds straight into a local reader (e.g. an image-description tool or a PDF

reader).

  • Where files land: `output_dir` argument if given, else the

`MAIL_ATTACHMENT_DOWNLOAD_DIR` environment variable, else the system temp dir.

Filenames are sanitized (basename only, control characters stripped) to

prevent path traversal, and prefixed with the message UID and part id to avoid

collisions.

  • Optional inline base64: set `include_base64: true` to also get the bytes

in the response, but only when the attachment is at most `max_inline_bytes`

(default 256 KiB). Off by default.

What's New in v0.4.8

  • `SAVE_SENT` is now per-account with a provider-aware default.

Previously, saving a copy of outgoing mail to the Sent folder via IMAP

APPEND was controlled by a single global flag, `MAIL_SMTP_SAVE_SENT`. The

problem: providers that already save sent mail server-side (Gmail,

Zoho) ended up with two identical copies in Sent, while a generic SMTP

server or Office 365 (which do not auto-save on SMTP submission) lost

the copy entirely when the flag was `false`.

  • Provider-aware default (when nothing is configured):
    • Gmail (`smtp.gmail.com`): saves server-side and deduplicates by

Message-ID → the MCP does not append (`false`).

    deduplicate → the MCP does not append (`false`), avoiding the

    duplicate.

      the MCP does append (`true`), or the sent copy would be lost.

      • Per-account override: `MAIL_SMTP__SAVE_SENT=true|false` takes

      priority over everything. The global `MAIL_SMTP_SAVE_SENT` still works as a

      coarse override (wins over the provider default, loses to the per-account

      override).

      • Precedence: per-accountglobalprovider-aware default.
      ProviderAuto-saves server-sideMCP default
      GmailYes (with dedupe)`false`
      ZohoYes (no dedupe)`false`
      Office 365 (SMTP)No`true`
      Generic SMTP / relaysNo`true`

      What's New in v0.4.7

      • **Critical fix — `graph_send_message` silently dropped attachments on

      threaded replies.** When called with `in_reply_to` + `attachments`, the

      `createReply → PATCH → send` flow included the attachments in the PATCH

      against `/me/messages/{id}`. Microsoft Graph treats `Message.attachments`

      as a navigation property and silently discards the field on PATCH

      (2xx response, no error), so the message went out as single-part

      `text/html` with no file. The MCP returned `status: ok` and the caller

      assumed success. Invisible data loss.

      • The fix: in `send_via_reply()`, attachments are now uploaded one by one

      to `POST /me/messages/{draft_id}/attachments` between the PATCH and the

      send. Files ` markup into the

      recipient's inbox. v0.4.6 adds a real validator that rejects the tool

      call before any SMTP / Graph / EWS attempt if `body_text` or `body_html`

      contains tool-call wrapper syntax. The check is wired into all 5 send

      paths (`smtp_send_message`, `smtp_reply_message`, `smtp_forward_message`,

      `graph_send_message`, `ews_send_message`).

      • The forbidden markers are case-insensitive and tightly scoped — only

      the pseudo-tags that have no legitimate use in human correspondence:

      ``, ``, ``, ``,

      ``, ``, ``,

      and ``. Generic technical content that happens

      to mention `` for an XML schema or `` in a code

      example still passes.

      • HARD RULE #1 wording updated to announce the server-side rejection,

      so the LLM knows it's a hard contract — not a suggestion it can ignore.

      • No breaking changes for clean callers: well-behaved messages send

      exactly as before.

      What's New in v0.4.5

      • `serverInfo` now reports `name="mail-mcp"` + the crate `version` (the

      framework previously returned its own `rmcp 0.16.0`, which never changes

      between releases). Useful for verifying the active version with `/mcp`, and

      so any client-side cache keyed by (server, version) invalidates on each bump.

      • MCP instructions reorganized: the 3 critical anti-concatenation rules

      (which in v0.4.3 and v0.4.4 sat at the end of the block and could be lost

      to truncation / diluted attention) now appear as **HARD RULE #1, #2, #3 at

      the TOP**, right after the title. Consolidated into 3 short paragraphs

      (previously 3 long sections, ~1500 characters combined).

      • No functional changes to the server. Same SMTP/IMAP/EWS/Graph, same

      tool set, same behavior. Only the text exposed to the client changed.

      Important for these rules to take effect

      Clients that resume a session with `claude --continue` (or `/resume`) do

      NOT refresh the MCP `system_prompt` — they keep the one from that

      session's first handshake. If your session predates v0.4.5, the rules won't

      reach your context even if the on-disk binary is updated. To receive them,

      start a NEW session in the project (not `--continue`).

      What's New in v0.4.4

      • Preview hygiene rule in MCP `instructions`: when the LLM shows the

      user the email preview before sending, it should render ONE clean

      version of the body (markdown-style bullets, bold, links as text + URL)

      and state that the message will go multipart — but it must NOT dump

      the raw HTML source (``, ``, ``...) into the

      preview. Two reasons:

      1. The human reviewer wants to read the message, not audit markup —

      showing the HTML is noise.

      2. Exhibiting both the plain-text string AND the HTML string side by

      side in the preview is exactly the context that has historically

      led LLMs to concatenate them in the eventual tool call (the bug

      v0.4.3 documented). Hiding the HTML source from the preview

      removes the temptation.

      Complements the PREVIEW DOES NOT EQUAL TOOL CALL rule introduced

      in v0.4.3.

      What's New in v0.4.3

      • Server-side guidance against malformed tool calls. The MCP

      `instructions` block now explicitly tells the calling LLM that

      `body_text` and `body_html` are TWO SEPARATE JSON fields and must

      NEVER be concatenated. Previous wording ("send BOTH body_text AND

      body_html") was ambiguous and some LLMs interpreted it as "concatenate

      both with `...` pseudo-tags inside a single

      `body_text` string". When that happens, the recipient sees garbled

      duplicated content, AND any later Claude session that opens the saved

      copy via this MCP gets a Usage Policy block (the leaked

      `...` looks like a prompt-injection attempt to safety

      filters). The new instruction shows a CORRECT vs WRONG example and

      bans pseudo-tags / tool-call wrapper syntax inside email fields.

      What's New in v0.4.2

      • Release pipeline fixed: the `publish-npm` job in the CI release

      workflow has been disabled. It was inherited from the upstream fork and

      tried to publish to `@bradsjm/mail-imap-mcp-rs`, a scope this org does

      not own — every release was 404-ing on that step. See "Releasing" below

      for the full explanation and how to re-enable npm publishing if needed.

      • Auto-trigger releases on tag push: `.github/workflows/release.yml`

      now fires on `push: tags: ['v*']`, so tagging `vX.Y.Z` and pushing is

      all it takes to cut a release. `workflow_dispatch` is retained as a

      manual escape hatch.

      • Cleanup: removed the dangling `init-npm-placeholder.yml` workflow

      (also referenced the fork's npm scope).

      • docs: README gains a "Releasing" section documenting the new flow

      and the npm decision.

      What's New in v0.4.1

      • Fix: `save_to_sent_folder` now archives the exact RFC822 bytes that were

      sent (via `lettre.formatted()`), instead of a hand-rolled text-only stub.

      The Sent-folder copy keeps the HTML body, the multipart/alternative

      structure, and the RFC 2047-encoded subject — no more `???` where accents

      used to be, and HTML is no longer silently dropped.

      • Improved: localized Sent-folder detection — `Enviado[s]`, `Elementos

      enviados`, `Enviadas`, `Itens enviados`, `Envoyés`, `Éléments envoyés`,

      `Gesendet`, `Posta inviata`, `Verzonden`, `Wysłane`, plus nested variants.

      Previously only English names were recognized, so Zoho/localized IMAP

      accounts fell through to a non-existent `"Sent"` folder.

      • Improved: `smtp_forward_message` accepts `body_html` (was hardcoded to

      plain-text only).

      • Improved: EWS send gains `bcc`, `in_reply_to`, `references` (via

      ``), plus full recipient + subject-length

      validation — now at parity with the SMTP and Graph send paths.

      • Improved: Graph API threading fallbacks now log. `WARN` when the

      message-lookup HTTP call fails (rate limit, 5xx, permissions) so operators

      see threading degraded due to a real error; `DEBUG` when the original

      message is legitimately not found.

      • Refactor: EWS XML parsing migrated from substring matching to

      `quick-xml`. Fixes a latent namespace-collision bug (`` vs

      ``), correctly decodes XML entities and CDATA, and handles

      attribute values containing `=` (common in base64-like EWS item IDs).

      • Cleanup: zero warnings on `cargo build --release`.
      • Tests: 64 (up from 47).

      Why This Project

      mail-mcpTypical email MCP
      IMAP read/write18 tools3-5 tools
      SMTP send/reply/forwardYesNo or broken
      Microsoft Graph APIYesNo
      EWS (Exchange Web Services)YesNo
      OAuth2 (XOAUTH2)NativeNo
      Multi-accountYesSingle account
      Microsoft 365 + HotmailBoth workUsually neither
      LanguageRust (fast, safe)TypeScript/Python
      Tests64 unit + integrationMocks only
      Warnings in release build0Varies

      Feature Matrix

      ProviderIMAPSMTPGraph APIEWSOAuth2Multi-account
      Microsoft 365 (enterprise)YesAdmin-dependentYesYesYesYes
      Hotmail / Outlook.comYesBlocked by MSYesYesYesYes
      GmailYesYesYesYes
      ZohoYesYesYes
      FastmailYesYesYes
      Any IMAP/SMTP serverYesYesYes

      > EWS is the simplest way to add Microsoft accounts — single OAuth2 token for both reading and sending. Works even on tenants that block Graph API and IMAP.

      Quickstart — Let Claude Code do it

      Copy and paste this prompt into Claude Code and it will install, compile, and configure everything for you:

      code
      Install and configure the mail-mcp MCP server from https://github.com/tecnologicachile/mail-mcp
      
      1. Clone the repo, build with cargo build --release
      2. Add the MCP server to .claude.json with the binary path
      3. For Microsoft accounts: use EWS (simplest) — run device code flow with
         client_id d3590ed6-52b3-4102-aeff-aad2292ab01c and scope
         https://outlook.office365.com/EWS.AccessAsUser.All offline_access
         Then configure MAIL_EWS__USER and MAIL_EWS__REFRESH_TOKEN
      4. For Gmail: configure MAIL_IMAP + MAIL_SMTP with App Password from
         https://myaccount.google.com/apppasswords
      5. For Zoho: configure MAIL_IMAP + MAIL_SMTP with standard password
      6. Enable write/send: MAIL_IMAP_WRITE_ENABLED=true, MAIL_SMTP_WRITE_ENABLED=true
      
      My email accounts to configure:
      -

      Replace the last line with your email(s). Claude Code will guide you through each step including the OAuth2 device code flow for Microsoft accounts.

      Manual Setup (2 minutes)

      bash
      git clone https://github.com/tecnologicachile/mail-mcp.git
      cd mail-mcp
      cargo build --release

      Add to your MCP client config (Claude Code, Cursor, etc.):

      json
      {
        "mcpServers": {
          "mail": {
            "command": "./target/release/mail-mcp",
            "env": {
              "MAIL_IMAP_DEFAULT_HOST": "imap.gmail.com",
              "MAIL_IMAP_DEFAULT_USER": "you@gmail.com",
              "MAIL_IMAP_DEFAULT_PASS": "your-app-password",
              "MAIL_SMTP_DEFAULT_HOST": "smtp.gmail.com",
              "MAIL_SMTP_DEFAULT_PORT": "587",
              "MAIL_SMTP_DEFAULT_USER": "you@gmail.com",
              "MAIL_SMTP_DEFAULT_PASS": "your-app-password",
              "MAIL_SMTP_DEFAULT_SECURE": "starttls",
              "MAIL_IMAP_WRITE_ENABLED": "true",
              "MAIL_SMTP_WRITE_ENABLED": "true"
            }
          }
        }
      }

      That's it. Your AI agent can now read, search, send, reply, and manage emails.

      Microsoft Account? Use Graph API

      Microsoft blocks SMTP on personal accounts. Use Graph API instead:

      json
      {
        "env": {
          "MAIL_IMAP_DEFAULT_HOST": "outlook.office365.com",
          "MAIL_IMAP_DEFAULT_USER": "you@hotmail.com",
          "MAIL_IMAP_DEFAULT_PASS": "your-app-password",
          "MAIL_OAUTH2_DEFAULT_PROVIDER": "microsoft",
          "MAIL_OAUTH2_DEFAULT_CLIENT_ID": "9e5f94bc-e8a4-4e73-b8be-63364c29d753",
          "MAIL_OAUTH2_DEFAULT_CLIENT_SECRET": "none",
          "MAIL_OAUTH2_DEFAULT_REFRESH_TOKEN": ""
        }
      }

      Get your token in 1 minute with device code flow. See Account Setup Guide.

      31 MCP Tools

      Read (9 tools)

      ToolWhat it does
      `list_all_accounts`List all accounts with capabilities (IMAP, SMTP, Graph, EWS)
      `imap_list_accounts`List IMAP accounts
      `imap_verify_account`Test connectivity and auth
      `imap_list_mailboxes`List folders
      `imap_mailbox_status`Message counts
      `imap_search_messages`Search with cursor pagination
      `imap_get_message`Parsed message (text, HTML, attachments)
      `imap_get_message_raw`RFC822 source
      `imap_get_attachment`Download one attachment to disk (bypasses the raw size cap)

      Write (11 tools)

      ToolWhat it does
      `imap_update_message_flags`Add/remove flags
      `imap_copy_message`Copy (cross-account supported)
      `imap_move_message`Move to folder
      `imap_delete_message`Delete with confirmation
      `imap_create_mailbox`Create folder
      `imap_delete_mailbox`Delete folder
      `imap_rename_mailbox`Rename folder
      `imap_append_message`Append raw message
      `imap_bulk_move`Move up to 500 at once
      `imap_bulk_delete`Delete up to 500 at once
      `imap_bulk_update_flags`Flag up to 500 at once

      Send (5 tools)

      ToolWhat it does
      `smtp_send_message`Send email (text/HTML, CC/BCC)
      `smtp_reply_message`Reply with threading headers
      `smtp_forward_message`Forward with original inline
      `smtp_verify_account`Test SMTP connectivity
      `graph_send_message`Send via Microsoft Graph API (with reply threading)

      EWS — Exchange Web Services (3 tools)

      ToolWhat it does
      `ews_search_messages`Search emails via EWS (inbox, sent, drafts, etc.)
      `ews_get_message`Get full email content via EWS
      `ews_send_message`Send email via EWS

      Attachments

      Send files with any send tool. Two modes:

      json
      // Large files — MCP reads from disk (recommended)
      "attachments": [{"file_path": "/path/to/report.pdf"}]
      
      // Small files — inline base64
      "attachments": [{"filename": "note.txt", "content_type": "text/plain", "content_base64": "SGVsbG8="}]

      Filename and MIME type are auto-detected from the file path. Reply with `include_original_attachments: true` to forward original attachments.

      Downloading an attachment from a received message: use `imap_get_attachment`

      with the `message_id` and a `part_id` (from `imap_get_message`) or `filename`.

      It writes the decoded file to disk and returns the path — no size cap, and the

      binary stays out of the response. Set the default download directory with

      `MAIL_ATTACHMENT_DOWNLOAD_DIR` (falls back to the system temp dir), or pass

      `output_dir` per call.

      Bulk Operations (2 tools)

      ToolWhat it does
      `imap_search_and_move`Search + move matches
      `imap_search_and_delete`Search + delete matches

      Setup Helper (1 tool)

      ToolWhat it does
      `get_setup_guide`Provider-specific setup instructions (Microsoft OAuth2, Gmail App Passwords, Zoho, etc.)

      Multi-Account

      Configure as many accounts as you need:

      bash
      # Gmail
      MAIL_IMAP_GMAIL_HOST=imap.gmail.com
      MAIL_IMAP_GMAIL_USER=me@gmail.com
      MAIL_IMAP_GMAIL_PASS=app-password
      
      # Microsoft 365
      MAIL_IMAP_WORK_HOST=outlook.office365.com
      MAIL_IMAP_WORK_USER=me@company.com
      MAIL_OAUTH2_WORK_PROVIDER=microsoft
      MAIL_OAUTH2_WORK_CLIENT_ID=your-client-id
      MAIL_OAUTH2_WORK_CLIENT_SECRET=none
      MAIL_OAUTH2_WORK_REFRESH_TOKEN=your-token
      
      # Zoho
      MAIL_IMAP_DEFAULT_HOST=imap.zoho.com
      MAIL_IMAP_DEFAULT_USER=info@mydomain.com
      MAIL_IMAP_DEFAULT_PASS=password
      MAIL_SMTP_DEFAULT_HOST=smtp.zoho.com
      MAIL_SMTP_DEFAULT_USER=info@mydomain.com
      MAIL_SMTP_DEFAULT_PASS=password
      MAIL_SMTP_DEFAULT_SECURE=starttls

      Use `account_id` in tool calls: `"account_id": "gmail"`, `"account_id": "work"`, `"account_id": "default"`.

      Security

      • TLS enforced on all connections (except localhost proxies)
      • Passwords in SecretString — never logged or returned in responses
      • Write operations gated — require explicit `MAIL_IMAP_WRITE_ENABLED=true`
      • Send operations gated — require explicit `MAIL_SMTP_WRITE_ENABLED=true`
      • Delete confirmation — requires `confirm: true`
      • HTML sanitized with ammonia (prevents XSS)
      • Bounded outputs — body text, HTML, attachments truncated to configurable limits
      • OAuth2 tokens cached with 10-minute refresh margin
      • No secrets in responses — credentials never exposed via MCP tools

      Configuration Reference

      Full environment variable reference

      IMAP (per account)

      VariableRequiredDefaultDescription
      `MAIL_IMAP__HOST`YesIMAP server
      `MAIL_IMAP__PORT`No993IMAP port
      `MAIL_IMAP__USER`YesUsername
      `MAIL_IMAP__PASS`Yes*Password (*optional with OAuth2)
      `MAIL_IMAP__SECURE`NotrueUse TLS

      SMTP (per account)

      VariableRequiredDefaultDescription
      `MAIL_SMTP__HOST`YesSMTP server
      `MAIL_SMTP__PORT`No587SMTP port
      `MAIL_SMTP__USER`YesUsername
      `MAIL_SMTP__PASS`NoPassword (optional with OAuth2)
      `MAIL_SMTP__SECURE`Nostarttls`starttls`, `tls`, or `plain`
      `MAIL_SMTP__FROM_EMAIL`No= `_USER`Sender address when it differs from the SMTP auth username (e.g. shared/group mailboxes)

      OAuth2 (per account)

      VariableRequiredDefaultDescription
      `MAIL_OAUTH2__PROVIDER`Yes`google` or `microsoft`
      `MAIL_OAUTH2__CLIENT_ID`YesOAuth2 client ID
      `MAIL_OAUTH2__CLIENT_SECRET`YesClient secret (`none` for public clients)
      `MAIL_OAUTH2__REFRESH_TOKEN`YesRefresh token

      Graph API OAuth2 (per account)

      VariableRequiredDefaultDescription
      `MAIL_GRAPH__PROVIDER`Yes`microsoft`
      `MAIL_GRAPH__CLIENT_ID`YesOAuth2 client ID
      `MAIL_GRAPH__CLIENT_SECRET`YesClient secret (`none` for public clients)
      `MAIL_GRAPH__REFRESH_TOKEN`YesRefresh token (Mail.Send scope)

      EWS — Exchange Web Services (per account, simplest for Microsoft)

      VariableRequiredDefaultDescription
      `MAIL_EWS__USER`YesEmail address
      `MAIL_EWS__REFRESH_TOKEN`YesOAuth2 refresh token (EWS scope)
      `MAIL_EWS__CLIENT_ID`No`d3590ed6...` (Microsoft Office)OAuth2 client ID
      `MAIL_EWS__CLIENT_SECRET`No`none`Client secret

      > Tip: EWS only needs 2 variables (USER + REFRESH_TOKEN). Client ID defaults to Microsoft Office which has all permissions pre-approved.

      Global Settings

      VariableDefaultDescription
      `MAIL_IMAP_WRITE_ENABLED`falseEnable IMAP write operations
      `MAIL_SMTP_WRITE_ENABLED`falseEnable SMTP/Graph send operations
      `MAIL_SMTP_SAVE_SENT`falseSave sent emails to IMAP Sent folder (enable if your provider doesn't auto-save on send — e.g. Gmail does, Zoho doesn't always)
      `MAIL_SMTP_CONNECT_TIMEOUT_MS`30000SMTP TCP/TLS/auth timeout (connect phase)
      `MAIL_SMTP_SEND_TIMEOUT_MS`300000SMTP DATA transmission timeout (5 min — accommodates large attachments)
      `MAIL_SMTP_TIMEOUT_MS`_(deprecated)_Legacy single timeout. Honored as fallback for `MAIL_SMTP_SEND_TIMEOUT_MS`. Prefer the split vars above.
      `MAIL_IMAP_CONNECT_TIMEOUT_MS`30000TCP connection timeout
      `MAIL_IMAP_GREETING_TIMEOUT_MS`15000TLS/greeting timeout
      `MAIL_IMAP_SOCKET_TIMEOUT_MS`300000Socket I/O timeout

      Roadmap

      • [x] IMAP read operations (search, fetch, parse)
      • [x] IMAP write operations (copy, move, delete, flags)
      • [x] IMAP bulk operations (up to 500 per call)
      • [x] Cursor-based pagination with TTL
      • [x] SMTP send, reply, forward
      • [x] Microsoft Graph API (sendMail)
      • [x] OAuth2 XOAUTH2 (Google + Microsoft)
      • [x] Separate Graph API tokens for enterprise
      • [x] Multi-account via environment variables
      • [x] PDF text extraction from attachments
      • [x] HTML sanitization (ammonia)
      • [x] Provider setup documentation with direct links
      • [x] Attachment sending (SMTP/Graph)
      • [x] Reply with original attachments
      • [x] CDATA sanitization (Zoho bug fix)
      • [x] Email confirmation protocol (preview before send)
      • [x] Token-optimized instructions (75% reduction)
      • [x] On-demand setup guide tool
      • [x] EWS (Exchange Web Services) — single token for read + send on Microsoft
      • [x] EWS with Microsoft Office Client ID (works on restricted tenants)
      • [x] Graph API threading — `createReply` flow for proper conversation threading
      • [x] HTML formatting guidance — LLM prefers multipart (text + HTML) for human emails
      • [x] Sent folder archiving preserves full MIME — byte-identical copy of what the recipient received (v0.4.1)
      • [x] Localized Sent folder detection — Spanish / Portuguese / French / German / Italian / Dutch / Polish (v0.4.1)
      • [x] EWS feature parity with SMTP/Graph — BCC, threading headers, recipient validation (v0.4.1)
      • [x] EWS XML parser via `quick-xml` — correct entity/CDATA/namespace handling (v0.4.1)
      • [ ] SQLite + FTS5 local email cache — instant searches (<10ms vs 3-10s)
      • [ ] Incremental sync — UIDVALIDITY + last UID delta sync
      • [ ] Connection pooling — persistent IMAP sessions per account
      • [ ] Cross-account search — search all accounts at once
      • [ ] Email statistics — counts, top senders, activity by date

      Future

      • [ ] Docker image
      • [ ] npm/npx distribution
      • [ ] Draft management
      • [ ] Contact search
      • [ ] IMAP IDLE (real-time notifications)
      • [ ] Hosted documentation site

      Documentation

      GuideDescription
      Account SetupStep-by-step per provider, OAuth2, App Passwords, Azure Client ID
      Tool ContractComplete tool definitions and schemas
      Message ID FormatStable message identifier format
      Cursor PaginationPagination behavior and expiration
      SecuritySecurity features and best practices
      Advanced ConfigurationTimeouts and performance tuning

      Development

      bash
      cargo test              # 64 unit + integration tests
      cargo fmt -- --check    # formatting
      cargo clippy --all-targets -- -D warnings  # linting

      See `AGENTS.md` for contributor guidelines.

      Releasing

      Releases are automated via `cargo-dist`. To ship a new version:

      1. Bump `version = "X.Y.Z"` in `Cargo.toml` (the release workflow enforces

      that this matches the pushed tag).

      2. Commit the bump + any release notes to `main`.

      3. Tag and push:

      bash
      git tag vX.Y.Z
         git push origin main --tags

      4. The `push: tags: ['v*']` trigger in `.github/workflows/release.yml`

      compiles binaries for Linux / macOS (Intel + Apple Silicon) / Windows,

      generates installer scripts (`.sh`, `.ps1`), creates the GitHub Release,

      and attaches all artifacts with SHA256 checksums.

      5. If anything fails you can re-run the workflow manually from the Actions

      tab (the `workflow_dispatch` trigger is preserved as an escape hatch).

      npm publishing is intentionally disabled. The upstream fork was

      configured to publish as `@bradsjm/mail-imap-mcp-rs`, a scope this

      organization does not own, which caused every release to 404 on `npm

      publish`. The npm tarball is still generated and attached to each GitHub

      Release so users can install via `npm install ./mail-mcp-npm-package.tar.gz`

      manually. To enable npm registry publishing for this fork: create an npm

      org (e.g. `@tecnologicachile`), configure Trusted Publishing on

      npmjs.com pointing at this repo, set `publish-jobs = ["npm"]` in

      `dist-workspace.toml`, and run `dist generate --allow-dirty` to restore

      the `publish-npm` job in `release.yml`.

      Contributing

      Contributions welcome! Check out the issues for good first issues.

      License

      MIT License — see LICENSE for details.

      Frequently asked questions

      What is mail-mcp?

      mail-mcp is Secure IMAP/SMTP/EWS/Graph API MCP server in Rust — Microsoft 365, Hotmail, Gmail, Zoho, OAuth2, multi-account

      How do I install mail-mcp?

      Open the GitHub repository and follow its README. Most MCP servers are added to your client's MCP config, then called by your agent.

      Is mail-mcp open source?

      Yes — it is hosted on GitHub at https://github.com/tecnologicachile/mail-mcp and has 73 stars.

      Related MCP tools

      Run your own MCP server? See who uses it and what to fix.

      Measure it with TrackMCP