Skip to content

Latest commit

 

History

History
116 lines (90 loc) · 4.19 KB

File metadata and controls

116 lines (90 loc) · 4.19 KB

Fastmail MCP Server

Project Overview

This is an MCP (Model Context Protocol) server that provides Claude with tools to interact with Fastmail via the JMAP API. The primary use case is interactive email cleanup - finding and managing newsletters, receipts, marketing emails, and other accumulated mail.

Architecture

  • Single-file server: server.py contains all code
  • Protocol: JMAP (JSON Meta Application Protocol) over HTTPS
  • Framework: FastMCP from the official MCP Python SDK
  • Auth: Bearer token via Fastmail API token
  • Caching: Session and mailbox data are cached in memory to reduce API calls

Key Files

  • server.py - The MCP server implementation
  • .env - Contains FASTMAIL_API_TOKEN (not in git)
  • .gitignore - Ignores .env
  • README.md - User-facing setup and usage docs

JMAP API Notes

Endpoints

  • Session: https://api.fastmail.com/jmap/session
  • API: https://api.fastmail.com/jmap/api/

Key JMAP Patterns

  • All requests use methodCalls array with [methodName, args, callId] format
  • Result references allow chaining: "#ids": {"resultOf": "a", "name": "Email/query", "path": "/ids"}
  • Capabilities must be declared in using array

Important: Mailbox Queries

  • Mailbox/query does NOT support filtering by role - this was a bug we fixed
  • To find mailbox by role (inbox, trash, drafts), use Mailbox/get to fetch all, then filter in code
  • Use helper functions get_mailbox_by_role() and get_mailbox_by_name()
  • Caching: The get_mailboxes() function caches mailbox data for the session lifetime to avoid repeated API calls

Important: JMAP Response Handling

  • JMAP returns None (not empty dict/list) for empty result fields
  • Never use "key" in response - this is True even when value is None
  • Always use response.get("key") which is falsy when value is None
  • Example: if response.get("updated"): not if "updated" in response:

Available Tools

Tool Purpose Destructive
fastmail_get_session_info Verify connection, show account info No
fastmail_list_mailboxes List all folders with counts No
fastmail_search_emails Search by sender, recipient, age, etc. No
fastmail_analyze_senders Top senders by volume No
fastmail_analyze_aliases Which aliases receive most mail No
fastmail_get_cleanup_candidates Find newsletters, shipping, marketing No
fastmail_delete_emails Move to trash or permanently delete Yes

Running Locally

# With uv (recommended)
uv run --with mcp --with httpx --with python-dotenv --with pydantic python server.py

# Or install deps and run directly
pip install mcp httpx python-dotenv pydantic
python server.py

Claude Desktop Config

Located at ~/Library/Application Support/Claude/claude_desktop_config.json:

{
  "mcpServers": {
    "fastmail": {
      "command": "/opt/homebrew/bin/uv",
      "args": [
        "run",
        "--with", "mcp",
        "--with", "httpx",
        "--with", "python-dotenv",
        "--with", "pydantic",
        "python",
        "/Users/greg/repos/fastmail-mcp/server.py"
      ]
    }
  }
}

Testing

# Verify syntax
python -m py_compile server.py

# Test imports
uv run --with mcp --with httpx --with python-dotenv --with pydantic python -c "import mcp; import httpx; import dotenv; print('OK')"

Common Issues

  1. "FASTMAIL_API_TOKEN not set" - Create .env file with token
  2. "Authentication failed" - Token expired or revoked, regenerate in Fastmail settings
  3. "Permission denied" - Token needs Email scope, not read-only
  4. Move to trash fails with Mailbox/query - Mailbox/query doesn't support role filter; use Mailbox/get + filtering (fixed in get_mailbox_by_role())
  5. TypeError: object of type 'NoneType' has no len() - JMAP returns None for empty fields; use response.get("key") not "key" in response

Future Enhancements

Potential improvements:

  • Add fastmail_empty_trash tool
  • Add filtering by date range (not just "older than X days")
  • Support for labels/tags if Fastmail adds them
  • Batch operations with progress reporting
  • Add cache invalidation/refresh mechanism for mailboxes