Skip to main content
Glama
apple-mail-mcp

apple-mail-mcp

by redpop

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-mcp

Then 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.md for 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_thread returns a conversation; messages carry a message:// 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 isError result 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: doctor checks 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_message reports file_not_found. See docs/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-mcp

The 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 setup

setup 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).app

Then 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:

  1. 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.

  2. 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 setup prints.

  3. setup reports 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

APPLE_MAIL_ACCOUNTS

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.

APPLE_MAIL_INCLUDE_JUNK

false

Include mailboxes with the junk role and their subfolders.

APPLE_MAIL_INCLUDE_TRASH

false

Include mailboxes with the trash role and their subfolders.

APPLE_MAIL_EXCLUDE_MAILBOXES

empty

Comma-separated mailboxes to hide. An entry matches a mailbox name (exact, ignoring case) or a path with /.

APPLE_MAIL_SHOW_SECURITY_MAILS

false

Show messages detected as security mails without withholding the body or masking the subject.

APPLE_MAIL_BODY_SEARCH

true

Allow bodyContains in search_messages. When false, using it is an INVALID_INPUT error.

APPLE_MAIL_BODY_INDEX

false

Build a full-text index of message bodies on disk, so bodyContains can search every message. Takes effect only with APPLE_MAIL_BODY_SEARCH. Turning it off deletes the index at the next start.

APPLE_MAIL_MAX_RESULTS

50

Upper bound for the limit of a search (1 to 100).

APPLE_MAIL_MAX_BODY_CHARS

20000

Upper bound for maxChars in get_message (1000 to 100000).

APPLE_MAIL_HELPER_PATH

not set

Development only: path to a helper bundle (Dev or Ad hoc). A bundle with the release bundle id is refused.

APPLE_MAIL_MCP_DEBUG

false

Write one line per helper request to stderr: command, duration and result code, for example apple-mail-mcp: search 42 ms ok. Never payloads or results.

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-mcp

Body 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 bodyContains scans as before, and the note says 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:sqlite with 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

list_mailboxes

Accounts and visible mailboxes with total and unread counts and a role (inbox, sent, junk, ...).

search_messages

Newest-first search by sender, recipient, subject, free text, mailbox, dates, unread, flagged, attachment, and optionally body text. Paginated with a cursor.

get_message

One message: headers, attachment names, body as plain text, optional removal of quoted replies.

get_thread

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 doctor

doctor 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

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 and SECURITY.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 tools
get_messageB
Read-onlyIdempotent

Returns one message with its headers, attachment names and body text, as plain text with quoted replies optionally removed. Changes nothing.

NameRequiredDescriptionDefault
idYes
maxCharsNo
stripQuotedNo

Output Schema

NameRequiredDescription
ccYes
idYes
toYes
bodyYes
dateYes
fromYes
linkYes
noteNo
unreadYes
flaggedYes
replyToYes
subjectYes
categoryYes
dateSentYes
reminderYes
threadIdYes
bodyLocalYes
importantYes
mailboxesYes
attachmentsYes
bodyAvailableYes
recipientCountYes
attachmentCountYes
attachmentNamesYes
subjectRedactedNo
bodyUnavailableReasonYes

TDQS

B3.2/5.0
Behavior3/5

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.

Conciseness4/5

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.

Completeness3/5

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.

Parameters2/5

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.

Purpose4/5

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.

Usage Guidelines3/5

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_threadA
Read-onlyIdempotent

Returns summaries of the messages in the conversation that contains the given message (up to the limit, oldest first), without bodies. Changes nothing.

NameRequiredDescriptionDefault
idYes
limitNo

Output Schema

NameRequiredDescription
noteNo
hasMoreYes
messagesYes
threadIdYes

TDQS

A3.8/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose4/5

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.

Usage Guidelines3/5

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_mailboxesA
Read-onlyIdempotent

Lists the mail accounts and mailboxes that are visible under the current settings, with message counts. Changes nothing.

NameRequiredDescriptionDefault

No parameters

Output Schema

NameRequiredDescription
noteNo
accountsYes

TDQS

A3.6/5.0
Behavior3/5

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.

Conciseness4/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose4/5

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.

Usage Guidelines3/5

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_messagesB
Read-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.

NameRequiredDescriptionDefault
toNo
fromNo
limitNo
queryNo
sinceNo
beforeNo
cursorNo
unreadNo
flaggedNo
subjectNo
categoryNo
mailboxIdsNo
bodyContainsNo
hasAttachmentNo

Output Schema

NameRequiredDescription
noteNo
scanNo
messagesYes
bodyIndexNo
nextCursorYes
totalMatchedNo
totalMatchedCappedNo

TDQS

B3.4/5.0
Behavior3/5

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.

Conciseness4/5

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.

Completeness3/5

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.

Parameters3/5

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.

Purpose4/5

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.

Usage Guidelines3/5

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.

  1. 3 tool updatesv0.3.0
    • Changedget_message2 fields changed
      • addedOutput schema / properties / reminder
        Added value: +{
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • changedOutput schema / required
        Previous 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"
        +]
    • Changedget_thread2 fields changed
      • addedOutput schema / properties / messages / items / properties / reminder
        Added value: +{
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • changedOutput schema / properties / messages / items / required
        Previous 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"
        +]
    • Changedsearch_messages2 fields changed
      • addedOutput schema / properties / messages / items / properties / reminder
        Added value: +{
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • changedOutput schema / properties / messages / items / required
        Previous 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"
        +]
  2. 4 tool updatesv0.2.0
    • First observedget_message
    • First observedget_thread
    • First observedlist_mailboxes
    • First observedsearch_messages

TDQS

A3.7/5.0

Scored across 4 tools

Disambiguation5/5

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.

Naming Consistency5/5

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.

Tool Count4/5

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.

Completeness3/5

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

ActivityMaintained
ResponsivenessSyncing

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables 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
  • A
    license
    A
    quality
    B
    maintenance
    Enables AI assistants to read, search, send, and manage email on macOS through programmatic access to Apple Mail.
    29
    MIT