Apple Mail MCP Server
An MCP server that lets AI assistants read, search, send, and manage Apple Mail on macOS, with both read-only and mutating tools.
Discovery: list accounts, mailboxes, rules, and templates
Read/search: search messages, fetch full messages and threads, read attachment content inline, render templates
Message actions: update read/flag/move state, delete messages to Trash, save attachments to disk
Drafts: create, update, and delete drafts; optionally send immediately; support reply/forward seeds, HTML bodies, templates, and attachments
Direct send: send new email, reply, reply all, or forward in a single call
Mailbox management: create, rename/re-parent, and delete mailboxes
Rules management: create, update, and delete Mail rules, with confirmation for dangerous actions
Templates: save, list, get, delete, and render reusable email templates
Account management: list and delete configured Mail accounts
Optional IMAP fast path: faster server-side search when IMAP credentials are configured
Supports AOL email accounts configured in Apple Mail, with optional IMAP integration for faster server-side search.
Provides full programmatic access to Apple Mail on macOS, enabling AI assistants to read, send, search, and manage emails, drafts, mailboxes, and rules.
Supports Gmail accounts configured in Apple Mail, with optional IMAP integration for faster server-side search.
Supports iCloud email accounts configured in Apple Mail, with optional IMAP integration for faster server-side search.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Apple Mail MCP Serverfind unread emails from Alice and summarize them"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Apple Mail MCP Server
An MCP server that provides programmatic access to Apple Mail, enabling AI assistants like Claude to read, send, search, and manage emails on macOS.
⚠️ Pre-1.0 — expect breaking changes. The MCP tool surface (tool names, parameters, return shapes) is still evolving as the project matures. Pin to a specific version (for example,
apple-mail-mcp==0.10.2) and review the CHANGELOG before upgrading.
Tools (35)
Grouped by lifecycle (12 read-only, 23 mutating):
Discovery —
list_accounts,list_mailboxes,list_rules,list_smart_mailboxes,list_templates: enumerate what's configured (no external cache — call per account).Read —
search_messages,get_messages,get_thread,get_attachment_content,get_template,render_template: read messages/threads, pull an attachment's content inline, and render templates.Message actions —
update_message(read/flag/move in one pass),delete_messages(→ Trash),save_attachments(to disk, byte-capped).Drafts —
create_draft(new / reply / forward, optionallysend_now),update_draft,delete_draft.Direct send —
send_email,reply,reply_all,forward: send in a single call, without going through a draft. Each one sends for real; there is no second confirmation step inside Mail.Accounts —
delete_account: remove a configured account from Mail.app.IMAP fast path —
imap_status: per-account verdict on whether server-side search is actually live, plus the installed commit (read-only, no password).setup_imap: store and verify an account's IMAP password from the conversation, instead of thesetup-imapCLI.Mailbox CRUD —
create_mailbox,update_mailbox(rename or move),delete_mailbox.Rules —
create_rule,update_rule,delete_rule.Smart mailboxes —
create_smart_mailbox,update_smart_mailbox,delete_smart_mailbox: a folder that filters mail without moving it, so the inbox keeps every message (a rule withmove_toempties it, one withcopy_todoubles the quota). No AppleScript surface exists, so these edit Mail's plist directly and require Mail to be quit; the mailbox appears at the next launch. When Mail is in iCloud, both the iCloud copy and the local mirror are written — writing only the local one is silently undone at the next launch.Templates (write) —
save_template,delete_template.
Destructive operations (delete_*, create_rule with move/forward/delete actions, create_draft with send_now=true) prompt for confirmation via MCP elicitation. See docs/reference/TOOLS.md for full parameters and return shapes.
Related MCP server: apple-mail-mcp
Prerequisites
macOS 10.15 (Catalina) or later
Python 3.10 or later
Apple Mail configured with at least one account
uv (recommended) or pip
Installation
# From source (recommended for development)
git clone https://github.com/LeChabrax/apple-mail-mcp.git
cd apple-mail-mcp
uv sync --devConfiguration
Add to your Claude Desktop config (~/Library/Application Support/Claude/claude_desktop_config.json). uv sync installs a console script at .venv/bin/apple-mail-mcp; point Claude Desktop at its absolute path — it's the most reliable form under Claude Desktop's restricted spawn environment (no reliance on uv being on PATH):
{
"mcpServers": {
"apple-mail": {
"command": "/path/to/apple-mail-mcp/.venv/bin/apple-mail-mcp"
}
}
}(Equivalent alternative if you prefer driving it through uv: "command": "uv", "args": ["--directory", "/path/to/apple-mail-mcp", "run", "apple-mail-mcp"].)
Optional: split read / write servers
Claude Desktop prompts per-tool for permission. If you want to batch-approve the 10 read tools (list / search / get) and still gate the 19 mutating tools per call, run the connector twice — once with --read-only, once without — under two separate mcpServers entries:
{
"mcpServers": {
"apple-mail-read": {
"command": "/path/to/apple-mail-mcp/.venv/bin/apple-mail-mcp",
"args": ["--read-only"]
},
"apple-mail-write": {
"command": "/path/to/apple-mail-mcp/.venv/bin/apple-mail-mcp"
}
}
}The --read-only server exposes only the 10 read tools, so Claude Desktop's per-server permission UI naturally groups them. The full server still gates writes individually. Trade-off: 2× connector processes. See docs/reference/TOOLS.md for the per-tool classification and a note on MCP annotation hints (readOnlyHint / destructiveHint / idempotentHint) which forward-compatible hosts may use to provide the same UX without the split.
Permissions
On first run, macOS will prompt for Automation access. Grant permission in: System Settings > Privacy & Security > Automation > Terminal (or your IDE)
Optional: faster search via IMAP
search_messages works out of the box via AppleScript. For large mailboxes (thousands of messages), AppleScript's whose clause can take 1–5 seconds per query — and a body/text search is far worse: 37s on a real mailbox where IMAP answers the same query in 3s, minutes on a cold cache. If you want faster server-side search, you can enable IMAP delegation per account by adding a Keychain entry.
Check what's live first. The fallback below is silent, so a mailbox that feels slow looks exactly like one that is simply large. apple-mail-mcp status prints, for every account, the host and port in use, whether a password is stored, and what a real connection returns — read-only, no password asked. It also prints the installed commit, which is the only way to tell an MCP server started before an update from one running the current code.
How it works. If credentials exist for an account, the server uses IMAP (fast, server-side SEARCH). Otherwise — or on any IMAP failure (offline, wrong password, timeout) — it silently falls back to AppleScript. You never lose functionality; you only gain speed when IMAP is configured and reachable. The normal opt-in is a Keychain entry (below); an environment-variable fallback (further down) covers contexts where the Keychain isn't usable.
One-time setup per account.
Get the password the IMAP server expects. On most providers — a self-hosted server, OVH, Infomaniak, any generic IMAP host — that is simply the mailbox password, and there is nothing to generate. Do not demand an app-specific password where none exists; that advice costs an hour of hunting for a setting that isn't there. Providers that DO require one, because they refuse a plain password over IMAP:
iCloud: appleid.apple.com/account/manage → App-Specific Passwords. Requires 2FA on your Apple ID (default).
Gmail: myaccount.google.com/apppasswords. Requires 2-Step Verification on your Google account.
Yahoo / Fastmail / AOL: generate an app password in the provider's account-security settings.
It is never the macOS login password, and it cannot be read out of Mail.app: its credentials live in the protected keychain, ACL-bound to Mail.app.
Run the
setup-imapsubcommand. It prompts for the password (no echo), writes the Keychain entry, and verifies by connecting:apple-mail-mcp setup-imap --account iCloudSubstitute the Mail.app account name exactly — whatever it's labeled in Mail.app (e.g.
iCloud,Gmail,"Yahoo!"). The CLI:looks up the account's primary email from Mail.app (override with
--email, which is persisted so runtime uses the same login — see the iCloud quirk below),prompts via
getpassso the password never lands in shell history,writes to Keychain at
apple-mail-mcp.imap.<account>(idempotent — re-running with a new password updates the existing entry),opens an IMAP connection and runs a real LOGIN to confirm the password works. On rejection it rolls back the Keychain entry so you can retry without leaving a broken item behind.
If you see a one-time "security wants to use the 'login' keychain" prompt on the next IMAP-backed call, click Always Allow.
Confirm with
apple-mail-mcp status. AWARNING: IMAP verification could not completeat step 2 means the entry was written but never proved against the server — the account stays on the slow path, silently.
To remove the entry later: apple-mail-mcp setup-imap --account iCloud --uninstall.
Environment-variable fallback (uvx / headless / CI)
Some contexts have no usable Keychain: uvx runs (ephemeral binary paths break the Keychain ACL, causing re-prompts or failures), Docker / CI (no Keychain at all), and background services (the ACL prompt blocks forever with no UI attached). For those, you can supply the IMAP password via an environment variable instead:
APPLE_MAIL_MCP_IMAP_PASSWORD_<SUFFIX><SUFFIX> is the Mail.app account name uppercased, with each run of non-alphanumeric characters collapsed to a single underscore and leading/trailing underscores trimmed:
Account name | Environment variable |
|
|
|
|
|
|
|
|
When set to a non-empty value, the env var is used in preference to any Keychain entry for that account (it's checked first, with no security shell-out). An empty or whitespace-only value is ignored and the Keychain path is used. The lookup composes with the name↔UUID fallback, so an env var keyed on the account name is still found when a caller passes the account's UUID.
⚠️ Security tradeoff. Environment variables are far less private than the Keychain — they're visible via
ps -E,launchctl getenv,/proc-style introspection, and process crash dumps, and they're easy to leak into logs or shell history. Use this only when the Keychain genuinely isn't an option (uvx, Docker, CI, headless). For Claude Desktop and standard local installs, stick withsetup-imap+ Keychain.Caveat: the name→suffix mapping isn't reversible —
Yahoo!andYahooboth map toYAHOO, and an account name with no ASCII letters/digits has no env-var form (use the Keychain for those).
Verifying the setup. The setup-imap command does this for you. If you want to spot-check post-hoc:
uv run python -c "from apple_mail_mcp.mail_connector import AppleMailConnector; \
print(AppleMailConnector().search_messages(account='<ACCOUNT_NAME>', limit=1))"If IMAP is working, the call returns in ~1 second. If it logs a WARNING about falling back (visible with --log-level=DEBUG), check that the account name matches Mail.app's account name exactly and that the email in your Keychain entry matches what email addresses of account returns.
Known provider quirks.
iCloud: the IMAP server accepts
@icloud.com/@me.comaliases as LOGIN username, not the Apple ID email. The server (andsetup-imap) readsemail addresses of accountfrom Mail.app for that reason. If your iCloud Apple ID is a third-party address (e.g. a@gmail.comApple ID) and Mail.app reports no@icloud.comaddress for the account, auto-detection can't find the right login —setup-imapwill fail with a hint to re-run with--email <your @icloud.com/@me.com address>. That--emailvalue is persisted (in~/.apple_mail_mcp/imap_login_overrides.json) so runtime resolution uses the same login (#341). It's a general override — use it for any account whose auto-detected IMAP login is wrong.Yahoo: app passwords have been progressively deprecated; the option may not be available for all accounts. If Yahoo's account-security page doesn't show the option, IMAP setup isn't possible for that account and AppleScript is the only path.
Gmail: requires 2-Step Verification enabled. If your Google Workspace admin has disabled app passwords at the tenant level, IMAP setup isn't possible for that account.
Gmail thread retrieval — All Mail visibility tradeoff.
find_thread_members(used internally by thread-aware queries) is fastest when[Gmail]/All Mailis exposed over IMAP — that path is ~5 round-trips, mailbox-count-independent. Many users hide All Mail (Gmail Settings → Forwarding and POP/IMAP → Folder size limits → "Do not show in IMAP") because it duplicates every message. When hidden, the connector falls back to a per-mailbox X-GM-THRID iteration (still ~6× faster than the universal BFS, but proportional to your label count — ~25s on a 92-label account). Expose All Mail if you want the headline speed; keep it hidden if you prefer the cleaner IMAP folder list.
Write operations (create_draft, update_draft, including the send_now=true send path) always use AppleScript regardless of IMAP configuration — these need Mail.app's compose UI.
Timeouts on very large mailboxes
The defaults are sized for ordinary mailboxes and are worth raising on a
large one. This module's own measurement is 148s for 100 cold-cache messages
on a 47k-message mailbox, so a server-side SEARCH there can outlast the
30s default and silently fall back to the slower AppleScript path.
Variable | Default | What it bounds |
| 30 | IMAP |
| 3 | IMAP connect + login. Raising it delays offline detection, so prefer leaving it alone. |
| 270 | How long a pooled connection may sit idle before being recycled. |
A non-numeric or non-positive value is ignored with a warning and the default is kept, so a typo cannot take the server down.
Development
# Setup
uv sync --dev
# Common commands
make test # Run unit tests
make lint # Lint with ruff
make typecheck # Type check with mypy
make check-all # All checks (lint, typecheck, test, complexity, version-sync, parity)
make coverage # Coverage report
make test-integration # Integration tests (requires Mail.app)
# Validation scripts
./scripts/check_version_sync.sh # Version consistency
./scripts/check_client_server_parity.sh # Connector-server alignment
./scripts/check_complexity.sh # Cyclomatic complexity
./scripts/check_applescript_safety.sh # AppleScript safety auditBranch Convention
{type}/issue-{num}-{description} — e.g., feature/issue-42-thread-support
Architecture
server.py (FastMCP tools — thin orchestration, validation, elicitation gates)
-> mail_connector.py (dispatch + domain logic)
-> AppleScript path: subprocess.run(["osascript", ...]) -> Apple Mail.app (universal baseline)
-> IMAP fast path: imap_connector.py -> the account's IMAP server (when hinted + Keychain creds)Dispatch model. AppleScript is the always-available baseline. When a read/mutation call supplies
an account (and, where relevant, mailbox) hint and the account has Keychain IMAP credentials,
the connector takes a server-side IMAP fast path; on any IMAP failure it falls back to AppleScript, so
you never lose functionality — you only gain speed. See
docs/reference/ARCHITECTURE.md for the full dispatch model, the
dual-emit message-ID scheme, the drafts lifecycle, and the IMAP thread tiers.
server.py — MCP tool registration, input validation, confirmation (elicitation) gates, response formatting
mail_connector.py — AppleScript generation/execution + IMAP-fast-path dispatch
imap_connector.py — IMAP client + connection pool (search, fetch, bulk-mutation fast paths)
security.py — Input sanitization, audit logging, confirmation flows
utils.py — Pure functions: escaping, parsing, validation
exceptions.py — Typed exception hierarchy
Security
Local execution only (no cloud processing)
Uses existing Mail.app authentication; IMAP app-passwords (opt-in) live in the macOS Keychain, never in the repo or config
All inputs sanitized and AppleScript-escaped (defense against AppleScript injection)
Destructive operations require user confirmation via MCP elicitation; rate limits + audit logging on top
save_attachmentsis byte-capped (per-attachment + aggregate) against disk-fill DoS
Docs:
SECURITY.md — vulnerability-reporting policy
docs/SECURITY.md — user-facing security posture & privacy
docs/guides/THREAT_MODEL.md — STRIDE trust-boundary analysis
docs/guides/SECURITY_CHECKLIST.md — per-feature contributor checklist
Contributing
See CONTRIBUTING.md for development workflow, coding standards, and PR process.
Credits
This project is a fork of apple-mail-mcp by Morgan Jeffries, which does all the heavy lifting: the AppleScript bridge, the IMAP fast path, the draft state store, the templates and the elicitation gates.
What this fork adds on top of upstream v0.10.2:
Addition | Why |
| Send in one call. Upstream only sends through |
| Remove a configured account from Mail.app. |
| Skip the elicitation prompt for callers that already gate sends on their own side. Off by default. |
Everything else, including the tool surface, the tests and the docs, comes from upstream. Bug reports about the shared parts are better filed there.
What Mail.app will not let this server do
Measured on macOS 15, worth knowing before opening an issue:
An account created over AppleScript is never persisted.
make new imap accountreturns an id andcount of accountssees it, but it is absent from Mail's Settings window and gone once Mail quits. Adding an account for real needs a configuration profile (com.apple.mail.managed), approved on screen. There is no scripted path:profiles installanswers "profiles tool no longer supports installs".enabledcannot be written on any account.set enabledraises-10000 AppleEvent handler failed, on a new account and on an existing active one, over AppleScript and over JXA, with every reference form. Mail's own sdef declares the property writable (noaccess="r", cocoa keyisActive); the implementation disagrees.Mail's Settings window is a stale snapshot. It lists accounts AppleScript no longer knows and omits ones it does. Never read account state from the UI.
License
Available Tools
29 toolscreate_draftA
Create a draft (fresh, reply, or forward). Optionally send immediately.
Mail.app's actual primitive is the draft — every outgoing message is a draft until sent. This tool lets callers create one, optionally seeded from an existing message (reply or forward), and either save it for later or send it now.
| Name | Required | Description | Default |
|---|---|---|---|
| cc | No | ||
| to | No | ||
| bcc | No | ||
| body | No | Body text. For reply/forward, a non-empty body REPLACES Mail's auto-quoted content; an empty body leaves the auto-quote intact (matches Mail.app's default reply behavior). | |
| subject | No | Subject. Required when both seeds are None. For reply/forward, ``None`` keeps Mail's ``Re:``/``Fwd:`` prefix. | |
| reply_to | No | Id of a message to reply to. Accepts either Mail.app's internal numeric id or an RFC 5322 Message-ID — pass the ``id`` field from any ``search_messages`` / ``get_messages`` row verbatim. Mutually exclusive with ``forward_of``. When set, ``to``/``cc`` recipients and ``subject`` are auto-derived from the original (override by passing them explicitly). | |
| send_now | No | ``False`` (default) saves as draft. ``True`` sends immediately and elicits user confirmation. | |
| body_html | No | Optional HTML body. When set, the draft is built as a multipart/alternative (HTML + a plain-text alternative taken from ``body``, or derived from the HTML when ``body`` is empty). HTML drafts are created over the clean IMAP path, so they REQUIRE IMAP credentials for the account and are limited to fresh save-as-draft: passing ``body_html`` with ``send_now`` or with ``reply_to``/``forward_of`` is rejected, and if IMAP can't engage the call fails (``error_type: "html_requires_imap"``) rather than silently downgrading to plain text. HTML is caller-trusted (not sanitized). (#251) | |
| reply_all | No | For ``reply_to`` only — use ``reply to all``. | |
| forward_of | No | Id of a message to forward. Accepts the same id forms as ``reply_to``. Mutually exclusive with ``reply_to``. ``to`` is required (recipient of the forward). | |
| from_account | No | Mail.app account name or UUID. ``None`` uses Mail's default; on a save-as-draft with exactly one enabled account, that account is adopted so the clean (no iOS quote bug) IMAP draft path can engage. | |
| seed_mailbox | No | Mailbox the reply_to/forward_of message lives in (e.g. the ``mailbox`` field from its ``search_messages`` row). Lets the clean save-as-draft path fetch the original directly so reply/forward drafts render without the iOS quote bug — supply it especially for replies to filed (non-INBOX) mail. Defaults to INBOX; a miss falls back transparently. | |
| template_name | No | Optional template to render for ``subject`` and ``body``. Caller-supplied ``subject``/``body`` override the rendered output. ``template_vars`` override auto-fills. | |
| template_vars | No | Variables to pass to the template renderer. Requires ``template_name``. | |
| attachment_paths | No | List of file paths to attach. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations are all false (no read-only, idempotency, or destructive hints), so they carry essentially no information and the description must carry the burden. The description does disclose the key behavioral trait — that this can 'send immediately' vs. 'save it for later' — which is meaningful. However, it omits side effects like the user confirmation prompt on send, visibility of saved drafts in Mail.app, or failure modes; the actual behavioral weight is carried by the parameter docs instead.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Four sentences total — one imperative verb-first opener plus a three-sentence framing paragraph — with zero fluff. The core action is front-loaded, and the conceptual paragraph earns its place by giving an agent a mental model for a genuinely complex 15-parameter tool. Every sentence carries weight.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 15-parameter tool with an output schema (so return values are already documented) and a detailed schema, the description covers the essentials: the three creation modes, the seed-from-original concept, and the save-vs-send distinction. The only real gap is that it doesn't help an agent choose between this and the send_email/reply/reply_all/forward siblings, but given the high schema coverage and output schema, the description is nearly complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 80%, putting this at the high-coverage baseline. The description's 'seeded from an existing message (reply or forward)' conceptually maps to reply_to/forward_of, but the schema already documents those with far more depth (including the RFC 5322 Message-ID forms and mutual exclusivity), so the description adds no syntax or format value. Baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb, resource, and scope: 'Create a draft (fresh, reply, or forward). Optionally send immediately.' The three modes are enumerated and the conceptual paragraph explains why a draft-creation tool is the right primitive, distinguishing it clearly from anything that just sends. This is a model of a clear operation + scope.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The explanation that 'every outgoing message is a draft until sent' provides clear conceptual context for when this tool is the right choice (any outbound mail), and the fresh/reply/forward modes plus the save-vs-send duality are explicit. However, it never names the overlapping siblings (send_email, reply, forward) or states when NOT to use this tool in favor of them, so it stops at clear context without explicit exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_mailboxAIdempotent
Create a new mailbox/folder.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Name of the new mailbox | |
| account | Yes | Mail.app account display name (e.g., "Gmail", "iCloud") or UUID (from list_accounts) to create the mailbox in. Names are convenient but unstable across renames; UUIDs are stable. | |
| parent_mailbox | No | Optional parent mailbox for nesting (None = top-level) |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The annotations already convey that this is a non-read-only, non-destructive, idempotent operation. The description adds no behavioral detail beyond the obvious creation action—such as duplicate handling, naming constraints, or account-specific behavior—so it provides little value beyond the structured annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single concise sentence with no wasted words, and the core action is front-loaded. While very brief, it earns its place as a clear minimally sufficient summary.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple creation tool, the combination of schema, annotations, and output schema covers the essential invocation details. The description doesn't need to explain return values or parameter semantics because those are already present in structured fields. It would only be incomplete if behavioral edge cases were not covered, but annotations compensate here.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, and the schema already thoroughly documents the 'name', 'account', and 'parent_mailbox' parameters, including UUID stability and nesting semantics. The description itself adds no parameter-level meaning, so the baseline score of 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('Create') and a clear resource ('mailbox/folder'), making the tool's purpose immediately understandable. It is naturally distinguished from sibling tools like list_mailboxes, update_mailbox, and delete_mailbox by the action it performs.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no explicit when-to-use guidance or mention of alternatives, but the action 'Create a new mailbox/folder' implicitly conveys that this tool is for creating mailboxes. It does not discuss when not to use it or contrast it with update_mailbox/delete_mailbox.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_ruleA
Create a new Mail.app rule.
Rules with actions that can move, forward, or delete mail
(delete / forward_to / move_to / copy_to) require user confirmation —
a single create can install automation that auto-forwards or deletes
all future mail (#222). Organizational-only rules (mark_read,
mark_flagged, flag_color) are created without a prompt. Mail.app
appends new rules to the end of the rule list, so the returned
rule_index equals the new total rule count.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Rule display name. Need not be unique. | |
| actions | Yes | Dict with at least one truthy entry from: - move_to: {"account": str, "mailbox": str} - copy_to: {"account": str, "mailbox": str} - mark_read: bool - mark_flagged: bool (with optional flag_color enum) - flag_color: 'none' | 'red' | 'orange' | 'yellow' | 'green' | 'blue' | 'purple' | 'gray' - delete: bool - forward_to: list[str] of email addresses | |
| enabled | No | Whether the rule is enabled on creation. Default True. | |
| conditions | Yes | List of condition dicts (at least one required). Each: - field: 'from' | 'to' | 'subject' | 'body' | 'any_recipient' | 'header_name' - operator: 'contains' | 'does_not_contain' | 'begins_with' | 'ends_with' | 'equals' - value: substring or value to match - header_name: required iff field == 'header_name' | |
| match_logic | No | 'all' (AND across conditions) or 'any' (OR). Default 'all'. | all |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations are thin (readOnlyHint=false, destructiveHint=false, idempotentHint=false), so the description carries the full load — and it delivers. It flags the counterintuitive fact that a single create can install automation with auto-forward/delete consequences, requires user confirmation for safe execution, and explains why the returned rule_index reflects the new total count. This is exactly the kind of non-obvious runtime behavior agents can't infer. A small deduction for not mentioning what happens if the user declines the confirmation dialog.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Beautifully structured: one line for purpose, two short paragraphs covering warning-worthy behavior and return semantics. The critical gotchas (confirmation prompt, append-to-end behavior) are front and center. A single point off for the cryptic '#222' reference — a dangling artifact that doesn't help an agent executing a task and pollutes the otherwise clean description.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with 5 params, nested objects, and an output schema, this definition is quite complete. The combination of annotations (safety/destructive profile), a rich 100% schema (parameter semantics), and a description that explains the non-obvious behavioral boundaries (confirmation requirement, rule ordering, return value) covers the major risks. What's missing: behavior on confirmation denial, and interaction with the 'enabled' flag. Given the safety-critical nature of the described operations, those two details would make it fully airtight.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%; every parameter is documented with examples, defaults, and structure. The description stays at the semantic layer — it doesn't try to re-document the schema, which is the right call. The action-type distinction (delete/forward_to/move_to/copy_to) ties the description to the schema's 'actions' param in a way that clarifies selection without duplicating it, but it's marginal value added beyond what the schema already provides.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a crisp 'Create a new Mail.app rule.' — a specific verb, a concrete domain resource, and a clear scope. Sibling tools (list_rules, update_rule, delete_rule) make the creation intent unambiguous; no reasonable agent could confuse this with an operation on an existing rule.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides a crisp decision boundary for when the tool is behaviorally different: actions that 'move, forward, or delete mail' trigger a user confirmation prompt, while 'organizational-only' actions do not. This is excellent operational context an agent needs before crafting a risky rule. It doesn't explicitly name alternatives ('use update_rule to modify an existing rule'), but it's clear enough given the parameter docs cover the action vocabulary.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_accountADestructive
Delete a mail account from Mail.app.
Removes the account entirely from Mail.app. Use the account UUID (from list_accounts) for stability across renames.
| Name | Required | Description | Default |
|---|---|---|---|
| account | Yes | Account display name (e.g., "Gmail") or UUID. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds context beyond the destructiveHint annotation by specifying 'Removes the account entirely,' and it warns about the stability issue with display names. This provides useful behavioral expectations for a destructive operation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two concise sentences, purpose first, then a practical tip. No fluff.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the one parameter, the destructive nature is stated, and the output schema is present, the description covers the essential information for an agent to invoke this tool correctly. It could mention prerequisites or side effects, but it's sufficient for this simple deletion.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema already describes the 'account' parameter as display name or UUID, but the description adds guidance to prefer UUID for stability, which is not in the schema. This enriches the semantics.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states 'Delete a mail account from Mail.app.' and 'Removes the account entirely from Mail.app.' This is specific to accounts, distinguishing it from sibling delete tools like delete_rule or delete_mailbox. The verb and resource are explicit.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description does not explicitly contrast with alternatives, but it provides a usage tip: 'Use the account UUID (from list_accounts) for stability across renames.' This suggests when to use this tool is for deleting accounts, but it doesn't say when not to use it or mention alternatives. It's adequate but not explicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_draftADestructiveIdempotent
Delete (move to Trash) an existing draft.
Lifecycle endpoint for cancellation. Mail.app moves the message to the Deleted Messages mailbox; recovery is technically possible but Mail.app no longer treats trashed drafts as editable, so this is effectively a one-way discard. No elicitation (recoverable from Trash) and no rate limit (local operation).
| Name | Required | Description | Default |
|---|---|---|---|
| draft_id | Yes | Mail.app id of the draft. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already flag destructive and idempotent behavior. The description adds specific consequences (moved to Deleted Messages, no longer editable, recovery possible but effectively one-way) and mentions there is no rate limit, which goes beyond the annotation hints.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise but includes a slightly redundant phrase 'No elicitation (recoverable from Trash)' that could be simplified. Overall it is easy to read and stays on point.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple delete operation, the description covers the action, side effects, and recovery nuance. It doesn't describe return values, but that is likely unnecessary for this tool; the context is sufficiently complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The only parameter draft_id is fully described as 'Mail.app id of the draft.' Schema coverage is 100%, and the description adds no further details, so it stays at the baseline.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Clearly states the verb 'Delete' and the resource 'draft', and clarifies it moves to Trash. This distinguishes it from other draft-related tools like update_draft or send_email.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Describes the lifecycle purpose as 'cancellation' and notes the one-way nature. While it doesn't explicitly list when not to use it, the context makes the intended use unambiguous relative to sibling tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_mailboxADestructiveIdempotent
Delete a mailbox via IMAP.
Mail.app's AppleScript dictionary doesn't expose a working delete
primitive for mailboxes, so this operation goes through IMAP. Requires
IMAP credentials in Keychain (#73 opt-in flow) — returns
error_type: "imap_required" when missing.
Always elicits user confirmation (destructive). By default refuses
non-empty mailboxes to prevent accidental data loss; pass
delete_messages=True to cascade.
Refused (#164): targeting the bare [Gmail] parent or any
[Gmail]/... child path returns error_type: "unsupported_gmail_system_label". Gmail's IMAP server doesn't
support DELETE for these paths.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Mailbox name. Slash-separated for nested mailboxes. | |
| account | Yes | Mail.app account display name or UUID. | |
| delete_messages | No | When False (default), refuse if the mailbox contains messages. When True, cascade-delete the mailbox and its contents. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description substantially extends the annotations (destructiveHint=true) by detailing that user confirmation is always elicited, default refusal for non-empty mailboxes, cascade option, Gmail system-label restriction, and the error_type when credentials are missing. This provides a rich behavioral model beyond the boolean hints, covering edge cases and safety measures.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is structured into logical sections: method, prerequisite, safety behavior, and Gmail exception. It is informative without being excessively verbose, though it could be tightened. The core purpose is front-loaded in the first sentence, which aids quick comprehension.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the destructive nature and conditional behaviors, the description covers all critical aspects: the IMAP mechanism, credential requirement, default safety refusal, cascade override, and unsupported Gmail paths. With an output schema present, return details are not needed. An agent has sufficient context to decide when and how to call this tool safely.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3. The description adds context for the delete_messages parameter by explaining the cascade behavior and default refusal, but this largely mirrors the schema descriptions. No significant new parameter-specific information is provided beyond what the schema already states.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description explicitly identifies the action ('Delete a mailbox') and the method ('via IMAP'), distinguishes it from AppleScript-based operations, and specifies the resource. This clearly differentiates it from sibling delete tools like delete_messages and delete_account.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides strong context on when to use this tool: when a mailbox needs deletion and AppleScript lacks a working primitive, requiring IMAP. It also mentions prerequisites (IMAP credentials) and default behavioral restrictions (refuses non-empty mailboxes). However, it does not explicitly name alternatives or exclusions, such as saying 'use delete_messages for deleting messages.' The resource distinction is implied but not explicitly stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_messagesADestructiveIdempotent
Delete messages (always moves to the account's Trash mailbox).
Destructive: gated behind user confirmation via MCP elicitation (issue #239), matching delete_rule / delete_mailbox / delete_template.
| Name | Required | Description | Default |
|---|---|---|---|
| account | No | Optional account name (or UUID) the messages live in. Must be provided together with `source_mailbox`. When both are given, the operation is much faster. | |
| permanent | No | Reserved; currently a no-op. Mail.app's AppleScript dictionary exposes no path to permanent-delete that bypasses Trash (issue #111). Passing True emits a DeprecationWarning; messages still go to Trash. Recoverable from the account's Trash mailbox until that mailbox is emptied. | |
| message_ids | Yes | List of message IDs to delete | |
| source_mailbox | No | Optional source mailbox name; see `account`. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations (destructiveHint=true), the description discloses that deletion is gated behind user confirmation via MCP elicitation and always moves messages to Trash rather than permanently deleting them. This is valuable behavioral context that annotations alone do not provide.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two short sentences with the core behavior front-loaded, followed by the important destructive-gating note and a cross-reference to sibling delete tools. Every sentence earns its place, and nothing is redundant.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a destructive tool with an output schema and fully documented parameters, the description covers purpose, Trash behavior, user-confirmation gating, and relation to sibling delete tools. No critical information needed to call the tool correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, and the per-parameter descriptions in the schema already explain account, source_mailbox, message_ids, and the no-op behavior of permanent. The tool description adds no new parameter-level detail, so the baseline score of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: 'Delete messages' with the clarifying behavior 'always moves to the account's Trash mailbox.' This distinguishes it from sibling tools like delete_draft, delete_rule, and delete_mailbox without needing to open schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives clear context that this tool deletes messages and always sends them to Trash, which is enough for an agent to know when to use it. It does not explicitly list exclusions or alternatives, but the tool name and scope make the primary use case unambiguous.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_ruleADestructiveIdempotent
Delete a Mail.app rule by 1-based positional index.
Destructive — requires user confirmation via MCP elicitation before running. Cannot be undone (Mail.app does not version rule history).
| Name | Required | Description | Default |
|---|---|---|---|
| rule_index | Yes | 1-based positional index from list_rules. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Explicitly describes destructive nature and irreversibility, supplementing the destructiveHint annotation with actionable context about user confirmation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two concise sentences with no redundancy; all information is directly relevant.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Covers the essential operational details (index, destruction, confirmation) and does not need to describe output since an output schema is present.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The parameter description explains the indexing scheme and its source (list_rules), adding value beyond the raw schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('Delete') and resource ('Mail.app rule'), clearly distinguishing it from sibling delete tools like delete_account or delete_draft.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Specifies the operand ('1-based positional index') and notes the requirement for user confirmation, making it clear when this tool is appropriate.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_templateADestructiveIdempotent
Delete a template by name.
Destructive — requires user confirmation via MCP elicitation before running.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Template name to delete. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description repeats the destructiveHint=true that the annotations already encode, but it adds the behavioral detail that MCP elicitation confirmation is required before the deletion runs. This informs the agent of an interaction step it must expect at runtime, which goes beyond the structured metadata. Slight ding because the description doesn't state what is destroyed (user templates vs. system templates) or whether deletion is reversible.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences with the verb and object front-loaded, followed by a clearly separated warning paragraph. Just enough whitespace and paragraph structure to separate the operation from the caution. Zero filler words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter tool with an output schema and full annotation coverage, the description is nearly sufficient. The main gap is the oddly-phrased 'via MCP elicitation' — it reads as if a template placeholder was left half-substituted, and it doesn't specify whether the confirmation is a simple yes/no or a multi-step interaction. The agent would want to know if deletion is recoverable before invoking this tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% with a single well-documented parameter, so the baseline of 3 applies. The description echoes the schema's 'by name' language without adding format, case-sensitivity, or wildcard details. No additional value added beyond the schema for the single parameter.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb and resource with a scoping qualifier: 'Delete a template by name.' The 'by name' qualifier clarifies the identifier semantics. This is unambiguous even among deletion-heavy siblings like delete_account, delete_rule, and delete_mailbox, since template deletion is clearly the resource being targeted.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The only extra-tool guidance is the warning that the operation is destructive and requires user confirmation. There is no explicit when-to-use vs. alternatives (e.g., no note to use save_template for edits or get_template for reads). The tool name carries the disambiguation burden, which works here only because the name is descriptive.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
forwardA
Forward an existing message to new recipients.
Convenience wrapper over create_draft with seed=forward.
| Name | Required | Description | Default |
|---|---|---|---|
| to | Yes | List of recipient email addresses. | |
| body | No | Optional intro text prepended above the forwarded content. | |
| send_now | No | True (default) sends immediately; False saves as draft. | |
| forward_of | Yes | Id of the message to forward (numeric Mail.app id or RFC 5322 Message-ID from search_messages/get_messages). | |
| from_account | No | Mail.app account name or UUID. None = Mail default. | |
| seed_mailbox | No | Folder the original lives in (default INBOX). |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The annotations already establish that the tool is not read-only, not idempotent, and not destructive. The description adds a useful implementation trait by identifying this as a wrapper over create_draft with seed=forward, but it does not disclose other behaviors like account/recipient requirements, how the original message is affected, or the actual side effect of sending immediately. This is moderate transparency but not outstanding.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is only two sentences and every sentence is informative: the first identifies the operation, the second sets expectations about how it composers with the existing create_draft API. It avoids fluff and is easy to scan.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Combined with a fully documented schema and an output schema, the description provides enough for the core invoke workflow. The wrapper-creating-draft note is valuable context, but the description remains thin on how to choose between forward and sibling tools with overlapping intent, so it is not fully self-sufficient.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema already documents all six parameters with 100% coverage, so the description carries no additional parameter-specific semantics. The 'seed=forward' note adds context but does not clarify individual parameters beyond what the schema already provides. Baseline 3 is appropriate here.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a clear verb and resource: 'Forward an existing message to new recipients.' It also positions the tool as a convenience wrapper over create_draft with seed=forward, which helps locate it in the API surface. However, it does not explicitly distinguish itself from reply/reply_all or send_email, so it falls just short of full differentiation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The phrase 'Convenience wrapper over create_draft with seed=forward' gives meaningful usage context: this is the forward-specific version of creating a draft, and the schema's send_now option clarifies immediate sending vs. draft-only. Exclusions or explicit alternatives such as 'use reply when preserving original recipients' are not stated, so it only partially explains when to pick this over sibling tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_attachment_contentARead-onlyIdempotent
Read one attachment's content inline, without writing it to disk.
For "triage" workflows where you want to inspect an attachment (a text
file, JSON, a small PDF) before deciding what to do with it — instead of
save_attachments → read the file → clean up.
| Name | Required | Description | Default |
|---|---|---|---|
| account | No | Mail.app account name or UUID. Supply it (with ``mailbox``) to use the faster IMAP path; pass the same value you read the message with so the attachment ordering matches. | |
| mailbox | No | Folder the message lives in (for the IMAP path). | |
| message_id | Yes | Message id, as returned by ``search_messages`` / ``get_messages`` (RFC 5322 Message-ID on the IMAP path, Mail's internal id on the AppleScript path). | |
| attachment_index | Yes | 0-based index into the message's attachments, in the same order ``get_attachments`` / ``get_messages`` (``include_attachments=True``) report them. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover readOnly/idempotent/destructive safety. The description adds the key behavioral trait (inline read, no disk write) and the implicit size caveat ('small PDF'). Does not mention pagination or truncation, but for a read tool this is sufficient.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short paragraphs with zero filler. The core action is front-loaded, and the second paragraph gives a concrete workflow example. Every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present (return values are handled there), plus clear usage guidance and annotations for safety, nothing essential is missing for an agent to correctly invoke the tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema covers all four parameters with detailed descriptions (e.g., attachment ordering, message_id provenance). The description adds no new parameter meaning beyond what's already in the schema, so baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Clearly states it reads one attachment's content inline without writing to disk, distinguishing it from the sibling save_attachments. Specific verb+resource, no ambiguity.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly provides when-to-use (triage workflows) and names the alternative save_attachments with an explicit 'instead of' path, making the decision logic crystal clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_messagesARead-onlyIdempotent
Get full details of one or more messages, with bodies.
Returns a list of message dicts (possibly of length 0 or 1). Pair with
search_messages (metadata-only) and get_thread (thread member
ids) to fetch bodies for specific messages.
| Name | Required | Description | Default |
|---|---|---|---|
| account | No | Mail.app account name. Together with ``mailbox``, activates the IMAP fast path for explicit ids: one round-trip lookup instead of an account×mailbox AppleScript scan (issue #72). Ignored for the ``"SELECTED"`` sentinel (selection is global). | |
| mailbox | No | Folder to look in for the IMAP fast path (e.g. "INBOX"). | |
| message_ids | Yes | List of message ids to fetch. May include the literal token ``"SELECTED"``, which the server resolves at call time to Mail.app's current UI selection (zero-or-more messages). Mixed lists like ``["SELECTED", "12345"]`` are valid. Empty list is a no-op (returns empty result, no error). Missing ids drop out silently (partial-results convention) — the response contains whatever was found. | |
| headers_only | No | Skip body fetch on the IMAP path for explicit ids (default: False). Silently ignored on the AppleScript fallback. | |
| include_content | No | Include message bodies (default: True). | |
| include_attachments | No | Include per-attachment metadata (name, mime_type, size, downloaded) on each message (default: True). Bounded cost — id-list cardinality is typically 1-10. Free on the IMAP fast path; cheap-enough on the AppleScript fallback for typical id counts. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Very thorough. It explains the no-op behavior for empty lists, silent dropping of missing ids (partial-results), the "SELECTED" sentinel resolution, and the IMAP fast-path vs AppleScript fallback tradeoffs. The return type is described as a list of message dicts. The annotations (readOnlyHint=true, destructiveHint=false) reinforce that this is a safe read, and the description even covers optional-parameter side effects like headers_only being ignored on the fallback. This is exactly the kind of behavioral disclosure an agent needs.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is efficient but loses a point for leaning heavily on the schema and a code reference. It states purpose, scope, and relationships to siblings in two sentences. Adjective: compact, though the schema carries much of the behavioral detail. Not verbose enough to be a 5.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Everything an agent needs is present: return format (list of dicts), edge cases (missing ids, empty list, SELECTED sentinel), and fallback behavior. Sibling routing is handled. The output schema understands what comes back. This is complete for a read tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, and the schema descriptions are unusually detailed. The 'account' and 'mailbox' parameters get extra context (IMAP fast path semantics) that is not directly inferable from the schema, which adds real value. However, the description text itself doesn't reiterate parameter details — it relies on the schema doing the heavy lifting. A 4 acknowledges the added value from the IMAP fast-path context in the schema, while not being a 5 since the description doesn't extend parameter semantics beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb ('fetch'), resource ('one or more messages'), and scope ('with bodies'). It also explicitly names the sibling tools it is not — search_messages (metadata-only) and get_thread (thread member ids) — so an agent can distinguish them without opening other schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Names the alternatives to pair with (search_messages and get_thread) and the condition for picking those, which routes the agent. It doesn't state when to use this tool over the IMAP fast path vs fallback, but the parameter docs carry that context, and the pairing tip is the key usage decision.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_templateARead-onlyIdempotent
Read a single template by name.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Template name (alphanumerics, underscore, hyphen; 1-64 chars). |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare the tool as read-only, idempotent, and non-destructive. The description confirms it is a read operation, which is consistent and adds no contradiction. However, it adds no extra behavior context (e.g., behavior on missing template, response format) beyond what annotations and schema imply.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single, concise sentence with no filler. It is front-loaded with the verb and resource, making it instantly scannable and efficient.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-read operation with one parameter fully documented in the schema and an output schema present, the description is adequate. It could mention when to use this over list_templates, but that falls under usage guidelines. The minimal description is sufficient for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema description covers 100% of the only parameter, 'name', including format and length. The description adds nothing beyond 'by name' which is already implied. With high schema coverage, the baseline of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a clear verb ('Read'), a specific resource ('single template'), and a discriminator ('by name'). It easily distinguishes from siblings like list_templates (list all) and save_template/delete_template (write operations). No ambiguity.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives no guidance on when to use this tool versus alternatives. It doesn't mention list_templates for bulk listing or render_template for processing, nor any conditions that would select one over the other. The usage is only implicitly obvious from the name.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_threadARead-onlyIdempotent
Return all messages in the thread containing the given message.
Looks up the anchor message by its id, then reconstructs the
conversation via the connector's tiered IMAP threading dispatch
(Tier 1 X-GM-THRID for Gmail, Tier 3 header-search BFS fallback)
or the AppleScript path. Result rows are sorted by date_received
ascending.
The returned ids can be piped into search_messages(source=[ids])
for filtered metadata or get_messages([ids]) for full bodies.
Known limitation: thread members whose subject was rewritten mid-conversation are missed on the AppleScript fallback path (subject prefilter tradeoff).
| Name | Required | Description | Default |
|---|---|---|---|
| message_id | Yes | Internal id of any message in the thread (from ``search_messages`` or ``get_messages`` results). |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, idempotentHint=true, and destructiveHint=false, so the safety profile is covered. The description adds meaningful behavioral context: the sorting order (date_received ascending), the underlying dispatch mechanism (tiered IMAP threading vs. AppleScript path), and a concrete limitation about missed threads when subject rewrite occurs on the AppleScript fallback. This goes well beyond the annotations and helps the agent anticipate edge cases.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is logically structured: it starts with the primary purpose, then explains the lookup mechanism, provides usage guidance, and ends with a known limitation. It is a bit longer than necessary but each sentence carries useful information. The front-loaded purpose makes the key intent immediately clear. It earns a 4 for effective organization despite slight verbosity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given that an output schema exists to define the return structure, the description does not need to explain return values. It covers the essential behavioral details (sorting, fallback paths, limitation) and provides integration suggestions. While it doesn't mention empty results or error conditions, these are typically not critical for a read-only retrieval tool. Overall, the description is complete enough for an agent to call the tool correctly and interpret the outcome.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The single parameter message_id is fully documented in the schema (100% coverage), including the note that it is an 'Internal id of any message in the thread (from search_messages or get_messages results).' The description does not add any additional meaning beyond the schema; it simply mentions the anchor message without elaborating on format or constraints. Since the schema already covers semantics, a baseline score of 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a precise statement: 'Return all messages in the thread containing the given message.' This specifies a clear verb (return), a distinct resource (thread), and a scoping condition (containing the given message). It is easily distinguishable from siblings like get_messages (which likely fetches specific message IDs) and search_messages (which searches by criteria), so an agent can select it without ambiguity.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit follow-up guidance: 'The returned ids can be piped into search_messages(source=[ids]) for filtered metadata or get_messages([ids]) for full bodies.' This tells the agent what to do with the output, which is valuable. It does not explicitly state when NOT to use this tool versus alternatives, but the threading focus and the pipe suggestions imply the intended use case. The known limitation also helps set expectations.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_accountsARead-onlyIdempotent
List all configured email accounts in Apple Mail.
Returns each account's id (UUID), display name, email addresses, account type, and enabled state. Account ids are stable across name changes; prefer them over names for identifying accounts.
Returns: Dictionary containing the accounts list.
Example: >>> list_accounts() {"success": True, "accounts": [ {"id": "B21B254B-...", "name": "Gmail", "email_addresses": ["me@gmail.com"], "account_type": "imap", "enabled": True}, ... ]}
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, and destructiveHint. The description adds meaningful behavior beyond annotations: it specifies the output fields, the stability of account IDs, and shows an example response. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The purpose is front-loaded and the field enumeration is concise. The 'Returns: Dictionary containing the accounts list.' line is somewhat redundant with the preceding sentence and example, but it does not significantly bloat the description.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter read-only list tool, the description is complete: it states the scope, the returned fields, the stable ID behavior, and the response shape via example. The annotations and available output schema cover the remaining context.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters and an empty input schema, so the description does not need to explain parameter meaning. The description adds relevant context about account IDs, but the baseline for zero parameters is 4.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the specific verb and resource: 'List all configured email accounts in Apple Mail.' It enumerates the returned fields and thereby distinguishes itself from siblings like list_rules and list_mailboxes by resource type.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No explicit when-to-use or when-not-to-use guidance is given relative to sibling tools. It does provide useful guidance for consuming the result ('Account ids are stable... prefer them over names'), but tool-selection context is only implied by the tool name and description.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_mailboxesARead-onlyIdempotent
List all mailboxes for an account.
| Name | Required | Description | Default |
|---|---|---|---|
| account | Yes | Mail.app account display name (e.g., "Gmail", "iCloud") or UUID (from list_accounts). Names are convenient but unstable across renames; UUIDs are stable. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, idempotentHint=true, and destructiveHint=false, covering the safety profile. The description adds only the scope ('all') and no additional behavioral detail, which is acceptable for a simple list operation but adds little beyond the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single, front-loaded sentence with no wasted words. Every word contributes to the meaning, making it appropriately concise for a simple tool.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the low complexity, full schema coverage, existing output schema, and annotations covering safety, the description is complete enough for an agent to invoke the tool correctly. A bit more guidance about prerequisites could push it higher, but nothing critical is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, and the account parameter is well documented with guidance on display names vs UUIDs and stability. The tool description itself does not add parameter detail, but the schema fully handles it, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('List') and resource ('all mailboxes') with a clear scope ('for an account'). It is unambiguous and distinct from sibling tools like list_accounts and the mailbox mutation tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description clearly conveys when the tool is useful: when all mailboxes for a particular account are needed. It doesn't explicitly state exclusions or alternatives, but no direct alternative for listing mailboxes exists among siblings, so the context is sufficiently clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_rulesARead-onlyIdempotent
List all Mail.app rules (read-only).
Returns each rule's display name and enabled state. Rule names are NOT guaranteed unique — Mail allows duplicates — and rules have no stable id via AppleScript. This tool is read-only; mutation (enable/disable, create, delete) is tracked as a separate enhancement.
Returns: Dictionary containing the rules list.
Example: >>> list_rules() {"success": True, "rules": [ {"name": "Junk filter", "enabled": True}, {"name": "News From Apple", "enabled": False}, ... ], "count": 2}
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations, the description adds meaningful behavioral caveats: rule names are not guaranteed unique and rules have no stable id via AppleScript. This is exactly the kind of context an agent needs to avoid assuming identity or uniqueness.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured and front-loaded with the core purpose. The return format and example are useful, and every sentence adds value without unnecessary fluff.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter read-only tool with annotations and an output schema, the description is complete. It covers purpose, return shape, example output, and the key caveats about duplicate names and missing stable IDs.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, so the schema already fully covers parameter semantics. The description adds no parameter details, but none are needed; the example output clarifies what the call returns.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'List all Mail.app rules (read-only).' It clearly distinguishes this from mutation siblings by emphasizing read-only behavior and noting that mutation is tracked separately.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly frames this as the read-only listing tool and states that mutation (enable/disable, create, delete) is a separate enhancement. It does not name the exact sibling tools to use instead, but the context is clear enough for an agent to route correctly.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_templatesARead-onlyIdempotent
List all stored email templates.
Templates live as files at ~/.apple_mail_mcp/templates/.md. Override the location with the APPLE_MAIL_MCP_HOME environment variable.
Returns: Dictionary with each template's name and subject (or null if no subject header is set).
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already provide readOnlyHint=true, idempotentHint=true, and destructiveHint=false. The description adds context beyond that by explaining the template storage location (files in ~/.apple_mail_mcp/templates/) and the environment variable override, as well as the exact return structure (name and subject, with null when no subject header). This enriches the behavioral understanding without contradicting the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is multi-paragraph but every part carries useful information: the purpose, the file storage location, the environment override, and the return format. It is front-loaded with the core purpose. It could be trimmed slightly, but the extra context is valuable and not redundant for a tool with no parameters.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter, read-only, idempotent operation with an output schema present, the description covers everything an agent needs: the action, the storage source, the return shape, and the environment override. There is no missing element that would prevent correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, and the schema defines no properties, so the description cannot add parameter details. However, it does mention the APPLE_MAIL_MCP_HOME environment variable, which influences behavior but is not a tool parameter. With no parameters to document, the description appropriately need not compensate for schema gaps, making the baseline 4 a fair score.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The first line 'List all stored email templates' clearly states the action (list) and resource (email templates), distinguishing it from sibling tools like get_template (single retrieval) or save_template (creation). The additional context about file location reinforces the scope without confusion.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description does not explicitly guide when to use this tool versus alternatives such as get_template or render_template. It relies on the verb 'list all' to imply broad retrieval, but it never states usage conditions, exclusions, or preferred scenarios. Given the numerous template-related siblings, explicit routing would improve clarity.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
render_templateARead-onlyIdempotent
Render a template into ready-to-send subject and body text.
No side effects — caller is responsible for passing the rendered
text to create_draft or update_draft (with send_now=True
when ready to send).
With message_id, the original sender's display name and email,
the original subject, and today's date are auto-populated as
recipient_name, recipient_email, original_subject, and
today. Without message_id, only today is auto-filled.
User-supplied vars always override auto-fills on conflict.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Template name to render. | |
| vars | No | Optional dict of variable overrides / additional values. | |
| message_id | No | Optional source-message id for reply context. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description explicitly says 'No side effects,' reinforcing the readOnlyHint/idempotentHint annotations. It also discloses detailed behavior: message_id auto-populates recipient_name, recipient_email, original_subject, and today, while user-supplied vars override auto-fills. This goes beyond the annotations and gives the agent a clear picture of invocation effects.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is three dense paragraphs with front-loaded purpose, followed by side-effect posture and auto-fill details. Every sentence carries operational weight, with no filler or redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With annotations already covering safety and an output schema present, the description supplies the remaining decision-relevant context: how output should be used downstream, when message_id affects placeholders, and the precedence of vars. An agent has everything needed to call this tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is met. The description adds substantial meaning: message_id is not just 'reply context' but drives specific variable auto-fills, and vars override those auto-fills. This is critical semantics that the schema alone does not convey.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb-resource pair: 'Render a template into ready-to-send subject and body text.' It also distinguishes the tool from sending siblings by explicitly framing the output as text for create_draft/update_draft and noting there are no side effects.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly warns 'No side effects' and instructs the caller to pass rendered text to create_draft or update_draft, including send_now=True for sending. This tells the agent when to use the tool (prepare content) and what it is not responsible for (sending/mutation), distinguishing it from send/reply/forward siblings.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
replyA
Reply to an existing message.
Convenience wrapper over create_draft with seed=reply.
| Name | Required | Description | Default |
|---|---|---|---|
| cc | No | CC recipients (None keeps auto-derived; [] clears). | |
| body | No | Reply body. Empty keeps Mail's auto-quoted original. | |
| reply_to | Yes | Id of the message to reply to (numeric Mail.app id or RFC 5322 Message-ID from search_messages/get_messages). | |
| send_now | No | True (default) sends immediately; False saves as draft. | |
| from_account | No | Mail.app account name or UUID. None = Mail default. | |
| seed_mailbox | No | Folder the original lives in (default INBOX). |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds some behavioral context beyond annotations by revealing that the tool is a wrapper over create_draft with seed=reply, clarifying that it creates drafts/reply flow. Annotations already declare readOnly=false and destructive=false, but the description doesn't go into side effects such as immediate sending or reply-all behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two short sentences with no filler. It front-loads the core action and then gives a functional relationship to create_draft.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With a rich parameter schema and an output schema, the description covers the essential behavior and wraps semantics. It misses an explicit distinction from reply_all and its exact sending/draft behavior, though the schema defaults cover the latter.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3 and the description doesn't need to restate parameter meanings. The 'seed=reply' detail gives useful conceptual context, but it doesn't add meaning beyond the schema for individual parameters.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names the operation with a specific verb and resource ('Reply to an existing message') and reveals its implementation path as a wrapper over create_draft with seed=reply. It does not explicitly differentiate from reply_all or forward, but the 'existing message' target makes the scope reasonably clear.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The phrase 'Convenience wrapper over create_draft with seed=reply' implies a targeted alternative to create_draft, but there is no explicit statement of when to choose this over reply_all or when a fuller create_draft call is preferable. Usage guidance is present only by implication.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
reply_allA
Reply to all recipients of an existing message.
Convenience wrapper over create_draft with seed=reply and reply_all=True.
| Name | Required | Description | Default |
|---|---|---|---|
| body | No | Reply body. Empty keeps Mail's auto-quoted original. | |
| reply_to | Yes | Id of the message to reply to (numeric Mail.app id or RFC 5322 Message-ID from search_messages/get_messages). | |
| send_now | No | True (default) sends immediately; False saves as draft. | |
| from_account | No | Mail.app account name or UUID. None = Mail default. | |
| seed_mailbox | No | Folder the original lives in (default INBOX). |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description does not contradict the annotations (readOnlyHint=false, destructiveHint=false). It adds a note about being a convenience wrapper, which implies the same behavior as create_draft but with predefined seed and reply_all flags. However, it does not detail side effects (e.g., sending an email is irreversible) or any requirements beyond what the schema covers. With minimal annotation support, the description provides only modest behavioral transparency.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences, front-loaded with the core purpose and the implementation note. No filler or repetition; highly scannable for an agent.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool is a simple wrapper with fully documented parameters in the schema. The description gives the essential intent and relationship to create_draft gradients, which is sufficient for safe use, though it does not mention potential side effects like sending delays or permission requirements, but those are not necessary for a simple send operation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema covers 100% of parameters with clear descriptions (e.g., 'Reply body. Auto-quotes original if empty', 'send immediately vs. save as draft'). The description itself does not add additional meaning beyond the schema, so it sits at the baseline of 3.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's function: 'Reply to all recipients of an existing message.' This is a specific verb+resource pairing. It also distinguishes itself from siblings by explicitly naming it a wrapper over create_draft with reply_all=True, indicating a specialized reply-all behavior.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description frames the tool as a 'Convenience wrapper over create_draft', implying that for more control one should use create_draft directly. This gives clear context on when to use this tool versus the more generic alternative, though it does not explicitly state exclusions or when NOT to use it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
save_attachmentsBIdempotent
Save attachments from a message to a directory.
| Name | Required | Description | Default |
|---|---|---|---|
| account | No | Mail.app account name or UUID. Supply it (with ``mailbox``) to take the faster IMAP path — one fetch instead of an account×mailbox AppleScript scan. Pass the same values you read the message with so attachment ordering matches (#371). Strongly recommended on Gmail, where the AppleScript fallback's unindexed cross-scan can take minutes and time out. | |
| mailbox | No | Folder the message lives in (e.g. "INBOX"), used with ``account`` for the IMAP fast path. | |
| message_id | Yes | Message ID from search results | |
| save_directory | Yes | Directory path to save attachments to | |
| attachment_indices | No | Specific attachment indices to save (0-based), None for all |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare idempotentHint=true, destructiveHint=false, and readOnlyHint=false. The description adds no behavioral context beyond the action itself—no mention of overwrite behavior, directory creation, or what happens when no attachments exist. It does not contradict annotations, but it also does not enrich them.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single, front-loaded, grammatically complete sentence: 'Save attachments from a message to a directory.' There is no filler or redundancy, and the core action is immediately clear.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the strong schema and output schema, the invocation details are well covered. However, the description lacks routing guidance against get_attachment_content and any edge-case expectations, such as handling existing files or missing attachments. It is adequate but not complete for an agent choosing between related tools.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, and the schema itself richly documents all parameters, including the IMAP fast-path behavior for account/mailbox and the 0-based attachment_indices. The description adds no parameter-level meaning beyond implicitly indicating a source message and target directory.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('save') with a clear resource ('attachments from a message') and destination ('to a directory'). This clearly distinguishes it from siblings like get_attachment_content, which implies retrieval rather than writing to disk.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives no guidance about when to use this tool versus alternatives. It does not mention get_attachment_content as the option for retrieving attachment data, nor does it state conditions such as preferring file output over in-memory content. The agent must infer usage solely from the sentence.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
save_templateAIdempotent
Create or overwrite a template.
| Name | Required | Description | Default |
|---|---|---|---|
| body | Yes | Template body text. May contain {placeholder} tokens. | |
| name | Yes | Template name (alphanumerics, underscore, hyphen; 1-64 chars). | |
| subject | No | Optional subject template. May also contain placeholders. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description discloses the key behavioral trait of overwriting an existing template, which goes beyond the annotations. This aligns with idempotentHint=true and destructiveHint=false, framing the tool as a safe upsert rather than a one-time mutation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is five words with zero filler, and the action verb is front-loaded. It is appropriately sized for a simple write tool and every word earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the full schema coverage, existing output schema, and clear annotations, 'Create or overwrite a template' is complete for an agent to invoke the tool correctly. The only non-obvious nuance, upsert behavior, is explicitly stated.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents all three parameters, including placeholder tokens, name constraints, and the optional subject. The description itself adds no parameter-level meaning, but the baseline of 3 applies because the schema carries the full burden.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states the exact operation ('Create or overwrite') and resource ('a template'), making the tool's purpose immediately clear. It also distinguishes it from sibling template tools (list_templates, get_template, delete_template, render_template) by naming the write/upsert behavior.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies the clear use case: call this tool when you need to create a new template or replace an existing one by name. It doesn't explicitly exclude alternatives, but the resource-specific verb 'save' plus 'create or overwrite' provides enough context to select it over the read/delete/render siblings.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_messagesARead-onlyIdempotent
Search for messages matching criteria. Returns metadata-only rows.
Two corpus modes:
source=None(default): search the given account/mailbox using the IMAP/AppleScript SEARCH path.accountis required.source=[id1, id2, ...]: scope the search to the specific messages identified by the given ids.account/mailboxare ignored; the connector resolves each id self-sufficiently. The resulting message dicts are post-filtered by the other criteria (sender_contains,read_status, etc.) — full filter composition. The literal token"SELECTED"may appear in the list and is server-resolved at call time to Mail.app's current UI selection (zero-or-more messages). Mixed lists like["SELECTED", "12345"]are valid. Missing ids drop out silently (partial-results).
For thread retrieval, call get_thread(message_id) to expand an
anchor into thread member ids, then optionally pipe those ids into
source=[ids] for filtered metadata browsing or into
get_messages([ids]) for full bodies.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum results to return (default: 50). | |
| source | No | Optional list of message ids (with optional ``"SELECTED"`` sentinel) to restrict the search to. ``None`` (default) searches the account/mailbox normally. | |
| account | No | Mail.app account display name (e.g., "Gmail", "iCloud") or UUID (from list_accounts). Required when ``source is None``; ignored when ``source`` is a list. Names are convenient but unstable across renames; UUIDs are stable. | |
| date_to | No | Inclusive upper bound on date received (full day included). ISO 8601 YYYY-MM-DD. | |
| mailbox | No | Mailbox name. Defaults to the account's real receiving mailbox, which is resolved by asking Mail instead of assuming it is called "INBOX" (it is not, on some accounts). Ignored when ``source`` is a list. | |
| date_from | No | Inclusive lower bound on date received. ISO 8601 YYYY-MM-DD. | |
| is_flagged | No | Filter by flagged status (true=flagged, false=not flagged). | |
| read_status | No | Filter by read status (true=read, false=unread). | |
| body_contains | No | Substring match against message body content. IMAP uses ``BODY`` predicate (sub-second); AppleScript reads ``content of msg`` per candidate (very slow on large mailboxes — measured 148s for 100 cold-cache messages). When the call commits to AppleScript with this filter set, a ``warnings`` field is included in the response. Case-insensitive on both paths. | |
| text_contains | No | Substring match against headers + body (RFC 3501 ``TEXT`` semantics). On AppleScript, approximated as ``content + subject + sender`` (recipients and other headers not matched). Same perf characteristics as ``body_contains``. | |
| has_attachment | No | Filter messages with (true) or without (false) attachments. | |
| sender_contains | No | Filter by sender email/domain substring. | |
| subject_contains | No | Filter by subject keywords substring. | |
| include_attachments | No | When True, each row includes an ``attachments`` field listing per-attachment metadata (name, mime_type, size, downloaded). Default False — opt-in because the AppleScript fallback path can be slow on cold caches (#142). Free on the IMAP fast path. To fetch attachment metadata for a known list of ids cheaply, prefer ``get_messages([ids])`` (default-on attachments, bounded cardinality). | |
| received_within_hours | No | Relative-time filter. When set, only return messages received within the last N hours (hour precision). Composes with ``date_from`` / ``date_to`` — the most restrictive filter wins. Must be a positive int. Days = 24, weeks = 168, etc. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The annotations already declare readOnlyHint=true, idempotentHint=true, and destructiveHint=false, so the safety profile is clear. The description adds valuable behavioral context such as the 'SELECTED' sentinel behavior, silent dropping of missing IDs with partial results, and the differences in behavior between IMAP and AppleScript paths. Some performance details are implementation-specific and could become stale quickly.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with clear sections and code formatting for the two modes. The performance notes (e.g., 'measured 148s for 100 cold-cache messages') and inline details like issue references (#142) add credibility but slightly extend length. Every sentence adds value, though some performance specifics could be condensed without loss of meaning.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (15 params, two modes, interactions with sibling tools like get_thread and get_messages), the description is remarkably complete. It covers mode selection, filter composition, performance trade-offs, and even error behavior like silently vanishing IDs. The presence of an output schema (though not shown in full) further supports the agent in understanding return types.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, and the description enhances the parameter semantics significantly by explaining the 'SELECTED' sentinel, performance implications (e.g., body_contains being slow on AppleScript), free vs. costly operations (include_attachments), and composition semantics like 'most restrictive filter wins'. This goes beyond what the schema alone provides.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool searches messages and returns metadata-only rows, with a specific verb and resource. It goes further to distinguish two distinct modes (default mailbox search vs. scoped by message IDs), which is specific and actionable. It also differentiates from siblings like get_thread and get_messages by explaining when to use each for thread retrieval.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly outlines when to use the tool versus alternatives, stating 'For thread retrieval, call get_thread(...) to expand an anchor into thread member ids, then optionally pipe those ids into source=[ids] or get_messages([ids])'. It also explains conditions like when account is required (source is None) versus ignored (source is a list), and notes post-filtering behavior.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
send_emailA
Send a new email immediately.
Convenience wrapper over create_draft with send_now=True.
| Name | Required | Description | Default |
|---|---|---|---|
| cc | No | CC recipients. | |
| to | Yes | List of recipient email addresses. | |
| bcc | No | BCC recipients. | |
| body | No | Plain text body. | |
| subject | Yes | Email subject. | |
| from_account | No | Mail.app account name or UUID. None = Mail default. | |
| attachment_paths | No | List of local file paths to attach. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description accurately describes the side effect (sending an email) without contradictions. Annotations already indicate it's a write operation and not idempotent, so the description doesn't need to restate that. It adds no misleading information.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is exceptionally concise: two short sentences that convey the essence of the tool. It includes the primary action, the immediacy, and the relationship to create_draft without any superfluous words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description is complete for the tool's purpose. It clearly states what the tool does and its relationship to a sibling tool. Since an output schema exists, return values need not be explained. No critical information is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The description does not elaborate on any parameters, but the input schema provides full descriptions for all seven parameters with 100% coverage. Per the rubric, a high coverage warrants a baseline score of 3, and the description adds no additional meaning.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the action: 'Send a new email immediately.' It also distinguishes itself from create_draft by explicitly noting it is a convenience wrapper with send_now=True, which removes ambiguity about its purpose compared to related tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies when to use it vs. create_draft by presenting it as a convenience wrapper that sends immediately, but it doesn't explicitly contrast with other alternatives like reply or forward. It provides enough context for an agent to infer the appropriate use case.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_draftADestructiveIdempotent
Update an existing draft. Implemented as delete-and-recreate.
Returns a NEW draft_id — Mail.app forbids mutating saved drafts, so update is implemented by reading the draft's current state, deleting it, and creating a new draft with the merged fields. Threading headers (for reply seeds) and forward anchor are preserved via persisted seed metadata.
Field merge semantics: any non-None argument overrides the existing
value. None keeps the existing value. attachment_paths=None
PRESERVES existing attachments (extracted via Mail's save
command); [] explicitly clears them; a list replaces.
For drafts created externally (not via create_draft), seed
recovery falls back to scanning Mail.app for the In-Reply-To header
— this can be slow on large mailboxes (~30s+ per call). Forward
seeds without disk state are misclassified as fresh; pass an
explicit body if so.
| Name | Required | Description | Default |
|---|---|---|---|
| cc | No | ||
| to | No | ||
| bcc | No | ||
| body | No | Override body. None keeps existing. Non-None replaces (including the empty string, which clears). | |
| subject | No | Override subject. None keeps existing. | |
| draft_id | Yes | Mail.app id of the existing draft. | |
| send_now | No | ``False`` (default) saves new draft. ``True`` sends after eliciting confirmation. | |
| body_html | No | Optional HTML body for the recreated draft (see ``create_draft``). Requires IMAP credentials and is limited to drafts whose seed is a fresh draft (not reply/forward) and to ``send_now=False``. NOTE: because the draft is recreated and draft state captures only plain text, an existing HTML draft is NOT preserved across an update unless ``body_html`` is passed again. (#251) | |
| from_account | No | Override sender. | |
| template_name | No | ||
| template_vars | No | ||
| attachment_paths | No | Override attachments. None preserves existing via temp-dir extraction; [] clears; list replaces. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description goes far beyond the annotations by revealing the delete-and-recreate implementation, the return of a NEW draft_id, preservation of threading metadata, attachment merge semantics, and performance caveats. Annotations only flag idempotent/destructive/read-only status; the description provides the operational detail needed to avoid surprising side effects. No contradiction with annotations exists.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is longer than average but each sentence carries essential information about behavior, edge cases, and performance. The delete-and-recreate fact is front-loaded, and bolded sections for return value and merge semantics aid scanning. Some verbosity around seed recovery could be trimmed, but it is all relevant to correct invocation.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given 12 parameters, destructive semantics, and complexity, the description is remarkably complete. It covers return value, field merge rules, attachment handling, body_html caveats, external-draft performance, and a forward-seed misclassification warning. The only minor gap is template behavior, but that is likely documented in create_draft, which is referenced. An agent has enough context to invoke safely.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With 58% schema description coverage, the description adds valuable global merge semantics: any non-None argument overrides, None preserves, and attachment_paths has special behavior for None, [], and a list. It also clarifies the non-obvious body_html limitation (must be re-passed or lost). This compensates well for parameters like template_name and template_vars that the schema leaves undocumented.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly identifies the operation as updating an existing draft, and immediately adds the crucial implementation detail that it is delete-and-recreate. This distinguishes it from create_draft and delete_draft, though the name alone could mislead until the description is read. The verb+resource+behavior is specific enough for an agent to know what the tool does.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explains when to use this tool: when a saved draft needs mutation, since Mail.app forbids direct editing. It gives guidance for externally-created drafts, warning about slow In-Reply-To scanning and recommending an explicit body for forward seeds. It does not explicitly name sibling alternatives, but the context makes the appropriate usage clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_mailboxADestructiveIdempotent
Rename and/or re-parent (move) an existing mailbox.
Two delivery paths:
Rename only (
new_nameset,new_parentisNone): AppleScript. Fast, no IMAP credentials needed.Move (
new_parentset; optionally combined with rename): IMAP RENAME. Requires IMAP credentials in Keychain (#73 opt-in flow) — returnserror_type: "imap_required"when missing.
At least one of new_name / new_parent must be provided.
Refused (#164): operations targeting the bare [Gmail] parent or
any [Gmail]/... child path return error_type: "unsupported_gmail_system_label". Applies to both the source
name and the resulting destination (new_parent join). Gmail's
IMAP server doesn't support normal RENAME semantics for these paths;
user-created Gmail labels (Newsletters, etc.) behave normally.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Current mailbox name. Slash-separated for nested mailboxes (e.g. ``"Archive/2024"``). | |
| account | Yes | Mail.app account display name or UUID. | |
| new_name | No | Replacement leaf name. ``None`` to keep the current leaf when moving. Path-traversal characters stripped via ``sanitize_mailbox_name``; an entirely-stripped value returns ``validation_error``. | |
| new_parent | No | Destination parent path. ``None`` keeps current parent (rename-only). ``""`` (empty string) moves to top-level. Non-empty string moves under that path. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=false, destructiveHint=true, and idempotentHint=true, and the description does not contradict them — the mutation framing is consistent. Beyond the annotations, it adds genuinely useful behavioral detail: the two execution mechanisms (AppleScript vs IMAP RENAME), the imap_required error for missing credentials, and the unsupported_gmail_system_label refusal with its rationale. This is valuable context that structured fields cannot express.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the purpose statement and uses headers, bullets, and inline code formatting to keep distinct behaviors scannable. It is longer than average, but every section maps to a concrete operational concern (delivery paths, validation, refusal conditions) so no sentence is wasted. Formatting trades some brevity for readability and earns its length.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with 4 parameters, two execution paths, and multiple edge cases, the description is remarkably complete. It covers prerequisites (IMAP Keychain credentials), the validation rule, both error_type outcomes, and the Gmail system-label distinction between refused and allowed paths. An output schema exists, so return-value prose is appropriately omitted; nothing needed to call this correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so per calibration the baseline is 3. The schema already thoroughly documents each parameter (leaf-name sanitization, None semantics, empty-string top-level move). The description adds value by explaining the interaction between new_name and new_parent — how their combination selects the delivery path — which goes slightly beyond the schema, but not enough to exceed baseline.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb-plus-resource statement — 'Rename and/or re-parent (move) an existing mailbox' — that clearly differentiates this from its siblings create_mailbox, delete_mailbox, and list_mailboxes. The two delivery paths further specify the operation's scope without ambiguity.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives strong conditional guidance: which path executes depending on whether new_name vs new_parent is set, when IMAP credentials are needed, and the validation requirement that at least one parameter be provided. It enumerates the two refusal conditions and their trigger criteria. It stops short of explicitly naming alternative tools or when-not-to-use scenarios, but the path selection logic is thorough enough that an agent can route correctly.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_messageADestructiveIdempotent
Update one or more messages: change read state, flag, and/or move, in one atomic call (#135).
Patch semantics — caller specifies only the fields to change. All
specified mutations apply in a single AppleScript pass via the
bulk-update helper. Replaces the previous mark_as_read,
move_messages, and flag_message tools.
Order of operations (matters for IMAP): read-state and flag changes apply first (in source mailbox), then the move. IMAP requires the message to exist in the source folder for STORE before MOVE.
| Name | Required | Description | Default |
|---|---|---|---|
| account | No | Account name or UUID hosting the destination mailbox. Required when `destination_mailbox` is set; also used with `source_mailbox` for narrow-path optimization. | |
| flagged | No | True to flag (default red if no `flag_color` set), False to clear the flag, None to leave unchanged. | |
| flag_color | No | Color name (orange, red, yellow, blue, green, purple, gray, none). Implies `flagged=True` unless "none". Validated against the existing flag-color schema. | |
| gmail_mode | No | **Deprecated and ignored (#364).** Previously selected a copy+delete strategy that silently routed Gmail moves through Trash and lost the message. The move strategy is now chosen automatically (IMAP relabel when configured; otherwise a verified AppleScript move). A Gmail label move that can't be confirmed returns `error_type: "imap_required"` — configure IMAP with `apple-mail-mcp setup-imap --account <name>`. Slated for removal at v1.0. | |
| message_ids | Yes | List of message IDs to update. | |
| read_status | No | True to mark as read, False to mark as unread, None to leave unchanged. | |
| source_mailbox | No | Source mailbox name. With `account`, narrows the AppleScript scan to one mailbox (O(N) instead of cross-scan). Required for reliable Gmail moves (the move is verified against the source). | |
| destination_mailbox | No | Move messages here (requires `account`). |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover destructiveHint and idempotentHint, so the description adds supplementary operational detail: atomicity, patch semantics (only specified fields change), order of operations (read/flag before move), and the IMAP requirement for STORE before MOVE. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, each dense with distinct information: primary action, patch semantics, and ordering/IMAP caveat. No filler, front-loaded with the core purpose, and easy to scan.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the output schema exists (covering return value), the description addresses the key behavioral aspects: atomicity, patch semantics, ordering for IMAP, and the replacement of old tools. Combined with the highly detailed schema and annotations, an agent has everything needed to invoke the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema covers all 8 parameters at 100%, so the baseline is 3. The description goes beyond the schema by clarifying that specified fields are treated as patch updates (None leaves unchanged, implicitly) and that move operations depend on source/destination mailbox ordering. This adds meaningful context for parameter usage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (update), resource (messages), and enumerates the three action types (read state, flag, move). Explicitly differentiates from its predecessors mark_as_read, move_messages, and flag_message, and by its name stands apart from delete_messages. An agent can immediately know what it does and how it differs.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Clearly tells the agent to use this in place of the three older tools, and explains the patch semantics and atomic call. It also specifies the ordering constraint for IMAP. It does not explicitly call out exclusions like 'use delete_messages for deletion', but the verb distinction and naming make that inference straightforward.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_ruleADestructiveIdempotent
Update an existing Mail.app rule (patch semantics).
Patch semantics: only fields you provide are changed. conditions and
actions, when provided, REPLACE their respective structures wholesale
(not merged).
Conditional confirmation: prompts the user via MCP elicitation when the
patch touches conditions or match_logic (which alter matching
scope), or replaces actions with a set that includes a dangerous
action (move / forward / delete / copy). An actions patch limited to
organizational flags (mark_read / mark_flagged / flag_color)
skips the prompt, as do patches limited to enabled and/or name
(trivially reversible). The enable/disable path replaces the removed
set_rule_enabled tool: call update_rule(rule_index, enabled=True|False).
Refuses to update any rule whose existing actions include something outside the supported schema (run-AppleScript, redirect, reply text, play sound, custom highlight color); raises MailUnsupportedRuleActionError. Edit such rules in Mail.app's UI.
| Name | Required | Description | Default |
|---|---|---|---|
| name | No | New name (only set if not None). | |
| actions | No | If provided, REPLACES all action flags wholesale. | |
| enabled | No | New enabled state (only set if not None). | |
| conditions | No | If provided, REPLACES all existing conditions. | |
| rule_index | Yes | 1-based positional index from list_rules. | |
| match_logic | No | 'all' or 'any', only set if not None. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already flag this as destructive and idempotent, but the description goes far beyond: it spells out replacement-not-merge semantics for conditions/actions, the conditional confirmation trigger (touching conditions/actions or moving/forwarding/deleting), which paths skip confirmation (flags, name/enabled), and the refusal behavior for unsupported actions. This is exactly the behavioral detail an agent needs before calling a destructive mutation, and it exceeds what the annotations convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but every sentence earns its place — patch semantics, confirmation triggers, refusal conditions, and the migration note all carry unique information. The section markers ('Patch semantics:', 'Conditional confirmation:') aid parsing, though a stricter use of line breaks or bullets would improve scanability. It's dense but not padded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description fully covers mutation semantics (patch vs replace), conditions for user confirmation, refusal cases, the replacement of the removed enable/disable tool, and correct usage of rule_index. With an output schema presumably present)Skip nothing needed about return values. Given the complexity of a patch tool with safety gates Submission rules, this is complete enough that I can't identify a behavior the agent would have to guess.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is essentially 100% (every param has a description), but the description adds the decisive semantics: `conditions`/`actions` REPLACE wholesale rather than merge, `rule_index` is 1-based from a specific prior call (list_rules), and the `enabled` path replaces a removed tool call. These semantics are not inferable from the schema field names alone and are exactly what prevents a wrong call.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Lead sentence states the verb (update), object (rule), and the patch model immediately: 'Update an existing Mail.app rule (patch semantics)'. The first paragraph unambiguously defines what is changed and how, and the closing sentence adds the boundary case (refusal for unsupported actions). No ambiguity about what the tool does.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Exceptional. It specifies when confirmation is prompted (matching-scope fields and dangerous actions), when it is skipped (organizational flags, enabled/name), and explicitly tells the agent that the enable/disable path replaces the removed `set_rule_enabled` tool with a concrete call pattern: `update_rule(rule_index, enabled=True|False)`. It also states the refusal condition and the fallback action (edit in UI). An agent has everything needed to decide when to call it safely.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
29 tool updates
v0.10.2- First observed
create_draft - First observed
create_mailbox - First observed
create_rule - First observed
delete_account - First observed
delete_draft - First observed
delete_mailbox - First observed
delete_messages - First observed
delete_rule - First observed
delete_template - First observed
forward - First observed
get_attachment_content - First observed
get_messages - First observed
get_template - First observed
get_thread - First observed
list_accounts - First observed
list_mailboxes - First observed
list_rules - First observed
list_templates - First observed
render_template - First observed
reply - First observed
reply_all - First observed
save_attachments - First observed
save_template - First observed
search_messages - First observed
send_email - First observed
update_draft - First observed
update_mailbox - First observed
update_message - First observed
update_rule
TDQS
Scored across 29 tools
Tools are mostly organized by resource with clear action prefixes, so accounts, rules, mailboxes, messages, templates, and drafts are easy to separate. The main overlap is the send path: send_email, reply, reply_all, and forward are all described as convenience wrappers over create_draft, which creates some selection ambiguity for outgoing-mail tasks.
Nearly all tools follow a consistent verb_noun snake_case pattern such as list_accounts, create_rule, delete_mailbox, and render_template. The exceptions are reply, reply_all, and forward, which are bare verbs without an explicit object and break the otherwise predictable rhythm.
At 29 tools, the surface is well beyond the typical well-scoped range and feels heavy for agents to navigate. Several outgoing-mail tools are redundant wrappers around create_draft, so the count could be meaningfully reduced without losing functionality.
Core workflows are broadly covered: accounts, rules, mailboxes, messages, threads, attachments, templates, and outgoing mail all have lifecycle support. Minor gaps remain, such as no account creation/update and list_rules not exposing rule indices needed by update_rule/delete_rule, but the overall surface supports realistic mail workflows.
Related MCP Connectors
Connect any mailbox to Claude, ChatGPT & AI: read, send, reply, schedule & search emails.
Let ChatGPT, Claude & Cursor use your Mac: email, calendar, iMessage, Teams, files. Local, free.
Email infrastructure for AI agents — send, receive, search, and reply to email over MCP.
Stateful email for AI agents — read inboxes, reply in-thread, draft with approval.
Related MCP Servers
- AlicenseAqualityBmaintenanceEnables AI assistants to interact with Apple Mail through natural language, providing comprehensive email management including reading, searching, composing, organizing, and analyzing emails across all configured accounts. Includes an expert skill system that teaches intelligent email workflows and productivity strategies.26202MIT
- AlicenseAqualityAmaintenanceEnables AI assistants to read, send, search, and manage emails in Apple Mail on macOS.25102MIT
- AlicenseNot gradedqualityAmaintenanceEnables using Apple Mail accounts to search, read, manage, draft, and send messages from Codex or Claude Code locally.MIT
- AlicenseBqualityBmaintenanceAn MCP server that gives AI assistants full access to Apple Mail -- read, search, compose, organize, and analyze emails via natural language.38MIT