yahoo-mail-mcp
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 typelist_emails– paginated listing with UID, flags, attachment metadataread_email– full text/html body, headers, attachment metadataread_raw– raw RFC 822 source for header/inspectionsearch_emails– filter by subject/sender/body, recipient, date range, unread/flagged/answeredget_thread– reconstruct a conversation via In-Reply-To referencesget_unsubscribe– List-Unsubscribe header (one-click URL / mailto address)extract_contacts– most frequent senders from recent mailextract_calendar– parse ICS calendar attachments
Manage
mark_read/mark_unreadflag_emails/unflag_emailsmove_emails/delete_emails(to Trash) /archive_emailscleanup_folder– bulk delete/archive by sender, age, unread, flaggedcreate_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_emailreply_email– sets In-Reply-To / References, auto-marks original as answeredforward_email– quotes the original messagereply_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 countsmailbox_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 buildOptional 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/sseIf 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.
Fill in
gcloud-env.yaml.example->env.yaml(local, gitignored) withYAHOO_EMAIL,YAHOO_APP_PASSWORD,YAHOO_MCP_TOKEN.gcloud auth login && gcloud config set project <PROJECT>./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
/healthzon*.run.appand 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_TOKENfrom theenvblock if present, otherwise from the project'senv.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=1 → 429, and more — live in
docs/LEARNINGS.md.
Verify your credentials first
npm run check:authRead-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 foldersLayout
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 testsDesign notes (Yahoo realities)
Yahoo does not advertise
IDLE,MOVE,UIDPLUS, orSPECIAL-USE.imapflowtransparently 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.envfiles 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.