Skip to main content
Glama
th-an

yahoo-mail-mcp

by th-an

yahoo-mail-mcp

A Model Context Protocol (MCP) server for Yahoo Mail over IMAP + SMTP. Gives AI assistants full email management — list, read, search, thread, flag, move, delete/trash, archive, folders, attachments, and send/reply/forward/draft — using your Yahoo app-specific password.

Built with the official MCP SDK, imapflow, mailparser, nodemailer, and zod. TypeScript, MIT licensed.

Features (31 tools)

Read

  • list_folders – list all IMAP folders with special-use type

  • list_emails – paginated listing with UID, flags, attachment metadata

  • read_email – full text/html body, headers, attachment metadata

  • read_raw – raw RFC 822 source for header/inspection

  • search_emails – filter by subject/sender/body, recipient, date range, unread/flagged/answered

  • get_thread – reconstruct a conversation via In-Reply-To references

  • get_unsubscribe – List-Unsubscribe header (one-click URL / mailto address)

  • extract_contacts – most frequent senders from recent mail

  • extract_calendar – parse ICS calendar attachments

Manage

  • mark_read / mark_unread

  • flag_emails / unflag_emails

  • move_emails / delete_emails (to Trash) / archive_emails

  • cleanup_folder – bulk delete/archive by sender, age, unread, flagged

  • create_folder / rename_folder

Attachments

  • list_attachments – metadata only (no body download)

  • get_attachment – single attachment as base64 (max 5 MB)

  • get_attachments – all attachments as base64 (5 MB each, 25 MB total)

Send (SMTP)

  • send_email

  • reply_email – sets In-Reply-To / References, auto-marks original as answered

  • forward_email – quotes the original message

  • reply_draft / forward_draft – save a reply/forward as a draft (no send)

  • save_draft / send_draft

Stats

  • get_email_stats – per-folder message and unread counts

  • mailbox_stats – per-folder counts + size (when the server exposes it)

Requirements

  • Node.js >= 18

  • A Yahoo Mail account with 2-step verification enabled and an app-specific password. Generate it at Yahoo Account Security -> Manage app passwords (16 chars, no spaces).

Setup

cp .env.example .env
# edit .env: set YAHOO_EMAIL and YAHOO_APP_PASSWORD
npm install
npm run build

Optional overrides: YAHOO_IMAP_HOST, YAHOO_IMAP_PORT, YAHOO_SMTP_HOST, YAHOO_SMTP_PORT (465 for implicit TLS), YAHOO_MCP_HOST, YAHOO_MCP_PORT, YAHOO_MCP_TOKEN (Bearer token for remote/SSE access).

Usage

stdio (Claude Desktop / Claude Code / Cursor):

{
  "mcpServers": {
    "yahoo-mail": {
      "command": "node",
      "args": ["/absolute/path/to/yahoo-mcp/dist/index.js"],
      "env": {
        "YAHOO_EMAIL": "you@yahoo.com",
        "YAHOO_APP_PASSWORD": "your-app-password"
      }
    }
  }
}

SSE / remote (for Claude.ai or network clients):

YAHOO_MCP_TOKEN=secret npm run start:sse
# SSE endpoint: http://127.0.0.1:3000/sse

If YAHOO_MCP_TOKEN is set, all SSE requests must include Authorization: Bearer <token>.

Streamable HTTP (single endpoint, stateless — recommended for public/remote):

YAHOO_MCP_TOKEN=secret npm run start:http
# POST http://127.0.0.1:8080/  (MCP endpoint)
# GET  http://127.0.0.1:8080/health  (health probe)

Stateless mode works with any MCP client: no session handshake is required, so each request may be handled by a fresh connection/instance. Requests must carry Authorization: Bearer <token> (Bearer token set via YAHOO_MCP_TOKEN).

Deploy to Google Cloud Run (free tier)

The containerized server is designed to run cost-free on Cloud Run: min-instances=0, max-instances=1, requests billed only while serving.

  1. Fill in gcloud-env.yaml.example -> env.yaml (local, gitignored) with YAHOO_EMAIL, YAHOO_APP_PASSWORD, YAHOO_MCP_TOKEN.

  2. gcloud auth login && gcloud config set project <PROJECT>

  3. ./scripts/deploy-gcloud.sh us-central1

The script builds via Cloud Build and prints the service URL. Requests are forwarded to the container on port 8080 (the app reads the PORT env var).

Note: Cloud Run's own front end reserves the exact path /healthz on *.run.app and never forwards it; the app's health probe is therefore served at /health.

In Claude.ai, add a Remote MCP server: URL is the service URL, and add header Authorization: Bearer <YAHOO_MCP_TOKEN>.

Connecting to Claude

  • Claude.ai — native remote MCP server: URL = service URL, request header Authorization: Bearer <token>. The server is stateless, so it reconnects cleanly after any idle gap.

  • Claude Desktop — Desktop can't speak raw Streamable HTTP, so run the local stdio bridge (scripts/stdio-bridge.mjs) that forwards to the remote endpoint and attaches the Bearer token. Add this exact block to ~/Library/Application Support/Claude/claude_desktop_config.json:

{
  "mcpServers": {
    "yahoo-mail": {
      "command": "node",
      "args": ["/ABS/PATH/TO/yahoo-mcp/scripts/stdio-bridge.mjs"],
      "env": {
        "MCP_URL": "https://YOUR-SERVICE-REGION.run.app/"
      }
    }
  }
}

The bridge reads YAHOO_MCP_TOKEN from the env block if present, otherwise from the project's env.yaml (auto-detected). Restart Claude Desktop after editing. See docs/CLAUDE_INTEGRATION.md for the full guide and troubleshooting.

Design history & gotchas

The notes on adapting this server to Claude / Streamable HTTP / Cloud Run — mcp-remote OAuth vs Bearer tokens, stateless enforceStrictCompliance, /healthz reserved by Cloud Run, concurrency=1429, and more — live in docs/LEARNINGS.md.

Verify your credentials first

npm run check:auth

Read-only IMAP login check + SMTP verify() (no mail is ever sent). See scripts/check-auth.ts.

Development

npm run dev          # run src via tsx
npm run build        # tsc -> dist
npm run typecheck    # tsc --noEmit
npm test             # vitest
npm run smoke        # end-to-end: boot server, list tools, list folders

Layout

src/
  index.ts          # MCP server: stdio, --sse, or --http (Streamable HTTP) transport
  config.ts         # zod-validated environment config
  loadEnv.ts        # loads .env then .secrets.env (no dotenv dependency)
  http.ts           # stateless Streamable HTTP handler: bearer auth, /health
  tools.ts          # 24 tool definitions (zod input schemas)
  mail/
    imap.ts         # IMAP service (imapflow, single shared connection)
    smtp.ts         # SMTP service (nodemailer: send/reply/forward/draft)
    types.ts        # shared types
scripts/
  check-auth.ts     # feasibility/auth verification
  smoke.ts          # MCP end-to-end smoke test
  deploy-gcloud.sh  # build + deploy to Cloud Run (gcloud, uses env.yaml)
  verify-remote.ts  # SDK client end-to-end check of a remote HTTP server
  push-all.sh       # push to public + private remotes (see SECURITY.md)
test/               # vitest tests

Design notes (Yahoo realities)

  • Yahoo does not advertise IDLE, MOVE, UIDPLUS, or SPECIAL-USE. imapflow transparently emulates MOVE (COPY + delete + EXPUNGE) and special-use is resolved by folder name, so all tools work unchanged.

  • No IMAP IDLE watcher, OAuth, or CalDAV/CardDAV: Yahoo doesn't support them, and app passwords avoid OAuth entirely.

  • One shared IMAP connection with per-mailbox locks respects Yahoo's limit of ~5 concurrent connections per IP.

Security

  • Credentials are read from local .env / .secrets.env files that are never committed. The public repository contains zero secrets.

  • Delete is a soft delete (move to Trash); nothing is permanently expunged.

License

MIT. See LICENSE.