Skip to content

Add configurable INBOX-only search optimisation to mail_move - #1

Open
bartonfriedland wants to merge 9 commits into
LionSR:mainfrom
bartonfriedland:feat/configurable-inbox-search
Open

Add configurable INBOX-only search optimisation to mail_move#1
bartonfriedland wants to merge 9 commits into
LionSR:mainfrom
bartonfriedland:feat/configurable-inbox-search

Conversation

@bartonfriedland

Copy link
Copy Markdown

Problem

The mail_move operation searches through ALL mailboxes in ALL accounts to find emails by Message-ID. This creates a cross-account performance penalty:

  • When one or more accounts have many mailboxes (e.g., 200+ folders with extensive organisation)
  • Operations on emails in other accounts still search through all mailboxes across all accounts
  • Operations can take 10-30+ seconds and Mail.app becomes unresponsive during search
  • Performance penalty applies regardless of which account contains the email

Solution

Added optional searchInboxOnly parameter (default: false) to mail_move:

  • When true: Only searches INBOX mailboxes (typically 2-5 folders across all accounts)
  • When false: Default behaviour, searches all mailboxes (backward compatible)
  • Performance gain: ~10-20x faster for INBOX operations (1-2 sec vs 10-30 sec)

Changes

Modified files:

  1. lib/mail-operations.ts - Added searchInboxOnly parameter to moveEmails()
  2. lib/schemas.ts - Added parameter to Zod schema
  3. index.ts - Added parameter to tool input schema and handler
  4. README.md - Documented parameter and added "Performance Considerations" section

Testing

Tested with multiple accounts including one with 200+ mailboxes:

  • searchInboxOnly: true - ✅ Success, ~1-2 seconds
  • searchInboxOnly: false - ✅ Success, ~10-30 seconds (Mail.app unresponsive during search)
  • Message-ID remains invariant across moves

Backward Compatibility

✅ Default is false - existing behaviour preserved
✅ No breaking changes to API
✅ Optional parameter - existing code continues to work

Future Enhancements

For discussion in a separate PR:

  • sourceAccount parameter to limit search scope to specific accounts
  • Early exit when all Message-IDs are found
  • Other performance optimisations

bartonfriedland and others added 9 commits October 11, 2025 12:04
Add optional searchInboxOnly parameter to mail_move operation to address
performance issues when users have accounts with many mailboxes.

Changes:
- Add searchInboxOnly parameter (default: false) to moveEmails() function
- Update Zod schema and MCP tool handler to support new parameter
- Add comprehensive performance documentation to README
- When enabled, only searches INBOX mailboxes for 10-20x performance gain

Performance impact: Reduces search from 200+ mailboxes to 2-5 INBOX folders
when enabled, improving operation time from 10-30s to 1-2s.

Backward compatible - default behaviour preserved.
- Add getMailById function to retrieve email by numeric ID or message-ID
- Add mail_read tool to MCP server with full content output
- Include content field in mail_get_unread and mail_get_latest output
- Add ccRecipients field to EmailMessage type

Enables agents to read full email bodies for processing and routing.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Replace `break outer;` labeled statement with `found` flag pattern
- Add missing flagIndex field to search result objects

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Added documentation for:
- mail_read (full email content retrieval)
- mail_search_inbox (fast inbox-only search)
- mail_search_mailbox (specific mailbox search)
- mail_search_by_flag (flag color search)

Organized tools into sections: Reading & Searching, Composing & Sending, Managing.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Three bugs fixed in the JXA layer:
- getUnreadMails() used Mail.mailboxes() (app-level, empty) instead of
  iterating Mail.accounts() per-account mailboxes
- getMailboxHierarchy() counted unread by iterating 100 msgs per mailbox
  across all mailboxes — now only counts for INBOX/Sent/Drafts
- getLatestMails() extracted msg.content() per message triggering IMAP
  fetch — now returns metadata only

New mail_get_inboxes tool provides single-call access to recent INBOX
messages across all enabled accounts with per-account message counts.

🤖 Generated with AI assistant support

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ailboxes

The JXA `whose` clause and full message iteration are both O(n) on
Apple Mail mailboxes, timing out at 30s+ on large inboxes. Replace
with indexed access scanning recent messages newest-first (O(1) per
message, up to 500 per mailbox).

Add optional accountName/mailboxName hint parameters so callers can
direct the lookup to the right mailbox immediately — listing functions
already return both fields for every message.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude Code's health checker expects servers to declare resources,
tools, and prompts capabilities even when not all are used. Declaring
only tools caused persistent "Failed to connect" status.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Enables running the server via `npx mcp-apple` in addition to
`node dist/index.js`.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replaced stdio transport with StreamableHTTPServerTransport on port 8263.
Bearer token auth, /health endpoint, session management.
start-server.sh for manual server management (launchd deferred due to
Node v25 ESM issue on macOS 26).

🤖 Generated with AI assistant support
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.

1 participant