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_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
Installation
With pipx (recommended)
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
After installing with pipx:
{
"mcpServers": {
"mail": {
"command": "jxa-mail-mcp"
}
}
}
Or from source:
{
"mcpServers": {
"mail": {
"command": "uv",
"args": ["run", "--directory", "/path/to/jxa-mail-mcp", "jxa-mail-mcp"]
}
}
}
Run directly
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
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"
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
- Separation of concerns: Python handles logic/types, JavaScript handles Mail.app interaction
- Builder pattern:
QueryBuilderconstructs optimized JXA scripts programmatically - Shared JS library:
mail_core.jsprovides reusable utilities injected into all scripts - 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.
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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file jxa_mail_mcp-0.1.0.tar.gz.
File metadata
- Download URL: jxa_mail_mcp-0.1.0.tar.gz
- Upload date:
- Size: 23.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
aef82f08391750dd482e7d28e745931ad752cc7e51be2b15268d7722ada798a7
|
|
| MD5 |
0bc1b338ae51c6cd55c460fd45c66033
|
|
| BLAKE2b-256 |
f7ba78dc265327a5c24858fd3300ea992ac4c5f05fada1d313645ccf2e666daa
|
File details
Details for the file jxa_mail_mcp-0.1.0-py3-none-any.whl.
File metadata
- Download URL: jxa_mail_mcp-0.1.0-py3-none-any.whl
- Upload date:
- Size: 25.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d5af79b37d379c366e22717e5e252f8eb882ded3a9f3537cbf4b3f158f3a3891
|
|
| MD5 |
183d03ed03d3fcff14c66b129b179e1a
|
|
| BLAKE2b-256 |
688b410d8fb8e2f7f78b1b4a884e0968e5f1bbde9c9403fc1563029c75e0a65d
|