Skip to main content
Glama
email-mcp

email-mcp

by sarequl
README.md
# @sarequl/email-mcp

An MCP server that lets AI agents read, search, send and organize email over IMAP and SMTP.
It runs locally over stdio, works with any MCP client (Claude Desktop, OpenBot, Cursor, …),
and needs only an email address and an app password.

- **Reads and receives:** list, read, search, threads, statistics, attachments, new-mail push
- **Sends:** new mail, reply / reply-all, forward with attachments, drafts, templates, scheduled send
- **Organizes:** read/unread/flag, move, archive, delete (to Trash), bulk actions, folders
- **Safe:** passwords never logged, tools annotated so clients ask before sending or deleting,
  nothing is ever expunged, optional read-only mode

## Quick start

```json
{
  "mcpServers": {
    "email": {
      "command": "npx",
      "args": ["-y", "@sarequl/email-mcp"],
      "env": {
        "EMAIL_ADDRESS": "you@gmail.com",
        "EMAIL_PASSWORD": "your-app-password"
      }
    }
  }
}
```

The domain of `EMAIL_ADDRESS` picks the IMAP and SMTP servers automatically for Gmail,
Google Workspace, Yahoo, iCloud, Zoho, Fastmail, GMX and AOL. Other domains are detected through
DNS SRV records and common host names; see [Custom domains](#custom-domains) if that fails.

Requires Node.js 20 or newer.

## Setting up your provider

### Gmail and Google Workspace (app password)

Gmail does not accept your normal password over IMAP. Create an **app password**:

1. Turn on 2-Step Verification: <https://myaccount.google.com/signinoptions/two-step-verification>
2. Open <https://myaccount.google.com/apppasswords>, enter a name such as `email-mcp`, click **Create**.
3. Copy the 16-character password (spaces do not matter) into `EMAIL_PASSWORD`.
4. IMAP is enabled by default on current Gmail accounts. If `check_health` reports an IMAP login
   failure, open Gmail → Settings → See all settings → Forwarding and POP/IMAP → Enable IMAP.

Google Workspace accounts work the same way, but the administrator must allow app passwords and
IMAP access for the organization.

### Yahoo

Account Security → **Generate app password** → use it as `EMAIL_PASSWORD`.

### iCloud

Sign in at <https://appleid.apple.com> → Sign-In and Security → **App-Specific Passwords** →
Generate. Use your full iCloud address as `EMAIL_ADDRESS`.

### Zoho, Fastmail, GMX, AOL

Each provider offers app passwords in its security settings; use one as `EMAIL_PASSWORD`.
Zoho and GMX also need IMAP access switched on in the mail settings.

### Outlook / Hotmail / Microsoft 365

The built-in table knows Outlook's servers, but Microsoft is retiring password sign-in for IMAP.
OAuth sign-in is on the roadmap (see `PLAN.md`); until then basic authentication may be rejected.

### Custom domains

If auto-detection fails, set the hosts explicitly:

| Variable | Meaning |
| --- | --- |
| `EMAIL_ADDRESS` | The mailbox address (required) |
| `EMAIL_PASSWORD` | App password or mailbox password (required) |
| `EMAIL_USERNAME` | Login name when it differs from the address |
| `EMAIL_IMAP_HOST`, `EMAIL_IMAP_PORT` | IMAP server (port defaults to 993) |
| `EMAIL_IMAP_SECURE` | `true` for implicit TLS (default for 993), `false` for plain/STARTTLS |
| `EMAIL_SMTP_HOST`, `EMAIL_SMTP_PORT` | SMTP server (port defaults to 587) |
| `EMAIL_SMTP_SECURE` | `true` for implicit TLS (default for 465), `false` for STARTTLS |
| `EMAIL_READ_ONLY` | `true` hides every tool that sends or changes mail |
| `EMAIL_CONFIG` | Path to the config file (default `~/.config/email-mcp/config.toml`) |
| `EMAIL_DOWNLOAD_DIR` | Where `download_attachment` saves files (default: a folder under the OS temp dir) |
| `EMAIL_LOG_LEVEL` | `debug` for verbose logs on stderr |

Run the `check_health` tool after setup: it logs in to both servers and tells you what failed.

## Multiple accounts

The environment variables define the default account. More accounts go in the config file
(`~/.config/email-mcp/config.toml` or the path in `EMAIL_CONFIG`):

```toml
[[accounts]]
name = "work"
email = "me@company.example"
password = "app-password"
# optional overrides, otherwise detected from the domain:
# username = "me"
# imap_host = "imap.company.example"
# imap_port = 993
# smtp_host = "smtp.company.example"
# smtp_port = 587
# appends_sent = false   # true if the provider stores sent mail itself

[[accounts]]
name = "personal"
email = "me@yahoo.com"
password = "another-app-password"
```

Every tool accepts an optional `account` argument (the `name`); the first account is the default.
`list_accounts` shows what is configured.

## Tools

| Area | Tools |
| --- | --- |
| Connection | `check_health`, `list_accounts` |
| Reading | `list_mailboxes`, `list_emails`, `get_email`, `search_emails`, `get_thread`, `get_email_stats`, `download_attachment` |
| Sending | `send_email`, `reply_email`, `forward_email`, `save_draft`, `send_draft` |
| Organizing | `mark_email`, `move_email`, `archive_email`, `delete_email`, `bulk_action`, `create_mailbox`, `rename_mailbox`, `delete_mailbox` |
| Smart | `extract_contacts`, `extract_calendar`, `list_templates`, `apply_template`, `schedule_email`, `list_scheduled`, `cancel_scheduled` |

Messages are addressed by mailbox path and UID, both returned by `list_emails` and
`search_emails`. Mailbox names `inbox`, `sent`, `drafts`, `trash`, `junk` and `archive` are
resolved to the provider's real folders (for example `[Gmail]/Sent Mail`).

### New-mail push

The server keeps an IMAP IDLE connection to each INBOX. When mail arrives it sends an MCP
`notifications/resources/updated` for the resource `email://inbox/latest` (and
`email://<account>/inbox/latest`) and a logging notification with the new message summaries.
Reading the resource returns the newest inbox messages.

### Templates

Put Markdown files in `<config dir>/templates/`, e.g. `~/.config/email-mcp/templates/welcome.md`:

```markdown
---
subject: Welcome {{name}}
---
Hello {{name}},

Welcome aboard!
```

`list_templates` lists them; `apply_template` fills the placeholders and previews, saves a draft
or sends.

### Scheduled send

`schedule_email` stores the message in `<config dir>/scheduled.json` and sends it when the time
comes, as long as the server is running. If the time passes while the server is off, the message is
sent at the next start. `list_scheduled` and `cancel_scheduled` manage the queue.

## Safety

- Passwords are never written to logs, and error messages have them redacted.
- Tools carry MCP annotations: reading tools are `readOnlyHint`, sending and deleting tools are
  `destructiveHint`, so well-behaved clients ask the user before running them.
- `delete_email` and `bulk_action delete` move messages to Trash. Nothing is ever expunged.
- `delete_mailbox` refuses special folders and non-empty folders unless `force` is set.
- Attachments are only read from local paths you pass and written to paths returned in the result.
- `EMAIL_READ_ONLY=true` removes every tool that sends or changes mail.

## Development

```bash
npm install
npm test          # automated tests against local fake IMAP and SMTP servers
npm run build     # compile to dist/
npm run dev       # run from source with tsx
```

### Live check against a real mailbox

`npm run live-check` logs in to a real account, lists folders, appends a test message to the
INBOX, reads it back, moves it to Trash, and sends a test email to the account itself. Use a test
mailbox, not your main one:

```bash
EMAIL_ADDRESS=test@gmail.com EMAIL_PASSWORD=app-password npm run live-check
```

## Releasing

Publishing is automated by `.github/workflows/publish.yml`. Once, add an npm granular access
token with publish rights as the `NPM_TOKEN` repository secret. Then for each release bump the
version in `package.json` and push to `main`:

```bash
npm version patch    # or minor / major
git push --follow-tags
```

Every push to `main` checks whether that version is on npm and, if not, runs the tests and
publishes `@sarequl/email-mcp` with provenance. A `v*` tag or GitHub release triggers the same
job.

## License

MIT