Skip to content

feat: add IMAP STARTTLS support with RFC 8314 security enum#121

Open
codefuturist wants to merge 10 commits intoai-zerolab:mainfrom
codefuturist:feat/imap-starttls
Open

feat: add IMAP STARTTLS support with RFC 8314 security enum#121
codefuturist wants to merge 10 commits intoai-zerolab:mainfrom
codefuturist:feat/imap-starttls

Conversation

@codefuturist
Copy link
Contributor

@codefuturist codefuturist commented Feb 14, 2026

Summary

Adds comprehensive IMAP STARTTLS support, RFC 8314-compliant connection security configuration, and a modernized Gradio settings UI.

Changes

Core: Connection Security

  • ConnectionSecurity enum (tls/starttls/none) replacing ambiguous use_ssl/start_ssl booleans
  • IMAP STARTTLS via asyncio.loop.start_tls() transport upgrade (aioimaplib has no built-in support)
  • _create_imap_connection() factory — handles TLS, STARTTLS, and plaintext uniformly
  • IMAP verify_ssl support (was SMTP-only before) with shared _create_imap_ssl_context()
  • Backward-compatible validator (mode="before") — explicit security takes precedence over legacy use_ssl/start_ssl; plaintext requires both flags explicitly false (secure by default)

Configuration

  • MCP_EMAIL_SERVER_IMAP_SECURITY / SMTP_SECURITY env vars
  • MCP_EMAIL_SERVER_IMAP_VERIFY_SSL env var
  • TOML: emails[].incoming.security / emails[].outgoing.security

Gradio UI

  • Connection Security dropdown (replaces SSL/StartSSL checkboxes) with auto-port update
  • Verify SSL Certificate checkbox for both IMAP and SMTP
  • Edit Existing Account dropdown — load saved accounts for editing with password masking
  • "Use same security for SMTP" checkbox — mirrors IMAP security settings to SMTP
  • "Test IMAP" / "Test SMTP" buttons — verify connection before saving, with user-friendly error messages
  • Clear / New Account button — reset form for creating new accounts
  • Password fields use type="password" for screen privacy

Documentation

  • README: env var table with TOML Key column, comprehensive TOML Configuration Reference (single + multi-account), security modes reference, ProtonMail Bridge example

Tests

  • 55 new tests (188 total): enum, backward compat, validator precedence, STARTTLS flow, SSL context, env vars, TOML compat, connection test helpers, update_email
  • make check + make test green

Breaking Changes

None — use_ssl/start_ssl fields are preserved as deprecated optional aliases with full backward compatibility.

Replace ambiguous use_ssl/start_ssl boolean pair with a clean
ConnectionSecurity enum (tls/starttls/none) per RFC 8314.

Changes:
- Add ConnectionSecurity enum to config.py
- Add model_validator for backward compat with use_ssl/start_ssl
- Implement IMAP STARTTLS transport upgrade via asyncio.loop.start_tls()
- Add _create_imap_connection() factory for TLS/STARTTLS/plaintext
- Add IMAP verify_ssl support for self-signed certificates
- Wire SMTP flags from security enum in EmailClient
- Add MCP_EMAIL_SERVER_IMAP_SECURITY/SMTP_SECURITY env vars
- Update existing tests to use _connect_imap instead of imap_class
- Add comprehensive tests for new security features
- Update README with security modes, env vars, and ProtonMail Bridge example

Existing configs with use_ssl/start_ssl continue to work unchanged.
The start_ssl field was previously ignored for IMAP — this fixes that.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings February 14, 2026 14:18
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds comprehensive IMAP STARTTLS support by replacing the ambiguous use_ssl/start_ssl boolean configuration with a clean ConnectionSecurity enum (tls/starttls/none) aligned with RFC 8314. This addresses a critical gap where IMAP STARTTLS was not implemented, causing failures with services like ProtonMail Bridge that require STARTTLS on port 1143.

Changes:

  • Introduced ConnectionSecurity enum to replace boolean use_ssl/start_ssl fields with clearer security modes
  • Implemented IMAP STARTTLS protocol upgrade using asyncio.loop.start_tls() following the same pattern as existing SMTP code
  • Added full backward compatibility through model validators and legacy env var support

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated no comments.

Show a summary per file
File Description
mcp_email_server/config.py Added ConnectionSecurity enum, model validator for legacy config migration, new env vars (*_SECURITY, IMAP_VERIFY_SSL), and helper functions for security parsing
mcp_email_server/emails/classic.py Implemented _imap_starttls() function, _create_imap_connection() factory, _create_imap_ssl_context(), and replaced all direct IMAP instantiation with factory pattern
mcp_email_server/ui.py Replaced SSL/Start SSL checkboxes with security dropdown, added verify SSL checkbox for IMAP, updated all form handling to use enum values
tests/test_imap_starttls.py Added 39 comprehensive tests covering enum behavior, backward compatibility, STARTTLS protocol flow, SSL contexts, env var parsing, and integration
tests/test_email_client.py Updated tests to mock _connect_imap() instead of imap_class
tests/test_email_attachments.py Updated tests to mock _connect_imap() instead of imap_class
README.md Documented security modes table, new env vars, ProtonMail Bridge configuration example, and marked deprecated env vars

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

- Add 'TOML Key' column to environment variables table mapping each
  variable to its corresponding TOML config path using dotted notation
- Add missing env vars to table: IMAP/SMTP-specific user_name/password,
  CONFIG_PATH
- Add 'TOML Configuration Reference' section with complete single-account
  example showing all supported fields
- Add multi-account example (Gmail + ProtonMail Bridge) demonstrating
  [[emails]] array-of-tables syntax with different security modes
- Add cross-reference note linking env var table to TOML reference

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@codecov
Copy link

codecov bot commented Feb 14, 2026

Codecov Report

❌ Patch coverage is 52.28758% with 146 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
mcp_email_server/ui.py 0.0% 141 Missing ⚠️
mcp_email_server/emails/classic.py 96.5% 3 Missing ⚠️
mcp_email_server/config.py 97.4% 1 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

Colin and others added 8 commits February 14, 2026 15:39
- Test _parse_security_env with None input, valid values, and invalid
  values (including warning log assertion)
- Test model validator non-dict passthrough edge case
- Improves patch coverage for config.py from 92.1% to ~97%

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Previously, setting only use_ssl=False (without start_ssl) would
default to security='none' (plaintext), which is a security risk.
Now plaintext mode requires both legacy fields to be explicitly
set to False. A single use_ssl=False or start_ssl=False alone
falls back to TLS (secure by default).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add event handlers on IMAP/SMTP security dropdowns that automatically
set the standard port when the user changes the connection security:
- TLS: IMAP 993, SMTP 465
- STARTTLS: IMAP 143, SMTP 587
- None: IMAP 143, SMTP 25

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add 'Edit Existing Account' dropdown to load/edit saved accounts
- Add 'Test IMAP' and 'Test SMTP' buttons with user-friendly error messages
- Add 'Use same security for SMTP' checkbox to mirror IMAP settings
- Add password masking (type=password) with placeholder for edit mode
- Add update_email() method to Settings model for atomic updates
- Add test_imap_connection() and test_smtp_connection() helpers
- Extract _resolve_passwords() to reduce save complexity (C901)
- Add 10 new tests for connection test helpers and update_email
- 188 tests passing, make check green

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add 7 tests for SMTP error branches (SSL, timeout, generic exception)
- Add 2 tests for IMAP generic OSError and generic exception paths
- Add test for invalid SMTP_SECURITY env var fallback
- Add tests for EmailSettings.init() legacy SMTP SSL/StartSSL paths
- Patch coverage: classic.py 96.5%, config.py 98.7% (196 total tests)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add integration test infrastructure with pure-Python (Tier 1) and
Docker-based (Tier 2) test tiers for real protocol validation.

Tier 1 — pytest-localserver + MockImapServer (18 tests):
- IMAP tests: connection, count, body, attachment, delete, append
- SMTP tests: send, CC/BCC, HTML, attachments, reply headers, unicode
- Pure Python, no Docker required, runs via 'make test-integration'

Tier 2 — GreenMail Docker (3 tests):
- Full SMTP→IMAP roundtrip: send, read body, delete
- Auto-skips if Docker unavailable, runs via 'make test-docker'

Infrastructure:
- pytest markers (integration, docker) with default exclusion
- Dependency groups in pyproject.toml (not required by default)
- Makefile targets: test-integration, test-docker, test-all
- 'make test' unchanged (196 unit tests, zero new deps)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add TLS and STARTTLS integration tests across all tiers:
- 16 new security tests (Tier 1): Implicit TLS IMAP/SMTP, STARTTLS SMTP,
  security mismatch detection, verify_ssl rejection of self-signed certs
- 3 new TLS Docker roundtrip tests (Tier 2): SMTPS/IMAPS end-to-end
- Self-signed certificate generation fixture (cryptography library)
- TLS-aware SMTP/IMAP fixtures for both tiers

Add defensive IMAP resource cleanup in test_imap_connection():
- _force_close_imap() helper closes transport and cancels _client_task
- Prevents leaked connections when IMAP operations fail or time out
- Workaround for aioimaplib#128 (tasks ignore asyncio cancellation)

Blocked tests (commented out with issue references):
- IMAP mismatch: plaintext-on-TLS + verify_ssl rejection (aioimaplib#128)
- Docker STARTTLS roundtrip: GreenMail lacks STARTTLS support (greenmail#135)

Update dependency versions: cryptography>=44.0.0 added to integration group.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants