MCPEmails

MCP serverCommunication

Connects your email inboxes so your AI can read, search, and send messages for you. Once added, it can also draft replies, organize your mail, and schedule emails to go out later. This works across multiple inboxes, so everything stays in one place.

Use MCPEmails in Claude, ChatGPT or Ahel Desktop

Free. Sign in, add MCPEmails and connect your AI. About a minute.

Also: Claude Code · Cursor · Codex

Then ask your AI: use MCPEmails

Details

Available today. Use it from your connected AI after setup.

Add the integration and connect your inbox. Then ask your AI to find, draft, or send an email to see it in action.

MCPEmailsStart free

What your AI can do with it

  • Search your inboxes for specific messages
  • Read emails and summarize what's in them
  • Send new emails on your behalf
  • Draft replies for you to review
  • Organize messages in your inboxes
  • Schedule emails to be sent later

Install MCPEmails

The server’s own address, for the clients that take one directly. Or connect Ahel once and every client you use reads it from one address, with the account kept on Ahel rather than in each client’s config.

  • Claude Code

    claude mcp add --transport http --scope user mcpemails 'https://mcpemails.com/api/mcp'

    Run it once in your project, then open /mcp to approve any sign-in the server asks for.

  • Claude Desktop

    https://mcpemails.com/api/mcp

    Add a custom connector in Settings, paste this address, and approve the sign-in.

  • Cursor

    cursor://anysphere.cursor-deeplink/mcp/install?name=mcpemails&config=eyJ1cmwiOiJodHRwczovL21jcGVtYWlscy5jb20vYXBpL21jcCJ9

    Open the link and Cursor adds the server at that address.

  • ChatGPT

    https://mcpemails.com/api/mcp

    In Settings, enable Developer mode, create an MCP app, and paste this address. Your plan and workspace must allow custom apps.

  • Codex

    codex mcp add mcpemails --url 'https://mcpemails.com/api/mcp'

    Run it once, then sign in with codex mcp login mcpemails if the server asks for an account.

From the project's README

As published by albretsen/mcpemails in README.md.

Give your AI agent an inbox. A hosted Model Context Protocol server that lets Claude, Cursor, or any MCP‑compatible client read, search, send, organize, and schedule email through your existing mailboxes — without ever storing your mail.

Connect a mailbox once, paste one URL into your agent, and it can work your inbox live. Email is fetched on demand and never retained; credentials are encrypted at rest and decrypted only at call time inside an isolated edge function.

🔗 mcpemails.com · 📚 Docs · 💳 Pricing


Contents

  • How it works
  • Quick start (connecting an agent)
  • Capabilities
  • Tools
  • OAuth scopes
  • Supported providers
  • Pricing
  • Architecture
  • Repository layout
  • Local development
  • Environment variables
  • Database & migrations
  • Deployment
  • Self-hosting
  • Internationalization
  • Security model

How it works

  1. Connect a mailbox. Sign in at mcpemails.com and connect Outlook / Microsoft 365 (Sign in with Microsoft, over Microsoft Graph), Gmail (a Google app password over IMAP by default, or Sign in with Google), or any IMAP/SMTP account (app password). Credentials are encrypted with AES‑256‑GCM before they touch the database.
  2. Get access. OAuth‑capable clients (claude.ai, Claude Desktop, Cursor) connect in one click via OAuth 2.0 + PKCE. Everything else uses a scoped API key (mcpe_…).
  3. Point your client at the server. The MCP endpoint is a single URL:
    https://mcpemails.com/api/mcp
    
  4. Your agent works the inbox. It calls tools like inbox_list, email_read (action: "search"), email_compose (action: "send"), and schedule (action: "create"). Each request fetches live from your provider — nothing is mirrored or cached server‑side.

Permissions are scoped per key, so you can hand an agent read:email only, or grant it send and folder management without ever exposing delete.

Quick start (connecting an agent)

Claude Desktop / Cursor (OAuth): add a remote MCP server pointing at https://mcpemails.com/api/mcp and approve the consent screen. Pick the scopes the agent should have.

API key (any MCP client): create a key in the dashboard, choose its scopes and (optionally) restrict it to specific inboxes, then send it as a bearer token:

// Example MCP client config
{
  "mcpServers": {
    "mcpemails": {
      "url": "https://mcpemails.com/api/mcp",
      "headers": { "Authorization": "Bearer mcpe_your_key_here" }
    }
  }
}

The protocol is JSON‑RPC 2.0 over HTTP (MCP 2025-06-18, Streamable transport). Start every session with inbox_list — it returns the inboxes the key can reach, their per‑provider capabilities, and a versioned compatibility profile. The profile marks normalized operations as exact, different, or unavailable, so agents can preserve provider differences rather than silently weakening a request.

Setup guides

Copy‑paste instructions per client, including where each one keeps its config file: mcpemails.com/docs/clients.

Claude · Claude Code · ChatGPT · Cursor · VS Code · Cline · Windsurf · Gemini CLI · Zed · JetBrains · Raycast · Warp · curl

Capabilities

  • Live, never stored — email is read straight from your provider on each call; no message bodies are persisted.
  • Multi‑provider — Outlook / Microsoft 365 via Microsoft Graph (Sign in with Microsoft), Gmail via a Google app password or Sign in with Google, plus any IMAP/SMTP mailbox (Fastmail, iCloud, Yahoo, Zoho, Yandex, self‑hosted…) via app password.
  • No relay — outbound mail is sent through your provider's SMTP/API, from your real address.
  • Granular scopes — eight permission scopes, grantable independently per API key and per inbox.
  • Batch & search‑and‑act — read, move, delete, or flag up to hundreds of messages in one call, including "search then move/delete" combinators.
  • Drafts & scheduling — compose drafts and queue messages for future send (server‑side dispatch).
  • Provider‑agnostic search — Gmail syntax, Microsoft Graph KQL and IMAP SEARCH are normalized behind one email_read (action: "search") interface.
  • Team‑ready: workspaces, members, roles, SSO, and an audit log on the Team plan.

Tools

17 tools you call directly. Most are resource-oriented and take an action argument that selects the specific operation (and, for actions that need different privileges, the required scope):

  • inbox_list - Lists the inboxes the key can reach, with each one's provider capabilities.
  • email_read - Lists, reads and searches messages, in batches, plus attachments, extracted attachment text and the original .eml.
  • email_organize - Moves, copies, flags and archives messages you name by id, singly or in batches.
  • email_search_and_move - Moves every message matching a search into a folder. Its own tool, and the destructive one, because a wrong filter relocates a whole inbox.
  • email_delete - Trashes or permanently deletes messages, singly, in batches, or by search.
  • email_compose - Sends, replies and forwards through your own provider, from your real address.
  • folder_list - Lists folders (labels on Gmail) with their provider-native IDs and message counts. Read-only.
  • folder - Creates, renames and deletes folders (labels on Gmail).
  • draft_list - Lists the drafts saved in the inbox, with their draft ids. Read-only.
  • draft - Creates, updates, sends and deletes drafts, including provider-native replies.
  • schedule_list - Lists what is queued for future delivery. Read-only.
  • schedule - Queues a message for future delivery, and cancels one that is queued.
  • signature_get - Reads the signature and sender name configured for an inbox. Read-only.
  • signature_set - Sets the signature appended to outbound mail, and the sender name.
  • automation_read - Lists triage rules, reads one, shows run history, and dry-runs a filter. Read-only.
  • automation - Creates, updates, enables, disables and deletes unattended triage rules, with no model in the loop.
  • contact_search - Looks up contacts by scanning recent mail live, with no stored address book.
ToolActionsScope(s)
inbox_list(single action)read:email
email_readlist, read, read_batch, search, attachment, extract, originalread:email (search also accepts search:email)
email_organizemove, move_batch, copy, copy_batch, flag, archivemanage:folders
email_search_and_move(single action)manage:folders
email_deletedelete, delete_batch, search_and_deletedelete:email
email_composesend, reply, forwardsend:email
folder_list(single action)read:email
foldercreate, rename, deletemanage:folders
draft_list(single action)manage:drafts
draftcreate, reply, update, send, deletemanage:drafts (create/reply/update/delete), read:email (reply also), send:email (send)
schedule_list(single action)schedule:email
schedulecreate, cancelschedule:email
signature_get(single action)read:email
signature_set(single action)send:email
automation_readlist, get, runs, previewmanage:automations
automationcreate, update, enable, disable, deletemanage:automations
contact_search(single action)manage:contacts (also accepts read:email)

A full-scope key sees 22 tools in tools/list: the 16 above plus six app-only tools (approval_review, approval_decide, approval_update, approval_schedule, bulk_execute, bulk_cancel). Those carry _meta.ui.visibility: ["app"] and drive the review card an MCP client renders for a held send or a previewed bulk operation, rather than being composed by hand.

Notes:

  • Tools accept either an explicit inbox_id (UUID) or an inbox email address; single‑inbox keys auto‑resolve the target.
  • Batch actions cap at 50 (email_read's read_batch) to 500 (move/delete/flag) messages per call.
  • For a targeted mutation, first use email_read with action: "search", then pass the returned message_id or message_ids to email_organize or email_delete. Search fields are accepted only by email_search_and_move and email_delete's search_and_delete action.
  • contact_search scans recent mail live, so there is no stored address book.
  • email_read's original action returns one complete provider-stored MIME message as a portable .eml file (up to 25 MB). It is read-only and never marks the message as read.
  • draft's send action requires send:email, not manage:drafts, so a key that can only manage drafts can't use them to bypass the send‑mail consent.
  • draft's reply action creates an unsent, provider-native reply in the source conversation. It needs both manage:drafts and read:email, and defaults to replying only to the sender.
  • The read-only halves (folder_list, draft_list, schedule_list, signature_get, automation_read) were split out of their write tools on 2026-09-09, so a read-only key is never shown a write tool. The old combined shapes (folder action: "list", draft action: "list", schedule action: "list", signature action: "get"/"set", automation action: "list"/"get"/"runs"/"preview") are still accepted on the wire for already-connected clients, but are no longer advertised.
  • automation manages unattended scheduled triage rules: a stored search plus one fixed action, evaluated on a cadence with no model in the loop. There is no delete-mail action, a forward always waits for human approval, and draft_reply only ever writes a draft. See docs/automations-trust-boundary.md.
  • tools/list only returns the tools your key (or OAuth token) is actually scoped for.

OAuth scopes

ScopeGrants
read:emailList inboxes & folders; list, read, and search messages; read an inbox's signature; look up contacts
search:emailNarrower alternative that grants only email_read's search action
send:emailSend, reply and forward; set the signature and sender name; also required to send a draft
manage:foldersCreate/rename/delete folders; move, copy, flag and archive messages
delete:emailTrash or permanently expunge messages
manage:draftsCreate, edit, and delete drafts (sending one also requires send:email)
manage:contactsLive contact lookup from recent mail
schedule:emailQueue messages for future delivery
manage:automationsCreate and manage unattended scheduled triage rules (no delete action; forwards stay approval-gated)

Supported providers

ProviderConnect viaRead/SearchSendFoldersPermanent deleteDrafts
Gmail / Google WorkspaceApp password (IMAP/SMTP) by default, or OAuth 2.0 (Sign in with Google)✅✅LabelsTrash only✅
FastmailApp password (IMAP/SMTP)✅✅✅✅✅
iCloud, Yahoo, AOL, GMX, Zoho, YandexApp password (IMAP/SMTP)✅✅✅✅✅
IONOS, STRATO, One.com, OVHcloud, Namecheap Private Email, Hostinger, Infomaniak, HetznerMailbox password (IMAP/SMTP)✅✅✅✅✅
cPanel, Plesk, SiteGround, Bluehost and other shared hostsMailbox password (IMAP/SMTP)✅✅✅✅✅
Any other IMAP/SMTP mailboxApp password or mailbox password✅✅✅✅✅

Mail on your own domain at a web host connects like any IMAP mailbox, and one workspace can hold several of them (info@, sales@, support@). Per-host settings and known pitfalls for 100 providers are at mcpemails.com/connect. | Outlook / Microsoft 365 | OAuth 2.0 (Microsoft Graph) | ✅ | ✅ | ✅ (nested) | ✅ | ✅ |

Outlook uses Microsoft Graph with "Sign in with Microsoft", not IMAP. Personal Microsoft accounts (outlook.com, hotmail.com, live.com, msn.com) connect directly. On work or school Microsoft 365 tenants, Microsoft's default consent policy stops employees approving mail permissions themselves, so an IT admin approves the app once for the organisation; the dashboard gives the user a shareable approval link to send them (/auth/outlook/admin-consent), and the admin needs no MCP Emails account. A Microsoft account with no Exchange Online mailbox is refused, with a pointer to IMAP. The label tools are Gmail-only (Outlook uses folders), except that an automation's label action applies an Outlook category; and Graph cannot combine a text search with the unread, attachment, flagged or date filters.

Pricing

The value metric is connected inboxes. Free connects one mailbox, Personal connects up to three, Pro connects every mailbox you own, and Team adds people, roles, and a separate workspace per client. Annual billing saves about 20%.

FreePersonalProTeam
Price$0$9/mo · $86.40/yr ($7.20/mo)$15/mo · $144/yr ($12/mo)$79/mo · $756/yr ($63/mo)
Connected inboxes13UnlimitedUnlimited
Email actions / month150 (first 7 days uncounted)No monthly cap (fair use)No monthly cap (fair use)No monthly cap (fair use)
API keysUnlimitedUnlimitedUnlimitedUnlimited
Members1 (owner only)1 (owner only)1 (owner only)Unlimited, with roles
Fair‑use rate limit60 req/min120 req/min300 req/min1,000 req/min
Team roles & workspacesNoNoNo✅
SSO (SAML/OIDC) + audit logNoNoNo✅
SupportCommunityEmailEmailPriority

Per‑API‑key limits also apply (100 req/min · 1,000/hr · 10,000/day). Rate limits are retryable: they come back as JSON-RPC error -32003 with data.retry_after in seconds.

Free workspaces get 150 email actions per calendar month (UTC). The first 7 days after signup are not counted, and inbox_list and the dashboard are never counted. When the allowance is reached, every other email action is refused until the 1st of the next month, unattended automations pause and resume automatically on the 1st, and the owner is emailed at 80% and at 100%. Personal, Pro and Team have no monthly cap, subject to fair use (their ceilings are abuse guards, not plan features, and are not printed anywhere). A refusal is not retryable and not a JSON-RPC error: it comes back as a normal tool result with isError: true, text that opens with the count and the reset date, and a _meta["com.mcpemails/usage_limit"] block that clears at reset_at.

Internal plan ids predate the names: solo is sold as Pro and pro is sold as Team. The newer personal id is the only one that matches its display name, Personal. Every user who existed before the 2026-08-19 repricing keeps unlimited inboxes for free, permanently, and every workspace created before the 2026-09-12 launch of the action allowance is an early member that is not metered against it. See apps/web/src/lib/stripe/plans.ts.

Architecture

flowchart LR
    Agent["MCP client<br/>(Claude, Cursor, …)"] -->|"JSON-RPC / OAuth or API key"| Web

    subgraph Vercel["Vercel — Next.js 16"]
      Web["/api/mcp route<br/>+ marketing site + dashboard"]
    end

    subgraph Supabase
      Edge["mcp-server<br/>edge function (Deno)"]
      DB[("Postgres<br/>RLS + encrypted creds")]
      Cron["token-refresh<br/>edge functions"]
    end

    Web -->|proxies| Edge
    Edge -->|decrypt creds, fetch live| Providers["Email providers<br/>Gmail API · Microsoft Graph · IMAP/SMTP"]
    Edge --> DB
    Cron --> DB
    Web --> Stripe[("Stripe<br/>billing")]
  • /api/mcp is a thin Next.js route handler that proxies to the Supabase edge function mcp-server — the real MCP implementation, where credentials are decrypted and provider calls are made.
  • The Postgres database stores workspaces, members, inboxes (encrypted tokens/passwords), hashed API keys, OAuth clients, scheduled sends, and an activity log — all guarded by Row‑Level Security.
  • Cron edge functions refresh Gmail/Outlook OAuth tokens before expiry.

Stack: Next.js 16 (App Router) · React 19 · next‑intl 4 · Supabase (Auth, Postgres, Edge Functions) · Stripe · Resend · TypeScript. Email parsing/sanitization via mailparser, jsdom, and isomorphic-dompurify.

Repository layout

.
├── apps/
│   └── web/                     # Next.js 16 app (marketing, dashboard, /api/mcp proxy)
│       ├── app/                 # App Router routes ([locale], dashboard, api, auth)
│       ├── components/          # marketing/ + dashboard/ React components
│       ├── messages/            # next-intl translations (en, nb, es, fr, zh)
│       ├── src/lib/             # stripe/, supabase/, blog/, crypto helpers
│       └── proxy.ts             # middleware: i18n + Supabase session + CDN cache
├── supabase/
│   ├── functions/
│   │   ├── mcp-server/          # the MCP server (tools, auth, scopes)
│   │   ├── gmail-token-refresh/
│   │   └── outlook-token-refresh/
│   └── migrations/              # SQL migrations (schema + RLS)
└── package.json                 # npm workspaces (apps/*)

Local development

Prerequisites: Node.js 20+, npm, and the Supabase CLI (for migrations and edge functions).

# 1. Install (npm workspaces — run from the repo root)
npm install

# 2. Configure environment
cp .env.example apps/web/.env.local
#   then fill in the values (see below) and generate the two secrets:
openssl rand -hex 32   # ENCRYPTION_KEY
openssl rand -hex 32   # CSRF_SECRET

# 3. Run the web app (http://localhost:3000)
npm run dev

# 4. Production build
npm run build

next.config.js validates required env vars at build/start and rejects weak ENCRYPTION_KEY values, so a misconfigured environment fails fast instead of at runtime.

Environment variables

Copy .env.example and fill in real values. Required in every environment:

VariablePurpose
NEXT_PUBLIC_SUPABASE_URL / NEXT_PUBLIC_SUPABASE_ANON_KEYSupabase client (public)
SUPABASE_SERVICE_ROLE_KEYServer‑side admin key (bypasses RLS) — secret
NEXT_PUBLIC_APP_URLCanonical base URL; drives OAuth redirect URIs
GOOGLE_SITE_VERIFICATION (optional)Google Search Console HTML-tag verification token; set only in production
ENCRYPTION_KEY64‑hex AES‑256‑GCM key for credentials at rest — secret
CSRF_SECRET64‑hex HMAC key for CSRF tokens (distinct from above) — secret

Feature‑dependent:

Variable(s)Needed for
GMAIL_CLIENT_ID / GMAIL_CLIENT_SECRETGmail OAuth (gmail.readonly, gmail.send, gmail.modify)
OUTLOOK_CLIENT_ID / OUTLOOK_CLIENT_SECRET / OUTLOOK_TENANT_IDOutlook OAuth (Mail.ReadWrite, Mail.Send, offline_access, openid, profile, email). OUTLOOK_TENANT_ID is optional and defaults to common
NEXT_PUBLIC_OAUTH_VERIFICATION_PENDINGShows the unverified‑app warning until Google verification completes (the Microsoft publisher is already verified)
STRIPE_SECRET_KEY / STRIPE_WEBHOOK_SECRET / NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEYBilling
STRIPE_PRICE_PERSONAL_MONTHLY / _YEARLY, STRIPE_PRICE_SOLO_MONTHLY / _YEARLY, STRIPE_PRICE_PRO_MONTHLY / _YEARLYPlan price IDs (personal = Personal, solo = Pro, pro = Team)
STRIPE_WEBHOOK_PROXY_KEYOptional. Restricts /api/stripe/webhook to the delivery queue in front of it. Unset = no restriction. Set it only AFTER the queue is sending the key, or every delivery 401s and is dead‑lettered.
STRIPE_WEBHOOK_TOLERANCE_SECONDSOptional. Stripe signature age limit, default 7 days. Wide because a queued replay carries its original signature; Stripe's own 300s default would reject every held retry.

Fastmail and other IMAP providers connect via app password and need no OAuth credentials.

Stripe webhooks are delivered through a queue. Stripe posts to the Queuey ingress, which forwards to /api/stripe/webhook. Two settings on that queue are load‑bearing: the payload must be raw passthrough (the signature is over exact bytes), and a mapped header must forward header:Stripe-Signature. Without the mapped header every delivery fails with Missing stripe-signature header. Note that STRIPE_WEBHOOK_SECRET must be the signing secret of the Stripe endpoint pointed at the ingress, not of any older direct endpoint.

Database & migrations

Schema and Row‑Level Security policies live in supabase/migrations/. Core tables: workspaces, workspace_members, inboxes (encrypted credentials, soft‑deleted), api_keys (hashed, scoped, inbox‑restricted), oauth_clients, scheduled_sends, workspace_invites, and a month‑partitioned activity_log.

# Apply migrations to the linked project
npx supabase db push

# Regenerate TypeScript types from the live schema
npm run gen:types

The Supabase CLI is the source of truth for DB changes in this project.

apps/web/src/types/database.types.ts is GENERATED

It is written in full by npm run gen:types (supabase gen types typescript --linked), which reads the live linked project, not the migration files. Do not hand-edit it: a hand-added column is silently dropped by the next regeneration, and a hand-added column that never existed in the database compiles clean and 500s in production. Regenerate it in the same change that applies a migration, and commit the result.

Tables, views and RPC functions are all covered, and every Supabase client declared in the .ts/.tsx of apps/web (which is the whole typechecked program) is SupabaseClient<Database>, so .from(), .select(), .insert() and .rpc() are type-checked against the real schema. If a table or column looks missing, the fix is npm run gen:types, never a local as any on the Supabase client. Casts that remain are for things the generator genuinely cannot express: jsonb payload shapes, nullable function arguments, and one helper that dispatches many RPCs through a single signature. Each of those carries a comment saying so.

Shortened here. Read the whole README on GitHub.

Signals

GitHub stars
8
Forks
2
Last commit
Oct 2026
Weekly downloads
38
Weekly_downloads
40 weekly_downloads
Hacker News mentions
20
Advanced
Delivery
emails MCP server → your Ahel connector (mcp.ahel.ai) → your AI.
Item type
mcp-server
Key
com-mcpemails-emails
Source
github.com/albretsen/mcpemails
Hosted endpoint
https://mcpemails.com/api/mcp