apple-mail-mcp
A read-only macOS Apple Mail MCP server for browsing mailboxes and searching/reading local Mail data.
List visible accounts and mailboxes with roles and total/unread message counts.
Search messages newest-first by sender, recipient, subject, free text, mailbox, date range, unread/flagged state, attachment presence, Mail category, and optional body text; results are paginated summaries without bodies.
Get one message with headers, recipients, attachment names, and plain-text body, with optional quoted-reply removal and character limit.
Get a conversation/thread as message summaries oldest-first, without bodies.
Return
message://deep links where macOS supports them.Withhold/redact detected security mails by default.
Optionally use a local full-text body index for faster searches across all message bodies.
It cannot send, reply, draft, move, delete, flag, read attachment contents, or modify mail.
Provides read-only access to Apple Mail, allowing search and retrieval of mailboxes, messages, and threads from Mail.app's local index on macOS.
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-mcpFind the mail from my landlord about the heating bill"
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
A read-only MCP server that lets an AI assistant search and read the mail in Apple Mail.
macOS only. It reads the local index that Mail.app keeps on your Mac.
claude mcp add apple-mail -- npx -y @redpop/apple-mail-mcpThen grant Full Disk Access once with npx -y @redpop/apple-mail-mcp setup. For Claude
Desktop there is a one-click bundle: see Install.
What it does
The main use is "find the mail about X":
"Find the mail from the landlord about the heating bill"
"What did Anna send me last week with an attachment?"
"Show the whole thread about the contract renewal"Four tools: list_mailboxes, search_messages, get_message, get_thread. Search covers
sender, recipient, subject, free text, mailbox, date range, unread, flagged and attachments,
and can also look inside message bodies. Results carry a message:// link that opens the
mail in Mail.app, where Mail stores the Message-ID (not on macOS 15). Details: docs/tools.md.
Related MCP server: Apple Mail Summary MCP
Why this one
Fast. It queries Mail's SQLite index instead of scripting Mail.app with AppleScript. On one real mailbox with about 240,000 message rows, a page of search results takes 4 to 90 ms in the helper, a subject search 25 ms after the first one; see
docs/architecture.mdfor the measurements and their limits.Full Disk Access goes to a small helper, not to your terminal or your AI client. The server reaches Mail's files through a small app, "Mail MCP Helper", and macOS binds the permission to that app alone. Node, Terminal and Claude never get it.
Read-only by construction, not by a flag. There is no code that sends, drafts, moves, deletes or flags mail, none that opens the database for writing, and none that opens a network connection.
Mail.app does not have to be running and no Automation prompt appears. Your data is only as fresh as Mail's last sync, though, and a message that Mail never downloaded has no body.
Threads and deep links.
get_threadreturns a conversation; messages carry amessage://link (not on macOS 15).Security mails are withheld by default. Messages that look like verification codes, password resets or sign-in links are shown without their body, with digits in the subject masked, and cannot be found by body search. Detection is best effort, not a guarantee.
Settings come from configuration, never from tool parameters, so a malicious mail cannot ask the model to switch them off.
Works with apple-calendar-mcp: "find the mail about Thursday's meeting". Same conventions: timestamps in ISO 8601 with offset, and errors as an
isErrorresult with one plain-English sentence per error code.
Requirements
macOS 15 or later. Verified with a real mailbox only on macOS 27.0.1 with Mail 16.0. The database layout of macOS 15.8 and 26.6 passes the helper's column check, taken from runners without a mail account. On macOS 15 Mail stores no Message-ID header, so results there carry no
message://link. Support depends on the layout of Mail's database, not on the version number:doctorchecks it. Reports from other versions are welcome, see Reporting problems.Node.js 22.16 or newer in the 22 line, or 24 and newer.
Mail set up with at least one account.
Exchange accounts are untested. Mailboxes and message metadata are listed, but bodies of Exchange mail are not read yet:
get_messagereportsfile_not_found. Seedocs/exchange.md.
Security model in short
A mailbox holds secrets: codes, reset links, contracts. Read this before connecting it to a model that can also reach the internet.
The helper holds Full Disk Access and runs only fixed commands. It has no free-form SQL and no path parameter, and it never parses mail content. Untrusted MIME is parsed in Node, which has no special permission.
Mail text is untrusted input. The server tells the model so, but cannot force it.
If your client also has a web or send tool, a malicious mail could try to make the model leak what it read. Use confirmations for those tools, or keep this server in a client without them.
Any program running as your user can start the helper and read your mail with it. This is less than giving Full Disk Access to Terminal, but it is not nothing.
The optional body index is a plain-text copy of your mail that any program of your user can read, without Full Disk Access. It is off by default; see Body index.
The full threat model and both residual risks: SECURITY.md.
Install
Claude Code and Cowork, as a plugin (settings in a dialog, updates with each release):
/plugin install apple-mail-mcp --marketplace redpop/apple-mail-mcpThe plugin runs the npm package below at the plugin's version. Grant Full Disk Access once with
npx -y @redpop/apple-mail-mcp setup.
Claude Code without the plugin (or any client that starts a command):
claude mcp add apple-mail -- npx -y @redpop/apple-mail-mcp
npx -y @redpop/apple-mail-mcp setupsetup walks you through the Full Disk Access grant. Settings are
environment variables, see Configuration.
Claude Desktop: download apple-mail-mcp-<version>.mcpb from the
latest release and open it. Claude
Desktop installs the extension and shows its settings page; the settings there are the same as
the environment variables. The first tool call without access opens System Settings and the
Finder window for the grant.
Each release lists SHA256SUMS and carries build provenance attestations for the bundle and
the helper zip.
Install from source
For development, or to run unreleased changes. You need the Xcode Command Line Tools (for Swift) and an Apple Development certificate in your keychain for a build that keeps its permission across rebuilds; without one the build is ad hoc and gets its own, weaker identity.
git clone https://github.com/redpop/apple-mail-mcp.git
cd apple-mail-mcp
npm ci
npm run build # TypeScript -> dist/
npm run build:helper # Swift helper -> bin/Mail MCP Helper (Dev).appThen give the helper Full Disk Access (next section) and register the server:
claude mcp add apple-mail \
-e APPLE_MAIL_HELPER_PATH="$PWD/bin/Mail MCP Helper (Dev).app" \
-- node "$PWD/dist/index.js"APPLE_MAIL_HELPER_PATH points the server at your local build. Without it the server looks
for the release bundle Mail MCP Helper.app inside the package, which only the release
pipeline produces. A local build is named "Mail MCP Helper (Dev)" (Apple Development
signature) or "Mail MCP Helper (Ad hoc)" (no certificate). Both use their own bundle id, so
they never disturb the permission of the release app.
The build script never signs with a Developer ID certificate outside CI.
Grant Full Disk Access
macOS protects ~/Library/Mail and offers no dialog to ask for access. You grant it once, to
the helper app:
Run
setup(from source:APPLE_MAIL_HELPER_PATH="$PWD/bin/Mail MCP Helper (Dev).app" node dist/index.js setup; from npm:npx -y @redpop/apple-mail-mcp setup). It opens System Settings at Privacy & Security > Full Disk Access, shows the helper in Finder, and waits until access works.Drag Mail MCP Helper from the Finder window into the list and switch it on. (For a local build the name is "Mail MCP Helper (Dev)".) Alternatively click +, press Cmd-Shift-G and paste the path that
setupprints.setupreports success and prints the command to add the server to Claude Code.
Without a terminal (for example with the MCPB bundle) the first tool call that is denied opens System Settings and the Finder window by itself, once per server process, and the error message lists the same steps.
Notes:
The permission belongs to the app's bundle id, not to its location. You can move the package or update it without granting again.
If "Mail MCP Helper" is listed and switched on but access is still denied, the entry is stale. Remove it with the minus button and add it again.
No restart is needed after granting.
More cases: docs/troubleshooting.md.
Configuration
Set these in the environment of the server, for example with claude mcp add -e NAME=value.
They cannot be changed through tool parameters. Booleans accept true, false, 1, 0.
An empty value counts as not set. An invalid value does not stop the server; every tool call
then fails with CONFIG_INVALID naming the setting, and nothing falls back to "allow all".
The server reads these settings once, at startup. After changing them, restart the client. In Claude Desktop, quit the app (Cmd-Q) and open it again: switching the extension off and on, or saving its settings, keeps the running server and its old settings.
Variable | Default | Meaning |
| empty (all accounts) | Comma-separated allowlist. An entry matches an account's UUID, address or name, ignoring case. A name shared by two accounts matches both; use the address or UUID to pick one. |
|
| Include mailboxes with the junk role and their subfolders. |
|
| Include mailboxes with the trash role and their subfolders. |
| empty | Comma-separated mailboxes to hide. An entry matches a mailbox name (exact, ignoring case) or a path with |
|
| Show messages detected as security mails without withholding the body or masking the subject. |
|
| Allow |
|
| Build a full-text index of message bodies on disk, so |
|
| Upper bound for the |
|
| Upper bound for |
| not set | Development only: path to a helper bundle (Dev or Ad hoc). A bundle with the release bundle id is refused. |
|
| Write one line per helper request to stderr: command, duration and result code, for example |
Example: only the work account, no mailbox called Archive.
claude mcp add apple-mail \
-e APPLE_MAIL_ACCOUNTS=work@example.com \
-e APPLE_MAIL_EXCLUDE_MAILBOXES=Archive \
-- npx -y @redpop/apple-mail-mcpBody index
Without an index, bodyContains decodes messages while it searches, so one call looks at no
more than the 2,000 newest candidates or 3 seconds of work. A mail from years ago can take many
pages to reach. With APPLE_MAIL_BODY_INDEX=true the server keeps a full-text index of the
message bodies and searches all of them in one call, with the same matches, order and
matchContext as the scan.
Disk space: about 14 KiB per mail. On the test mailbox, 121,000 mails took 1.7 GiB.
Build time: about 3 ms per mail, just under 6 minutes for 120,000 mails. The server builds it in the background after its first successful tool call and continues after a restart. Tool calls go first.
Until it is complete
bodyContainsscans as before, and thenotesays how far the build is. Terms shorter than 3 characters are always scanned.It keeps itself current: new mail is added before each body search and every minute, deleted mail is removed every 30 minutes, and a changed configuration is applied at the next start.
Where:
~/Library/Application Support/apple-mail-mcp/body-index/, directory 0700, files 0600, excluded from Time Machine. To rebuild it, delete the directory.Turning it off deletes it at the next start of the server.
Requires Node 22.16 or newer in the 22 line, or 24 and newer (for
node:sqlitewith FTS5). Without it the index stays off and the search scans live.
doctor shows the state under body-index. The index is a copy of your mail text that any
program of your user can read, without Full Disk Access. Read
SECURITY.md before you turn
it on.
Tools
Tool | What it does |
| Accounts and visible mailboxes with total and unread counts and a role (inbox, sent, junk, ...). |
| Newest-first search by sender, recipient, subject, free text, mailbox, dates, unread, flagged, attachment, and optionally body text. Paginated with a cursor. |
| One message: headers, attachment names, body as plain text, optional removal of quoted replies. |
| The messages of one conversation, oldest first, without bodies. |
All four are marked read-only. Parameters, results and error codes: docs/tools.md.
Diagnostics and bug reports
npx -y @redpop/apple-mail-mcp doctor # from source: node dist/index.js doctordoctor checks the macOS version, the helper's signature, Full Disk Access, the database
layout, how many messages have a local file, the mailbox roles, how many removed accounts and
accounts with Mail switched off are hidden, the effective settings and the state of the body
index. Its default output contains no names, addresses or subjects, so you can paste it into an
issue as it is. --verbose adds mailbox names and account identifiers; --json is for
machines; --schema prints only the database fingerprint, any missing columns and the
CREATE TABLE statements of Mail's database (structure, no data). Exit code 1 means at least one check
failed.
Reporting problems
Open an issue at https://github.com/redpop/apple-mail-mcp/issues and attach the output of
doctor. If you run it on a macOS version other than 27, open a
Schema report
with the output of doctor --schema instead: that is the most useful report we can get.
Security problems go through GitHub Security Advisories, see SECURITY.md.
More documentation
docs/tools.md: every tool, its parameters, results and errorsdocs/architecture.md: how it works and what was measureddocs/troubleshooting.md: permission and database problemsdocs/exchange.md: what is known about Exchange accountsSECURITY.md: threat model and residual risksdocs/releasing.md: how releases are built, signed and published (maintainers)
Not included, on purpose
Sending, replying, drafts, moving, deleting, flagging, attachment contents, semantic search, link extraction. A read-only server is easier to trust.
Privacy Policy
This policy covers the server and its helper app, as published on npm, in the GitHub releases and in the MCP Registry.
What is collected
Nothing is collected by the author or by any service. The server has no telemetry, analytics or crash reporting, and neither the server nor the helper opens a network connection.
On your Mac, the server reads what its tools need: Mail's local index and message files under
~/Library/Mail, and the name and user name of mail accounts from
~/Library/Accounts/Accounts4.sqlite. Your configuration limits what is read (accounts, junk,
trash, excluded mailboxes, security mails).
How it is used and stored
Data is read only when your AI client calls a tool, and the result goes back only to that
client. The server keeps two kinds of files, both under
~/Library/Application Support/apple-mail-mcp/, directory 0700, files 0600:
state.json: when access to Mail last worked, plus the helper's version and checksum. No mail data.The body index, only if you set
APPLE_MAIL_BODY_INDEX=true: a full-text copy of the bodies of the messages your configuration makes visible, never of security mails. See Body index andSECURITY.md.
With APPLE_MAIL_MCP_DEBUG=true the server writes one line per request to stderr, with timings
and counts but no mail content. Your client decides whether it keeps that output in its logs.
Sharing with third parties
The server shares nothing with anyone. What your AI client does with a tool result is covered by
the client's and the model provider's privacy policies: a cloud-hosted model receives the results
the client sends it. Installing or updating through npm or npx downloads the package from the
npm registry, under npm's terms.
Retention
state.json is overwritten in place and stays until you delete it. The body index stays current
while it is on and is deleted at the next server start after you turn it off. To remove
everything, delete ~/Library/Application Support/apple-mail-mcp/. The server never changes
or deletes anything under ~/Library/Mail.
Contact
Questions about this policy: open an issue at
https://github.com/redpop/apple-mail-mcp/issues. Security problems: report them privately as
SECURITY.md describes.
License
MIT, see LICENSE.
Available Tools
4 toolsget_messageBRead-onlyIdempotent
Returns one message with its headers, attachment names and body text, as plain text with quoted replies optionally removed. Changes nothing.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | ||
| maxChars | No | ||
| stripQuoted | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| cc | Yes | |
| id | Yes | |
| to | Yes | |
| body | Yes | |
| date | Yes | |
| from | Yes | |
| link | Yes | |
| note | No | |
| unread | Yes | |
| flagged | Yes | |
| replyTo | Yes | |
| subject | Yes | |
| category | Yes | |
| dateSent | Yes | |
| reminder | Yes | |
| threadId | Yes | |
| bodyLocal | Yes | |
| important | Yes | |
| mailboxes | Yes | |
| attachments | Yes | |
| bodyAvailable | Yes | |
| recipientCount | Yes | |
| attachmentCount | Yes | |
| attachmentNames | Yes | |
| subjectRedacted | No | |
| bodyUnavailableReason | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The annotations already declare readOnlyHint=true and idempotentHint=true, so "Changes nothing" is largely redundant. The description does add real behavioral context beyond the structured fields: output is plain text, attachments are represented by names rather than content, and quoted replies can be stripped — useful for anticipating output shape even though an output schema 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?
A single dense sentence that front-loads what is returned and what the caller controls. Every clause earns its place; only the slightly tangled "plain text with quoted replies optionally removed" construction costs it a 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?
With an output schema and full annotations, the description needn't explain return values or safety, and it covers the read semantics well. It falls short on the maxChars truncation contract, which is exactly the kind of silent behavior an agent needs to know to avoid misreading a truncated body as 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 0%, so the description must carry parameter meaning and largely does not. It explains stripQuoted ("quoted replies optionally removed") but says nothing about maxChars or that output is truncated at a character cap, nor about the numeric-only id format — the truncation point is the biggest silent gap.
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 ("Returns one message") and enumerates what comes back: headers, attachment names, and body text. It is distinguishable from search_messages and get_thread by being the single-message, single-id fetch, though it never names those siblings to make the contrast 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?
There is no explicit when-to-use statement, no prerequisites, and no named alternative such as search_messages or get_thread. Usage is only implied by the phrase "Returns one message," which lets an agent infer single-message retrieval but leaves the choice against siblings to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_threadARead-onlyIdempotent
Returns summaries of the messages in the conversation that contains the given message (up to the limit, oldest first), without bodies. Changes nothing.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | ||
| limit | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| note | No | |
| hasMore | Yes | |
| messages | Yes | |
| threadId | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, and openWorldHint=false, so "Changes nothing" is largely redundant. The description earns credit for behavior annotations don't cover: results are summaries with bodies stripped, returned oldest first, and capped by the limit.
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?
A single front-loaded sentence: the return content and shape come first, the ordering and cap parenthetical next, and the safety note last. No filler and no repetition of the title.
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, the description need not document return structure, and for a two-parameter read tool it covers what the agent needs to call it correctly. Minor gaps remain: whether the referenced message itself is included in the summaries, and what happens for an unknown or non-existent id.
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 0%, so the description carries the burden, and it does explain both parameters in prose: the id is the message whose containing conversation is fetched, and limit caps how many message summaries come back. However, it omits that id must be a numeric string and that limit defaults to 50 with a 1-100 range, which the schema only conveys structurally.
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 gives a specific verb and resource ("Returns summaries of the messages in the conversation") plus the key scoping rule that the thread is the one containing the given message. "Without bodies" implicitly contrasts it with get_message, but no sibling is named explicitly, so differentiation is inferred rather than stated.
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?
Usage is implied by the resource framing: reach for this when you have a message id and want the surrounding conversation rather than a single full message. There is no explicit when-to-use/when-not guidance and no pointer to search_messages or get_message as alternatives, leaving routing to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_mailboxesARead-onlyIdempotent
Lists the mail accounts and mailboxes that are visible under the current settings, with message counts. Changes nothing.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| note | No | |
| accounts | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint and openWorldHint=false, so "Changes nothing" largely restates structured data. The genuine added value is the visibility scoping ("visible under the current settings"), which signals results are permission-dependent, but nothing is said about result size or limits.
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?
A single front-loaded sentence that names the resource before its qualifiers, with no padding. The trailing "Changes nothing" clause is mildly redundant given the readOnly annotation, costing a perfect score.
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 listing tool with a full output schema and complete annotations, the description covers purpose, output content, and permission scoping. Only edge-case behavior (empty results, counting semantics) is left unstated, which is minor.
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 takes zero parameters and the schema is closed (additionalProperties: false), so there is no parameter semantics to document. Baseline 4 applies; the description correctly implies no inputs are needed.
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 ("Lists") and a specific resource ("mail accounts and mailboxes") plus what is returned ("message counts"). The resource is clearly distinct from the message-level siblings (search_messages, get_message, get_thread), though no sibling is named explicitly.
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?
"Visible under the current settings" implies a discovery/setup use, but the description never states when to prefer this over the sibling tools or what to do next. Usage is inferable rather than spelled out.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_messagesBRead-onlyIdempotent
Searches mail by sender, recipient, subject, free text, mailbox, date range, unread, flagged and attachment state, and Mail category, newest first, and returns message summaries without bodies. Changes nothing.
| Name | Required | Description | Default |
|---|---|---|---|
| to | No | ||
| from | No | ||
| limit | No | ||
| query | No | ||
| since | No | ||
| before | No | ||
| cursor | No | ||
| unread | No | ||
| flagged | No | ||
| subject | No | ||
| category | No | ||
| mailboxIds | No | ||
| bodyContains | No | ||
| hasAttachment | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| note | No | |
| scan | No | |
| messages | Yes | |
| bodyIndex | No | |
| nextCursor | Yes | |
| totalMatched | No | |
| totalMatchedCapped | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint and closed-world, so 'Changes nothing' is largely redundant. The valuable additions are behavioral: results are ordered newest first and carry summaries without bodies, which shapes how an agent consumes output. It still says nothing about pagination behavior or result caps, which matter for a search tool.
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?
A single dense sentence that front-loads the verb and resource, then packs filter scope and return shape into one clause; every clause carries information. The long conjunction chain is slightly hard to parse but wastes no 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?
An output schema exists, so return values need no explanation, but with 14 parameters and zero schema descriptions the description should cover pagination (limit/cursor) and the query vs bodyContains split, and it does neither. Adequate for filter discovery, incomplete for correct invocation of paged searches.
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 0% across 14 parameters, so the description must carry the semantics, and it does enumerate most filters (sender, recipient, subject, free text, mailbox, date range, unread, flagged, attachment, category), mapping them to the schema properties. However, it never explains limit or cursor, and does not distinguish 'query' from 'bodyContains', leaving two documented-but-ambiguous filters.
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?
Specific verb ('Searches') plus resource ('mail') with an enumerated list of filterable fields and the return shape ('message summaries without bodies'), which lets an agent distinguish it from get_message/get_thread/list_mailboxes by inference. It stops short of naming any sibling explicitly, so it is clear but not maximally differentiated.
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?
Usage is implied: search when you need to find messages, and use get_message (not named) when you need bodies, since results omit them. There is no explicit when-to-use statement, no guidance on combining filters, and no mention of how to page through results.
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.
3 tool updates
v0.3.0- Changed
get_message2 fields changed- added
Output schema / properties / reminderAdded value: +{ + "type": [ + "string", + "null" + ] +} - changed
Output schema / requiredPrevious value: -[ - "id", - "link", - "subject", - "from", - "to", - "cc", - "recipientCount", - "date", - "mailboxes", - "unread", - "flagged", - "attachmentNames", - "attachmentCount", - "threadId", - "bodyLocal", - "category", - "important", - "replyTo", - "dateSent", - "body", - "bodyAvailable", - "bodyUnavailableReason", - "attachments" -]New value: +[ + "id", + "link", + "subject", + "from", + "to", + "cc", + "recipientCount", + "date", + "mailboxes", + "unread", + "flagged", + "attachmentNames", + "attachmentCount", + "threadId", + "bodyLocal", + "category", + "important", + "reminder", + "replyTo", + "dateSent", + "body", + "bodyAvailable", + "bodyUnavailableReason", + "attachments" +]
- Changed
get_thread2 fields changed- added
Output schema / properties / messages / items / properties / reminderAdded value: +{ + "type": [ + "string", + "null" + ] +} - changed
Output schema / properties / messages / items / requiredPrevious value: -[ - "id", - "link", - "subject", - "from", - "to", - "cc", - "recipientCount", - "date", - "mailboxes", - "unread", - "flagged", - "attachmentNames", - "attachmentCount", - "threadId", - "bodyLocal", - "category", - "important" -]New value: +[ + "id", + "link", + "subject", + "from", + "to", + "cc", + "recipientCount", + "date", + "mailboxes", + "unread", + "flagged", + "attachmentNames", + "attachmentCount", + "threadId", + "bodyLocal", + "category", + "important", + "reminder" +]
- Changed
search_messages2 fields changed- added
Output schema / properties / messages / items / properties / reminderAdded value: +{ + "type": [ + "string", + "null" + ] +} - changed
Output schema / properties / messages / items / requiredPrevious value: -[ - "id", - "link", - "subject", - "from", - "to", - "cc", - "recipientCount", - "date", - "mailboxes", - "unread", - "flagged", - "attachmentNames", - "attachmentCount", - "threadId", - "bodyLocal", - "category", - "important" -]New value: +[ + "id", + "link", + "subject", + "from", + "to", + "cc", + "recipientCount", + "date", + "mailboxes", + "unread", + "flagged", + "attachmentNames", + "attachmentCount", + "threadId", + "bodyLocal", + "category", + "important", + "reminder" +]
4 tool updates
v0.2.0- First observed
get_message - First observed
get_thread - First observed
list_mailboxes - First observed
search_messages
TDQS
Scored across 4 tools
Each tool targets a clearly different operation: bulk search, single-message retrieval, thread retrieval, and mailbox enumeration. The distinction between search_messages, get_message, and get_thread (summaries vs. full body vs. conversation context) is spelled out explicitly, leaving no realistic chance of misselection.
All names follow a clean snake_case verb_noun pattern (search_messages, get_message, get_thread, list_mailboxes) with verbs that accurately reflect the action. No mixing of conventions or stray suffixes.
Four tools is somewhat lean, but each earns its place and covers a distinct read workflow without redundancy. For a deliberately read-only mail surface this is well-scoped, though slightly narrow for a server named after the whole mail app.
Core discovery and reading are well covered, but there are notable gaps even for a read-only server: no way to fetch attachment contents, no mailbox/account detail beyond counts, and no write operations (send, move, flag, mark read) if those are in scope. Agents can search and read but will hit dead ends on attachment or state-changing tasks.
Maintenance
Related MCP Connectors
Email for AI agents: read, search and send from your Gmail, Outlook, Yahoo, iCloud or IMAP inbox.
Search connected email, read and manage messages, draft and send, and manage rules for new mail.
Search and triage all your mailboxes; draft replies and campaigns; send only as you allow.
Read and search FranklyMail email and prepare drafts, replies, and forwards for human approval.
Related MCP Servers
- AlicenseAqualityBmaintenanceEnables AI assistants to read, send, search, and manage emails in Apple Mail on macOS.2562 PyPI106MIT
- AlicenseNot gradedqualityDmaintenanceEnables AI agents to fetch emails from local Apple Mail accounts and mailboxes, and parse Google Scholar alert emails to extract paper titles and links.MIT
- AlicenseNot gradedqualityBmaintenanceEnables using Apple Mail accounts to search, read, manage, draft, and send messages from Codex or Claude Code locally.MIT
- AlicenseAqualityBmaintenanceEnables AI assistants to read, search, send, and manage email on macOS through programmatic access to Apple Mail.29MIT