Apple Mail search says when it is broken instead of returning nothing (#446 option 1) - #469
Open
davekilleen wants to merge 1 commit into
Open
Apple Mail search says when it is broken instead of returning nothing (#446 option 1)#469davekilleen wants to merge 1 commit into
davekilleen wants to merge 1 commit into
Conversation
What this pull request touches
Gates that will judge this change
Based on 6 changed files. |
davekilleen
force-pushed
the
fix/apple-mail-search-honest-status
branch
from
August 11, 2026 18:43
948b818 to
42cacb1
Compare
Community Apple Mail servers have two data paths with different permission models. List and read drive Mail.app through Automation permission and work. Search queries a local index that only exists after a manual `apple-mail-mcp index` run, which itself requires Full Disk Access. When that index is absent the search tool returns empty-with-a-keyword-hint rather than an error, so the model retries different keywords instead of diagnosing, and email-aware flows degrade to listing-and-reading with no signal that anything is wrong. The server's startup sync also no-ops silently without Full Disk Access, so even a once-built index quietly stales. Reported in #446 after months of undetected empty results. Adds a deep Doctor check, mail.apple-search, modelled on calendar.access: - OFF when no Apple Mail server is registered, so it never nags the majority who have not opted in. Registration is detected at user scope (~/.claude.json, the scope Dex's own ensure-mcp-user-scope hook steers people to) as well as project scope. - UNKNOWN when a server is registered off macOS, or the index cannot be read, including the existing sandbox-failure shape. - BROKEN, with the exact fix and the Full Disk Access prerequisite named, when the command is missing, the index was never built, the index is empty, or the index is older than seven days. Staleness is reported because a silently stale index produces the same false-empty answers as a missing one. - OK only when an index exists and is fresh. Adds /apple-mail-setup, which orders the steps so the one that gets skipped cannot be: Full Disk Access is granted, and Terminal relaunched, before the index build rather than after, and the flow ends in verification rather than assumption. Tests cover all six verdict paths plus registration detection at either scope. The two failures in test_doctor.py on Linux (launchctl-dependent) are pre-existing and unrelated. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
davekilleen
force-pushed
the
fix/apple-mail-search-honest-status
branch
from
August 12, 2026 00:29
42cacb1 to
b8a6b20
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes the silent-failure path reported in #446. Takes option 1 from that issue (guided setup + honest health). Option 3 (the upstream ask) is being routed to the server's maintainer; option 2 (assessing alternative mail backends) is deferred.
Community Apple Mail servers have two data paths with different permission models, and only one of them announces failure:
apple-mail-mcp indexrun, which itself needs Full Disk AccessBecause list and read keep working, the integration looks healthy. Search returns empty with a "try fewer keywords" hint, so the model retries different keywords instead of diagnosing, email-aware flows degrade to listing-and-reading, and nothing ever surfaces that the index does not exist. The reporter ran that way for months. The server's startup sync also no-ops silently without Full Disk Access, so even a once-built index quietly stales — producing the same false-empty answers as a missing one.
This is squarely the
feature_statuscontract inCLAUDE.md: a broken feature must saybrokenand hand back the fix, not degrade quietly.The check
New deep check
mail.apple-searchincore/utils/doctor.py, modelled directly oncalendar.access(the closest existing probe — macOS-only, permission-dependent, external tool):OFF— no Apple Mail server registered. The majority case; never nags.UNKNOWN— registered but not macOS, or the index could not be read (including the existing sandbox-failure shape).BROKEN— command missing, index never built, index empty, or index older than 7 days. Each carries the exact fix and names the Full Disk Access prerequisite, via a tier-3Healand auser_message.OK— index exists and is fresh.Registration is detected at user scope (
~/.claude.json) as well as project scope, because user scope is what Dex's ownensure-mcp-user-scope.cjshook steers people towards — and it is how the reporter had it registered. A project-scope-only check would have reportedOFFon the exact vault that was broken.Staleness is treated as broken rather than merely noted, because a stale index and a missing one are indistinguishable from where the user sits: both return answers that look like "no matching email."
The guided setup
New
/apple-mail-setupskill, following thecalendar-setuppattern. The ordering is the substance: Full Disk Access is granted and Terminal relaunched before the index build, not after — that ordering is what people get wrong, and getting it wrong is what produces the silent failure. The flow ends inapple-mail-mcp statusplus/dex-doctorverification rather than assuming success, and names the "quit Terminal completely" trap that makes a granted permission look ungranted.Verification
core/tests/test_doctor.pycovering all six verdict paths and registration detection at either scope.core/tests/test_doctor.py: 310 passed, 2 failed — both (test_entity_dead_letter_heal_round_trip_returns_probe_to_ok,test_launchctl_domain_failure_is_an_unknown_instrument) fail identically on cleanmainon Linux, beinglaunchctl-dependent. Confirmed by running them on a detached checkout oforigin/main.core/tests/test_skill_integrity.py: 119 passed (new skill frontmatter valid).core/tests/test_release_catalog*.py,test_release_tag_uniqueness.py: 50 passed.docs/architecture/INVENTORY.mdregenerated;scripts/check-architecture-inventory.shreports current.Not verified here: the macOS-only paths cannot execute on this Linux host —
_is_macos()and_apple_mail_cli_present()are monkeypatched in tests, ascalendar.accessalready does for EventKit. The probe's real-machine behaviour wants one confirmation run on a Mac.Changelog entry added as [1.98.0] in the house plain-English style.
🤖 Generated with Claude Code