yahoo-mail-mcp
by th-an
README.md
# 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
```bash
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):
```json
{
"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):
```bash
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):
```bash
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`:
```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](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](docs/LEARNINGS.md).
## Verify your credentials first
```bash
npm run check:auth
```
Read-only IMAP login check + SMTP `verify()` (no mail is ever sent). See
`scripts/check-auth.ts`.
## Development
```bash
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](LICENSE).
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessSyncing