{
  "markdown": "# mcp-server-email\n\n[![CI](https://github.com/boutquin/mcp-server-email/actions/workflows/ci.yml/badge.svg)](https://github.com/boutquin/mcp-server-email/actions/workflows/ci.yml)\n[![codecov](https://codecov.io/gh/boutquin/mcp-server-email/branch/main/graph/badge.svg?token=Y3EWPR8X6K)](https://codecov.io/gh/boutquin/mcp-server-email)\n[![Go](https://img.shields.io/badge/Go-1.24+-00ADD8?logo=go&logoColor=white)](https://go.dev)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)\n[![MCP](https://img.shields.io/badge/MCP-compatible-blueviolet)](https://modelcontextprotocol.io)\n\nMulti-account email server for the [Model Context Protocol](https://modelcontextprotocol.io). Gives LLMs full email access — read, send, search, organize — over IMAP/SMTP with connection pooling, rate limiting, and retry. Designed as the remote-email counterpart to [apple-bridge](https://github.com/boutquin/apple-bridge) (local Mail.app access).\n\n## Quick Start\n\n1. **Install**\n\n   ```bash\n   go install github.com/boutquin/mcp-server-email/cmd/mcp-server-email@latest\n   ```\n\n2. **Create a config file** (`~/.config/mcp-email/accounts.json`)\n\n   For well-known providers (Gmail, Outlook, Yahoo, iCloud, Fastmail, Zoho), host and port are auto-detected from the email domain — just provide credentials:\n\n   ```json\n   [\n     {\n       \"id\": \"hello\",\n       \"email\": \"hello@gmail.com\",\n       \"username\": \"hello@gmail.com\",\n       \"password\": \"app-password-here\"\n     }\n   ]\n   ```\n\n   For custom mail servers, specify host and port explicitly:\n\n   ```json\n   [\n     {\n       \"id\": \"work\",\n       \"email\": \"hello@example.com\",\n       \"imap_host\": \"mail.example.com\",\n       \"imap_port\": 993,\n       \"smtp_host\": \"mail.example.com\",\n       \"smtp_port\": 465,\n       \"username\": \"hello@example.com\",\n       \"password\": \"app-password-here\"\n     }\n   ]\n   ```\n\n   ```bash\n   chmod 600 ~/.config/mcp-email/accounts.json\n   ```\n\n3. **Add to Claude Code** (`~/.claude.json`)\n\n   ```json\n   {\n     \"mcpServers\": {\n       \"email\": {\n         \"command\": \"mcp-server-email\",\n         \"env\": {\n           \"EMAIL_CONFIG_FILE\": \"~/.config/mcp-email/accounts.json\"\n         }\n       }\n     }\n   }\n   ```\n\n4. **Restart Claude Code** — the `email_*` tools are now available.\n\n## Installation\n\n### Go Install (recommended for Go developers)\n\n```bash\ngo install github.com/boutquin/mcp-server-email/cmd/mcp-server-email@latest\n```\n\n### Homebrew (macOS/Linux)\n\n```bash\nbrew install boutquin/tap/mcp-server-email\n```\n\n### Binary Download\n\nDownload pre-built binaries for your platform from [GitHub Releases](https://github.com/boutquin/mcp-server-email/releases).\n\nAvailable for: Linux (amd64, arm64), macOS (amd64, arm64), Windows (amd64, arm64).\n\n### Docker\n\n```bash\ndocker run --rm \\\n  -e EMAIL_ACCOUNTS='[{\"id\":\"main\",\"email\":\"user@example.com\",\"imap_host\":\"mail.example.com\",\"imap_port\":993,\"smtp_host\":\"mail.example.com\",\"smtp_port\":465,\"username\":\"user@example.com\",\"password\":\"app-password\"}]' \\\n  ghcr.io/boutquin/mcp-server-email:latest\n```\n\n### MCP Bundle (Claude Desktop)\n\nDownload the `.mcpb` file from [Releases](https://github.com/boutquin/mcp-server-email/releases) and open in Claude Desktop.\n\n### Build from source\n\n```bash\ngit clone https://github.com/boutquin/mcp-server-email.git\ncd mcp-server-email\ngo build -o mcp-server-email ./cmd/mcp-server-email\n```\n\n## Configuration\n\nAccounts are loaded once at startup. Changes require a server restart.\n\n### Config file vs environment variable\n\n| Approach | Best for |\n|----------|----------|\n| `EMAIL_CONFIG_FILE` — path to a JSON file | Production use. File can be permission-locked (`chmod 600`) |\n| `EMAIL_ACCOUNTS` — inline JSON in env var | Testing, CI, or containerized deployments |\n\nIf both are set, `EMAIL_ACCOUNTS` takes precedence.\n\n### Account JSON schema\n\n```json\n[\n  {\n    \"id\": \"hello\",\n    \"email\": \"hello@example.com\",\n    \"imap_host\": \"mail.example.com\",\n    \"imap_port\": 993,\n    \"smtp_host\": \"mail.example.com\",\n    \"smtp_port\": 465,\n    \"username\": \"hello@example.com\",\n    \"password\": \"app-password-here\"\n  }\n]\n```\n\n| Field | Required | Description |\n|-------|----------|-------------|\n| `id` | Yes | Unique account identifier |\n| `email` | Yes | Email address |\n| `imap_host` | No* | IMAP server hostname |\n| `imap_port` | No* | IMAP port (993 = implicit TLS, 143 = STARTTLS) |\n| `smtp_host` | No* | SMTP server hostname |\n| `smtp_port` | No* | SMTP port (465 = implicit TLS, 587 = STARTTLS) |\n| `username` | Yes | Login username |\n| `password` | Yes** | App password or account password |\n| `use_starttls` | No | Override TLS auto-detection (`true`/`false`) |\n| `insecure_skip_verify` | No | Skip TLS certificate verification (dev/testing) |\n| `auth_method` | No | `\"password\"` (default) or `\"oauth2\"` |\n| `oauth_client_id` | No | OAuth2 client ID (required when `auth_method` is `\"oauth2\"`) |\n| `oauth_client_secret` | No | OAuth2 client secret |\n| `oauth_token_file` | No | Override token file path |\n\n*Host and port are auto-detected for well-known providers (see below). Required for custom servers.\n**Not required when using OAuth2 authentication.\n\n### Provider auto-detection\n\nWhen `imap_host`/`smtp_host` are omitted, the server detects settings from the email domain:\n\n| Provider | Domains | IMAP | SMTP |\n|----------|---------|------|------|\n| Gmail | `gmail.com`, `googlemail.com` | `imap.gmail.com:993` | `smtp.gmail.com:587` |\n| Outlook | `outlook.com`, `hotmail.com`, `live.com` | `outlook.office365.com:993` | `smtp.office365.com:587` |\n| Yahoo | `yahoo.com` | `imap.mail.yahoo.com:993` | `smtp.mail.yahoo.com:587` |\n| iCloud | `icloud.com`, `me.com`, `mac.com` | `imap.mail.me.com:993` | `smtp.mail.me.com:587` |\n| Fastmail | `fastmail.com`, `fastmail.fm` | `imap.fastmail.com:993` | `smtp.fastmail.com:587` |\n| Zoho | `zoho.com`, `zohomail.com` | `imap.zoho.com:993` | `smtp.zoho.com:587` |\n\nExplicit host/port in the config always takes precedence over auto-detection.\n\n### TLS modes\n\nTLS mode is auto-detected from port:\n\n| Port | Protocol | Mode |\n|------|----------|------|\n| 993 | IMAP | Implicit TLS |\n| 143 | IMAP | STARTTLS |\n| 465 | SMTP | Implicit TLS |\n| 587 | SMTP | STARTTLS |\n\nOverride with `\"use_starttls\": true` or `\"use_starttls\": false` in the account object. Omit for auto-detection (recommended).\n\n### OAuth2 authentication\n\nFor providers that support it (Gmail, Outlook), you can use OAuth2 instead of app passwords. This uses the device code flow (RFC 8628) — no browser redirect needed.\n\n1. **Create OAuth2 credentials** in the provider's developer console (Google Cloud Console or Azure AD)\n\n2. **Configure the account** with `auth_method: \"oauth2\"`:\n\n   ```json\n   [\n     {\n       \"id\": \"gmail\",\n       \"email\": \"user@gmail.com\",\n       \"username\": \"user@gmail.com\",\n       \"auth_method\": \"oauth2\",\n       \"oauth_client_id\": \"your-client-id.apps.googleusercontent.com\",\n       \"oauth_client_secret\": \"your-client-secret\"\n     }\n   ]\n   ```\n\n3. **On first connection**, the server initiates the device code flow — printing a verification URL and code to stderr. Visit the URL and enter the code to authorize.\n\n4. **Tokens are persisted** in `~/.config/mcp-email/tokens/` and automatically refreshed. Subsequent connections reuse the stored token without re-authorization.\n\nSupported OAuth2 providers: **Gmail** (`gmail.com`, `googlemail.com`) and **Outlook** (`outlook.com`, `hotmail.com`, `live.com`).\n\n### Environment variables\n\n| Variable | Required | Default | Description |\n|----------|----------|---------|-------------|\n| `EMAIL_CONFIG_FILE` | Yes* | — | Path to JSON config file |\n| `EMAIL_ACCOUNTS` | Yes* | — | JSON array of account configs (inline) |\n| `EMAIL_DEFAULT_ACCOUNT` | No | First account | Default account ID |\n| `EMAIL_IMAP_TIMEOUT_MS` | No | `30000` | IMAP operation timeout (ms) |\n| `EMAIL_SMTP_TIMEOUT_MS` | No | `30000` | SMTP operation timeout (ms) |\n| `EMAIL_IMAP_RATE_LIMIT` | No | `60` | IMAP requests/minute/account |\n| `EMAIL_SMTP_RATE_LIMIT` | No | `100` | SMTP sends/hour/account |\n| `MAX_ATTACHMENT_SIZE_MB` | No | `18` | Max size per attachment (MB) |\n| `MAX_TOTAL_ATTACHMENT_SIZE_MB` | No | `18` | Max total attachment size per message (MB) |\n| `MAX_DOWNLOAD_SIZE_MB` | No | `25` | Max attachment download size (MB) |\n| `EMAIL_POOL_CLOSE_TIMEOUT_MS` | No | `5000` | Pool close timeout (ms) |\n| `EMAIL_DEBUG` | No | `false` | Debug logging to stderr |\n| `LOG_LEVEL` | No | `info` | Log level: `debug`, `info`, `warn`, `error` |\n| `LOG_FORMAT` | No | `json` | Log format: `json` or `text` |\n\n*One of `EMAIL_CONFIG_FILE` or `EMAIL_ACCOUNTS` is required.\n\n### Config file permissions\n\nThe config file contains account passwords. Always restrict access:\n\n```bash\nchmod 600 ~/.config/mcp-email/accounts.json\n```\n\n## Tools (22)\n\n### Account & folder tools\n\n| Tool | Description | Key params |\n|------|-------------|------------|\n| `email_accounts` | List configured accounts with connection status | — |\n| `email_folders` | List all folders with unread/total counts | `account?` |\n| `email_folder_create` | Create new folder | `name`, `account?` |\n\n### Message listing\n\n| Tool | Description | Key params |\n|------|-------------|------------|\n| `email_list` | List messages in folder | `folder?`, `limit?`, `offset?`, `includeBody?`, `account?` |\n| `email_unread` | List unread messages | `folder?`, `limit?`, `includeBody?`, `account?` |\n| `email_search` | Search subject and body | `query`, `from?`, `to?`, `since?`, `before?`, `folder?`, `limit?`, `includeBody?`, `account?` |\n\n### Message operations\n\n| Tool | Description | Key params |\n|------|-------------|------------|\n| `email_get` | Get full message by ID | `id` |\n| `email_read_body` | Read email body with pagination | `id`, `offset?`, `limit?`, `format?` |\n| `email_move` | Move message to folder | `id`, `destination` |\n| `email_copy` | Copy message to folder | `id`, `destination` |\n| `email_delete` | Delete message (trash or permanent expunge) | `id`, `permanent?` |\n| `email_mark_read` | Mark as read/unread | `id`, `read` |\n| `email_flag` | Flag/unflag message | `id`, `flagged` |\n| `email_reply` | Reply to a message (sets In-Reply-To, References, quotes body) | `id`, `body`, `all?`, `cc?`, `bcc?`, `isHtml?`, `account?` |\n| `email_forward` | Forward a message (re-attaches original attachments) | `id`, `to`, `body?`, `cc?`, `bcc?`, `isHtml?`, `account?` |\n| `email_batch` | Batch operations on multiple messages | `action`, `ids`, `destination?`, `permanent?`, `read?`, `flagged?` |\n\n### Attachments & threads\n\n| Tool | Description | Key params |\n|------|-------------|------------|\n| `email_attachment_list` | List attachments on a message | `id` |\n| `email_attachment_get` | Download attachment by index | `id`, `index`, `saveTo?` |\n| `email_thread` | Get conversation thread (searches across INBOX, Sent, Archive, and All Mail) | `id` |\n\n### Send & drafts\n\n| Tool | Description | Key params |\n|------|-------------|------------|\n| `email_send` | Send via SMTP with optional attachments | `to`, `subject`, `body`, `cc?`, `bcc?`, `replyTo?`, `isHtml?`, `attachments?`, `account?` |\n| `email_draft_create` | Save draft with optional attachments | `to?`, `subject?`, `body?`, `cc?`, `bcc?`, `isHtml?`, `attachments?`, `account?` |\n| `email_draft_send` | Send existing draft | `id` |\n\nAll optional `account` params default to the configured default account.\n\n## Search\n\n`email_search` searches both **subject and body** using IMAP `SEARCH OR (SUBJECT \"q\") (BODY \"q\")`.\n\nOptional filters narrow the candidate set server-side before body scanning:\n\n| Filter | Format | Example |\n|--------|--------|---------|\n| `from` | Email address or name | `\"alice@example.com\"` |\n| `to` | Email address or name | `\"bob@example.com\"` |\n| `since` | `YYYY-MM-DD` | `\"2026-01-01\"` |\n| `before` | `YYYY-MM-DD` | `\"2026-02-01\"` |\n\nThe existing operation timeout (default 30s) prevents hung body searches on large mailboxes.\n\n## Attachments\n\n`email_send` and `email_draft_create` accept an `attachments` parameter — an array of file references on the server host:\n\n```json\n{\n  \"attachments\": [\n    {\"path\": \"/tmp/report.pdf\"},\n    {\"path\": \"/tmp/data.csv\", \"filename\": \"Q1-data.csv\", \"content_type\": \"text/csv\"}\n  ]\n}\n```\n\n| Parameter | Required | Description |\n|-----------|----------|-------------|\n| `path` | Yes | Absolute file path on the server host |\n| `filename` | No | Override display filename (defaults to basename of `path`) |\n| `content_type` | No | MIME type (auto-detected from file extension if omitted) |\n\n**Limits (defaults):** 18 MB per file, 18 MB total (pre-base64 encoding; stays under 25 MB SMTP cap after encoding). Configurable via `MAX_ATTACHMENT_SIZE_MB` and `MAX_TOTAL_ATTACHMENT_SIZE_MB` environment variables.\n\n**Download limit:** Attachment downloads (`email_attachment_get`) are capped at 25 MB by default, configurable via `MAX_DOWNLOAD_SIZE_MB`.\n\nValidation failures (missing file, non-absolute path, size exceeded) return `INVALID_ARGUMENT`.\n\n## Message IDs\n\nMessage IDs are composite strings encoding account, mailbox, and UID:\n\n```\n{account}:{mailbox}:{uid}\n```\n\nExample: `hello:INBOX:12345`\n\nAll CRUD tools (`email_get`, `email_move`, `email_copy`, `email_delete`, `email_mark_read`, `email_flag`, `email_draft_send`) extract the account and folder from the ID — no separate params needed.\n\n## Error Codes\n\nAll errors are returned as MCP tool errors with a structured code prefix:\n\n| Code | Meaning |\n|------|---------|\n| `AUTH_FAILED` | IMAP/SMTP authentication failed |\n| `CONNECTION_FAILED` | Cannot connect to server |\n| `ACCOUNT_NOT_FOUND` | Unknown account ID |\n| `FOLDER_NOT_FOUND` | Mailbox doesn't exist |\n| `MESSAGE_NOT_FOUND` | UID not found in mailbox |\n| `INVALID_ARGUMENT` | Missing/invalid parameter (including attachment validation) |\n| `TIMEOUT` | Operation timed out |\n| `INTERNAL` | Unexpected server error |\n\n## Resources\n\n| URI | Description |\n|-----|-------------|\n| `email://status` | Server version, account connection state, rate limit configuration |\n\n## Comparison with apple-bridge\n\nThis server and [apple-bridge](https://github.com/boutquin/apple-bridge) share an `Email` model and parameter semantics (`limit`, `includeBody`, `folder`, `query`) so LLMs can work with both interchangeably. Key differences:\n\n| Aspect | mcp-server-email | apple-bridge |\n|--------|------------------|--------------|\n| Transport | IMAP/SMTP (remote) | Mail.app (local) |\n| Tool prefix | `email_*` | `mail_*` |\n| Message ID | `{account}:{mailbox}:{uid}` | RFC 5322 Message-ID |\n| Folder create | Supported | Not supported (Mail.app requires UI) |\n| Copy message | Supported | Not supported |\n| Draft send | Supported | Not supported (Mail.app uses compose UI) |\n| Attachments (send) | File path on server host | Not yet supported |\n\n## Development\n\n### Prerequisites\n\n- Go 1.24+\n- [golangci-lint](https://golangci-lint.run/)\n- [Docker](https://docs.docker.com/get-docker/) (for integration tests only)\n\n### Build\n\n```bash\ngo build ./...\n```\n\n### Unit tests\n\n```bash\nmake test\n# or: go test -race -count=1 ./...\n```\n\nUnit tests use mock implementations of the `imap.Operations` and `smtp.Operations` interfaces — no live mail server needed.\n\n### Benchmarks\n\n```bash\ngo test -bench=. -benchmem ./...\n```\n\n| Benchmark | Package | What it measures |\n|-----------|---------|------------------|\n| `BenchmarkPoolGetRelease` | `imap` | Connection pool acquire/release cycle |\n| `BenchmarkExtractAttachments` | `imap` | MIME attachment extraction |\n| `BenchmarkHtmlToText` | `tools` | HTML-to-plain-text conversion |\n| `BenchmarkLimiterAllow` | `retry` | Rate limiter (sequential) |\n| `BenchmarkLimiterAllow_Parallel` | `retry` | Rate limiter (concurrent) |\n\n### Fuzz testing\n\nFuzz targets ship with seed corpora in `testdata/fuzz/` directories. Run a specific target:\n\n```bash\ngo test -fuzz=FuzzParseMessageID ./internal/models/ -fuzztime=30s\n```\n\n| Target | Package | What it fuzzes |\n|--------|---------|----------------|\n| `FuzzBuildSearchCriteria` | `imap` | IMAP search query builder |\n| `FuzzExtractAttachmentByIndex` | `imap` | Attachment index boundary handling |\n| `FuzzExtractContentType` | `imap` | MIME content-type parser |\n| `FuzzParseMessageID` | `models` | Composite message ID codec |\n| `FuzzHtmlToText` | `tools` | HTML-to-text sanitizer |\n| `FuzzSplitAddresses` | `tools` | Email address list splitter |\n\n### Lint\n\n```bash\nmake lint\n# or: golangci-lint run ./...\n```\n\n### Integration tests\n\nIntegration tests exercise the full IMAP/SMTP stack against a real mail server. They are isolated behind the `integration` build tag and **never run** during `go test ./...`.\n\n#### Mail server: Greenmail\n\nTests use [Greenmail](https://greenmail-mail-test.github.io/greenmail/), a lightweight Java mail server packaged as a Docker image. Key details that affect how you run it:\n\n| Setting | Value | Why it matters |\n|---------|-------|----------------|\n| IMAPS port | **3993** | Greenmail's SSL IMAP port (not 993). The code auto-detects TLS from port number, so tests explicitly set `UseStartTLS=false` to force implicit TLS on this non-standard port. |\n| SMTPS port | **3465** | Greenmail's SSL SMTP port (not 465). Same `UseStartTLS=false` override. |\n| Bind address | **`0.0.0.0`** | Greenmail defaults to `127.0.0.1` *inside the container*, which makes Docker port-mapping silently fail (connections get EOF). You **must** pass `-Dgreenmail.hostname=0.0.0.0`. |\n| Username | **`test`** | Greenmail uses the *local part only* (before `@`) as the login username — not the full email address. If the user is `test@example.com`, the IMAP/SMTP username is `test`. |\n| TLS certificates | **Self-signed** | Greenmail generates self-signed certs. Tests set `InsecureSkipVerify: true` in the account config to accept them. |\n\n#### Quick start (one command)\n\n```bash\nmake test-integration\n```\n\nThis starts a Greenmail container, runs all integration tests, then tears down the container — regardless of pass/fail.\n\n#### Manual step-by-step\n\nIf you need to iterate on tests without restarting the container each time:\n\n1. **Start Greenmail**\n\n   ```bash\n   docker run -d --name greenmail \\\n     -p 3465:3465 -p 3993:3993 \\\n     -e \"GREENMAIL_OPTS=-Dgreenmail.setup.test.all -Dgreenmail.users=test:password@example.com -Dgreenmail.hostname=0.0.0.0\" \\\n     greenmail/standalone:2.1.0\n   ```\n\n   Wait ~3 seconds for the JVM to start.\n\n2. **Run integration tests**\n\n   ```bash\n   TEST_IMAP_HOST=localhost TEST_IMAP_PORT=3993 \\\n   TEST_SMTP_HOST=localhost TEST_SMTP_PORT=3465 \\\n   TEST_EMAIL=test@example.com TEST_PASSWORD=password \\\n     go test -tags=integration -race -v ./...\n   ```\n\n3. **Tear down** when done\n\n   ```bash\n   docker stop greenmail && docker rm greenmail\n   ```\n\n#### Test environment variables\n\n| Variable | Required | Default | Description |\n|----------|----------|---------|-------------|\n| `TEST_IMAP_HOST` | Yes* | — | IMAP server hostname. Tests skip if unset. |\n| `TEST_IMAP_PORT` | No | `3993` | IMAPS port |\n| `TEST_SMTP_HOST` | Yes* | — | SMTP server hostname. Tests skip if unset. |\n| `TEST_SMTP_PORT` | No | `3465` | SMTPS port |\n| `TEST_EMAIL` | No | `test@example.com` | Email address for the test account |\n| `TEST_USERNAME` | No | Local part of `TEST_EMAIL` | IMAP/SMTP login username (Greenmail uses local part only) |\n| `TEST_PASSWORD` | No | `password` | Account password |\n\n*If the corresponding `HOST` variable is unset, that test file's tests are skipped with a message (not failed).\n\n#### What the tests cover\n\n**IMAP** (`internal/imap/integration_test.go` — 7 tests):\n\n| Test | What it verifies |\n|------|------------------|\n| `ConnectAndListFolders` | TLS connection, authentication, folder listing, INBOX exists |\n| `SendAndListMessages` | SMTP send → IMAP receive round-trip, body content match |\n| `SearchBySubject` | IMAP SEARCH by subject string |\n| `DeleteMessagePermanent` | Flag as deleted + expunge, verify message is gone |\n| `MoveMessage` | IMAP MOVE to another folder (skips if server lacks MOVE extension) |\n| `DraftWorkflow` | SaveDraft → GetDraft → DeleteDraft lifecycle (skips if no APPENDUID) |\n| `MarkReadAndFlag` | Set read/flagged flags, verify via GetMessage |\n\n**SMTP** (`internal/smtp/integration_test.go` — 4 tests):\n\n| Test | What it verifies |\n|------|------------------|\n| `SendPlainText` | Plain-text email delivery, body content verified via IMAP |\n| `SendHTML` | HTML email delivery, Content-Type verified as `text/html` |\n| `SendWithAttachment` | Multipart MIME with attachment, filename verified in metadata |\n| `RateLimitTokenConsumption` | Sending consumes a rate-limit token |\n\n#### Troubleshooting\n\n| Symptom | Cause | Fix |\n|---------|-------|-----|\n| `EOF` or `connection reset` on connect | Greenmail bound to `127.0.0.1` inside container | Add `-Dgreenmail.hostname=0.0.0.0` to `GREENMAIL_OPTS` |\n| `TLS handshake failure` / certificate error | Self-signed certs rejected | Test configs already set `InsecureSkipVerify: true` — if writing new tests, do the same |\n| `Invalid login/password` | Using full email as username | Greenmail expects the local part only (`test`, not `test@example.com`). Set `TEST_USERNAME` or let it default. |\n| `STARTTLS` error on port 3993 | Using STARTTLS on an implicit-TLS port | Test configs set `UseStartTLS=false`. Don't use ports 3143/3025 (plain, no TLS). |\n| Tests skip with \"not set\" | `TEST_IMAP_HOST` / `TEST_SMTP_HOST` not exported | Export the env vars or use the `make test-integration` target |\n| `MoveMessage` test skips | Greenmail may not support MOVE | Expected — test uses `t.Skip()` |\n\n#### CI\n\nIntegration tests run automatically in GitHub Actions via the `integration` job in `.github/workflows/ci.yml`. The job uses a Greenmail service container — no manual Docker setup needed. See the workflow file for the exact configuration.\n\n### Coverage\n\nTo generate a combined unit + integration coverage report:\n\n```bash\n# With Greenmail running (see above):\nTEST_IMAP_HOST=localhost TEST_IMAP_PORT=3993 \\\nTEST_SMTP_HOST=localhost TEST_SMTP_PORT=3465 \\\nTEST_EMAIL=test@example.com TEST_PASSWORD=password \\\n  go test -tags=integration -race -coverprofile=coverage.out ./...\n\ngo tool cover -func=coverage.out | tail -1   # total percentage\ngo tool cover -html=coverage.out             # open in browser\n```\n\n### Architecture\n\n```\nmcp-server-email/\n├── cmd/mcp-server-email/     # Entry point\n└── internal/\n    ├── auth/                 # OAuth2 device code flow, XOAUTH2 SASL, token store\n    ├── config/               # Multi-account configuration, provider auto-detection\n    ├── imap/                 # IMAP client (split by concern), connection pool, Operations interface\n    │   ├── client.go         # Client struct, lifecycle, shared helpers\n    │   ├── client_messages.go # List, search, get, attachments\n    │   ├── client_folders.go  # Folder ops, role cache\n    │   ├── client_drafts.go   # Draft save/get/delete\n    │   ├── client_flags.go    # Flags, move, copy, delete\n    │   └── pool.go           # Connection pool with configurable close timeout\n    ├── log/                  # Structured logging (slog) initialization\n    ├── models/               # Email model, message ID codec, error types\n    ├── resources/            # email://status resource\n    ├── retry/                # Token-bucket rate limiter\n    ├── smtp/                 # SMTP client, Operations interface\n    └── tools/                # 22 MCP tool handlers + registration\n```\n\nTool handlers are decoupled from IMAP/SMTP clients via the `imap.Operations` and `smtp.Operations` interfaces, enabling comprehensive unit testing with mocks.\n\n### Dependencies\n\nThis project uses [go-imap v2](https://github.com/emersion/go-imap) (currently v2.0.0-beta.8).\nThe v2 API is not yet stable — breaking changes may occur before the v2.0.0 release.\nWe pin the exact version in `go.mod` and will upgrade promptly when stable is released.\n\n## License\n\n[MIT](LICENSE)\n",
  "bytes": 23899,
  "sha": "c76ca49237e7657cc2da10f07a4bf750fe6b8dbf18de3d026f7ce64e299354a9",
  "repo_slug": "boutquin/mcp-server-email",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_boutquin_email_4afa3172/readme"
}