Back to Directory/Developer Tools

io.github.Bitfloo/mailoo

Mailoo — email MCP server for IMAP, SMTP, and ManageSieve. Multi-account, per-folder profiles.

Developer ToolsTypeScriptv0.1.7

Mailoo

standard-readme compliant license CI

Mailoo is Bitfloo's IMAP/SMTP MCP server: multi-mailbox, with profiles per account and per folder.

This is a public LGPL-3.0-or-later fork of email-mcp. It is not an official codefuturist project. See Upstream / Attribution.

Enables AI assistants to read, search, send, manage, schedule, and analyze emails across multiple accounts. Exposes 56 tools, 7 prompts, and 6 resources over the MCP protocol with OAuth2 support (experimental), email scheduling, calendar extraction, analytics, provider-aware label management, real-time IMAP IDLE watcher with AI-powered triage, customizable presets and static rules, ManageSieve filters, and a guided setup wizard.

Behaviour for Sent copies, IMAP4rev2, Sieve, attachment savePath, and read-only side effects is documented in docs/configuration.md and docs/tools.md.

Highlights

FeatureIn this tree
Multi-account IMAP/SMTP✅
Send / reply / forward✅
Drafts & templates✅
Provider-aware labels & bulk ops✅
Schedule future emails✅
Real-time IMAP IDLE watcher✅
AI triage with presets✅
Desktop & webhook alerts✅
Calendar (ICS) extraction✅
Email analytics✅
OAuth2 (Gmail / M365)✅ experimental
Guided setup wizard✅
ManageSieve (server-side filters)✅
Sender auth headers (SPF/DKIM/DMARC)✅

Table of Contents

Security

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

  • All connections use TLS/STARTTLS encryption (ManageSieve PLAIN also requires TLS — docs)
  • Passwords are never logged; audit trail records operations without credentials
  • Token-bucket rate limiter prevents abuse (configurable per account)
  • OAuth2 XOAUTH2 authentication for Gmail and Microsoft 365 (experimental)
  • Attachment downloads: 5 MB as base64, or up to 50 MB when writing savePath under the working directory (docs)

Docs

TopicWhere
Sent APPEND, IMAP4rev2, Sieve, read_only, stdio EOFdocs/configuration.md
savePath, search dates, get_email_security, sieve tools, send/draft attachments, RFC 2047docs/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 and pnpm 9.

@bitfloo/mailoo is not on npmjs yet. Until the first npm publish, install from git:

git clone https://github.com/Bitfloo/mailoo.git
cd mailoo
pnpm install && pnpm build

Then run the local CLI:

node dist/main.js setup
# later: node dist/main.js account add | stdio | test | …

After npm publish

Once @bitfloo/mailoo is on npmjs, these will work:

npx @bitfloo/mailoo setup
# or
pnpm dlx @bitfloo/mailoo setup

# Or install globally
npm install -g @bitfloo/mailoo
# or
pnpm add -g @bitfloo/mailoo

Docker

The running image needs Docker, not Node on the host. First-time config still needs a local clone (node dist/main.js setup after pnpm build) or a hand-written TOML, then mount that config into the container. ghcr.io/bitfloo/mailoo is not published for anonymous pull. Build locally (docker-compose.yml uses build: .):

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

Intended image name: ghcr.io/bitfloo/mailoo. When images are published, tags will follow bare semver (no v prefix), e.g. ghcr.io/bitfloo/mailoo:0.1.0.

Note: The server uses stdio transport. Config must be created on the host first (node dist/main.js setup after a local clone, or manually) and mounted into the container.

Usage

Until npm publish, commands below are node dist/main.js <subcommand> from a local clone (or mailoo <subcommand> if that bin is on your PATH from the clone). After @bitfloo/mailoo is on npmjs, npx @bitfloo/mailoo / a global mailoo will work the same way.

Setup

# Add an email account interactively (recommended)
node dist/main.js account add

# Or use the legacy alias
node dist/main.js setup

# Or create a template config manually
node dist/main.js config init

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

Test Connections

node dist/main.js test            # all accounts
node dist/main.js test personal   # specific account

node dist/main.js test / mailoo test is a live-account connection probe, not Vitest. Unit and integration tests are pnpm test / pnpm test:integration (see Contributing).

Configure Your MCP Client

Working path today: clone and build, then either run the guided installer and choose Direct node, or paste a local-node snippet below. There is no VS Code / MCP gallery listing. npx @bitfloo/mailoo is not an easy path until the package is on npmjs.

node dist/main.js install

The installer also offers npx, pnpm dlx, and a global mailoo binary. Those need @bitfloo/mailoo on npmjs — skip them until then.

Or add the server manually. Replace /absolute/path/to/mailoo with your clone (the directory that contains dist/main.js after pnpm build). If the clone’s mailoo bin is already on your PATH, you can use "command": "mailoo" with "args": ["stdio"] instead of node + dist/main.js.

Claude Desktop

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

{
  "mcpServers": {
    "mailoo": {
      "command": "node",
      "args": ["/absolute/path/to/mailoo/dist/main.js", "stdio"]
    }
  }
}
VS Code (GitHub Copilot)

Mailoo is not in the VS Code Extensions gallery. Point Copilot at your local build.

Workspace (.vscode/mcp.json):

{
  "servers": {
    "mailoo": {
      "type": "stdio",
      "command": "node",
      "args": ["/absolute/path/to/mailoo/dist/main.js", "stdio"]
    }
  }
}

User config (settings.json, all workspaces):

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

{
  "mcp": {
    "servers": {
      "mailoo": {
        "type": "stdio",
        "command": "node",
        "args": ["/absolute/path/to/mailoo/dist/main.js", "stdio"]
      }
    }
  }
}
Cursor

Edit ~/.cursor/mcp.json:

{
  "mcpServers": {
    "mailoo": {
      "command": "node",
      "args": ["/absolute/path/to/mailoo/dist/main.js", "stdio"]
    }
  }
}
Windsurf

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

{
  "mcpServers": {
    "mailoo": {
      "command": "node",
      "args": ["/absolute/path/to/mailoo/dist/main.js", "stdio"]
    }
  }
}
Zed

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

{
  "context_servers": {
    "mailoo": {
      "command": {
        "path": "node",
        "args": ["/absolute/path/to/mailoo/dist/main.js", "stdio"]
      }
    }
  }
}
Mistral Vibe

Add to ~/.vibe/config.toml:

[[mcp_servers]]
name = "mailoo"
transport = "stdio"
command = "node"
args = ["/absolute/path/to/mailoo/dist/main.js", "stdio"]

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

[[mcp_servers]]
name = "mailoo"
transport = "stdio"
command = "node"
args = ["/absolute/path/to/mailoo/dist/main.js", "stdio"]
env = { "EMAIL_ACCOUNTS" = "<your-accounts-json>" }

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. ghcr.io/bitfloo/mailoo is not published for anonymous pull, so build first (docker build -t ghcr.io/bitfloo/mailoo .):

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

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

{
  "mcpServers": {
    "mailoo": {
      "command": "docker",
      "args": [
        "run", "--rm", "-i",
        "-v", "~/.config/mailoo:/home/node/.config/mailoo:ro",
        "ghcr.io/bitfloo/mailoo"
      ]
    }
  }
}
Single-account via environment variables (no config file needed)
{
  "mcpServers": {
    "mailoo": {
      "command": "node",
      "args": ["/absolute/path/to/mailoo/dist/main.js", "stdio"],
      "env": {
        "MCP_EMAIL_ADDRESS": "you@gmail.com",
        "MCP_EMAIL_PASSWORD": "your-app-password",
        "MCP_EMAIL_IMAP_HOST": "imap.gmail.com",
        "MCP_EMAIL_SMTP_HOST": "smtp.gmail.com"
      }
    }
  }
}
After npm publish — npx @bitfloo/mailoo

Once @bitfloo/mailoo is on npmjs, you can swap the local node / dist/main.js launch for:

"command": "npx",
"args": ["-y", "@bitfloo/mailoo", "stdio"]

Same idea for Zed (path: npx) and Vibe (command = "npx"). Until then, npx @bitfloo/mailoo will fail.

CLI Commands

Until npm publish, invoke these as node dist/main.js <command> from a built clone (or mailoo <command> if that bin is on your PATH).

node dist/main.js [command]

Commands:
  stdio                     Run as MCP server over stdio (default)
  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                     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
  --version, -v             Print the package version
  help                      Show help

Configuration

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

[settings]
rate_limit = 10  # max emails per minute per account
read_only = false
save_to_sent = true  # see docs/configuration.md — Gmail already files Sent

[[accounts]]
name = "personal"
email = "you@gmail.com"
full_name = "Your Name"
password = "your-app-password"

[accounts.imap]
host = "imap.gmail.com"
port = 993
tls = true
# disable_imap4rev2 = true  # Strato and similar SEARCH bugs
# sieve_host = "imap.example.com"
# sieve_port = 4190

[accounts.smtp]
host = "smtp.gmail.com"
port = 465
tls = true
starttls = false
verify_ssl = true

[accounts.smtp.pool]
enabled = true
max_connections = 1
max_messages = 100
OAuth2 (experimental)

Note: OAuth2 support is experimental. Token refresh and provider-specific flows may require additional testing in your environment.

[[accounts]]
name = "work"
email = "you@company.com"
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.gmail.com"
port = 993
tls = true

[accounts.smtp]
host = "smtp.gmail.com"
port = 465
tls = true

[accounts.smtp.pool]
enabled = true
max_connections = 1
max_messages = 100
Environment Variables

For single-account setups (overrides config file):

VariableDefaultDescription
MCP_EMAIL_ADDRESSrequiredEmail address
MCP_EMAIL_PASSWORDrequiredPassword or app password
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

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 and every 60 seconds while the MCP server is running
  2. CLI — node dist/main.js scheduler check for manual or cron-based processing
  3. OS-level daemon — node dist/main.js 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. Failed sends are retried up to 3 times before being marked failed.

Setting up the daemon
# Install (macOS launchd / Linux crontab — runs every minute)
node dist/main.js scheduler install

# Verify it's running
node dist/main.js scheduler status

# View pending / sent / failed scheduled emails
node dist/main.js scheduler list

# Trigger a manual check immediately
node dist/main.js scheduler check

# Remove the daemon
node dist/main.js scheduler uninstall

Scheduled emails are stored as JSON files in ~/.local/state/mailoo/scheduled/ with status-based locking. Each entry tracks attempts (max 3) and the last error, so you can inspect failures with scheduler list.

Real-time Watcher & AI Hooks

The IMAP IDLE watcher monitors configured mailboxes in real-time using persistent IDLE connections (separate from tool connections). 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:

[settings.watcher]
enabled = true
folders = ["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 labels
auto_flag = true        # flag urgent emails
batch_delay = 5         # seconds to batch before triage

# User context — appended to preset's AI prompt
custom_instructions = """
I'm a software engineer. Emails from @mycompany.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 = "*@github.com" }
actions = { labels = ["Dev"], mark_read = true }

[[settings.hooks.rules]]
name = "Newsletter Archive"
match = { from = "*@substack.com|*@buttondown.email" }
actions = { labels = ["Newsletter"] }

[[settings.hooks.rules]]
name = "VIP Contacts"
match = { from = "ceo@company.com|cto@company.com" }
actions = { flag = true, labels = ["VIP"] }
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 (*@github.com) with | as OR separator (*@github.com|*@gitlab.com). All conditions within a match are AND'd. 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❌
[settings.hooks.alerts]
desktop = true              # OS-level notifications (macOS/Linux/Windows)
sound = true                # play sound for urgent emails
urgency_threshold = "high"  # minimum priority to trigger desktop alert
webhook_url = "https://ntfy.sh/my-email-alerts"  # optional: Slack, Discord, ntfy.sh, etc.
webhook_events = ["urgent", "high"]

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:

# Ubuntu / Debian
sudo apt install libnotify-bin pulseaudio-utils

# Fedora
sudo dnf install libnotify pulseaudio-utils

# Arch
sudo 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:

{
  "event": "email.urgent",
  "account": "work",
  "sender": { "name": "John CEO", "address": "ceo@company.com" },
  "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:

[[settings.hooks.rules]]
name = "VIP Contacts"
match = { from = "ceo@company.com" }
actions = { flag = true, alert = true, labels = ["VIP"] }

Features:

  • Auto-reconnect — Exponential backoff (1s → 60s) on connection failures
  • 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
  • Resource subscriptions — Pushes notifications/resources/updated for unread counts

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).

Default classify path sends mail_headers (subject, From, attachment names, extracted links, auth codes) to api.typesafe.ai. include_body = true additionally sends mail_body. 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.

[settings.system_one]
enabled = false
include_body = false
auto_move = false
auto_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.

API

Tools (56)

Read (18)
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)
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
delete_mailboxPermanently delete a mailbox and contents
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
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)

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 + OAuth2 endpoints (experimental)
│   └── 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/
│   └── manager.ts         — Lazy persistent IMAP/SMTP with OAuth2 (experimental)
├── 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)
│   ├── 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 (56)
├── prompts/               — MCP prompt definitions (7)
├── resources/             — MCP resource definitions (6)
├── safety/                — Audit trail, rate limiter, stdio lifecycle
├── utils/                 — RFC 2047 compose, MIME body, auth headers
└── types/                 — Shared TypeScript types

Maintainers

Bitfloo

Upstream / Attribution

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

Contributing

PRs accepted against develop (GitHub default). main is the release line and is kept in sync with develop. Please conform to the standard-readme specification when editing this README. See CONTRIBUTING.md.

# Development workflow
pnpm install
pnpm typecheck          # type check
pnpm check              # lint and format
pnpm ci:local           # lint, typecheck, unit; GreenMail if Docker is up
pnpm test               # unit tests (Vitest; no mail server)
pnpm test:integration   # GreenMail IMAP/SMTP (needs Docker)
pnpm test:all           # unit plus GreenMail
pnpm build              # build
pnpm start              # run

pnpm test:integration and pnpm test:all need Docker. pnpm ci:local skips GreenMail when Docker is down. GitHub runs linux GreenMail and an image build on pull requests, not on every push. mailoo test / node dist/main.js test is a live-account connection probe, not Vitest.

License

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

Installation

Source-derived launch command. Check the maintainer’s required arguments and credentials before running:

bash
npx -y @bitfloo/mailoo

Set up in your AI client

Merge this template into ~/Library/Application Support/Claude/claude_desktop_config.json. Keep existing servers. Add any arguments, credentials, and permissions required by the maintainer; this template has not been install-tested.

json
{
  "mcpServers": {
    "io-github-bitfloo-mailoo": {
      "command": "npx",
      "args": [
        "-y",
        "@bitfloo/mailoo"
      ]
    }
  }
}

Restart Claude Desktop completely for changes to take effect. Confirm the server appears connected in the client’s tool list, then try a read-only example from its documentation.

Claude Desktop setup reference

Package

@bitfloo/mailoonpm

Compatible MCP Clients

io.github.Bitfloo/mailoo works with any MCP-compatible client. Copy the config snippet from the Configuration section above and add it to the file shown for your client, then restart the application.

  • Claude Desktop~/Library/Application Support/Claude/claude_desktop_config.jsonRestart Claude Desktop completely for changes to take effect.
  • Cursor~/.cursor/mcp.jsonRestart Cursor for changes to take effect.
  • VS Code.vscode/mcp.jsonReload VS Code window for changes to take effect.
  • Windsurf~/.codeium/windsurf/mcp_config.jsonRestart Windsurf for changes to take effect.
  • Claude Code.mcp.jsonSave at the project root, then start Claude Code in that project and review the MCP server approval prompt. Keep real credentials out of shared files.

Learn More