Skip to main content
Glama
cat-people-82

Email MCP Server

README.md
# Email MCP Server

A [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server that provides email access via IMAP and SMTP. Enables AI agents and applications to read, search, send, and manage emails through a standardized HTTP/SSE interface.

## Features

- **9 Email Tools** via MCP:
  - `list_folders` - List all IMAP mailboxes/folders
  - `search_emails` - Search emails with filters (sender, subject, date range, read/flagged status)
  - `read_email` - Fetch full email content including body and attachment metadata
  - `mark_email` - Mark emails as read/unread or flagged/unflagged
  - `move_email` - Move emails between folders
  - `send_email` - Send new emails (plain text or multipart HTML)
  - `reply_email` - Reply to emails preserving thread headers
  - `download_attachment` - Download email attachments to workspace directory
  - `send_email_with_attachments` - Send emails with file attachments from workspace

- **Production-Ready Architecture**:
  - Async-native IMAP/SMTP with connection pooling
  - Pydantic-based configuration and validation
  - Comprehensive error handling with structured exceptions
  - API key authentication via X-API-Key header
  - systemd service file for Ubuntu deployment

- **Test-Driven Development**:
  - 85+ unit and integration tests
  - 100% coverage of core functionality
  - Mocked email servers for reproducible testing

## Install with Claude Desktop (Recommended)

The easiest way to use this server is via the MCPB bundle — a single-file install
for [Claude Desktop](https://claude.ai/download).

### 1. Download

Download `email_mcp.mcpb` from the [latest release](../../releases/latest).

### 2. Install

Double-click `email_mcp.mcpb`. Claude Desktop will open an installation dialog.

### 3. Configure

Fill in your mail server credentials when prompted. All values are stored in the
OS keychain (macOS Keychain / Linux Secret Service):

| Field | Description | Example |
|-------|-------------|---------|
| **IMAP Host** | IMAP server hostname | `imap.gmail.com` |
| **IMAP Port** | IMAP server port | `993` (SSL) · `143` (STARTTLS) |
| **IMAP Username** | Your email address | `you@example.com` |
| **IMAP Password** | Password or app-specific password | `xxxx xxxx xxxx xxxx` |
| **IMAP SSL** | Use SSL/TLS for IMAP | `true` (port 993) · `false` (port 143) |
| **SMTP Host** | SMTP server hostname | `smtp.gmail.com` |
| **SMTP Port** | SMTP server port | `587` (STARTTLS) · `465` (SSL) |
| **SMTP Username** | Your email address | `you@example.com` |
| **SMTP Password** | Password or app-specific password | `xxxx xxxx xxxx xxxx` |
| **SMTP STARTTLS** | STARTTLS mode | `true` (port 587) · `false` · `none` (auto) |

> **Gmail users:** You must use an [App Password](https://support.google.com/accounts/answer/185833),
> not your regular Google account password. Enable IMAP under Gmail Settings →
> Forwarding and POP/IMAP.

### 4. Use

Once installed, Claude can access your email. Try:

> "List my unread emails from this week"
> "Search for emails from alice@example.com about the project"
> "Send an email to bob@example.com with subject 'Hello' and body 'Hi Bob!'"
> "Download the first attachment from email UID 12345"
> "Send an email with the report.pdf attachment to team@example.com"

---

## Requirements

- Python 3.10+ (3.13 recommended)
- IMAP and SMTP server access
- API key for MCP authentication

## Quick Start

### 1. Clone and Set Up

```bash
git clone https://github.com/pzanna/email_mcp.git
cd email_mcp
python3 -m venv .venv
source .venv/bin/activate  # On Windows: .venv\Scripts\activate
pip install -r requirements.txt
```

### 2. Configure Environment

```bash
cp .env.example .env
# Edit .env with your credentials
```

Required environment variables:

```bash
# IMAP Configuration
IMAP_HOST=imap.gmail.com
IMAP_PORT=993
IMAP_USER=you@gmail.com
IMAP_PASSWORD=your-app-password
IMAP_SSL=true

# SMTP Configuration
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=you@gmail.com
SMTP_PASSWORD=your-app-password
# SMTP_STARTTLS mode: "none" (auto), "true" (force), "false" (disable)
SMTP_STARTTLS=true

# MCP Server Configuration
MCP_API_KEY=your-secret-api-key-here
MCP_HOST=127.0.0.1
MCP_PORT=8420
MCP_SERVER_NAME=email-mcp
MCP_BASE_URL=http://localhost:8420

# Optional
DEFAULT_FROM_NAME=Your Name
MAX_SEARCH_RESULTS=50
IMAP_POOL_SIZE=3

# Attachment Configuration
EMAIL_BASE_DIR=/path/to/workspace
MAX_ATTACHMENT_SIZE_MB=50
```

### 3. Run the Server

**Local development (Mac/Linux):**

```bash
source .venv/bin/activate
uvicorn main:app --host 127.0.0.1 --port 8420
```

**Access the server:**

- MCP endpoint: `http://localhost:8420/mcp`
- Health check: `http://localhost:8420/health`
- API docs: `http://localhost:8420/docs`

## Testing

```bash
# Run all tests
pytest tests/ -v

# Run with coverage
pytest tests/ --cov=. --cov-report=html

# Run specific test file
pytest tests/test_integration.py -v

# Run specific test
pytest tests/test_send.py::test_send_email_plain_text -v
```

All 85+ tests should pass.

## Usage Examples

### Authentication

All MCP endpoints require the `X-API-Key` header:

```bash
curl -H "X-API-Key: your-secret-api-key-here" \
  http://localhost:8420/mcp/tools
```

### List Available Tools

```bash
curl -X GET http://localhost:8420/mcp/tools \
  -H "X-API-Key: your-secret-api-key-here"
```

### Search Emails

```bash
curl -X POST http://localhost:8420/mcp/call \
  -H "X-API-Key: your-secret-api-key-here" \
  -H "Content-Type: application/json" \
  -d '{
    "method": "tools/call",
    "params": {
      "name": "search_emails",
      "arguments": {
        "from": "user@example.com",
        "subject": "invoice",
        "since": "2024-01-01",
        "limit": 10
      }
    }
  }'
```

### Send Email

```bash
curl -X POST http://localhost:8420/mcp/call \
  -H "X-API-Key: your-secret-api-key-here" \
  -H "Content-Type: application/json" \
  -d '{
    "method": "tools/call",
    "params": {
      "name": "send_email",
      "arguments": {
        "to": ["recipient@example.com"],
        "subject": "Test Email",
        "body": "This is a test email.",
        "from_name": "My Name"
      }
    }
  }'
```

### Read Email

```bash
curl -X POST http://localhost:8420/mcp/call \
  -H "X-API-Key: your-secret-api-key-here" \
  -H "Content-Type: application/json" \
  -d '{
    "method": "tools/call",
    "params": {
      "name": "read_email",
      "arguments": {
        "uid": "12345",
        "folder": "INBOX"
      }
    }
  }'
```

### Download Email Attachment

```bash
curl -X POST http://localhost:8420/mcp/call \
  -H "X-API-Key: your-secret-api-key-here" \
  -H "Content-Type: application/json" \
  -d '{
    "method": "tools/call",
    "params": {
      "name": "download_attachment",
      "arguments": {
        "uid": "12345",
        "attachment_index": 0,
        "folder": "INBOX",
        "filename_override": "renamed_file.pdf"
      }
    }
  }'
```

Downloads the attachment to `EMAIL_BASE_DIR/attachments/email/downloads/` with security validation to ensure files stay within the base directory.

### Send Email with Attachments

```bash
curl -X POST http://localhost:8420/mcp/call \
  -H "X-API-Key: your-secret-api-key-here" \
  -H "Content-Type: application/json" \
  -d '{
    "method": "tools/call",
    "params": {
      "name": "send_email_with_attachments",
      "arguments": {
        "to": ["recipient@example.com"],
        "cc": ["manager@example.com"],
        "subject": "Monthly Report",
        "body": "Please find the reports attached.",
        "body_html": "<p>Please find the <strong>reports</strong> attached.</p>",
        "from_name": "John Doe",
        "attachment_paths": [
          "attachments/email/uploads/report1.pdf",
          "attachments/email/uploads/report2.xlsx"
        ]
      }
    }
  }'
```

Attaches files from the workspace directory with full security validation and size limit enforcement.

## Attachment Handling

The email MCP server provides secure attachment handling with workspace-based file management.

### Workspace Directory Structure

All attachment operations are confined to the configured workspace directory:

```
EMAIL_BASE_DIR/
└── attachments/
    └── email/
        ├── downloads/    # Downloaded email attachments
        └── uploads/      # Files ready to attach to outgoing emails
```

### Security Features

- **Workspace Confinement**: All file operations are restricted to `EMAIL_BASE_DIR`
- **Path Traversal Protection**: Prevents access to files outside the workspace
- **Filename Sanitization**: Removes dangerous characters and handles reserved names
- **Size Limits**: Configurable per-file and total attachment size limits
- **File Type Validation**: Ensures attachment paths point to actual files

### Workflow Examples

**Download → Send Workflow**:
1. Use `download_attachment` to save email attachments to `downloads/`
2. Move or copy files to `uploads/` as needed
3. Use `send_email_with_attachments` to send files from `uploads/`

**Direct Upload Workflow**:
1. Place files in the `uploads/` directory
2. Use `send_email_with_attachments` with workspace-relative paths

### Configuration

```bash
# Workspace directory (required for attachment operations)
EMAIL_BASE_DIR=/path/to/your/workspace

# Maximum attachment size per file (default: 50MB)
MAX_ATTACHMENT_SIZE_MB=50
```

## Deployment

### Ubuntu Server with systemd

1. **Copy files to server:**

```bash
scp -r email_mcp user@server:/home/user/
```

1. **Set up Python environment:**

```bash
ssh user@server
cd ~/email_mcp
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
```

1. **Configure for remote access:**
Edit `.env`:

```bash
MCP_HOST=0.0.0.0  # Allow remote connections
MCP_BASE_URL=http://<your-server-ip>:8420  # Your server IP
```

1. **Install systemd service:**

```bash
# Edit email-mcp.service to match your paths
sudo cp email-mcp.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable email-mcp
sudo systemctl start email-mcp
```

1. **Check status:**

```bash
sudo systemctl status email-mcp
sudo journalctl -u email-mcp -f  # View logs
```

### Configure MCP Client

Add to your MCP client configuration:

```json
{
  "mcpServers": {
    "email": {
      "url": "http://localhost:8420/mcp",
      "headers": {
        "X-API-Key": "your-secret-api-key-here"
      }
    }
  }
}
```

For remote server, use `http://<your-server-ip>:8420/mcp` (replace with your server IP).

## Architecture

```
email_mcp/
├── main.py                 # FastAPI application entry point (HTTP/SSE mode)
├── mcp_server.py           # Stdio MCP entry point (MCPB / Claude Desktop mode)
├── config.py               # Pydantic settings (env vars)
├── auth.py                 # API key authentication middleware
├── imap/
│   ├── client.py          # IMAP connection pool
│   ├── read.py            # list_folders, read_email
│   ├── search.py          # search_emails
│   ├── flags.py           # mark_email, move_email
│   └── attachments.py     # download_attachment
├── smtp/
│   ├── client.py          # send_email, reply_email
│   └── attachments.py     # send_email_with_attachments
├── tools/
│   ├── definitions.py     # MCP tool schemas
│   ├── handlers.py        # Tool request routing
│   └── mcp_routes.py      # MCP HTTP endpoints
├── utils/
│   └── attachment_utils.py # Secure file handling utilities
└── tests/                  # 85+ unit and integration tests
```

### Key Design Patterns

- **Connection Pooling**: asyncio.Semaphore limits concurrent IMAP connections (default: 3)
- **Error Handling**: Structured exceptions map to MCP error responses
- **Email Threading**: In-Reply-To and References headers for reply chains
- **Multipart Messages**: Walk message tree to extract text/HTML/attachments
- **No Binary Transfer**: Attachments return metadata only (filename, size, content_type)

## Troubleshooting

### Gmail-Specific Setup

1. **Enable IMAP**: Settings → Forwarding and POP/IMAP → Enable IMAP
2. **App Password**: Use an [App Password](https://support.google.com/accounts/answer/185833) instead of your regular password
3. **Gmail SMTP**: Use `smtp.gmail.com:587` with STARTTLS

### Connection Issues

```bash
# Test IMAP connection
openssl s_client -connect imap.gmail.com:993

# Test SMTP connection
openssl s_client -starttls smtp -connect smtp.gmail.com:587
```

### Common Errors

- **`CONNECTION_TIMEOUT`**: Check IMAP_HOST and IMAP_PORT
- **`AUTH_FAILED`**: Verify credentials, use App Password for Gmail
- **`FOLDER_NOT_FOUND`**: Folder names are case-sensitive (use `list_folders` to verify)
- **`MESSAGE_NOT_FOUND`**: UID may be invalid or message was deleted

**`SMTPException - Connection already using TLS`**: Set `SMTP_STARTTLS=none` in `.env` for auto mode. Valid values are `none`, `true`, and `false`.

## Development

### Project Structure

- `imap/` - IMAP client and tools (read, search, flags)
- `smtp/` - SMTP client and tools (send, reply)
- `tools/` - MCP endpoint handlers and schemas
- `tests/` - Unit and integration tests

### Running in Development

```bash
# Auto-reload on file changes
uvicorn main:app --reload --host 127.0.0.1 --port 8420

# Debug mode with verbose logging
LOG_LEVEL=DEBUG uvicorn main:app --host 127.0.0.1 --port 8420
```

### Adding New Tools

1. Define Pydantic models for input/output in appropriate module
2. Implement async function with error handling
3. Add tool schema to `tools/definitions.py`
4. Wire dispatcher in `tools/mcp_routes.py`
5. Write tests following TDD approach

## License

MIT

## Contributing

Contributions are welcome! Please:

1. Write tests for all new features
2. Follow existing code style (black, isort, mypy)
3. Update this README for significant changes
4. Ensure all tests pass before submitting PRs

## Support

For issues, questions, or feature requests, please open an issue on GitHub.