Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 34 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,47 @@ test: ## Test the code with pytest
@echo "🚀 Testing code: Running pytest"
@uv run python -m pytest --cov --cov-config=pyproject.toml --cov-report=xml -vv -s

.PHONY: test-integration
test-integration: ## Run integration tests (real protocol servers, no Docker)
@echo "🚀 Running integration tests"
@uv run --group integration python -m pytest -m integration -o "addopts=" -vv -s

.PHONY: test-docker
test-docker: ## Run Docker integration tests (requires Docker)
@echo "🐳 Running Docker integration tests"
@uv run --group docker python -m pytest -m docker -o "addopts=" -vv -s

.PHONY: test-all
test-all: ## Run all tests (unit + integration + Docker)
@echo "🚀 Running all tests"
@uv run --group integration --group docker python -m pytest -o "addopts=" --cov --cov-config=pyproject.toml --cov-report=xml -vv -s

.PHONY: build
build: clean-build ## Build wheel file
@echo "🚀 Creating wheel file"
@uvx --from build pyproject-build --installer uv

.PHONY: clean-build
clean-build: ## Clean build artifacts
@echo "🚀 Removing build artifacts"
@uv run python -c "import shutil; import os; shutil.rmtree('dist') if os.path.exists('dist') else None"
@rm -rf dist build *.egg-info

.PHONY: clean
clean: clean-build ## Remove build, test, and cache artifacts
@find . -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true
@find . -type f -name "*.py[co]" -delete 2>/dev/null || true
@rm -rf .pytest_cache .coverage coverage.xml htmlcov .mypy_cache .ruff_cache .hypothesis
@echo "✨ Clean complete"

.PHONY: clean-docker
clean-docker: ## Stop and remove Docker test containers and volumes
@echo "🐳 Cleaning Docker test environment"
@docker compose -f tests/docker/docker-compose.yml down -v --remove-orphans 2>/dev/null || true
@echo "✨ Docker clean complete"

.PHONY: clean-all
clean-all: clean clean-docker ## Remove all artifacts including tox, docs, and Docker
@rm -rf .tox .nox site
@echo "✨ Deep clean complete"

.PHONY: publish
publish: ## Publish a release to PyPI.
Expand Down
181 changes: 162 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,24 +63,136 @@ You can also configure the email server using environment variables, which is pa

#### Available Environment Variables

| Variable | Description | Default | Required |
| --------------------------------------------- | ------------------------------------------------- | ------------- | -------- |
| `MCP_EMAIL_SERVER_ACCOUNT_NAME` | Account identifier | `"default"` | No |
| `MCP_EMAIL_SERVER_FULL_NAME` | Display name | Email prefix | No |
| `MCP_EMAIL_SERVER_EMAIL_ADDRESS` | Email address | - | Yes |
| `MCP_EMAIL_SERVER_USER_NAME` | Login username | Same as email | No |
| `MCP_EMAIL_SERVER_PASSWORD` | Email password | - | Yes |
| `MCP_EMAIL_SERVER_IMAP_HOST` | IMAP server host | - | Yes |
| `MCP_EMAIL_SERVER_IMAP_PORT` | IMAP server port | `993` | No |
| `MCP_EMAIL_SERVER_IMAP_SSL` | Enable IMAP SSL | `true` | No |
| `MCP_EMAIL_SERVER_SMTP_HOST` | SMTP server host | - | Yes |
| `MCP_EMAIL_SERVER_SMTP_PORT` | SMTP server port | `465` | No |
| `MCP_EMAIL_SERVER_SMTP_SSL` | Enable SMTP SSL | `true` | No |
| `MCP_EMAIL_SERVER_SMTP_START_SSL` | Enable STARTTLS | `false` | No |
| `MCP_EMAIL_SERVER_SMTP_VERIFY_SSL` | Verify SSL certificates (disable for self-signed) | `true` | No |
| `MCP_EMAIL_SERVER_ENABLE_ATTACHMENT_DOWNLOAD` | Enable attachment download | `false` | No |
| `MCP_EMAIL_SERVER_SAVE_TO_SENT` | Save sent emails to IMAP Sent folder | `true` | No |
| `MCP_EMAIL_SERVER_SENT_FOLDER_NAME` | Custom Sent folder name (auto-detect if not set) | - | No |
| Variable | TOML Key | Description | Default | Required |
| --------------------------------------------- | ------------------------------ | ------------------------------------------------------ | ------------------------------------------------ | -------- |
| `MCP_EMAIL_SERVER_CONFIG_PATH` | — | Path to TOML config file | `~/.config/zerolib/mcp_email_server/config.toml` | No |
| `MCP_EMAIL_SERVER_ACCOUNT_NAME` | `emails[].account_name` | Account identifier | `"default"` | No |
| `MCP_EMAIL_SERVER_FULL_NAME` | `emails[].full_name` | Display name | Email prefix | No |
| `MCP_EMAIL_SERVER_EMAIL_ADDRESS` | `emails[].email_address` | Email address | — | Yes |
| `MCP_EMAIL_SERVER_USER_NAME` | `emails[].incoming.user_name` | Login username (IMAP & SMTP) | Same as email | No |
| `MCP_EMAIL_SERVER_PASSWORD` | `emails[].incoming.password` | Email password (IMAP & SMTP) | — | Yes |
| `MCP_EMAIL_SERVER_IMAP_HOST` | `emails[].incoming.host` | IMAP server host | — | Yes |
| `MCP_EMAIL_SERVER_IMAP_PORT` | `emails[].incoming.port` | IMAP server port | `993` | No |
| `MCP_EMAIL_SERVER_IMAP_SECURITY` | `emails[].incoming.security` | IMAP connection security: `tls`, `starttls`, or `none` | `tls` | No |
| `MCP_EMAIL_SERVER_IMAP_VERIFY_SSL` | `emails[].incoming.verify_ssl` | Verify IMAP SSL certificates | `true` | No |
| `MCP_EMAIL_SERVER_IMAP_USER_NAME` | `emails[].incoming.user_name` | IMAP-specific username (overrides `USER_NAME`) | Same as `USER_NAME` | No |
| `MCP_EMAIL_SERVER_IMAP_PASSWORD` | `emails[].incoming.password` | IMAP-specific password (overrides `PASSWORD`) | Same as `PASSWORD` | No |
| `MCP_EMAIL_SERVER_SMTP_HOST` | `emails[].outgoing.host` | SMTP server host | — | Yes |
| `MCP_EMAIL_SERVER_SMTP_PORT` | `emails[].outgoing.port` | SMTP server port | `465` | No |
| `MCP_EMAIL_SERVER_SMTP_SECURITY` | `emails[].outgoing.security` | SMTP connection security: `tls`, `starttls`, or `none` | `tls` | No |
| `MCP_EMAIL_SERVER_SMTP_VERIFY_SSL` | `emails[].outgoing.verify_ssl` | Verify SMTP SSL certificates | `true` | No |
| `MCP_EMAIL_SERVER_SMTP_USER_NAME` | `emails[].outgoing.user_name` | SMTP-specific username (overrides `USER_NAME`) | Same as `USER_NAME` | No |
| `MCP_EMAIL_SERVER_SMTP_PASSWORD` | `emails[].outgoing.password` | SMTP-specific password (overrides `PASSWORD`) | Same as `PASSWORD` | No |
| `MCP_EMAIL_SERVER_SAVE_TO_SENT` | `emails[].save_to_sent` | Save sent emails to IMAP Sent folder | `true` | No |
| `MCP_EMAIL_SERVER_SENT_FOLDER_NAME` | `emails[].sent_folder_name` | Custom Sent folder name (auto-detect if not set) | — | No |
| `MCP_EMAIL_SERVER_ENABLE_ATTACHMENT_DOWNLOAD` | `enable_attachment_download` | Enable attachment download | `false` | No |

> **Note:** The `emails[].` prefix corresponds to `[[emails]]` in TOML (array of tables). See [TOML Configuration Reference](#toml-configuration-reference) for the full config structure.

> **Deprecated:** `MCP_EMAIL_SERVER_IMAP_SSL`, `MCP_EMAIL_SERVER_SMTP_SSL`, and `MCP_EMAIL_SERVER_SMTP_START_SSL` still work for backward compatibility but are superseded by the `*_SECURITY` variables above.

#### Connection Security Modes

The `security` field (or `*_SECURITY` env var) controls how the connection to the mail server is encrypted, per [RFC 8314](https://tools.ietf.org/html/rfc8314):

| Mode | Description | IMAP Port | SMTP Port |
| ---------- | ----------------------------------------------------- | --------- | --------- |
| `tls` | **Implicit TLS** — encrypted from the first byte | 993 | 465 |
| `starttls` | **STARTTLS** — connect plaintext, then upgrade to TLS | 143 | 587 |
| `none` | **No encryption** — plaintext only (not recommended) | 143 | 25 |

### TOML Configuration Reference

The configuration file is located at `~/.config/zerolib/mcp_email_server/config.toml` by default (override with `MCP_EMAIL_SERVER_CONFIG_PATH`). You can also configure settings via the UI: `uvx mcp-email-server@latest ui`

#### Single Account

```toml
# Global settings
enable_attachment_download = false # Set to true to allow attachment downloads

# Email account — use [[emails]] for each account
[[emails]]
account_name = "work"
full_name = "John Doe"
email_address = "john@example.com"
save_to_sent = true # Save sent emails to IMAP Sent folder
# sent_folder_name = "Sent" # Optional: override auto-detected Sent folder name

# IMAP (incoming mail)
[emails.incoming]
host = "imap.gmail.com"
port = 993
user_name = "john@example.com"
password = "your-app-password"
security = "tls" # "tls" (default) | "starttls" | "none"
verify_ssl = true # Set to false for self-signed certificates

# SMTP (outgoing mail)
[emails.outgoing]
host = "smtp.gmail.com"
port = 465
user_name = "john@example.com"
password = "your-app-password"
security = "tls" # "tls" (default) | "starttls" | "none"
verify_ssl = true # Set to false for self-signed certificates
```

#### Multiple Accounts

Add multiple `[[emails]]` blocks to configure several email accounts. Each account has its own IMAP/SMTP settings and can use different security modes:

```toml
enable_attachment_download = true

# Account 1: Work Gmail (Implicit TLS)
[[emails]]
account_name = "work"
full_name = "John Doe"
email_address = "john@company.com"
save_to_sent = true

[emails.incoming]
host = "imap.gmail.com"
port = 993
user_name = "john@company.com"
password = "gmail-app-password"
security = "tls"
verify_ssl = true

[emails.outgoing]
host = "smtp.gmail.com"
port = 465
user_name = "john@company.com"
password = "gmail-app-password"
security = "tls"
verify_ssl = true

# Account 2: Personal ProtonMail via Bridge (STARTTLS + self-signed certs)
[[emails]]
account_name = "personal"
full_name = "John Doe"
email_address = "john@proton.me"
save_to_sent = true

[emails.incoming]
host = "127.0.0.1"
port = 1143
user_name = "john@proton.me"
password = "bridge-password"
security = "starttls"
verify_ssl = false

[emails.outgoing]
host = "127.0.0.1"
port = 1025
user_name = "john@proton.me"
password = "bridge-password"
security = "starttls"
verify_ssl = false
```

> **Tip:** Use `account_name` to distinguish accounts when calling MCP tools (e.g., `get_emails(account_name="work")`).

### Enabling Attachment Downloads

Expand Down Expand Up @@ -153,7 +265,7 @@ sent_folder_name = "INBOX.Sent"

### Self-Signed Certificates (e.g., ProtonMail Bridge)

If you're using a local mail server with self-signed certificates (like ProtonMail Bridge), you'll need to disable SSL certificate verification:
If you're using a local mail server with self-signed certificates (like ProtonMail Bridge), you'll need to disable SSL certificate verification for both IMAP and SMTP:

```json
{
Expand All @@ -162,6 +274,7 @@ If you're using a local mail server with self-signed certificates (like ProtonMa
"command": "uvx",
"args": ["mcp-email-server@latest", "stdio"],
"env": {
"MCP_EMAIL_SERVER_IMAP_VERIFY_SSL": "false",
"MCP_EMAIL_SERVER_SMTP_VERIFY_SSL": "false"
}
}
Expand All @@ -176,7 +289,37 @@ Or in TOML configuration:
account_name = "protonmail"
# ... other settings ...

[emails.incoming]
verify_ssl = false

[emails.outgoing]
verify_ssl = false
```

#### ProtonMail Bridge Example

ProtonMail Bridge uses STARTTLS on local ports with self-signed certificates:

```toml
[[emails]]
account_name = "protonmail"
full_name = "Your Name"
email_address = "you@proton.me"

[emails.incoming]
host = "127.0.0.1"
port = 1143
user_name = "you@proton.me"
password = "your-bridge-password"
security = "starttls"
verify_ssl = false

[emails.outgoing]
host = "127.0.0.1"
port = 1025
user_name = "you@proton.me"
password = "your-bridge-password"
security = "starttls"
verify_ssl = false
```

Expand Down
Loading
Loading