Skip to main content

Fast MCP server for Apple Mail via optimized JXA scripts

This project has been archived.

The maintainers of this project have marked this project as archived. No new releases are expected.

Project description

JXA Mail MCP

A fast MCP (Model Context Protocol) server for Apple Mail, using optimized JXA (JavaScript for Automation) scripts with batch property fetching for 87x faster performance.

Features

  • list_accounts - List all configured email accounts
  • list_mailboxes - List mailboxes for an account
  • get_emails - Fetch emails from any mailbox with pagination
  • get_email - Fetch a single email with full body content
  • get_todays_emails - Fetch all emails received today
  • get_unread_emails - Fetch unread emails
  • get_flagged_emails - Fetch flagged emails
  • search_emails - Search emails by subject or sender
  • fuzzy_search_emails - Typo-tolerant search using trigram + Levenshtein matching
  • search_email_bodies - Fuzzy search within email body content

Installation

No installation required

Use pipx run to run directly from PyPI:

pipx run jxa-mail-mcp

With pipx (optional)

For faster startup, install globally:

pipx install jxa-mail-mcp

From source

Requires Python 3.13+ and uv:

git clone https://github.com/imdinu/jxa-mail-mcp
cd jxa-mail-mcp
uv sync

Usage

Add to Claude Code

Using pipx run (no installation required):

{
  "mcpServers": {
    "mail": {
      "command": "pipx",
      "args": ["run", "jxa-mail-mcp"]
    }
  }
}

Or if installed with pipx install jxa-mail-mcp:

{
  "mcpServers": {
    "mail": {
      "command": "jxa-mail-mcp"
    }
  }
}

Run directly

pipx run jxa-mail-mcp
# or after installing
jxa-mail-mcp

Configuration

Set default account and mailbox via environment variables:

export JXA_MAIL_DEFAULT_ACCOUNT="Work"
export JXA_MAIL_DEFAULT_MAILBOX="Inbox"

Or in Claude Code config:

{
  "mcpServers": {
    "mail": {
      "command": "jxa-mail-mcp",
      "env": {
        "JXA_MAIL_DEFAULT_ACCOUNT": "Work"
      }
    }
  }
}

Test in Python

from jxa_mail_mcp.server import (
    get_todays_emails,
    search_emails,
    fuzzy_search_emails,
    search_email_bodies,
    get_email,
)

emails = get_todays_emails(account="iCloud", mailbox="Inbox")
results = search_emails("meeting", account="Work", limit=10)

# Fuzzy search - tolerates typos
results = fuzzy_search_emails("meetting nottes", limit=10)  # finds "meeting notes"

# Search within email bodies (slower but searches full content)
results = search_email_bodies("project deadline", account="Work", limit=10)

# Get full email content
email = get_email(message_id=12345, account="Work", mailbox="INBOX")
print(email["content"])  # Full body text

Architecture

src/jxa_mail_mcp/
├── __init__.py         # Exports mcp instance and main()
├── server.py           # FastMCP server and MCP tools
├── config.py           # Environment variable configuration
├── builders.py         # QueryBuilder for constructing JXA scripts
├── executor.py         # JXA script execution utilities
└── jxa/
    ├── __init__.py     # Exports MAIL_CORE_JS
    └── mail_core.js    # Shared JXA utilities library

Design Principles

  1. Separation of concerns: Python handles logic/types, JavaScript handles Mail.app interaction
  2. Builder pattern: QueryBuilder constructs optimized JXA scripts programmatically
  3. Shared JS library: mail_core.js provides reusable utilities injected into all scripts
  4. Type safety: Python type hints ensure correct usage

Performance

The Problem

Naive AppleScript/JXA iteration is extremely slow:

// SLOW: ~54 seconds for a few hundred messages
for (let msg of inbox.messages()) {
    results.push({
        from: msg.sender(),      // IPC call to Mail.app
        subject: msg.subject(),  // IPC call to Mail.app
    });
}

Each property access triggers a separate Apple Event IPC round-trip.

The Solution: Batch Property Fetching

JXA supports fetching a property from all elements at once:

// FAST: ~0.6 seconds (87x faster)
const msgs = inbox.messages;
const senders = msgs.sender();   // Single IPC call returns array
const subjects = msgs.subject(); // Single IPC call returns array

for (let i = 0; i < senders.length; i++) {
    results.push({ from: senders[i], subject: subjects[i] });
}

Benchmark Results

Method Time Speedup
AppleScript (per-message) 54.1s 1x
JXA (per-message) 53.9s 1x
JXA (batch fetching) 0.62s 87x

Fuzzy Search Performance

Fuzzy search uses trigrams for fast candidate selection and Levenshtein distance for accurate ranking. Tested on a mailbox with ~6,000 emails:

Search Type Time Overhead
Regular search ~360ms -
Fuzzy search ~480ms +33%

The trigram pre-filtering keeps fuzzy search fast by avoiding expensive Levenshtein calculations on non-matching words.

Example: Searching for "reserch studies" (typo) correctly finds "research studies" with 0.94 similarity score.

Body Search Performance

Body search (search_email_bodies) fetches full email content, which is slower than metadata-only search but enables searching within email text:

Search Type Time (20 emails) Notes
Metadata search ~0.1s Subject/sender only
Body search ~7s Full content fetch + fuzzy match
Single email fetch ~0.3s get_email() with full body

Body search uses a tiered matching approach:

  1. Exact substring (score 0.95) - Fast, catches most searches
  2. Trigram similarity (score 0.25-0.72) - Tolerates typos without expensive Levenshtein on long text

Development

uv sync
uv run ruff check src/
uv run ruff format src/

# Test
uv run python -c "
from jxa_mail_mcp.server import list_accounts, get_todays_emails
print('Accounts:', len(list_accounts()))
print('Today:', len(get_todays_emails()))
"

License

GPL-3.0-or-later

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

jxa_mail_mcp-0.2.0.tar.gz (25.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

jxa_mail_mcp-0.2.0-py3-none-any.whl (27.9 kB view details)

Uploaded Python 3

File details

Details for the file jxa_mail_mcp-0.2.0.tar.gz.

File metadata

  • Download URL: jxa_mail_mcp-0.2.0.tar.gz
  • Upload date:
  • Size: 25.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.27 {"installer":{"name":"uv","version":"0.9.27","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for jxa_mail_mcp-0.2.0.tar.gz
Algorithm Hash digest
SHA256 55d80b8a92c51d2f77a1233131769e3e9058a72d1e8e11549a00a2a120cc557e
MD5 e09a9e004d874ce7488ac298c03659c2
BLAKE2b-256 673692e0049b3290183b06e6c7b647125d627816737ddc9667f215cef95a8484

See more details on using hashes here.

File details

Details for the file jxa_mail_mcp-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: jxa_mail_mcp-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 27.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.27 {"installer":{"name":"uv","version":"0.9.27","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for jxa_mail_mcp-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a948663131c02ec7984013ed3dfac20227d69fee2c43e07c47dcee081f4cd635
MD5 d78f896e7ff45eda10c1d6acf1c4bddf
BLAKE2b-256 60542ee7278fbcf8c2f584617847e13543d53858ff07dd2fefe9a8b11201745d

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page