| name | api-design |
| description | Use BEFORE adding any new tool, parameter, or endpoint to the Apple Mail MCP server. Also use when considering API changes, evaluating feature requests, or when tempted to create a specialized operation. Contains the decision tree that prevents tool sprawl and the anti-pattern catalog. |
Apple Mail MCP API Design
The current API has 17 tools organized into 4 phases. Tool count should grow slowly and deliberately. Every new tool request must pass through the decision tree.
Decision Tree: Use BEFORE Adding Any Tool
Can an existing tool handle this with current parameters? (70% of cases: YES)
|-- Searching with a new filter? -> Add parameter to search_messages()
|-- Reading with different format? -> Add parameter to get_messages()
|-- Sending/composing with new options? -> Extend create_draft() or update_draft()
|
Can an existing tool handle this with a NEW parameter? (20% of cases: YES)
|-- Example: search by date range -> add date_from, date_to to search_messages()
|-- Example: search flagged only -> add is_flagged to search_messages()
|
Is this truly a distinct operation? (10% of cases: MAYBE)
|-- Different Mail.app object? (accounts, rules, smart mailboxes)
|-- Different action type? (not CRUD on messages/mailboxes)
|
If NO to all three: You might need a new tool. This is rare.
Anti-Patterns (Never Do These)
Field-Specific Tools
mark_as_flagged(message_id)
mark_as_unflagged(message_id)
mark_as_junk(message_id)
update_message(message_ids, read_status=True)
update_message(message_ids, flag_color="red")
update_message(message_ids, destination_mailbox="Archive", account="Gmail")
Specialized Search Tools
get_unread_messages()
get_flagged_messages()
get_messages_from_sender()
search_messages(read_status="unread")
search_messages(is_flagged=True)
search_messages(sender_contains="user@example.com")
Formatted Text Returns
def list_mailboxes():
return "Inbox (42 messages)\nSent (15 messages)"
def list_mailboxes():
return {"success": True, "mailboxes": [{"name": "Inbox", "count": 42}, ...]}
Response Format Rules
- Always return
dict[str, Any] with "success": bool
- Errors include
"error" (message) and "error_type" (category)
- Never return formatted text strings — return structured data
- Include context fields — e.g.,
"messages_found": 5 alongside the results
Tool Naming Convention
- Verb + noun:
search_messages, create_draft, delete_messages
- Plural when accepting lists:
delete_messages, get_messages
- CRUD-style for multi-field mutation:
update_message(read_status, flag_color, destination_mailbox, ...) over per-field verbs (mark_as_read, flag_message, move_messages). Patch semantics — unset fields are unchanged.
Adding a New Tool Checklist
- Verify it doesn't fit in an existing tool (run through decision tree above)
- Write unit tests (mock AppleScript)
- Write integration tests (real Mail.app)
- Implement in
mail_connector.py
- Expose in
server.py
- Run
./scripts/check_client_server_parity.sh
- Update
docs/reference/TOOLS.md
- Add blind eval scenarios if tool has non-obvious parameters