Hypermail Mcp
БесплатноНе проверенUnified email MCP server — operate any inbox (Outlook now, IMAP/Gmail later) by passing an email address.
Описание
Unified email MCP server — operate any inbox (Outlook now, IMAP/Gmail later) by passing an email address.
README
A Model Context Protocol server that lets an agent operate any of the user's inboxes through a single, unified tool surface.
v0.7.22 — Fixed IMAP
edit_draftupdates so body-only edits preserve the draft's existing recipients, subject, reply context, and attachments.v0.7.21 —
send_emailanddraft_emailnow treat the exact string"false"forinReplyToas the booleanfalse, so agents that serialize that explicit new-message marker as text can forward withforwardMessageIdinstead of incorrectly triggering the reply/forward exclusivity check.v0.7.20 — Hardened long-running IMAP and new-email polling paths: IMAP connections now use explicit connection/greeting/socket/operation timeouts, reset stuck connections, and report post-TLS authentication failures with account-specific remediation guidance.
get_new_emailsnow applies per-account timeouts during candidate collection and hydration so one hanging account does not block all-account polling; partial timeout failures are returned alongside successful accounts' emails.v0.7.19 — Hardened Outlook ID handling when Microsoft Graph
$searchreturns IDs that/me/messages/{id}rejects as malformed:search_emailsnow returns a translated readable immutable ID when available, andread_emailretries malformed/stale IDs throughtranslateExchangeIdsbefore failing.list_emailsalso falls back from localized folder display names such asÉléments envoyésto the matching Graph folder ID.v0.7.18 —
search_emailscan now omitaccountto search all registered accounts in parallel, returning account-annotated results plus per-account errors without discarding successful matches. All-accountget_new_emailspolling also collects and hydrates per-account batches in parallel while preserving the global oldest-first limit.v0.7.17 — Hardened Outlook message IDs returned by
list_emailsandsearch_emails: Outlook reads now request Microsoft Graph immutable IDs,search_emailsprobes results and marks stale/not-found messages withstale: true, andread_email/read_attachmentkeep a legacy mutable-ID fallback for older IDs. Graph errors now include structured diagnostic details when available.v0.7.16 — Hardened
draft_emailafter successful draft creation: if post-save readback fails, the tool now returns the created draft ID withwarninganddraftReadbackErrorinstead of losing the draft behind an error. Compose operations also emit sanitizedHYPERMAIL_DEBUGstage logs, IMAP drafts now use the server-advertised\\Draftsmailbox when present, and the CLI supports--version.v0.7.15 — Hardened IMAP draft creation: drafts now append directly to the Drafts mailbox, retry without the
\\Draftflag when an IMAP server rejects flagged APPEND, and preserve IMAP reply/forward context in drafts the same waysend_emaildoes. IMAP operations for a single account are serialized to avoid shared-connection races, and IMAP write failures now include safe server response details instead of onlyCommand failed. Account setup responses no longer expose stored provider tokens/passwords. Also rejects multiline plain text passed asformat: "html", and adds the Hermesget_new_emailspoller example.v0.7.14 — Added opt-in
HYPERMAIL_DEBUGstructured stderr logs with redaction for server startup, account-store locking/checkpoint writes, andget_new_emailscandidate/claim flow. Hardened Outlookedit_draftbody updates by requesting Graph's updated representation, re-reading with retries, and returning an error instead ofedited: trueif the requested body change is not observable; Outlook body edits also get one safe replay after attachment handling when the final read is stale.v0.7.13 — Hardened
get_new_emailsagainst duplicate delivery when multiple MCP processes share the same encrypted account store. Store writes now reload and merge under a cross-process lock, checkpoints are monotonic, and new-email batches are atomically claimed before being returned.trash_emailalso now uses provider-native trash operations for Gmail and IMAP trash aliases.v0.7.12 — Hardened Outlook reply/forward draft formatting when Microsoft Graph labels generated thread history as HTML but returns plain/unstructured text. Such histories are now defensively normalized with escaped content and line breaks so quoted messages remain readable.
v0.7.11 — Fixed Outlook reply/forward drafts whose Graph-generated quoted bodies are plain text by escaping them and patching the draft as HTML, so newly composed HTML replies no longer render as raw text.
v0.7.10 — Fixed an account-store race where Gmail/Outlook token refreshes could overwrite
get_new_emailscheckpoints. Checkpoint updates now preserve token data and merge delivered IDs at the same timestamp.edit_draftbody edits now require exact selected-section replacement (old_text+new_text) so reply/forward history is preserved instead of overwritten.v0.7.9 — Replaced server-side email watch/webhook/script delivery with the pull-based
get_new_emailstool. Agents schedule their own repeated calls and fetch bounded batches of new inbox email.v0.7.8 — Default Gmail loopback redirect URI changed from random-port
/oauth2callbackto fixedhttp://127.0.0.1:33333/callback(still overridable viaHYPERMAIL_GMAIL_REDIRECT_URI)..data/encryption key directory added to.gitignore.v0.7.7 — Env-only configuration. Runtime config now comes from flat
HYPERMAIL_*environment variables plus selected CLI overrides. Config files and legacy provider env names are no longer read. Hosted Gmail OAuth callbacks are supported viaHYPERMAIL_GMAIL_REDIRECT_URI; local loopback and manual completion still work.v0.7.6 — Gmail setup uses OAuth authorization URLs instead of Google's rejected device-code flow for Gmail API scopes.
complete_add_accountaccepts a final redirected URL or rawcode/state, and provider credentials use dedicatedHYPERMAIL_GMAIL_*/HYPERMAIL_OUTLOOK_*env vars.v0.7.5 — Attachments via file path on
send_email/draft_email(attachmentsparam).edit_draftgainsnew_attachmentsandremove_attachments—add_attachment_to_draftis removed (23 tools now). Draft editing uses multi-strategy thread boundary detection for more reliable quoted-thread preservation. Published CLI installs the MCP SDK dependency so global/npx runs do not fail on a missing SDK module.v0.7.4 —
inReplyTois now a required parameter onsend_emailanddraft_email(was optional). Set it tofalsefor a new email, or pass a message ID to thread a reply. This forces the agent to make an explicit choice instead of silently treating replies as new conversations.v0.7.3 —
edit_draftnow preserves the quoted thread history when editing Outlook reply/forward drafts. Previously, editing a draft body would overwrite the entire content — including the quoted thread. Now only the answer part (above the spacer delimiter) is replaced.v0.7.1 — Every config field is now settable via a dedicated
HYPERMAIL_*env var. Legacy provider env vars are no longer accepted. See Environment Variables for the full reference.
v0.6.3 — Unify stdio and HTTP modes into a single feature set. Removed email watch (inbox polling, SSE push, notification buffer), agent multi-tenancy (
agents.yaml,x-api-keyauth, per-agent allowlists), and thecheck_notificationstool. Droppedjs-yamldependency. Dockerfile simplified to a singleinstall → build → prunestep.v0.6.2 — Version source-of-truth fix:
version.tsnow imports directly frompackage.jsoninstead of hardcoding, preventing version drift between the two files.v0.6.1 — Docker deployment (standalone Dockerfile with HEALTHCHECK), email notification bug fixes (ID-based dedup, pagination cap, dynamic re-scan), Node 22 base image, dropped docker-compose.
v0.5.0 — Replaced optional
isHtmlboolean with requiredformatparameter ("html"|"markdown") onsend_email,draft_email, andedit_draft. Markdown bodies are converted to HTML viamarkedso recipients always see clean HTML.v0.4.3 — Upgraded Zod to v4.4.3. Fixed MCP SDK v1.29.0 compatibility by wrapping all tool schemas in
z.object()and replacing discriminated union output schemas that causedvalidateToolOutputcrashes.
The agent doesn't care whether an address is a work Outlook account, a personal
Microsoft account, a personal IMAP mailbox, or Gmail — it just calls
list_emails, search_emails, read_email, send_email and passes the email
address as the account argument. The server routes to the right backend.
v1 status: Outlook / Microsoft 365 (personal + work) fully supported via
Microsoft Graph. IMAP (any IMAP server) supported via imapflow + nodemailer.
Gmail supported via Google OAuth authorization-code flow with local loopback or
hosted callbacks plus remote-safe manual completion.
Why
- Existing Outlook/M365 MCP servers (e.g.
@softeria/ms-365-mcp-server) expose ~200 raw Graph endpoints and are tied to a single signed-in user. - This project wraps the same proven stack (
@azure/msal-nodefor auth,@microsoft/microsoft-graph-clientfor HTTP) but exposes only a small, provider-agnostic email API and supports multiple accounts at once, keyed by email address.
Install / run
npm install -g hypermail-mcp # or pnpm / npx
hypermail-mcp --help
Run as a stdio MCP server (the default) — wire it into your MCP host:
Claude Desktop / Claude Code
{
"mcpServers": {
"hyper-email": {
"command": "npx",
"args": ["-y", "hypermail-mcp"]
}
}
}
Or via the CLI:
claude mcp add hypermail -- npx -y hypermail-mcp
As a hosted HTTP server
hypermail-mcp --http --port 3000 --host 0.0.0.0
# endpoint: http://<host>:3000/mcp (Streamable HTTP transport, session-aware)
When hosted, set HYPERMAIL_KEY so the account file is reproducibly
decryptable across restarts and redeploys.
Docker
# Build
docker build -t hypermail-mcp .
# Run
# Pass secret values from your shell or deployment environment; do not commit them.
docker run -d \
--name hypermail-mcp \
-p 3000:3000 \
-e HYPERMAIL_KEY \
-e HYPERMAIL_OUTLOOK_CLIENT_ID \
-e HYPERMAIL_OUTLOOK_TENANT_ID \
-v hypermail-data:/var/lib/mcp \
hypermail-mcp
The image runs the server in HTTP mode on port 3000 with a 30-second
HEALTHCHECK against /mcp. Data is persisted via a Docker volume at
/var/lib/mcp.
Development
To test the HTTP server locally:
# Terminal 1: auto-rebuild TypeScript on save
pnpm dev
# Terminal 2: start HTTP server with env/CLI config
pnpm dev:http
The server listens on http://127.0.0.1:3000/mcp.
Runtime and provider configuration
Hypermail uses flat HYPERMAIL_* environment variables as the source of truth.
There is no runtime config file. CLI flags only override transport, host, port,
and data directory for a single invocation.
CLI flags: --http, --port, --host, --data-dir, --version, --help.
Subcommands: hypermail-mcp generate-key — generate a base64 32-byte key for
HYPERMAIL_KEY.
Local CLI / env example
export HYPERMAIL_KEY="$(hypermail-mcp generate-key)"
export HYPERMAIL_DATA_DIR="$HOME/.local/share/hypermail-mcp"
export HYPERMAIL_OUTLOOK_CLIENT_ID="<your-client-id>"
hypermail-mcp
Generic MCP client JSON example
{
"mcpServers": {
"hypermail": {
"command": "npx",
"args": ["-y", "hypermail-mcp"],
"env": {
"HYPERMAIL_KEY": "${HYPERMAIL_KEY}",
"HYPERMAIL_DATA_DIR": "${HYPERMAIL_DATA_DIR}",
"HYPERMAIL_OUTLOOK_CLIENT_ID": "${HYPERMAIL_OUTLOOK_CLIENT_ID}"
}
}
}
}
Environment Variables
| Env var | Purpose | Default / behavior |
|---|---|---|
HYPERMAIL_DATA_DIR |
Account/token store location | ${XDG_DATA_HOME:-~/.local/share}/hypermail-mcp |
HYPERMAIL_KEY |
32-byte AES-256-GCM key as hex/base64, or any passphrase derived via SHA-256 | If unset, generates and persists a local key and prints a startup warning |
HYPERMAIL_TRANSPORT |
Runtime transport: stdio or http |
stdio; --http overrides to http |
HYPERMAIL_HTTP_PORT |
HTTP bind port | 3000; invalid HTTP-mode values warn and fall back |
HYPERMAIL_HTTP_HOST |
HTTP bind host | 127.0.0.1; invalid HTTP-mode values warn and fall back |
HYPERMAIL_OUTLOOK_CLIENT_ID |
Optional custom Azure/Entra public client ID | Built-in public client |
HYPERMAIL_OUTLOOK_TENANT_ID |
Optional Outlook tenant/authority selector | common |
HYPERMAIL_GMAIL_CLIENT_ID |
Google OAuth client ID | Required when adding a Gmail account |
HYPERMAIL_GMAIL_CLIENT_SECRET |
Google OAuth client secret, when issued by the client type | unset |
HYPERMAIL_GMAIL_REDIRECT_URI |
Hosted Gmail OAuth callback URI | Local loopback callback when unset |
HYPERMAIL_TOOLS_ENABLED |
Comma-separated tool allowlist | Empty/unset means no filtering |
HYPERMAIL_TOOLS_DISABLED |
Comma-separated tool blocklist | Empty/unset means no filtering |
HYPERMAIL_DEBUG |
Enable structured debug logs to stderr (1, true, yes, on, or debug) |
Disabled by default |
Priority order: selected CLI flags > HYPERMAIL_* env vars > hardcoded defaults.
Per-tool filtering (HYPERMAIL_TOOLS_ENABLED / HYPERMAIL_TOOLS_DISABLED) lets
operators ship minimal agent-facing surfaces. If both non-empty lists are set,
or either list contains an unknown tool name, startup fails.
Tools
All "email" tools take an account argument — the email address of the inbox
to operate on. The server resolves the right provider from the encrypted
account store.
| Tool | Inputs | Notes |
|---|---|---|
list_accounts |
— | Returns registered emails + provider, no secrets. |
add_account |
provider, email?, config? |
Starts the provider add flow. Outlook returns a device code; Gmail returns an OAuth URL. Ready account responses include public metadata only and never return stored tokens/passwords. Returns {handle, verification:{type, userCode, verificationUri, expiresAt, message}} for pending flows. |
complete_add_account |
provider, handle, authorizationResponse?, code?, state? |
Returns pending / ready / expired / error. Ready account responses include public metadata only and never return stored tokens/passwords. Gmail accepts a pasted final redirected URL or raw code/state for remote-safe completion. |
get_account_settings |
account |
Get signature (HTML) and style preferences for an account. |
set_account_settings |
account, signature?, signaturePath?, style? |
Set signature HTML (inline or via file path) and font preferences. |
remove_account |
email |
Deletes tokens for the account. |
list_emails |
account, folder?, limit?, unreadOnly?, skip? |
Defaults: folder=inbox, limit=25. Supports pagination via skip — response includes hasMore. Outlook accepts well-known folder names, folder IDs, and falls back from localized display names to matching folder IDs. |
get_new_emails |
account?, limit? |
Pull new inbox emails not previously returned by this tool. limit defaults to 10 and is global when account is omitted. Returns full markdown bodies with attachment metadata; bodies may be truncated. |
search_emails |
account?, query, limit? |
Search one account when account is provided, or omit it to search all registered accounts in parallel. Returns account-annotated summaries and partial per-account errors. Uses KQL on Outlook, and normalizes malformed Outlook search-result IDs to readable immutable IDs when possible. |
read_email |
account, id, format? |
Returns full body + recipients + attachment metadata. format: markdown (default), html, or text. Outlook retries malformed/stale IDs through Graph ID translation before failing. |
read_attachment |
account, messageId, attachmentId |
Download an attachment to a temporary file and return its path. |
archive_email |
account, id |
Move a message to the Archive folder. |
trash_email |
account, id |
Move a message to Deleted Items (trash). |
move_email |
account, id, destination |
Move to any folder by well-known name (inbox, drafts, etc.) or custom folder ID. |
send_email |
account, to[], cc?, bcc?, subject, body, format, include_signature, inReplyTo, replyAll?, forwardMessageId?, attachments? |
Send an email. format ("html" or "markdown") controls body format — Markdown is converted to HTML via marked; multiline plain text with format: "html" is rejected, so use "markdown" for paragraphs or add tags like <p>/<br>. Appends signature when include_signature is true. inReplyTo sends as threaded reply; forwardMessageId sends as forward. inReplyTo is required — set to false for new emails. attachments is an optional array of {filePath, name?} — files are read from disk and encoded automatically. |
draft_email |
account, to[], cc?, bcc?, subject, body, format, include_signature, inReplyTo, replyAll?, forwardMessageId?, attachments? |
Save as draft instead of sending. Same params as send_email including attachments. Returns the draft message ID and, when readback succeeds, HTML body (draftHtml). If the draft is created but readback fails, returns the draft ID with warning and draftReadbackError so callers can retry read_email or continue with send_draft. inReplyTo is required — set to false for new emails. |
edit_draft |
account, id, to?, cc?, bcc?, subject?, old_text?, new_text?, body?, format?, include_signature?, new_attachments?, remove_attachments? |
Edit an existing draft by ID. Body edits require exact selected-section replacement: copy old_text from the current draft HTML (draftHtml or read_email with format: "html") and provide new_text; the match must occur exactly once, and unselected content such as reply/forward history is preserved. Deprecated body is only an alias for new_text when old_text is also provided; body-only full replacement is rejected. Multiline plain-text replacements with format: "html" are rejected; use "markdown" for paragraphs or add tags like <p>/<br>. Body edits are re-read after saving; if the updated body is not observable after retries, the tool returns an error instead of reporting success. new_attachments adds files ({filePath, name?}[]); remove_attachments removes by attachment ID (string[]). Returns the updated draft ID, HTML body (draftHtml), and attachment metadata. |
send_draft |
account, id |
Send an existing draft email by ID. Use with draft IDs returned by draft_email or edit_draft. |
list_folders |
account, parentFolderId? |
List available mail folders. Returns top-level folders by default, or children of parentFolderId. |
create_folder |
account, displayName, parentFolderId? |
Create a new mail folder under root (default) or the given parent. |
delete_folder |
account, folderId |
Delete a mail folder by ID. |
rename_folder |
account, folderId, newName |
Rename an existing mail folder. |
mark_read |
account, id |
Mark a message as read. |
mark_unread |
account, id |
Mark a message as unread. |
Pull new emails
get_new_emails is the replacement for server-side watch/push delivery. The
server does not run background cron jobs; agents or their harnesses call this
tool on their own schedule, for example every 30–60 seconds.
Behavior:
- Polls inbox only.
accountis optional. When omitted, the tool checks all registered accounts.limitdefaults to10. In all-account mode, the limit is a global total across accounts, selected by oldestreceivedAtfirst.- All-account polling performs per-account candidate collection and hydration in parallel, then returns the combined batch in oldest-first order.
- First use for an account initializes its checkpoint to the newest inbox email and returns no emails for that account.
- Later calls return emails not previously returned by this tool, oldest first.
- Returned bodies are markdown and may be truncated around 20k characters; call
read_emailfor the full body when needed. - Attachments are returned as metadata only; call
read_attachmentfor content. - The tool does not mark emails as read.
limit: 0can initialize/check state without fetching message bodies.
All-account calls return partial failures as errors: [{ account, message }]
and still return successful accounts' emails. Pollers should continue processing
returned emails when errors is non-empty, and use those entries to notify or
log which accounts are failing.
See examples/hermes/ for a Hermes scheduler integration that polls this tool and hands new-email payloads to a Hermes agent.
Add-account flows
Outlook
- Agent calls
add_account({ provider: "outlook" }). - Server returns:
{ "status": "pending", "handle": "…uuid…", "verification": { "type": "device_code", "userCode": "ABCD-EFGH", "verificationUri": "https://microsoft.com/devicelogin", "expiresAt": "2025-…", "message": "To sign in, use a web browser to open …" } } - The user opens the URL and enters the code.
- Agent polls
complete_add_account({ provider: "outlook", handle })until it returns{ "status": "ready", "account": {...} }. - From then on, any tool can be called with
account: "<that-email>".
IMAP
IMAP accounts are added synchronously with host/user/password settings. If the server accepts TCP/TLS but closes during login, Hypermail reports an IMAP authentication failure. Check the mailbox password or app-password, confirm IMAP access is enabled by the provider, then re-add or update the account.
Gmail
Gmail uses Google OAuth 2.0, matching the official Gmail MCP model. Google's
device-code endpoint rejects Gmail API scopes, so Hypermail uses an authorization
URL with a real callback. Service accounts are only suitable for Google
Workspace domain-wide delegation; they don't grant server-to-server access to
consumer @gmail.com inboxes.
For local stdio/Desktop OAuth clients, Hypermail starts a temporary
127.0.0.1 loopback callback server automatically. For hosted HTTP deployments,
set HYPERMAIL_GMAIL_REDIRECT_URI and register the exact URI in Google Auth
Platform, for example:
HYPERMAIL_TRANSPORT=http
HYPERMAIL_GMAIL_REDIRECT_URI=https://mail.example.com/oauth/gmail/callback
- Configure
HYPERMAIL_GMAIL_CLIENT_IDand, when issued by your Google client type,HYPERMAIL_GMAIL_CLIENT_SECRET. Use a Desktop client for local loopback, or a Web client for hosted HTTP callbacks. - Agent calls
add_account({ provider: "gmail" }). - Server returns an OAuth URL:
{ "status": "pending", "handle": "…uuid…", "verification": { "type": "oauth_url", "userCode": "", "verificationUri": "https://accounts.google.com/o/oauth2/v2/auth?...", "expiresAt": "2025-…", "message": "Open this URL in a browser to authorize Gmail access..." } } - The user opens
verificationUriand grants access. If the configured callback is reachable, the browser shows a small success page and the agent can pollcomplete_add_account({ provider: "gmail", handle })until ready. - If the browser cannot reach the callback, the manual fallback still works:
copy the final redirected URL from the browser address bar and call:
{ "provider": "gmail", "handle": "…uuid…", "authorizationResponse": "http://127.0.0.1:54321/oauth2callback?code=...&state=..." } complete_add_accountvalidates state, exchanges the code for tokens, stores the account, and returns{ "status": "ready", "account": {...} }.
Roadmap
- Threading / conversations.
- Calendar integration.
Project layout
src/
cli.ts # arg parsing + entry
server.ts # MCP server, stdio + HTTP transports, session management
version.ts # version constant
config.ts # env-only config types + resolution
store/
account-store.ts # encrypted multi-account store (AES-256-GCM)
crypto.ts # AES-256-GCM encrypt/decrypt, key resolution, atomic writes
providers/
types.ts # EmailProvider interface + shared DTOs
registry.ts # routes account email → provider
outlook/
auth.ts # msal-node device-code flow
client.ts # @microsoft/microsoft-graph-client factory
index.ts # OutlookProvider implementation
imap/index.ts # IMAP provider (imapflow + nodemailer)
gmail/
auth.ts # Google OAuth authorization-code flow
client.ts # Gmail API (googleapis)
index.ts # GmailProvider implementation
shared/ # shared utilities across providers
tools/
index.ts # MCP tool registrations
accounts.ts # list/add/remove/complete-add account tools
browse.ts # list/search/read email tools
new-emails.ts # get_new_emails pull/checkpoint tool
compose.ts # send/draft/edit/send-draft tools
folders.ts # list/create/delete/rename folder tools
organize.ts # archive/trash/move/mark-read/mark-unread tools
shared.ts # shared tool helpers
License
MIT
Установить Hypermail Mcp в Claude Desktop, Claude Code, Cursor
unyly install hypermail-mcpСтавит в Claude Desktop, Claude Code, Cursor и VS Code — сам разбирается с npx, uvx и сборкой из исходников.
Впервые? Поставь CLI: curl -fsSL https://unyly.org/install | sh
Или настроить вручную
Выполни в терминале:
claude mcp add hypermail-mcp -- npx -y hypermail-mcpFAQ
Hypermail Mcp MCP бесплатный?
Да, Hypermail Mcp MCP бесплатный — установка в пару кликов через Unyly без оплаты.
Нужен ли API-ключ для Hypermail Mcp?
Нет, Hypermail Mcp работает без API-ключей и переменных окружения.
Hypermail Mcp — hosted или self-hosted?
Self-hosted: сервер запускается локально на твоей машине командой из раздела установки.
Как установить Hypermail Mcp в Claude Desktop, Claude Code или Cursor?
Открой Hypermail Mcp на unyly.org, выбери вкладку своего клиента (Claude Desktop, Claude Code, Cursor) и нажми Install — конфиг сгенерируется автоматически, без правки JSON.
Похожие MCP
Gmail
Read, send and search emails from Claude
автор: GoogleSlack
Send, search and summarize Slack messages
автор: SlackRunbear
No-code MCP client for team chat platforms, such as Slack, Microsoft Teams, and Discord.
Discord Server
A community discord server dedicated to MCP by [Frank Fiegel](https://github.com/punkpeye)
Compare Hypermail Mcp with
Не уверен что выбрать?
Найди свой стек за 60 секунд
Автор?
Embed-бейдж для README
Похожее
Все в категории communication
