npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

hypermail-mcp

v0.7.8

Published

Unified email MCP server — operate any inbox (Outlook now, IMAP/Gmail later) by passing an email address.

Readme

hypermail-mcp

A Model Context Protocol server that lets an agent operate any of the user's inboxes through a single, unified tool surface.

v0.7.8 — Default Gmail loopback redirect URI changed from random-port /oauth2callback to fixed http://127.0.0.1:33333/callback (still overridable via HYPERMAIL_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 via HYPERMAIL_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_account accepts a final redirected URL or raw code/state, and provider credentials use dedicated HYPERMAIL_GMAIL_* / HYPERMAIL_OUTLOOK_* env vars.

v0.7.5 — Attachments via file path on send_email/draft_email (attachments param). edit_draft gains new_attachments and remove_attachmentsadd_attachment_to_draft is removed (23 tools now). Draft editing uses multi-strategy thread boundary detection for more reliable quoted-thread preservation. Watcher now supports shell-command notification alongside webhook delivery. Published CLI installs the MCP SDK dependency so global/npx runs do not fail on a missing SDK module.

v0.7.4inReplyTo is now a required parameter on send_email and draft_email (was optional). Set it to false for 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.3edit_draft now 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.7.0 — Email watch mode: background poll loop detects new inbox messages and POSTs them to a configurable webhook URL (e.g. Mastra). Opt-in — disabled by default, enabled via HYPERMAIL_WATCH_ENABLED=true. Works in both stdio and HTTP transport modes.

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-key auth, per-agent allowlists), and the check_notifications tool. Dropped js-yaml dependency. Dockerfile simplified to a single install → build → prune step.

v0.6.2 — Version source-of-truth fix: version.ts now imports directly from package.json instead 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 isHtml boolean with required format parameter ("html" | "markdown") on send_email, draft_email, and edit_draft. Markdown bodies are converted to HTML via marked so 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 caused validateToolOutput crashes.

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-node for auth, @microsoft/microsoft-graph-client for 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, --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_WATCH_ENABLED | Enable inbox polling: true or false | false | | HYPERMAIL_WATCH_POLL_SECONDS | Watcher polling cadence | 10 | | HYPERMAIL_WATCH_WEBHOOK_URL | Webhook delivery target | Required if watch is enabled and no notify command is set | | HYPERMAIL_WATCH_WEBHOOK_RETRY_ATTEMPTS | Webhook retry attempts | 5 | | HYPERMAIL_WATCH_WEBHOOK_RETRY_DELAY_MS | Webhook exponential-backoff base delay | 1000 | | HYPERMAIL_WATCH_NOTIFY_COMMAND | Shell command run with EmailFull JSON on stdin | Required if watch is enabled and no webhook is set | | HYPERMAIL_WATCH_NOTIFY_TIMEOUT_MS | Notify-command execution timeout | 30000 | | HYPERMAIL_WATCH_NOTIFY_RETRY_ATTEMPTS | Notify-command retry attempts | 5 | | HYPERMAIL_WATCH_NOTIFY_RETRY_DELAY_MS | Notify-command exponential-backoff base delay | 1000 |

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. Returns {handle, verification:{type, userCode, verificationUri, expiresAt, message}}. | | complete_add_account | provider, handle, authorizationResponse?, code?, state? | Returns pending / ready / expired / error. 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. | | search_emails | account, query, limit? | KQL on Outlook. | | read_email | account, id, format? | Returns full body + recipients + attachment metadata. format: markdown (default), html, or text. | | 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. 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 HTML body (draftHtml). inReplyTo is required — set to false for new emails. | | edit_draft | account, id, to?, cc?, bcc?, subject?, body?, format?, include_signature?, new_attachments?, remove_attachments? | Edit an existing draft by ID. Only provided fields are updated. 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. |

Email Watch

When enabled, hypermail-mcp runs a background poll loop that scans inboxes for new messages and delivers each one via webhook POST and/or shell command. Intended for push-based email triage — downstream agents receive full email content without polling.

HYPERMAIL_WATCH_ENABLED=true \
HYPERMAIL_WATCH_POLL_SECONDS=10 \
HYPERMAIL_WATCH_WEBHOOK_URL=http://localhost:3000/api/email-webhook \
HYPERMAIL_WATCH_NOTIFY_COMMAND='node /path/to/email-handler.js' \
hypermail-mcp

Validation: If HYPERMAIL_WATCH_ENABLED=true, startup requires at least one of HYPERMAIL_WATCH_WEBHOOK_URL or HYPERMAIL_WATCH_NOTIFY_COMMAND. Webhook URLs are syntax-validated, and notify commands must be non-empty. Startup does not test network reachability or execute the command.

Behavior:

  • Polls all accounts in the store, inbox only.
  • Detects new emails via lastSeenIds (capped at 200) stored in the encrypted account file — no duplicate emits across restarts.
  • Two delivery modes (can be used together):
    • Webhook: One POST per email (full body as EmailFull JSON).
    • Notify command: Executes HYPERMAIL_WATCH_NOTIFY_COMMAND through the platform shell with the EmailFull JSON piped to stdin.
  • Both modes use exponential backoff (baseDelay × 2^attempt). Retries on failures (non-2xx for webhook, non-zero exit for command). Logs and moves on after maxAttempts exhausted — never blocks the poll loop.
  • Command delivery is fire-and-forget — the poll loop continues while delivery runs in the background.
  • Works in both stdio and HTTP transport modes — the poll interval fires normally alongside MCP message handling.

Rate limits: Polling every 10s on a single inbox = 6 req/min = 0.6% of Microsoft Graph's 10,000 req/10min per-user limit. Safe for personal inboxes.

Add-account flows

Outlook

  1. Agent calls add_account({ provider: "outlook" }).
  2. 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 …"
      }
    }
  3. The user opens the URL and enters the code.
  4. Agent polls complete_add_account({ provider: "outlook", handle }) until it returns { "status": "ready", "account": {...} }.
  5. From then on, any tool can be called with account: "<that-email>".

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
  1. Configure HYPERMAIL_GMAIL_CLIENT_ID and, 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.
  2. Agent calls add_account({ provider: "gmail" }).
  3. 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..."
      }
    }
  4. The user opens verificationUri and grants access. If the configured callback is reachable, the browser shows a small success page and the agent can poll complete_add_account({ provider: "gmail", handle }) until ready.
  5. 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=..."
    }
  6. complete_add_account validates 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
  watcher/
    manager.ts                 # WatcherManager — inbox poll loop + dedup
    webhook.ts                 # HTTP POST with exponential backoff retry
    script.ts                  # shell-command delivery with retry/timeout
    index.ts                   # barrel export
  tools/
    index.ts                   # MCP tool registrations
    accounts.ts                # list/add/remove/complete-add account tools
    browse.ts                  # list/search/read email tools
    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