Mailoo - Email MCP Server

io.github.Bitfloo由 GitHub 帳號 Bitfloo 發布v0.3.5更新於 Oct 31, 2026

Email MCP server for AI agents: IMAP/SMTP, Gmail, Outlook, calendar, ManageSieve, AI triage, OAuth2.

已驗證STDIO僅桌面效率與工作流溝通與協作

概覽

AI 產生的概覽

本機 MCP 伺服器,讓 AI 助理透過 IMAP/SMTP 讀取、搜尋、寄送、排程與分揀郵件,並可操作行事曆。

可以做什麼
  • 讀取與檢索郵件:列出帳號、列出郵件、依 ID 取得單封郵件,唯讀模式只會開放讀取類工具。
  • 寄送與管理郵件:寄信、回覆、轉寄、寄送草稿、刪除、批次操作與信箱管理,高風險工具支援 dryRun 預覽。
  • 排程寄送:透過伺服器、命令列或系統層級常駐程式處理待寄佇列。
  • 新郵件自動化:IMAP IDLE 監看搭配靜態規則與 AI 掛鉤預設,進行分揀、加標籤、加旗標與建立行事曆事件。
  • 管理 Sieve 腳本,並在支援時建立行事曆事件與提醒。
適用情境
適合讓助理摘要收件匣、起草或寄送回覆、歸檔與標記郵件,或安排稍後寄出的郵件。最適合單一使用者的桌面環境,讓助理直接操作自己的 IMAP/SMTP 信箱,而非託管式郵件 API。
執行需求
需要 Node.js 24 或更新版本與 npx,或使用 Docker。需要一個信箱帳號的 IMAP 與 SMTP 主機名稱,以及密碼或應用程式專用密碼,或 OAuth2 的用戶端 ID、用戶端密鑰與更新權杖。設定存於帳號精靈產生的 TOML 檔,或透過 MCP_EMAIL_* 環境變數提供。需要能連線郵件伺服器;桌面通知與排程常駐程式依作業系統而異。
安裝前請注意
預設開啟寫入:除非啟用唯讀模式,否則可以寄送、刪除、批次操作與修改 Sieve 腳本,建議設定 MCP_EMAIL_READ_ONLY 或使用 dryRun。郵件內容會傳送給 MCP 用戶端及其模型供應商;選用的分類與 webhook 會把資料送到第三方服務。涉及密鑰包括 MCP_EMAIL_OAUTH2_CLIENT_SECRET、MCP_EMAIL_HTTP_TOKEN 與 MCP_EMAIL_ALERT_WEBHOOK_URL。不可信內容標記無法保證抵禦郵件中的提示注入。OAuth2 支援仍屬實驗性。

安裝

在 SourceWeft 中

  1. 開啟 儀表板中的 Mailoo - Email MCP Server,將其新增到工作區。
  2. 為需要使用其工具的對話啟用該服務。

Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。

其他 MCP 客戶端

參照 儲存庫 中的啟動說明。

README

Mailoo

[standard-readme compliant] [license] [CI] [M8ven]

Mailoo is an open-source MCP server you run yourself to connect AI assistants and agents to multiple email accounts over IMAP and SMTP.

  • Find what you need — search and read email, threads, and attachments across accounts.
  • Act on email — prepare drafts, reply, and schedule messages through your assistant or agent.
  • Choose your setup — guided setup for Claude Desktop or Cursor, or a standard MCP connection for your own agent.

Maintained by Bitfloo, forked from email-mcp. Licensed under LGPL-3.0-or-later; see NOTICE for attribution.

Quickstart for Claude Desktop or Cursor

Install Node.js 24 or newer and prepare your email account first, including an app password if your provider requires one. Then run these commands in a terminal:

bash
npx -y @bitfloo/mailoo@latest account addnpx -y @bitfloo/mailoo@latest config editnpx -y @bitfloo/mailoo@latest install

In config edit, press Enter to keep the rate limit (10 for a new config), then choose Yes for Read-only mode? (disables all write operations). This saves read-only mode in TOML. Choose your client in the installer, restart it, and ask: “Summarize up to five unread emails. Include each sender, subject and any requested next step. Ask which account to use if needed.”

The wizard prints a read-only/core manual client entry; the installer does not apply those environment overrides. This guided path uses the read-only setting you saved in TOML. With untouched default settings, the installer's entry runs writable/full.

Follow the guided setup and check your first result →

Quickstart for agent builders

With Node.js 24 or newer and account credentials ready, create a local account using npx -y @bitfloo/mailoo@latest account add. Connect your MCP client over stdio:

json
{  "mcpServers": {    "mailoo": {      "command": "npx",      "args": ["-y", "@bitfloo/mailoo@latest", "stdio"],      "env": { "MCP_EMAIL_READ_ONLY": "true", "MCP_EMAIL_TOOLSET": "core" }    }  }}

Initialize the connection, list tools, then call list_accounts, list_emails, and get_email with the returned account name and message ID.

Follow the MCP request sequence and inspect its results →

Both quickstarts configure read-only access for the first read. The walkthrough also covers feature requirements and fallbacks, including AI triage, local calendars, ManageSieve, and experimental OAuth.

To enable writes later, run config edit, keep the rate limit, and choose No for read-only mode; for the builder entry, set MCP_EMAIL_READ_ONLY to "false". Restart the server after either change. Request sending or changes explicitly when you intend them. You can request a dryRun: true preview separately; Mailoo does not require a preview before sending.

Mailoo connects to the mail servers you configure. Your MCP client and its model provider receive the mail content you request. Optional TypeSafe classification and webhooks send data to their configured services when enabled. Usage reporting is off by default. See Capabilities & data flows for destinations and fields.

Highlights

Up to 57 tools across full, core, and minimal profiles, up to 7 prompts, and 6 resources. Includes calendar and reminders, scheduling, ManageSieve filters, and optional AI triage. See API for tools and docs/tools.md for behaviour and limits.

FeatureIn this tree
Multi-account IMAP/SMTP✅
Send / reply / forward (with dryRun preview)✅
Drafts & templates✅
Provider-aware labels & bulk ops✅
Schedule future emails✅
Real-time IMAP IDLE watcher✅
AI triage with presets✅
Desktop & webhook alerts✅
Calendar (ICS, Apple Calendar/Reminders)✅
Email analytics✅
OAuth2 (Gmail / M365)✅ experimental
Guided setup wizard✅
ManageSieve (server-side filters)✅
Sender auth headers (SPF/DKIM/DMARC)✅
Toolset profiles (full / core / minimal) & GBNF small-LLM ergonomics✅
MCP outputSchema & structuredContent✅

Table of Contents

Security & Sandboxing Architecture

Policy and how to report a vulnerability: SECURITY.md.

Mailoo applies controls to specific network, filesystem, and tool operations. It also labels untrusted mail for the model. Those labels do not guarantee how an LLM will respond to instructions inside an email.

[Mailoo Architecture]

Enforced operation controls:

  • Zero Shell Execution (execFile only) — Native OS integrations (macOS osascript for Calendar/Reminders/notifications, Linux notify-send, Windows powershell, and crontab installation) invoke child_process.execFile with argument arrays (shell: false), avoiding shell parsing of arguments.
  • SSRF & DNS-Rebinding Protection (net.BlockList) — Outbound alert webhooks validate URLs against an IPv4/IPv6 net.BlockList covering loopback, RFC 1918 private ranges, link-local/cloud metadata (169.254.169.254, 100.100.100.200), CGNAT, NAT64, 6to4, and reserved prefixes both at configuration time and at socket connection time (createWebhookConnectLookup), preventing DNS-rebinding attacks (src/safety/validation.ts).
  • Symlink-Free Write Containment (savePath) — Attachment and .eml exports via savePath are strictly confined to the working directory, walk every path segment with lstat, refuse hidden dotfiles and OS persistence directories (Library/LaunchAgents, .ssh, .gnupg), and open targets with O_CREAT | O_EXCL | O_NOFOLLOW under a 50 MiB cap (5 MiB inline base64 cap). An .eml export is capped by its own size; an attachment by its encoded size in the message, which for base64 runs above the file size (details).
  • Strict Credential File Permissions (0600) — config.toml is written and verified with mode 0600 inside a 0700 config directory ($XDG_CONFIG_HOME/mailoo/config.toml). Passwords and message bodies are redacted from the JSONL write audit log (src/safety/audit.ts).
  • TLS & STARTTLS Verification — tls enforces implicit TLS with certificate verification (verify_ssl = true by default); starttls fails closed if the server does not advertise STARTTLS. Turning both off is accepted only for a loopback host. ManageSieve refuses AUTHENTICATE PLAIN over unencrypted sockets without STARTTLS.
  • Operational Guardrails (MCP_EMAIL_READ_ONLY, MCP_EMAIL_TOOLSET, dryRun) —
    • MCP_EMAIL_READ_ONLY=true disables all write tools, background watchers, hooks, and schedulers at startup, exposing only the 31 read-only tools.
    • MCP_EMAIL_TOOLSET=full|core|minimal scopes the registered tool surface to match the agent's capability tier and context budget.
    • dryRun: true on high-risk tools (send_email, reply_email, forward_email, send_draft, delete_email, bulk_action, delete_mailbox, sieve_put_script, sieve_delete_script) validates all parameters and account resolution and returns a [DRY RUN] preview without modifying server state.
  • Per-Account Rate Limiting — rate_limit (default 10 sends/minute) enforces an independent token bucket per account (src/safety/rate-limiter.ts). Buckets live in memory, so each server process and each mailoo scheduler check run counts on its own.

Untrusted-content labels: Email subjects, sender headers, bodies, previews, and authentication results are wrapped in <<<UNTRUSTED_EXTERNAL_CONTENT>>> … <<<END_UNTRUSTED_EXTERNAL_CONTENT>>> delimiters. Mailoo strips forged copies of these markers from the payload before wrapping it. This fencing identifies external content as data for the receiving model; it is not a sandbox for model behaviour or a guarantee against prompt injection.

LLM Ergonomics & Toolset Profiles

Mailoo is designed to work across both frontier models (Claude, GPT, Gemini) and local/small 3B–8B parameter models (llama.cpp GBNF grammars, Ollama, vLLM):

  • Toolset Profiles (MCP_EMAIL_TOOLSET / settings.toolset):
    • full (default) — All 57 tools (31 in read_only mode), including Calendar, Reminders, ManageSieve, Watcher, Scheduler, Templates, Contacts, and Analytics.
    • core — 19 tools (10 in read_only mode) covering core email workflows (accounts, mailboxes, emails, threads, send, drafts, manage, bulk, attachments, health).
    • minimal — 14 tools (8 in read_only mode) tailored for 3B–4B models with tight context budgets (accounts, mailboxes, emails, send, manage, health).
  • Optional account & Smart Account Resolution — When only a single account is configured or default_account (MCP_EMAIL_DEFAULT_ACCOUNT) is set, the account parameter is completely optional across tools. Account lookup also matches case-insensitively by profile name or email address (e.g., passing "[email protected]" resolves the matching account), and returns actionable error messages listing valid accounts (name (email) pairs) when ambiguous.
  • Lossless Parameter Normalization & GBNF Compatibility — Single-message tools accept emailId, id, or email_id as either string or integer (42 or "42"). Batch tools (get_emails, bulk_action) accept string or numeric ID arrays (["42", "43"] or [42, 43]). list_emails and search_emails accept both hasAttachment and has_attachment. All Zod schemas avoid regex lookaheads so llama.cpp GBNF grammar compilation succeeds without errors.
  • Context Window Protection — get_email and get_thread default to format="text" and enforce a default maxLength of 20000 characters (appending a clear truncation notice when exceeded) so large HTML newsletters never overflow small context windows.
  • MCP outputSchema & structuredContent — Tools declare structured outputSchema contracts and return machine-readable structuredContent alongside human-readable text blocks.
  • Outgoing attachment paths must be regular files under the working directory or home — Security considerations

Docs

TopicWhere
First account, Claude Desktop / Cursor, and an agent's first MCP callsdocs/getting-started.md
IMAP credentials, Gmail / iCloud app passwords, and experimental Google / Microsoft OAuthdocs/authentication.md
Sent APPEND, IMAP4rev2, Sieve, read_only, reconnect backoff, stdio EOF, shutdown, one mailbox writer per machinedocs/configuration.md
savePath, search dates, get_email_security, sieve tools, send/draft attachments, RFC 2047, protected folders, busy-mailbox timeout, bulk_action ids, calendar attachment savesdocs/tools.md
Performance notesdocs/performance-roadmap.md

Background

Most MCP email implementations provide only basic read/send. This server aims to be a full-featured email client for AI assistants, covering the entire lifecycle: reading, composing, managing, scheduling, and analyzing email — all from a single MCP server.

Key design decisions:

  • XDG-compliant config — TOML at ~/.config/mailoo/config.toml
  • Multi-account — Operate across multiple IMAP/SMTP accounts simultaneously
  • Layered services — Business logic is decoupled from MCP wiring for testability
  • Provider auto-detection — Gmail, Outlook, Yahoo, iCloud, Fastmail, ProtonMail, Zoho, GMX

Install

Requires Node.js ≥ 24.

Before opening the wizard, prepare your provider credentials. For a complete first run, choose the Claude Desktop / Cursor or agent builder quickstart.

bash
npx -y @bitfloo/mailoo@latest account add

That writes the local config and prints an MCP client snippet. The same package runs the server and the other commands:

bash
npx -y @bitfloo/mailoo@latestnpx -y @bitfloo/mailoo@latest test

npx -y @bitfloo/mailoo@latest with no subcommand starts the MCP server over stdio. Keep the @latest tag: npx then checks the registry on every start, while a bare package name runs any copy installed globally or in the current project, however old. Or install the mailoo command:

bash
npm install -g @bitfloo/mailoo# orpnpm add -g @bitfloo/mailoo# or, without a global install:pnpm dlx @bitfloo/mailoo account add

From a clone

Building this repository needs pnpm 12 (the packageManager version in package.json):

bash
git clone https://github.com/Bitfloo/mailoo.gitcd mailoopnpm install && pnpm buildnode dist/main.js account add

Docker

The running image needs Docker, not Node on the host. Create the config first with npx -y @bitfloo/mailoo@latest account add (or write the TOML by hand), then mount that directory into the container.

The image is ghcr.io/bitfloo/mailoo, built for linux/amd64 and linux/arm64. Version tags are bare semver (no v prefix); latest, alpine and the -bookworm / -alpine variants are published too. The 0.3 tag follows the newest 0.3.x release. Production setups should pin the full version from the release (it matches the npm version) or the image digest.

bash
docker pull ghcr.io/bitfloo/mailoo:0.3

To build from a clone instead (docker-compose.yml uses build: .):

bash
docker build -t ghcr.io/bitfloo/mailoo .

Note: The server uses stdio transport. Config is created on the host (npx -y @bitfloo/mailoo@latest account add, or a hand-written TOML) and mounted into the container.

Installing or starting Mailoo does not send a usage report. An install count stays off until MAILOO_INSTALL_COUNTER is 1, true, on, or yes (any letter case, surrounding spaces ignored) and MAILOO_INSTALL_COUNTER_URL is set. The POST body is only product, version, event (install), and node_major. Unset, 0, false, no, or off keeps it off. Full rules: Install count.

Usage

Commands below use npx -y @bitfloo/mailoo@latest. A global install accepts the same subcommands as mailoo. From a clone, after pnpm build, those subcommands are node dist/main.js (see From a clone).

Setup

bash
# Add an email account interactively (recommended)npx -y @bitfloo/mailoo@latest account add
# Or use the deprecated alias (same wizard)npx -y @bitfloo/mailoo@latest setup
# Or create a template config manuallynpx -y @bitfloo/mailoo@latest config init

The setup wizard auto-detects server settings, tests connections, saves config, and outputs the MCP client config snippet.

Test Connections

bash
npx -y @bitfloo/mailoo@latest test            # all accountsnpx -y @bitfloo/mailoo@latest test personal   # specific account

npx -y @bitfloo/mailoo@latest test / mailoo test is a live-account connection probe, not Vitest. Unit and integration tests are pnpm test / pnpm test:integration (see Contributing). OAuth2 accounts log in with an access token refreshed from the configured refresh token.

Configure Your MCP Client

Run the guided installer for Claude Desktop, Cursor or Windsurf, or paste a snippet below for any client. Mailoo is published to the official MCP Registry as io.github.Bitfloo/mailoo.

bash
npx -y @bitfloo/mailoo@latest install

The installer can register an npx, pnpm dlx, or global mailoo launch. A mailoo binary already on PATH can use "command": "mailoo" with "args": ["stdio"].

Claude Desktop

Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):

json
{  "mcpServers": {    "mailoo": {      "command": "npx",      "args": ["-y", "@bitfloo/mailoo@latest", "stdio"]    }  }}
Claude Code

Register the server with the claude CLI. The default scope is the current project only. Put --scope user before the server name to make it available in every project, or --scope project to write a shareable .mcp.json; anything after the -- is passed to the server, not to claude:

bash
claude mcp add mailoo -- npx -y @bitfloo/mailoo@latest stdio# or, available in every project:claude mcp add --scope user mailoo -- npx -y @bitfloo/mailoo@latest stdio
VS Code (GitHub Copilot)

Mailoo is not in the VS Code Extensions gallery. Point Copilot at the npx launch below.

Workspace (.vscode/mcp.json):

json
{  "servers": {    "mailoo": {      "type": "stdio",      "command": "npx",      "args": ["-y", "@bitfloo/mailoo@latest", "stdio"]    }  }}

User config (settings.json, all workspaces):

Open the Command Palette → Preferences: Open User Settings (JSON) and add:

json
{  "mcp": {    "servers": {      "mailoo": {        "type": "stdio",        "command": "npx",        "args": ["-y", "@bitfloo/mailoo@latest", "stdio"]      }    }  }}
Cursor

Edit ~/.cursor/mcp.json:

json
{  "mcpServers": {    "mailoo": {      "command": "npx",      "args": ["-y", "@bitfloo/mailoo@latest", "stdio"]    }  }}
Windsurf

Edit ~/.codeium/windsurf/mcp_config.json:

json
{  "mcpServers": {    "mailoo": {      "command": "npx",      "args": ["-y", "@bitfloo/mailoo@latest", "stdio"]    }  }}
Zed

Edit ~/.config/zed/settings.json:

json
{  "context_servers": {    "mailoo": {      "command": {        "path": "npx",        "args": ["-y", "@bitfloo/mailoo@latest", "stdio"]      }    }  }}
Mistral Vibe

Add to ~/.vibe/config.toml:

toml
[[mcp_servers]]name = "mailoo"transport = "stdio"command = "npx"args = ["-y", "@bitfloo/mailoo@latest", "stdio"]

To pass credentials directly instead of using a config file, use the env field:

toml
[[mcp_servers]]name = "mailoo"transport = "stdio"command = "npx"args = ["-y", "@bitfloo/mailoo@latest", "stdio"]env = { MCP_EMAIL_ADDRESS = "[email protected]", MCP_EMAIL_PASSWORD = "your-app-password", MCP_EMAIL_IMAP_HOST = "imap.example.com", MCP_EMAIL_SMTP_HOST = "smtp.example.com" }

MCP tools are exposed as mailoo_<tool_name> (e.g. mailoo_list_emails). Restart Vibe after editing the config.

Docker (any MCP client)

Run the server in a container — mount your config directory read-only. The image is ghcr.io/bitfloo/mailoo:0.3 (or build it with docker build -t ghcr.io/bitfloo/mailoo .):

bash
docker run --rm -i \  -v ~/.config/mailoo:/home/node/.config/mailoo:ro \  ghcr.io/bitfloo/mailoo:0.3

For MCP client configuration (e.g. Claude Desktop):

Replace /absolute/path/to/mailoo-config below with the full absolute path to the host directory containing config.toml. MCP clients spawn Docker directly: they do not expand ~ or shell variables in JSON arguments. The shell example above relies on shell expansion. On Windows with Docker Desktop, use a shared Windows host path such as C:/absolute/path/to/mailoo-config in this argument; do not copy the Unix path or shell line continuations.

json
{  "mcpServers": {    "mailoo": {      "command": "docker",      "args": [        "run", "--rm", "-i",        "-v", "/absolute/path/to/mailoo-config:/home/node/.config/mailoo:ro",        "ghcr.io/bitfloo/mailoo:0.3"      ]    }  }}
Single-account via environment variables (no config file needed)
json
{  "mcpServers": {    "mailoo": {      "command": "npx",      "args": ["-y", "@bitfloo/mailoo@latest", "stdio"],      "env": {        "MCP_EMAIL_ADDRESS": "[email protected]",        "MCP_EMAIL_PASSWORD": "your-app-password",        "MCP_EMAIL_IMAP_HOST": "imap.example.com",        "MCP_EMAIL_SMTP_HOST": "smtp.example.com"      }    }  }}

CLI Commands

npx -y @bitfloo/mailoo@latest [command]
Commands:  stdio                     Run as MCP server over stdio (default)  http [port] [host]        Streamable HTTP (default: port 8080 on 127.0.0.1 and ::1)  account list              List all configured accounts  account add               Add a new email account interactively  account edit [name]       Edit an existing account  account delete [name]     Remove an account  setup                     Deprecated alias for 'account add'  test                      Test connections for all or a specific account  install                   Register mailoo with MCP clients interactively  install status            Show registration status for detected clients  install remove            Unregister mailoo from MCP clients  config show               Show config (passwords masked)  config edit               Edit global settings (rate limit, read-only)  config path               Print config file path  config init               Create template config  scheduler check           Process pending scheduled emails  scheduler list            Show all scheduled emails  scheduler install         Install OS-level scheduler (launchd/crontab)  scheduler uninstall       Remove OS-level scheduler  scheduler status          Show scheduler installation status  notify test               Send a test desktop notification  notify status             Check platform notification support  --version, -v             Print the package version  help                      Show help

HTTP

http listens on 127.0.0.1 and ::1 (port 8080 unless you pass another port). A non-loopback address or a non-loopback name in MCP_EMAIL_HTTP_ALLOWED_HOSTS requires MCP_EMAIL_HTTP_TOKEN. Details, including 0.0.0.0 / :: and the 8 MiB body limit: docs/configuration.md.

bash
npx -y @bitfloo/mailoo@latest httpMCP_EMAIL_HTTP_TOKEN='replace-with-a-long-random-secret' npx -y @bitfloo/mailoo@latest http 8080 192.0.2.10

Clients send Authorization: Bearer <token> when a token is configured.

Configuration

Located at $XDG_CONFIG_HOME/mailoo/config.toml (default: ~/.config/mailoo/config.toml).

toml
[settings]rate_limit = 10  # max emails per minute per accountread_only = falsetoolset = "full"  # "full" | "core" | "minimal"default_account = "personal"  # optional when only one account is configuredsave_to_sent = true  # see docs/configuration.md — Gmail already files Sent
[[accounts]]name = "personal"email = "[email protected]"full_name = "Your Name"password = "your-app-password"
[accounts.imap]host = "imap.example.com"port = 993tls = true# disable_imap4rev2 = true  # Strato and similar SEARCH bugs# sieve_host = "imap.example.com"# sieve_port = 4190
[accounts.smtp]host = "smtp.example.com"port = 465tls = truestarttls = falseverify_ssl = true
[accounts.smtp.pool]enabled = truemax_connections = 1max_messages = 100

An IMAP or SMTP block with tls = false and starttls = false, in the file or from MCP_EMAIL_*, loads only for localhost, an address in 127.0.0.0/8 written in full (such as 127.0.0.1), or ::1; any other host is refused at config load.

OAuth2 (experimental)

Note: OAuth2 support is experimental and requires your own provider application and initial refresh token. The account wizard does not perform browser authorization. Follow the authentication guide for app registration, consent, token exchange, and connection checks.

Token refresh and code exchange give up after 15 seconds, so a provider that stops answering fails the connect with OAuth2 token refresh timed out after 15s instead of hanging it. Callers that need a fresh token at the same time share one refresh request. The IDLE watcher logs in with the same access tokens.

toml
[[accounts]]name = "work"email = "[email protected]"full_name = "Your Name"
[accounts.oauth2]provider = "google"            # or "microsoft"client_id = "your-client-id"client_secret = "your-client-secret"refresh_token = "your-refresh-token"
[accounts.imap]host = "imap.example.com"port = 993tls = true
[accounts.smtp]host = "smtp.example.com"port = 465tls = true
[accounts.smtp.pool]enabled = truemax_connections = 1max_messages = 100
Environment Variables

For single-account setups (overrides config file):

VariableDefaultDescription
MCP_EMAIL_ADDRESSrequiredEmail address
MCP_EMAIL_PASSWORDrequired without OAuth2Password or app password. Not needed for an OAuth2 account (MCP_EMAIL_OAUTH2_PROVIDER set)
MCP_EMAIL_OAUTH2_PROVIDER—google, microsoft or custom, read exactly as set; any other value is refused. Unset or empty means no OAuth2. An account in the environment replaces the config file, so custom set here fails at the first token request. Configure a custom account entirely in the config file, with token_url and auth_url under [accounts.oauth2]
MCP_EMAIL_OAUTH2_CLIENT_IDrequired with a providerOAuth2 client id
MCP_EMAIL_OAUTH2_CLIENT_SECRETrequired with a providerOAuth2 client secret
MCP_EMAIL_OAUTH2_REFRESH_TOKENrequired with a providerOAuth2 refresh token
MCP_EMAIL_IMAP_HOSTrequiredIMAP server hostname
MCP_EMAIL_SMTP_HOSTrequiredSMTP server hostname
MCP_EMAIL_ACCOUNT_NAMEdefaultAccount name
MCP_EMAIL_FULL_NAME—Display name
MCP_EMAIL_USERNAMEemailLogin username
MCP_EMAIL_IMAP_PORT993IMAP port
MCP_EMAIL_IMAP_TLStrueIMAP TLS
MCP_EMAIL_SMTP_PORT465SMTP port
MCP_EMAIL_SMTP_TLStrueSMTP TLS
MCP_EMAIL_SMTP_STARTTLSfalseSMTP STARTTLS
MCP_EMAIL_SMTP_VERIFY_SSLtrueVerify SSL certificates
MCP_EMAIL_SMTP_POOL_ENABLEDtrueEnable SMTP transport pooling
MCP_EMAIL_SMTP_POOL_MAX_CONNECTIONS1Max pooled SMTP connections
MCP_EMAIL_SMTP_POOL_MAX_MESSAGES100Max messages per pooled connection
MCP_EMAIL_RATE_LIMIT10Max sends per minute
MCP_EMAIL_READ_ONLYfalseDisable all write tools, background watchers, and schedulers
MCP_EMAIL_TOOLSETfullToolset profile to expose (full, core, or minimal)
MCP_EMAIL_DEFAULT_ACCOUNT—Default account name or email when account is omitted

Without the address, both hosts, and a password or a provider, the environment describes no account: Mailoo loads the config file instead, or fails with No configuration found. when there is none.

Sent copies, IMAP4rev2, Sieve host/port, and read_only env vars: docs/configuration.md.

Email Scheduling

The scheduler enables future email delivery with a layered architecture:

  1. MCP auto-check — Processes the queue on server startup, before the watcher connects, and every 60 seconds while the MCP server is running. With several stdio servers, only the one that runs the watcher and hooks checks. A check still running when the next one is due is skipped, and failed sends are logged to the connected MCP client at warning
  2. CLI — npx -y @bitfloo/mailoo@latest scheduler check for manual or cron-based processing. It sends with the same account settings as the server, including OAuth2 and save_to_sent, and closes its IMAP/SMTP connections before it exits
  3. OS-level daemon — npx -y @bitfloo/mailoo@latest scheduler install sets up launchd (macOS) or crontab (Linux) to run every minute, independently of the MCP server

Important — the daemon must be installed for reliable delivery. Without it, scheduled emails only fire while an AI client is actively connected. Your machine also needs to be running at the scheduled time; if it's asleep or off, the daemon will process overdue emails on next wake/startup. A failing send is attempted up to 3 times before the email is marked failed.

Setting up the daemon
bash
# Install (macOS launchd / Linux crontab — runs every minute)npx -y @bitfloo/mailoo@latest scheduler install
# Verify it's runningnpx -y @bitfloo/mailoo@latest scheduler status
# View pending / sent / failed scheduled emailsnpx -y @bitfloo/mailoo@latest scheduler list
# Trigger a manual check immediatelynpx -y @bitfloo/mailoo@latest scheduler check
# Remove the daemonnpx -y @bitfloo/mailoo@latest scheduler uninstall

Scheduled emails are JSON files in ~/.local/state/mailoo/scheduled/. Before sending, a check claims the entry with a lock file next to it and keeps that claim fresh while SMTP runs, so the in-process timer, mailoo scheduler check and other server processes do not send it twice. If the process dies after the server accepted a message but before it was recorded as sent, the entry can be sent again after the claim goes stale (at least once, not exactly once). Every attempt for one entry carries the same Message-ID, <schedule id@account domain>, so a receiving server or client can drop the second copy. Once SMTP has accepted a message, a failure to update the queue file is reported, and the entry is not sent again as long as either the sent record or the move into sent/ succeeded. If both failed, the next check sends it again unless it has used all its attempts, then it is marked failed. Each entry tracks its attempts (at most 3) and the last error; list_scheduled returns both, and scheduler list shows the error of a failed entry. An entry the rate limit holds back stays pending for the next check and does not use an attempt.

Real-time Watcher & AI Hooks

The IMAP IDLE watcher monitors configured mailboxes in real-time using persistent IDLE connections (separate from tool connections). Each folder stays in IDLE between fetches, IDLE resumes about a second after each fetch, and idle_timeout sets how often IDLE is restarted. With several stdio servers on one machine (one per MCP client window), only one of them runs the watcher, hooks and scheduler; see configuration. When new emails arrive:

  1. Static rules — Pattern-match on from/to/subject → apply labels, flag, or mark read instantly (no AI)
  2. AI triage — Remaining emails are analyzed via MCP sampling with a customizable preset prompt
  3. Notify mode — Falls back to logging if AI triage is disabled

Configure in config.toml:

toml
[settings.watcher]enabled = truefolders = ["INBOX"]idle_timeout = 1740     # 29 minutes (IMAP spec max is 30)
[settings.hooks]on_new_email = "triage" # "triage" | "notify" | "none"preset = "inbox-zero"   # "inbox-zero" | "gtd" | "priority-focus" | "notification-only" | "custom"auto_label = true       # apply AI-suggested labelsauto_flag = true        # flag urgent emailsbatch_delay = 5         # seconds to batch before triage
# User context — appended to preset's AI promptcustom_instructions = """I'm a software engineer. Emails from @example.com are always high priority.Newsletters I read: TL;DR, Hacker Newsletter."""
# Static rules — run BEFORE AI, skip AI if matched[[settings.hooks.rules]]name = "GitHub Notifications"match = { from = "*@example.com" }actions = { labels = ["Dev"], mark_read = true }
[[settings.hooks.rules]]name = "Newsletter Archive"match = { from = "*@example.com|*@example.test" }actions = { labels = ["Newsletter"] }
[[settings.hooks.rules]]name = "VIP Contacts"match = { from = "[email protected]|[email protected]" }actions = { flag = true, labels = ["VIP"] }

Batches run one at a time. At most 500 new emails wait for the next batch; more are skipped with a warning in the log and stay in the mailbox unchanged.

Presets
PresetFocusSuggested Labels
inbox-zeroAggressive categorization + archivingNewsletter, Notification, Updates, Finance, Social, Promo
gtdGetting Things Done contexts@Action, @Waiting, @Reference, @Someday, @Delegated
priority-focusSimple priority classification (default)(none — just priority + flag)
notification-onlyNo AI triage, just log(none)
customUser defines full system promptUser-defined
Static Rules

Static rules use glob-style patterns (*@example.com) with | as OR separator (*@example.com|*@example.test). All conditions within a match are AND'd. A to pattern matches when any one recipient matches. An empty pattern counts as no condition, and a rule with no condition matches nothing. First matching rule wins.

Available actions: labels (string array), flag (boolean), mark_read (boolean), alert (boolean — forces desktop notification).

Alerts

Urgency-based multi-channel notification routing — grab attention for important emails even when you're not looking at the chat. All channels are opt-in and disabled by default.

PriorityDesktopSoundMCP Log LevelWebhook
urgent✅ Banner🔊 Alertalert✅
high✅ Banner🔇 Silentwarning✅
normal❌❌info❌
low❌❌debug❌

The MCP log entry at the listed level names the account, folder, UID, labels and rule. In alert entries, sender and subject appear only at debug: in a separate entry, or for low in the single entry.

toml
[settings.hooks.alerts]desktop = true              # OS-level notifications (macOS/Linux/Windows)sound = true                # play sound for urgent emailsurgency_threshold = "high"  # minimum priority to trigger desktop alertwebhook_url = "https://ntfy.sh/my-email-alerts"  # optional: Slack, Discord, ntfy.sh, etc.webhook_events = ["urgent", "high"]allow_private_webhooks = false  # default; true allows a LAN, VPN, or tailnet target

Details: docs/configuration.md.

Supported platforms: macOS (Notification Center via osascript), Linux (notify-send), Windows (PowerShell toast). Zero npm dependencies — uses native OS commands.

Notification setup by platform:

macOS

Desktop notifications use osascript (built-in). The terminal app running the MCP server needs notification permission:

  1. Open System Settings → Notifications & Focus
  2. Find your terminal app (Terminal, iTerm2, VS Code, Cursor, etc.)
  3. Enable Allow Notifications and choose Banners or Alerts
  4. Ensure Focus / Do Not Disturb is not blocking notifications

Use check_notification_setup to diagnose and test_notification to verify.

Linux

Requires notify-send from libnotify. For sound alerts, paplay is also needed:

bash
# Ubuntu / Debiansudo apt install libnotify-bin pulseaudio-utils
# Fedorasudo dnf install libnotify pulseaudio-utils
# Archsudo pacman -S libnotify

Desktop notifications require a running display server (X11/Wayland) — they will not work in headless/SSH sessions.

Windows

Uses PowerShell toast notifications (built-in):

  1. Open Settings → System → Notifications
  2. Ensure Notifications is turned on
  3. Set Focus Assist to allow notifications
  4. If using Windows Terminal, ensure its notifications are enabled

AI-configurable: The AI can check, test, and configure notifications at runtime:

  • check_notification_setup — diagnose platform support and show setup instructions
  • test_notification — send a test notification to verify everything works
  • configure_alerts — enable/disable desktop, sound, threshold, webhook (with optional persist to config file)

Webhook payload:

json
{  "event": "email.urgent",  "account": "work",  "sender": { "name": "John CEO", "address": "[email protected]" },  "subject": "Q4 Review Due Today",  "priority": "urgent",  "labels": ["VIP"],  "rule": "VIP Contacts",  "timestamp": "2026-02-18T11:30:00Z"}

Static rules can force desktop notifications with alert = true, regardless of urgency threshold:

toml
[[settings.hooks.rules]]name = "VIP Contacts"match = { from = "[email protected]" }actions = { flag = true, alert = true, labels = ["VIP"] }

Features:

  • Auto-reconnect — Retries a dropped or failed folder connection on the reconnect backoff schedule; mail delivered while the connection was down is reported after the reconnect, unless the server changed the folder's UIDVALIDITY; a folder that has not connected yet reports only mail that arrives after its first successful connect
  • Batching — Groups arrivals within a configurable delay to reduce AI calls
  • Rate limiting — Max 10 sampling calls per minute
  • Graceful degradation — Falls back to notify mode if client doesn't support sampling

System One (opt-in typed filing)

Optional TypeSafe System One classification on residue mail after static rules. Off by default. Both settings.watcher.enabled and settings.system_one.enabled must be on. Set TYPESAFE_API_KEY in the environment (never in TOML).

Only new mail in source_folders (default INBOX) that no static rule handled is classified. For each such message Mailoo sends one request to api.typesafe.ai (or TYPESAFE_BASE_URL). The request carries (src/services/mail-arrival/state.ts, src/services/mail-arrival/questions.ts):

DataSent
SenderDisplay name and address from From
SubjectThe full subject
LinksURLs found in the subject, and in the body when include_body = true
AttachmentsFile name and MIME type of each attachment, not the file contents
Sender authenticationSPF, DKIM (result and signing domain), and DMARC results; the From, Reply-To, and Return-Path domains; whether a List-Unsubscribe header is present
MailboxThe configured account name and the folder the message is in
Folder rulesFor each [[settings.system_one.folders]] entry: path, description, and false_criteria, or the other folder paths when false_criteria is unset
ModelThe model setting (default jev-latest)
BodyOnly with include_body = true: plain text without the quoted reply chain, cut to body_max_chars (default 6000, maximum 24000)

Not sent: To, Cc, and Bcc addresses, Message-ID, other raw headers, attachment contents, and the body while include_body is false.

Mailoo remembers the last 5000 messages it classified and does not send them again. A message that reaches two of your accounts is classified once in each. A message whose request, or whose header read, failed is not remembered, so the next notice of it tries again.

auto_move and auto_flag are separate poles and default false. Filing destinations come from folders[].path (validated against IMAP LIST), not a live listing of every mailbox.

toml
[settings.system_one]enabled = falseinclude_body = falseauto_move = falseauto_flag = false
[[settings.system_one.folders]]path = "Receipts"description = "Invoices, receipts, and payment confirmations."

on_new_email = "notify" or "triage" both feed System One when it is on. none stays off.

Capabilities & data flows

Outbound calls, child processes, files, and environment variables below are what src/ does. Hook triage that uses MCP sampling stays inside the connected client; Mailoo does not dial a separate model host for that path.

Network

DestinationWhenCode
Configured IMAP host and portReads, writes, IDLEsrc/connections/manager.ts, src/services/watcher.service.ts
Configured SMTP host and portSendssrc/connections/manager.ts
ManageSieve host (IMAP host if unset) port 4190 by default (sieve_port / MCP_EMAIL_SIEVE_PORT)Filter scriptssrc/services/sieve.service.ts
https://oauth2.googleapis.com/tokenGoogle token refresh and code exchangesrc/services/oauth.service.ts
https://accounts.google.com/o/oauth2/v2/authAuthorization URL for the operator's browser; the process does not fetch itsrc/services/oauth.service.ts
https://login.microsoftonline.com/common/oauth2/v2.0/tokenMicrosoft token refresh and code exchangesrc/services/oauth.service.ts
https://login.microsoftonline.com/common/oauth2/v2.0/authorizeAuthorization URL for the operator's browser; the process does not fetch itsrc/services/oauth.service.ts
Custom token_url / auth_urlSame split when oauth2.provider is custom. Config validation requires https; plain http is accepted only for localhost, 127.0.0.0/8, or ::1 (src/config/schema.ts)src/services/oauth.service.ts
Configured webhook URL (http or https POST)Alerts, after a DNS lookup of that hostsrc/services/notifier.service.ts, src/safety/validation.ts
https://api.typesafe.ai (or TYPESAFE_BASE_URL)System One classification, only when that integration is on. Fields sent: System Onesrc/services/mail-arrival/index.ts
MAILOO_INSTALL_COUNTER_URL when MAILOO_INSTALL_COUNTER is onOne POST after the stdio or HTTP server starts. No request when either variable is unset. Fields: Install countsrc/services/install-count.ts

mailoo http listens. The install count below is the only outbound call that mode adds, and it runs only when that count is on and a URL is set. Provider presets in src/cli/providers.ts fill the IMAP and SMTP hosts the wizard saves.

Install count

The count is off. Nothing is sent at npm install, at account add, or when the server starts, until both of the following are set:

  • MAILOO_INSTALL_COUNTER is 1, true, on, or yes (any letter case, surrounding spaces ignored)
  • MAILOO_INSTALL_COUNTER_URL is the collector URL

0, false, no, off, or an unset MAILOO_INSTALL_COUNTER keeps the count off. An opted-in server with a blank URL does not call out. There is no built-in collector address.

Bitfloo runs a collector at https://mailoo.bitfloo.com/install. It keeps one count per UTC day, Mailoo version, and Node.js major, stores no IP address, and shows the total on mailoo.bitfloo.com. To be counted there:

sh
MAILOO_INSTALL_COUNTER=1 MAILOO_INSTALL_COUNTER_URL=https://mailoo.bitfloo.com/install npx -y @bitfloo/mailoo@latest

One POST runs after the stdio or http server has started. A later start does not send again. account add, test, the scheduler, and the other commands do not send. The count is skipped when CI is non-blank and not 0, false, no, or off in any letter case, and when the process is a test run (VITEST non-blank, or NODE_ENV=test).

The body is a JSON object with exactly these fields:

FieldValue
productmailoo
versionthe running package version
eventinstall
node_majorthe Node.js major version, one integer

Request headers are content-type: application/json and user-agent: mailoo. The request does not follow redirects. http is refused except for localhost, 127.0.0.0/8, or ::1. Any other URL must be https. A URL with a username, a password, a query, or a fragment is refused.

The body never includes an email address, account name, password, token, mail-server hostname, message id, folder name, or anything taken from a message. It also never includes a machine id, an operating-system username, a home directory, a working directory, or an IP address.

Before the request, the server creates $XDG_STATE_HOME/mailoo/install-count (mode 0600) containing the digit 1 and nothing else, and sends only if it created that file. The file stays whatever the collector answers, so a state directory sends at most one request: two servers that start at once send one between them, and a refused, failed, or timed-out request is not retried. A state directory that cannot be written sends nothing. Delete the file to allow this state directory to count again.

Processes

ProgramWhenCode
osascriptmacOS notifications, calendar events, reminderssrc/services/notifier.service.ts, src/services/osascript.ts (calendar and reminders)
notify-sendLinux desktop notificationssrc/services/notifier.service.ts
paplayLinux notification soundsrc/services/notifier.service.ts
powershellWindows balloon notificationssrc/services/notifier.service.ts
which (Windows: where)Checks that osascript, afplay, notify-send, paplay, or powershell exists. afplay is only probed; macOS sound goes through osascriptsrc/services/notifier.service.ts
xdg-openOpens a temporary calendar file on Linuxsrc/services/local-calendar.service.ts
launchctlInstalls, removes or checks (scheduler status) the macOS scheduler agentsrc/cli/scheduler.ts, src/cli/scheduler-install.ts
crontabInstalls, removes or checks (scheduler status) the Linux scheduler linesrc/cli/scheduler.ts, src/cli/scheduler-install.ts

Files

Paths follow the XDG defaults in src/config/xdg.ts unless XDG_CONFIG_HOME, XDG_DATA_HOME, or XDG_STATE_HOME is set.

PathWhat is writtenCode
$XDG_CONFIG_HOME/mailoo/config.toml (mode 0600)Accounts and settings, including passwords and OAuth secretssrc/config/loader.ts; configure_alerts with save through src/services/alerts-config.ts
$XDG_DATA_HOME/mailoo/audit.logAppend-only audit lines. File and directory modes: SECURITY.mdsrc/safety/audit.ts
$XDG_STATE_HOME/mailoo/scheduled/ and scheduled/sent/Scheduled-send JSON records (mode 0600), replaced through a temp file and renamesrc/services/scheduler.service.ts
$XDG_DATA_HOME/mailoo/calendar-attachments/Attachment files saved for a calendar eventsrc/services/imap.service.ts
$XDG_STATE_HOME/mailoo/mailbox-writers.lockWhich stdio server runs hooks, the watcher and the scheduler. The holder refreshes it and deletes it on shutdown; timings in configurationsrc/safety/writer-lease.ts
$XDG_STATE_HOME/mailoo/install-count (mode 0600)The digit 1, written before the install count request and kept whatever the answer. No identity. Absent until an opted-in server first tries to countsrc/services/install-count.ts
$XDG_STATE_HOME/mailoo/calendar-processed.json (mode 0600) and .lockWhich messages already had an automatic event added, found, or declined in the dialog, with each email's subject, replaced through a temp file and rename. A failed or timed-out add is not recorded, so the email stays eligible. A damaged file stops automatic calendar processing, and makes analyze_email_for_scheduling report an error, instead of starting oversrc/utils/calendar-state.ts
savePath under the working directorydownload_attachment or export_email when that argument is setsrc/tools/attachments.tool.ts, src/tools/export.tool.ts
OS temp directory, mailoo-event-*.icsLinux calendar file passed to xdg-opensrc/services/local-calendar.service.ts
~/Library/LaunchAgents/com.bitfloo.mailoo.scheduler.plistmacOS scheduler agent. Its stdout and stderr are /tmp/mailoo-scheduler.logsrc/cli/scheduler-install.ts, src/cli/scheduler.ts
User crontabOne Mailoo line on Linuxsrc/cli/scheduler-install.ts
MCP client config (Claude, Cursor, Windsurf)mailoo install merges the launch entry and replaces the file through a temp file and rename (mode 0600). A file that is not a JSON object (for example JSON with comments) is left unchanged and the command stopssrc/cli/install-commands.ts, src/cli/client-config.ts

Templates under $XDG_CONFIG_HOME/mailoo/templates/ are read, not written by the server.

Environment

Single-account setup reads MCP_EMAIL_* (src/config/loader.ts). The HTTP listener reads MCP_EMAIL_HTTP_HOST, MCP_EMAIL_HTTP_TOKEN, and MCP_EMAIL_HTTP_ALLOWED_HOSTS (src/safety/http-transport.ts). The same names are the Smithery config keys and the packages[].environmentVariables list in server.json.

NameRole
MCP_EMAIL_ADDRESS, MCP_EMAIL_PASSWORD, MCP_EMAIL_IMAP_HOST, MCP_EMAIL_SMTP_HOSTAccount. Password is required unless MCP_EMAIL_OAUTH2_PROVIDER is set
MCP_EMAIL_ACCOUNT_NAME, MCP_EMAIL_FULL_NAME, MCP_EMAIL_USERNAMEIdentity
MCP_EMAIL_IMAP_PORT, MCP_EMAIL_IMAP_TLS, MCP_EMAIL_IMAP_STARTTLS, MCP_EMAIL_IMAP_VERIFY_SSL, MCP_EMAIL_IMAP_DISABLE_IMAP4REV2, MCP_EMAIL_SIEVE_HOST, MCP_EMAIL_SIEVE_PORTIMAP and ManageSieve
MCP_EMAIL_SMTP_PORT, MCP_EMAIL_SMTP_TLS, MCP_EMAIL_SMTP_STARTTLS, MCP_EMAIL_SMTP_VERIFY_SSL, MCP_EMAIL_SMTP_POOL_ENABLED, MCP_EMAIL_SMTP_POOL_MAX_CONNECTIONS, MCP_EMAIL_SMTP_POOL_MAX_MESSAGESSMTP
MCP_EMAIL_OAUTH2_PROVIDER, MCP_EMAIL_OAUTH2_CLIENT_ID, MCP_EMAIL_OAUTH2_CLIENT_SECRET, MCP_EMAIL_OAUTH2_REFRESH_TOKENOAuth2
MCP_EMAIL_RATE_LIMIT, MCP_EMAIL_READ_ONLY, MCP_EMAIL_TOOLSET, MCP_EMAIL_DEFAULT_ACCOUNT, MCP_EMAIL_SAVE_TO_SENTSettings
MCP_EMAIL_WATCHER_ENABLED, MCP_EMAIL_WATCHER_FOLDERS, MCP_EMAIL_WATCHER_IDLE_TIMEOUTIDLE watcher
MCP_EMAIL_HOOK_ON_NEW_EMAIL, MCP_EMAIL_HOOK_PRESET, MCP_EMAIL_HOOK_AUTO_LABEL, MCP_EMAIL_HOOK_AUTO_FLAG, MCP_EMAIL_HOOK_BATCH_DELAY, MCP_EMAIL_HOOK_CUSTOM_INSTRUCTIONSHooks
MCP_EMAIL_ALERT_DESKTOP, MCP_EMAIL_ALERT_SOUND, MCP_EMAIL_ALERT_URGENCY_THRESHOLD, MCP_EMAIL_ALERT_WEBHOOK_URL, MCP_EMAIL_ALERT_WEBHOOK_ALLOW_PRIVATEAlerts
MCP_EMAIL_HOOK_AUTO_CALENDAR, MCP_EMAIL_HOOK_CALENDAR_NAME, MCP_EMAIL_HOOK_CALENDAR_ALARM_MINUTES, MCP_EMAIL_HOOK_CALENDAR_CONFIRMAutomatic calendar
MCP_EMAIL_SYSTEM_ONE_ENABLEDSystem One switch. The API key is TYPESAFE_API_KEY, not a MCP_EMAIL_* variable
TYPESAFE_API_KEY, TYPESAFE_BASE_URLSystem One credential and optional API root. The SDK reads TYPESAFE_BASE_URL
XDG_CONFIG_HOME, XDG_DATA_HOME, XDG_STATE_HOME, APPDATAOverride the directories above. APPDATA is the Windows Claude config directory used by mailoo install
MCP_EMAIL_HTTP_HOST, MCP_EMAIL_HTTP_TOKEN, MCP_EMAIL_HTTP_ALLOWED_HOSTSHTTP listen policy
MAILOO_INSTALL_COUNTERInstall count switch. Unset is off. 1, true, on, or yes opts in (any letter case, surrounding spaces ignored). 0, false, no, or off forces it off. See Install count
MAILOO_INSTALL_COUNTER_URLCollector URL for that count. Unset means no request, even when the switch is on

API

Tools (57)

Read (19)
ToolDescription
list_accountsList all configured email accounts
list_mailboxesList folders with unread counts and special-use flags
list_emailsPaginated email listing with date, sender, subject, and flag filters
get_emailRead full email content with attachment metadata
get_emailsFetch full content of multiple emails in a single call (max 20)
get_email_statusGet read/flag/label state of an email without fetching the body
search_emailsSearch by keyword; since/before (aliases start_date/end_date)
download_attachmentDownload an attachment (base64, or savePath to disk — not a read-only write)
export_emailExport one message as its raw RFC 822 .eml source (base64, or savePath to disk); flags unchanged
find_email_folderDiscover the real folder(s) an email resides in (resolves virtual folders)
extract_contactsExtract unique contacts from recent email headers
get_threadReconstruct a conversation thread via References/In-Reply-To
list_templatesList available email templates
get_email_statsEmail analytics — volume, top senders, daily trends
check_healthConnection health, latency, quota, and IMAP capabilities
get_email_securityRead-only SPF/DKIM/DMARC and From/Reply-To/Return-Path domains
sieve_statusWhether ManageSieve is reachable (default port 4190)
sieve_list_scriptsList ManageSieve scripts
sieve_get_scriptDownload a ManageSieve script
Write (12)
ToolDescription
send_emailSend a new email (plain text or HTML, CC/BCC, attachments)
reply_emailReply with proper threading (In-Reply-To, References)
forward_emailForward with original content quoted
save_draftSave a draft (RFC 2047 subjects; optional attachments)
send_draftSend an existing draft and remove from Drafts
apply_templateApply a template with variable substitution
schedule_emailSchedule an email for future delivery
list_scheduledList scheduled emails by status
cancel_scheduledCancel a pending scheduled email
sieve_put_scriptCreate or replace a ManageSieve script (does not activate)
sieve_delete_scriptDelete a ManageSieve script
sieve_activate_scriptActivate a script (empty name deactivates all)
Manage (7)
ToolDescription
move_emailMove email between folders
delete_emailMove to Trash or permanently delete
mark_emailMark as read/unread, flag/unflag
bulk_actionBatch operation on up to 100 emails
create_mailboxCreate a new mailbox folder
rename_mailboxRename an existing mailbox folder (not INBOX or a special-use folder)
delete_mailboxPermanently delete a mailbox and contents (not INBOX or a special-use folder)
Labels (5)
ToolDescription
list_labelsDiscover available labels (auto-detects provider strategy)
add_labelAdd a label to an email (ProtonMail folders, Gmail X-GM-LABELS, or IMAP keywords)
remove_labelRemove a label from an email
create_labelCreate a new label
delete_labelDelete a label (on Gmail, not INBOX or a special-use folder)

Note: On servers that store labels as IMAP keywords, a keyword must be an IMAP atom (RFC 9051): no spaces, control or non-ASCII characters, and none of ( ) { % * " \ ]. Any other label is refused with an error before anything is sent, so Receipts (2024) fails (an older release accepted it). See add_label / remove_label for removing an existing non-atom keyword.

Watcher & Alerts (6)
ToolDescription
get_watcher_statusShow IMAP IDLE connections, folders being monitored, and last-seen UIDs
list_presetsList available AI triage presets with descriptions and suggested labels
get_hooks_configShow current hooks configuration — preset, rules, and custom instructions
configure_alertsUpdate alert/notification settings at runtime
check_notification_setupDiagnose desktop notification support and provide setup instructions
test_notificationSend a test notification to verify OS permissions are configured
Calendar & Reminders (8)
ToolDescription
extract_calendarExtract ICS/iCalendar events from an email
analyze_email_for_schedulingAnalyze an email to detect events and reminder-worthy content
add_to_calendarAdd an email event to the local calendar (macOS/Linux)
create_reminderCreate a reminder in macOS Reminders.app from an email
list_calendarsList all available local calendars
list_eventsList local calendar events with optional title, date, and calendar filters
list_remindersList Reminders.app items with optional title and list filters
check_calendar_permissionsCheck whether the local calendar is accessible

Parameter-level notes for the tools above: docs/tools.md.

Prompts (7)

A prompt is listed only when the toolset registers the tools it calls: summarize_thread needs core or full, and summarize_meetings needs full. Where a send, draft, or bulk tool is missing (minimal, read_only), the prompt leaves that step out.

PromptDescription
triage_inboxCategorize and prioritize unread emails with suggested actions
summarize_threadSummarize an email conversation thread
compose_replyDraft a context-aware reply to an email
draft_from_contextCompose a new email from provided context and instructions
extract_action_itemsExtract actionable tasks from email threads
summarize_meetingsSummarize upcoming calendar events from emails
cleanup_inboxSuggest emails to archive, delete, or unsubscribe from

Resources (6)

ResourceURIDescription
Accountsemail://accountsList of configured accounts
Mailboxesemail://{account}/mailboxesFolder tree for an account
Unreademail://{account}/unreadUnread email summary
Templatesemail://templatesAvailable email templates
Statsemail://{account}/statsEmail statistics snapshot
Scheduledemail://scheduledPending scheduled emails

Provider Auto-Detection

ProviderDomains
Gmailgmail.com
Outlook / Hotmailoutlook.com, hotmail.com, live.com
Yahoo Mailyahoo.com, ymail.com
iCloudicloud.com, me.com, mac.com
Fastmailfastmail.com
ProtonMail Bridgeproton.me, protonmail.com
Zoho Mailzoho.com
GMXgmx.com, gmx.de, gmx.net

Architecture

src/├── main.ts                — Entry point and subcommand routing├── server.ts              — MCP server factory├── logging.ts             — MCP protocol logging bridge├── cli/                   — Interactive CLI commands│   ├── account-commands.ts — Account CRUD (list, add, edit, delete)│   ├── setup.ts           — Legacy setup alias → account add│   ├── test.ts            — Connection tester│   ├── config-commands.ts — Config management (show, edit, path, init)│   ├── install-commands.ts — MCP client registration (install, status, remove)│   ├── providers.ts       — Provider auto-detection (IMAP/SMTP presets)│   └── scheduler.ts       — Scheduler CLI├── config/                — Configuration layer│   ├── xdg.ts             — XDG Base Directory paths│   ├── schema.ts          — Zod validation schemas│   └── loader.ts          — Config loader (TOML + env vars)├── connections/│   ├── auth.ts            — IMAP/SMTP credentials (password or OAuth2)│   ├── backoff.ts         — Reconnect delays with jitter after a failed connect│   ├── manager.ts         — Lazy persistent IMAP/SMTP with OAuth2 (experimental)│   └── probe.ts           — One-shot IMAP/SMTP login checks (setup wizard, mailoo test)├── services/              — Business logic│   ├── imap.service.ts    — IMAP operations│   ├── label-strategy.ts  — Provider-aware label strategy (ProtonMail/Gmail/IMAP keywords)│   ├── smtp.service.ts    — SMTP operations + optional \\Sent APPEND│   ├── sieve.service.ts   — ManageSieve (RFC 5804)│   ├── template.service.ts — Email template engine│   ├── oauth.service.ts   — OAuth2 token management (experimental)│   ├── mail-services.ts   — IMAP/SMTP services from the config, for the servers and the scheduler CLI│   ├── calendar.service.ts — ICS/iCalendar parsing│   ├── scheduler.service.ts — Email scheduling queue│   ├── watcher.service.ts — IMAP IDLE real-time watcher with auto-reconnect│   ├── hooks.service.ts   — AI triage via MCP sampling + static rules + auto-labeling/flagging│   ├── notifier.service.ts — Multi-channel notification dispatcher (desktop/sound/webhook)│   ├── presets.ts         — Built-in hook presets (inbox-zero, gtd, priority-focus, etc.)│   └── event-bus.ts       — Typed EventEmitter for internal email events├── tools/                 — MCP tool definitions (57)├── prompts/               — MCP prompt definitions (7)├── resources/             — MCP resource definitions (6)├── safety/                — Audit trail, rate limiter, stdio lifecycle, shutdown, writer lease│   └── http-transport.ts  — Streamable HTTP listen policy├── utils/                 — RFC 2047 compose, MIME body, auth headers└── types/                 — Shared TypeScript types

Maintainers

Bitfloo

Contributing

PRs accepted against develop (GitHub default). develop is the release line; releases are tagged v* from it. Please conform to the standard-readme specification when editing this README. See CONTRIBUTING.md.

bash
# Development workflowpnpm installpnpm typecheck          # type checkpnpm check              # lint and formatpnpm ci:local           # lint, typecheck, unit; GreenMail if Docker is uppnpm ci:release         # same checks; a working Docker runtime and GreenMail are requiredpnpm test               # unit tests (Vitest; no mail server)pnpm test:integration   # GreenMail IMAP/SMTP (needs Docker)pnpm test:e2e           # packed release, production dependencies, stdio MCP, GreenMailpnpm test:all           # unit plus GreenMailpnpm build              # buildpnpm start              # run

pnpm test:integration, pnpm test:e2e, pnpm test:all, and pnpm ci:release need Docker. The packed release journey is also included in the integration suite. pnpm ci:local skips GreenMail when Docker is down; pnpm ci:release fails instead. Publishing to npm, the MCP Registry, and npm aliases requires the release verification job to pass; Docker publishing also requires the MCP Registry job to succeed. What GitHub runs on a pull request is listed in CONTRIBUTING.md. mailoo test / npx -y @bitfloo/mailoo@latest test is a live-account connection probe, not Vitest.

Attribution & License

This is a public LGPL-3.0-or-later fork of email-mcp by codefuturist. It is not an official codefuturist project. Original copyright remains with the original authors. Mailoo branding, Bitfloo trademarks, and new code are Copyright (c) 2026 Bitfloo.

Licensed under LGPL-3.0-or-later. The GNU GPL-3 text required by LGPL-3 is in COPYING. Attribution is recorded in NOTICE.

來源:README.md,提交 2ea6d84

工具

0
SourceWeft 曾在隔離沙箱中以無憑證方式啟動該軟體包一次,以列出其工具,但未能完成:伺服器未能啟動。

版本歷史

11
  1. v0.3.5最新Oct 31, 2026
  2. v0.3.4Oct 31, 2026
  3. v0.3.3Oct 29, 2026
  4. v0.3.0Oct 26, 2026
  5. v0.2.2Oct 18, 2026
  6. v0.2.1Oct 16, 2026
  7. v0.2.0Oct 10, 2026
  8. v0.1.8Oct 7, 2026
  9. v0.1.7Sep 29, 2026
  10. v0.1.6Sep 28, 2026
  11. v0.1.5Sep 23, 2026

評分

已滿足適用檢查項的 50%

該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。

  • 近 30 天內可連通25 分

    請保持端點能夠回應 MCP 握手;SourceWeft 每 30 天探測一次。

  • 已宣告認證方式10 分

    請在註冊中心條目中宣告憑證請求標頭,或保持端點能夠回應探測,以便判定其認證方式。

  • 已列出工具15 分

    請讓伺服器在無憑證時回應 tools/list,或發布可在沙箱中無憑證啟動的軟體包。

  • 描述必需15 分
  • 圖示10 分
  • 文件15 分
  • 授權條款10 分