jxa-mail-mcp 0.2.0__py3-none-any.whl → 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of jxa-mail-mcp might be problematic. Click here for more details.

@@ -0,0 +1,355 @@
1
+ Metadata-Version: 2.4
2
+ Name: jxa-mail-mcp
3
+ Version: 0.3.0
4
+ Summary: Fast MCP server for Apple Mail with FTS5 search index
5
+ Project-URL: Homepage, https://github.com/imdinu/jxa-mail-mcp
6
+ Project-URL: Repository, https://github.com/imdinu/jxa-mail-mcp
7
+ Project-URL: Issues, https://github.com/imdinu/jxa-mail-mcp/issues
8
+ Author-email: Ioan-Mihail Dinu <iodinu@icloud.com>
9
+ License-Expression: GPL-3.0-or-later
10
+ License-File: LICENSE
11
+ Keywords: apple-mail,automation,email,jxa,macos,mcp,model-context-protocol
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Environment :: MacOS X
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
16
+ Classifier: Operating System :: MacOS
17
+ Classifier: Programming Language :: JavaScript
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Communications :: Email
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Requires-Python: >=3.13
23
+ Requires-Dist: beautifulsoup4>=4.12
24
+ Requires-Dist: cyclopts>=5.0.0a1
25
+ Requires-Dist: fastmcp<4,>=3.0.0b1
26
+ Description-Content-Type: text/markdown
27
+
28
+ # JXA Mail MCP
29
+
30
+ [![Python 3.13+](https://img.shields.io/badge/python-3.13+-blue.svg)](https://www.python.org/downloads/)
31
+ [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
32
+ [![macOS](https://img.shields.io/badge/platform-macOS-lightgrey.svg)](https://www.apple.com/macos/)
33
+ [![MCP](https://img.shields.io/badge/MCP-compatible-green.svg)](https://modelcontextprotocol.io/)
34
+ [![Code style: ruff](https://img.shields.io/badge/code%20style-ruff-000000.svg)](https://github.com/astral-sh/ruff)
35
+
36
+ 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, plus an optional **FTS5 search index** for **700-3500x faster** body search (~2ms vs ~7s).
37
+
38
+ ## Features
39
+
40
+ ### Email Tools
41
+ - **list_accounts** - List all configured email accounts
42
+ - **list_mailboxes** - List mailboxes for an account
43
+ - **get_emails** - Fetch emails from any mailbox with pagination
44
+ - **get_email** - Fetch a single email with full body content
45
+ - **get_todays_emails** - Fetch all emails received today
46
+ - **get_unread_emails** - Fetch unread emails
47
+ - **get_flagged_emails** - Fetch flagged emails
48
+ - **search_emails** - Search emails by subject or sender
49
+ - **fuzzy_search_emails** - Typo-tolerant search using trigram + Levenshtein matching
50
+ - **search_email_bodies** - Full-text search within email bodies (~100x faster with index)
51
+
52
+ ### Index Tools
53
+ - **index_status** - Get FTS5 index statistics
54
+ - **sync_index** - Sync new emails to the index
55
+ - **rebuild_index** - Force rebuild the index from disk
56
+
57
+ ## Installation
58
+
59
+ ### No installation required
60
+
61
+ Use `pipx run` to run directly from PyPI:
62
+
63
+ ```bash
64
+ pipx run jxa-mail-mcp
65
+ ```
66
+
67
+ ### With pipx (optional)
68
+
69
+ For faster startup, install globally:
70
+
71
+ ```bash
72
+ pipx install jxa-mail-mcp
73
+ ```
74
+
75
+ ### From source
76
+
77
+ Requires Python 3.13+ and [uv](https://docs.astral.sh/uv/):
78
+
79
+ ```bash
80
+ git clone https://github.com/imdinu/jxa-mail-mcp
81
+ cd jxa-mail-mcp
82
+ uv sync
83
+ ```
84
+
85
+ ## Quick Start
86
+
87
+ ### 1. Add to Claude Code
88
+
89
+ ```json
90
+ {
91
+ "mcpServers": {
92
+ "mail": {
93
+ "command": "jxa-mail-mcp"
94
+ }
95
+ }
96
+ }
97
+ ```
98
+
99
+ ### 2. Build the Search Index (Optional but Recommended)
100
+
101
+ For instant body search (~2ms instead of ~7s), build the FTS5 index:
102
+
103
+ ```bash
104
+ # Grant Full Disk Access to Terminal first:
105
+ # System Settings → Privacy & Security → Full Disk Access → Add Terminal
106
+
107
+ jxa-mail-mcp index --verbose
108
+ # → Indexed 22,696 emails in 1m 7.6s
109
+ # → Database size: 130.5 MB
110
+ ```
111
+
112
+ ### 3. Use with Claude
113
+
114
+ Once configured, you can search emails, get today's messages, find unread emails, and more through natural conversation.
115
+
116
+ ## CLI Commands
117
+
118
+ ```bash
119
+ jxa-mail-mcp # Run MCP server (default)
120
+ jxa-mail-mcp serve # Run MCP server explicitly
121
+ jxa-mail-mcp --watch # Run with real-time index updates
122
+ jxa-mail-mcp index # Build search index from disk
123
+ jxa-mail-mcp status # Show index statistics
124
+ jxa-mail-mcp rebuild # Force rebuild index
125
+ ```
126
+
127
+ ### Real-Time Index Updates
128
+
129
+ Use `--watch` to automatically update the index when new emails arrive:
130
+
131
+ ```bash
132
+ jxa-mail-mcp --watch
133
+ # or
134
+ jxa-mail-mcp serve --watch
135
+ ```
136
+
137
+ The file watcher monitors `~/Library/Mail/V10/` for `.emlx` changes and updates the index in real-time. Requires Full Disk Access.
138
+
139
+ ## Configuration
140
+
141
+ ### Environment Variables
142
+
143
+ | Variable | Default | Description |
144
+ |----------|---------|-------------|
145
+ | `JXA_MAIL_DEFAULT_ACCOUNT` | First account | Default email account |
146
+ | `JXA_MAIL_DEFAULT_MAILBOX` | `Inbox` | Default mailbox |
147
+ | `JXA_MAIL_INDEX_PATH` | `~/.jxa-mail-mcp/index.db` | Index database location |
148
+ | `JXA_MAIL_INDEX_MAX_EMAILS` | `5000` | Max emails per mailbox to index |
149
+ | `JXA_MAIL_INDEX_STALENESS_HOURS` | `24` | Hours before index is stale |
150
+
151
+ ### Claude Code Config
152
+
153
+ ```json
154
+ {
155
+ "mcpServers": {
156
+ "mail": {
157
+ "command": "jxa-mail-mcp",
158
+ "env": {
159
+ "JXA_MAIL_DEFAULT_ACCOUNT": "Work"
160
+ }
161
+ }
162
+ }
163
+ }
164
+ ```
165
+
166
+ ## FTS5 Search Index
167
+
168
+ The FTS5 index makes `search_email_bodies()` ~100x faster by pre-indexing email content.
169
+
170
+ ### How It Works
171
+
172
+ 1. **Build from disk**: `jxa-mail-mcp index` reads `.emlx` files directly (~30x faster than JXA)
173
+ 2. **Startup sync**: New emails are synced via JXA when the server starts
174
+ 3. **Real-time updates**: `--watch` flag enables file watcher for automatic index updates
175
+ 4. **Fast search**: Queries use SQLite FTS5 with BM25 ranking
176
+
177
+ ### Requirements
178
+
179
+ Building the index requires **Full Disk Access** for Terminal:
180
+ 1. Open **System Settings**
181
+ 2. Go to **Privacy & Security → Full Disk Access**
182
+ 3. Add and enable **Terminal.app** (or your terminal emulator)
183
+ 4. Restart terminal
184
+
185
+ The MCP server itself does NOT need Full Disk Access (uses JXA for syncing).
186
+
187
+ ### Performance Comparison
188
+
189
+ | Operation | Without Index | With Index | Speedup |
190
+ |-----------|---------------|------------|---------|
191
+ | Body search | ~7,000ms | ~2-10ms | **700-3500x** |
192
+ | Initial index build | N/A | ~1-2 min | One-time |
193
+ | Startup sync | N/A | ~1-2s | Incremental |
194
+ | Index size | N/A | ~6 KB/email | - |
195
+
196
+ #### Real-World Benchmarks (22,696 emails)
197
+
198
+ | Query | Results | Time |
199
+ |-------|---------|------|
200
+ | "invoice" | 20 | 2.5ms |
201
+ | "meeting tomorrow" | 20 | 1.3ms |
202
+ | "password reset" | 20 | 0.6ms |
203
+ | "shipping confirmation" | 10 | 4.1ms |
204
+
205
+ ## Architecture
206
+
207
+ ```
208
+ src/jxa_mail_mcp/
209
+ ├── __init__.py # CLI entry point
210
+ ├── cli.py # CLI commands (index, status, rebuild)
211
+ ├── server.py # FastMCP server and MCP tools
212
+ ├── config.py # Environment variable configuration
213
+ ├── builders.py # QueryBuilder for constructing JXA scripts
214
+ ├── executor.py # Async JXA script execution utilities
215
+ ├── index/ # FTS5 search index module
216
+ │ ├── __init__.py # Exports IndexManager
217
+ │ ├── schema.py # SQLite schema, migrations, and utilities
218
+ │ ├── manager.py # IndexManager class
219
+ │ ├── disk.py # Direct .emlx file reading
220
+ │ ├── sync.py # JXA-based incremental sync
221
+ │ ├── search.py # FTS5 search functions
222
+ │ └── watcher.py # Real-time file watcher
223
+ └── jxa/
224
+ ├── __init__.py # Exports MAIL_CORE_JS
225
+ └── mail_core.js # Shared JXA utilities library
226
+ ```
227
+
228
+ ### Design Principles
229
+
230
+ 1. **Separation of concerns**: Python handles logic/types, JavaScript handles Mail.app interaction
231
+ 2. **Builder pattern**: `QueryBuilder` constructs optimized JXA scripts programmatically
232
+ 3. **Shared JS library**: `mail_core.js` provides reusable utilities injected into all scripts
233
+ 4. **Hybrid indexing**: Disk reading for speed, JXA for incremental updates
234
+ 5. **Async execution**: All JXA calls use `asyncio.create_subprocess_exec` for non-blocking I/O
235
+ 6. **Type safety**: Python type hints and TypedDict for clear API contracts
236
+
237
+ ### Hybrid Access Pattern
238
+
239
+ | Access Method | Use Case | Latency | When Used |
240
+ |---------------|----------|---------|-----------|
241
+ | **JXA (Live)** | Real-time ops, small queries | ~100-300ms | `get_email()`, `list_mailboxes()` |
242
+ | **FTS5 (Cached)** | Body search, complex filtering | ~2-10ms | `search_email_bodies()` |
243
+ | **Disk (Batch)** | Initial indexing | ~15ms/100 emails | `jxa-mail-mcp index` |
244
+
245
+ ## Performance
246
+
247
+ ### Batch Property Fetching (87x faster)
248
+
249
+ Naive AppleScript/JXA iteration is extremely slow because each property access triggers a separate Apple Event IPC round-trip. We use batch property fetching instead:
250
+
251
+ ```javascript
252
+ // FAST: ~0.6 seconds (87x faster than per-message iteration)
253
+ const msgs = inbox.messages;
254
+ const senders = msgs.sender(); // Single IPC call returns array
255
+ const subjects = msgs.subject(); // Single IPC call returns array
256
+ ```
257
+
258
+ ### Benchmark Results
259
+
260
+ | Method | Time | Speedup |
261
+ |--------|------|---------|
262
+ | AppleScript (per-message) | 54.1s | 1x |
263
+ | JXA (per-message) | 53.9s | 1x |
264
+ | **JXA (batch fetching)** | **0.62s** | **87x** |
265
+
266
+ ### Body Search with FTS5 Index
267
+
268
+ | Search Type | Without Index | With Index | Speedup |
269
+ |-------------|---------------|------------|---------|
270
+ | Body search | ~7,000ms | ~2-10ms | **700-3500x** |
271
+ | Metadata search | ~100ms | ~100ms | - |
272
+
273
+ The FTS5 index uses:
274
+ - **Porter stemmer**: "meeting" matches "meetings", "met"
275
+ - **BM25 ranking**: Results sorted by relevance (term frequency × inverse document frequency)
276
+ - **External content table**: Shares storage with main emails table for efficiency
277
+
278
+ ### Fuzzy Search Performance
279
+
280
+ Fuzzy search uses trigrams for fast candidate selection and Levenshtein distance for accurate ranking:
281
+
282
+ | Search Type | Time (~6,000 emails) |
283
+ |-------------|---------------------|
284
+ | Regular search | ~360ms |
285
+ | Fuzzy search | ~480ms (+33%) |
286
+
287
+ ## Development
288
+
289
+ ```bash
290
+ uv sync
291
+ uv run ruff check src/
292
+ uv run ruff format src/
293
+
294
+ # Run unit tests
295
+ uv run pytest
296
+
297
+ # Run tests with verbose output
298
+ uv run pytest -v
299
+
300
+ # Manual test
301
+ uv run python -c "
302
+ import asyncio
303
+ from jxa_mail_mcp.server import list_accounts, get_todays_emails
304
+ print('Accounts:', len(asyncio.run(list_accounts())))
305
+ print('Today:', len(asyncio.run(get_todays_emails())))
306
+ "
307
+
308
+ # Test index
309
+ uv run python -c "
310
+ from jxa_mail_mcp.index import IndexManager
311
+ m = IndexManager.get_instance()
312
+ if m.has_index():
313
+ stats = m.get_stats()
314
+ print(f'Indexed: {stats.email_count} emails')
315
+ "
316
+ ```
317
+
318
+ ## Security
319
+
320
+ ### Implemented Protections
321
+
322
+ | Threat | Mitigation | Location |
323
+ |--------|------------|----------|
324
+ | SQL Injection | Parameterized queries | `search.py`, `sync.py` |
325
+ | JXA Injection | `json.dumps()` serialization | `sync.py`, `executor.py` |
326
+ | FTS5 Query Injection | Special character escaping | `search.py` |
327
+ | XSS via HTML Emails | BeautifulSoup HTML parsing | `disk.py` |
328
+ | DoS via Large Files | 25 MB file size limit | `disk.py` |
329
+ | Path Traversal | Path validation in watcher | `watcher.py` |
330
+ | Data Exposure | Database created with 0600 permissions | `schema.py` |
331
+
332
+ ## Troubleshooting
333
+
334
+ ### ModuleNotFoundError after install
335
+
336
+ If you get `ModuleNotFoundError: No module named 'jxa_mail_mcp'` even though
337
+ the package is installed, reset the virtual environment:
338
+
339
+ ```bash
340
+ rm -rf .venv
341
+ uv sync --upgrade
342
+ ```
343
+
344
+ ### Full Disk Access denied
345
+
346
+ The `jxa-mail-mcp index` command requires Full Disk Access to read Mail.app's
347
+ data files. Grant access in:
348
+
349
+ **System Settings → Privacy & Security → Full Disk Access → Add Terminal**
350
+
351
+ Then restart your terminal.
352
+
353
+ ## License
354
+
355
+ GPL-3.0-or-later
@@ -0,0 +1,20 @@
1
+ jxa_mail_mcp/__init__.py,sha256=gTvZFiwPoXLEPlSgySCC-AwirEiUu7BWNf_-aLCF0xs,551
2
+ jxa_mail_mcp/builders.py,sha256=-dApdhFAndaCBnm6Lgt_isDjhGfbIfQO9xrB6JSG2zM,7571
3
+ jxa_mail_mcp/cli.py,sha256=AoKJclwk6ui1ViXAI6eaVaaw1RPx83JBHBrER_zAuB4,10281
4
+ jxa_mail_mcp/config.py,sha256=QVHe-myFHN-09sjgAXdPtHsnmN7neH76qs668mKfmQc,2002
5
+ jxa_mail_mcp/executor.py,sha256=V_2CIajR2E6AqTvaI7FDYca6RbLzhpgncjiRY1mbeVw,7433
6
+ jxa_mail_mcp/server.py,sha256=yp3RF1k1iWQrxr_oX8r_T6-khF5hVE85a45V7JunlRg,23408
7
+ jxa_mail_mcp/index/__init__.py,sha256=zQW4j1bUWHZ9V0ApOrR_hdZiuKRrooOzl5b0b_WKUS8,535
8
+ jxa_mail_mcp/index/disk.py,sha256=x0AC0RWEWiGYTaYtpLEOu5PN8FP0b9ZzfeFoEODeKuw,14221
9
+ jxa_mail_mcp/index/manager.py,sha256=I4Jrsrc_7ZjgXNggQKiMqW83UWtL2YokFtJf_Yqak7g,14647
10
+ jxa_mail_mcp/index/schema.py,sha256=VGx3LmgqmwS4lVwIgHAhr_7_l6THKyoDSd6fhKvXEqA,8896
11
+ jxa_mail_mcp/index/search.py,sha256=GodYEY-pI8Vzq062Wk3l0TU_euKv6hGYv0gHbvcK-bQ,8898
12
+ jxa_mail_mcp/index/sync.py,sha256=e2fPB2o1ZdqwF_3rjS3Yi4uLKPfJ4YML480r6Ics_Fs,8574
13
+ jxa_mail_mcp/index/watcher.py,sha256=SCeYaKT0ncinfCf30rfNEJsu5GVsMev_o81pOrOgiiY,11622
14
+ jxa_mail_mcp/jxa/__init__.py,sha256=9UOQfRfZQNh-Z11msvBPYY34zFZALXXuk4moloqRT38,212
15
+ jxa_mail_mcp/jxa/mail_core.js,sha256=btmwmiquOjQ_6EE2d7WyWMDr-rSqkcPxY-LIAsmx3rE,11803
16
+ jxa_mail_mcp-0.3.0.dist-info/METADATA,sha256=dOLW-wTqHY_ZhEqoEdXq7MAOMHcy-UqO_WGS8uMnoGo,11619
17
+ jxa_mail_mcp-0.3.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
18
+ jxa_mail_mcp-0.3.0.dist-info/entry_points.txt,sha256=02Yq1vcKewiaIMcukAPh43Q2f4FmxhSzMGvwhD_7iJk,51
19
+ jxa_mail_mcp-0.3.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
20
+ jxa_mail_mcp-0.3.0.dist-info/RECORD,,
@@ -1,264 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: jxa-mail-mcp
3
- Version: 0.2.0
4
- Summary: Fast MCP server for Apple Mail via optimized JXA scripts
5
- Project-URL: Homepage, https://github.com/imdinu/jxa-mail-mcp
6
- Project-URL: Repository, https://github.com/imdinu/jxa-mail-mcp
7
- Project-URL: Issues, https://github.com/imdinu/jxa-mail-mcp/issues
8
- Author-email: Ioan-Mihail Dinu <iodinu@icloud.com>
9
- License-Expression: GPL-3.0-or-later
10
- License-File: LICENSE
11
- Keywords: apple-mail,automation,email,jxa,macos,mcp,model-context-protocol
12
- Classifier: Development Status :: 3 - Alpha
13
- Classifier: Environment :: MacOS X
14
- Classifier: Intended Audience :: Developers
15
- Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
16
- Classifier: Operating System :: MacOS
17
- Classifier: Programming Language :: JavaScript
18
- Classifier: Programming Language :: Python :: 3
19
- Classifier: Programming Language :: Python :: 3.13
20
- Classifier: Topic :: Communications :: Email
21
- Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
- Requires-Python: >=3.13
23
- Requires-Dist: cyclopts>=5.0.0a1
24
- Requires-Dist: fastmcp<4,>=3.0.0b1
25
- Description-Content-Type: text/markdown
26
-
27
- # JXA Mail MCP
28
-
29
- 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.
30
-
31
- ## Features
32
-
33
- - **list_accounts** - List all configured email accounts
34
- - **list_mailboxes** - List mailboxes for an account
35
- - **get_emails** - Fetch emails from any mailbox with pagination
36
- - **get_email** - Fetch a single email with full body content
37
- - **get_todays_emails** - Fetch all emails received today
38
- - **get_unread_emails** - Fetch unread emails
39
- - **get_flagged_emails** - Fetch flagged emails
40
- - **search_emails** - Search emails by subject or sender
41
- - **fuzzy_search_emails** - Typo-tolerant search using trigram + Levenshtein matching
42
- - **search_email_bodies** - Fuzzy search within email body content
43
-
44
- ## Installation
45
-
46
- ### No installation required
47
-
48
- Use `pipx run` to run directly from PyPI:
49
-
50
- ```bash
51
- pipx run jxa-mail-mcp
52
- ```
53
-
54
- ### With pipx (optional)
55
-
56
- For faster startup, install globally:
57
-
58
- ```bash
59
- pipx install jxa-mail-mcp
60
- ```
61
-
62
- ### From source
63
-
64
- Requires Python 3.13+ and [uv](https://docs.astral.sh/uv/):
65
-
66
- ```bash
67
- git clone https://github.com/imdinu/jxa-mail-mcp
68
- cd jxa-mail-mcp
69
- uv sync
70
- ```
71
-
72
- ## Usage
73
-
74
- ### Add to Claude Code
75
-
76
- Using `pipx run` (no installation required):
77
-
78
- ```json
79
- {
80
- "mcpServers": {
81
- "mail": {
82
- "command": "pipx",
83
- "args": ["run", "jxa-mail-mcp"]
84
- }
85
- }
86
- }
87
- ```
88
-
89
- Or if installed with `pipx install jxa-mail-mcp`:
90
-
91
- ```json
92
- {
93
- "mcpServers": {
94
- "mail": {
95
- "command": "jxa-mail-mcp"
96
- }
97
- }
98
- }
99
- ```
100
-
101
- ### Run directly
102
-
103
- ```bash
104
- pipx run jxa-mail-mcp
105
- # or after installing
106
- jxa-mail-mcp
107
- ```
108
-
109
- ### Configuration
110
-
111
- Set default account and mailbox via environment variables:
112
-
113
- ```bash
114
- export JXA_MAIL_DEFAULT_ACCOUNT="Work"
115
- export JXA_MAIL_DEFAULT_MAILBOX="Inbox"
116
- ```
117
-
118
- Or in Claude Code config:
119
-
120
- ```json
121
- {
122
- "mcpServers": {
123
- "mail": {
124
- "command": "jxa-mail-mcp",
125
- "env": {
126
- "JXA_MAIL_DEFAULT_ACCOUNT": "Work"
127
- }
128
- }
129
- }
130
- }
131
- ```
132
-
133
- ### Test in Python
134
-
135
- ```python
136
- from jxa_mail_mcp.server import (
137
- get_todays_emails,
138
- search_emails,
139
- fuzzy_search_emails,
140
- search_email_bodies,
141
- get_email,
142
- )
143
-
144
- emails = get_todays_emails(account="iCloud", mailbox="Inbox")
145
- results = search_emails("meeting", account="Work", limit=10)
146
-
147
- # Fuzzy search - tolerates typos
148
- results = fuzzy_search_emails("meetting nottes", limit=10) # finds "meeting notes"
149
-
150
- # Search within email bodies (slower but searches full content)
151
- results = search_email_bodies("project deadline", account="Work", limit=10)
152
-
153
- # Get full email content
154
- email = get_email(message_id=12345, account="Work", mailbox="INBOX")
155
- print(email["content"]) # Full body text
156
- ```
157
-
158
- ## Architecture
159
-
160
- ```
161
- src/jxa_mail_mcp/
162
- ├── __init__.py # Exports mcp instance and main()
163
- ├── server.py # FastMCP server and MCP tools
164
- ├── config.py # Environment variable configuration
165
- ├── builders.py # QueryBuilder for constructing JXA scripts
166
- ├── executor.py # JXA script execution utilities
167
- └── jxa/
168
- ├── __init__.py # Exports MAIL_CORE_JS
169
- └── mail_core.js # Shared JXA utilities library
170
- ```
171
-
172
- ### Design Principles
173
-
174
- 1. **Separation of concerns**: Python handles logic/types, JavaScript handles Mail.app interaction
175
- 2. **Builder pattern**: `QueryBuilder` constructs optimized JXA scripts programmatically
176
- 3. **Shared JS library**: `mail_core.js` provides reusable utilities injected into all scripts
177
- 4. **Type safety**: Python type hints ensure correct usage
178
-
179
- ## Performance
180
-
181
- ### The Problem
182
-
183
- Naive AppleScript/JXA iteration is extremely slow:
184
-
185
- ```javascript
186
- // SLOW: ~54 seconds for a few hundred messages
187
- for (let msg of inbox.messages()) {
188
- results.push({
189
- from: msg.sender(), // IPC call to Mail.app
190
- subject: msg.subject(), // IPC call to Mail.app
191
- });
192
- }
193
- ```
194
-
195
- Each property access triggers a separate Apple Event IPC round-trip.
196
-
197
- ### The Solution: Batch Property Fetching
198
-
199
- JXA supports fetching a property from all elements at once:
200
-
201
- ```javascript
202
- // FAST: ~0.6 seconds (87x faster)
203
- const msgs = inbox.messages;
204
- const senders = msgs.sender(); // Single IPC call returns array
205
- const subjects = msgs.subject(); // Single IPC call returns array
206
-
207
- for (let i = 0; i < senders.length; i++) {
208
- results.push({ from: senders[i], subject: subjects[i] });
209
- }
210
- ```
211
-
212
- ### Benchmark Results
213
-
214
- | Method | Time | Speedup |
215
- |--------|------|---------|
216
- | AppleScript (per-message) | 54.1s | 1x |
217
- | JXA (per-message) | 53.9s | 1x |
218
- | **JXA (batch fetching)** | **0.62s** | **87x** |
219
-
220
- ### Fuzzy Search Performance
221
-
222
- Fuzzy search uses trigrams for fast candidate selection and Levenshtein distance for accurate ranking. Tested on a mailbox with ~6,000 emails:
223
-
224
- | Search Type | Time | Overhead |
225
- |-------------|------|----------|
226
- | Regular search | ~360ms | - |
227
- | Fuzzy search | ~480ms | +33% |
228
-
229
- The trigram pre-filtering keeps fuzzy search fast by avoiding expensive Levenshtein calculations on non-matching words.
230
-
231
- **Example**: Searching for "reserch studies" (typo) correctly finds "research studies" with 0.94 similarity score.
232
-
233
- ### Body Search Performance
234
-
235
- Body search (`search_email_bodies`) fetches full email content, which is slower than metadata-only search but enables searching within email text:
236
-
237
- | Search Type | Time (20 emails) | Notes |
238
- |-------------|------------------|-------|
239
- | Metadata search | ~0.1s | Subject/sender only |
240
- | Body search | ~7s | Full content fetch + fuzzy match |
241
- | Single email fetch | ~0.3s | `get_email()` with full body |
242
-
243
- Body search uses a tiered matching approach:
244
- 1. **Exact substring** (score 0.95) - Fast, catches most searches
245
- 2. **Trigram similarity** (score 0.25-0.72) - Tolerates typos without expensive Levenshtein on long text
246
-
247
- ## Development
248
-
249
- ```bash
250
- uv sync
251
- uv run ruff check src/
252
- uv run ruff format src/
253
-
254
- # Test
255
- uv run python -c "
256
- from jxa_mail_mcp.server import list_accounts, get_todays_emails
257
- print('Accounts:', len(list_accounts()))
258
- print('Today:', len(get_todays_emails()))
259
- "
260
- ```
261
-
262
- ## License
263
-
264
- GPL-3.0-or-later
@@ -1,12 +0,0 @@
1
- jxa_mail_mcp/__init__.py,sha256=99llxfT4C82jO2bvqHNz8wjXcNhAW9yA1z8BdD7zNHw,205
2
- jxa_mail_mcp/builders.py,sha256=-dApdhFAndaCBnm6Lgt_isDjhGfbIfQO9xrB6JSG2zM,7571
3
- jxa_mail_mcp/config.py,sha256=yfzTGnfyu_9veyUxgNlSqgcci2fYe1v3rHbroUnPDxM,726
4
- jxa_mail_mcp/executor.py,sha256=fsIX59V1h0V2PWMiGstwAtS9MoX4mKDTDJdlTVLzYIg,2214
5
- jxa_mail_mcp/server.py,sha256=79qsUzFRJ_YwblZ-XGwBfciyY7T06eSBBmQ5-KYMbwY,17329
6
- jxa_mail_mcp/jxa/__init__.py,sha256=9UOQfRfZQNh-Z11msvBPYY34zFZALXXuk4moloqRT38,212
7
- jxa_mail_mcp/jxa/mail_core.js,sha256=btmwmiquOjQ_6EE2d7WyWMDr-rSqkcPxY-LIAsmx3rE,11803
8
- jxa_mail_mcp-0.2.0.dist-info/METADATA,sha256=Jqh0R3hMo1VoAG3Wkl5u3kfoId9s4wf55xbSJi0jU00,7210
9
- jxa_mail_mcp-0.2.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
10
- jxa_mail_mcp-0.2.0.dist-info/entry_points.txt,sha256=02Yq1vcKewiaIMcukAPh43Q2f4FmxhSzMGvwhD_7iJk,51
11
- jxa_mail_mcp-0.2.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
12
- jxa_mail_mcp-0.2.0.dist-info/RECORD,,