-
Notifications
You must be signed in to change notification settings - Fork 0
Development and contributing
covers:
- tests/*.py
- .github/workflows/*.yml last_verified: 2026-06-30
uv venv && uv sync --all-extras # or: pip install -e ".[dev,full]"
uv run pytest # full suite — no Mac/Mail.app required
uv run ruff check src tests~80% of this codebase (everything under read/, knowledge/, core/, storage/, server.py,
cli.py) is plain Python + SQLite and tests fully on any platform with synthetic fixtures:
-
tests/helpers.py::build_emlx_bytes()/write_message()construct real.emlxbyte streams (byte-count header, RFC822 message, XML plist trailer) on disk in a temp directory — the indexer, parser, threader, and triage heuristics all run against these for real, not mocked. -
tests/helpers.py::FakeJXAExecutoris the documented mock boundary for the write layer: programmed per JXA function name with a Python callable, records every call made. Used bytests/test_resolver.pyandtests/test_write_layer.pyto verify the resolution algorithm,guard()'s every safety check, andundo_last()— without touching Mail.app at all. -
tests/test_jxa_executor.pyis the one place that runs realosascriptsubprocesses — deliberately scoped to scripts that never callApplication("Mail"), so no Automation permission is needed, while still genuinely exercising the timeout/process-group-kill mechanics (not mocked). -
tests/test_vector_search.pyuses a deterministicFakeEmbeddingBackend(keyword-presence vectors, no ML) against a realsqlite-vecextension (pytest.importorskip("sqlite_vec")— skips gracefully if not installed; the[dev]extra includes it specifically so CI exercises this path rather than always skipping).
The only things this suite cannot verify without a real, fully-configured Mac: the actual
write/scripts/mail_core.js JXA against a live Mail.app (compose/reply/forward/move/trash/
drafts), and the Apple NaturalLanguage/Foundation Models integrations (PyObjC and Swift-helper
runtime behavior — see Search and the
swift/foundation-models-summarizer/README.md
for what was verified by compiling vs. what needs manual verification).
-
apple-mail-mcp index build --fullthenindex status. -
apple-mail-mcp search "<term>" --highlight,get_email_thread,overview,needs-response. -
Non-destructive first:
apple-mail-mcp move <id> --to-mailbox Archive --dry-run,apple-mail-mcp trash --action delete_permanent ...(defaults todry_run=true). -
apple-mail-mcp compose --account ... --to ... --subject test --body test --mode draft. - A single real
move, thenapple-mail-mcp undo-lastto confirm the round-trip. -
update_email_statusflag/unflag. - Confirm
--read-onlyblocks every write tool (should fail in milliseconds, never launching Mail.app — see Performance & benchmarks for why this is specifically checked). - Register with an actual MCP client (see Install per client) and run
recipe run daily-triage.
Any change to a subsystem updates its mapped Wiki page in the same commit — see the
knowledge map in
CLAUDE.md. Each Wiki
page's front-matter (covers: source globs, last_verified: date) is checked by
scripts/check_docs_sync.py, wired into pre-commit and CI — it warns (not a hard failure, to
avoid false positives from unrelated changes) when a covered source file's mtime is newer than
the page's recorded last_verified date.
GitHub Actions (.github/workflows/ci.yml) runs on macOS runners (this project's core logic and
the documented test boundary both require macOS for the real-osascript and real-sqlite-vec
tests to run, not just skip): ruff check, pytest, and scripts/check_docs_sync.py.
make pyz # builds dist/apple-mail-mcp{,-full}.pyz
scripts/publish_wiki.sh # syncs docs/wiki/ -> the GitHub wiki repoPython 3.10+, type hints everywhere, pydantic models for all tool I/O. No comments explaining what code does — only the non-obvious why (a constraint, a workaround, an invariant). See CLAUDE.md for the full conventions list and the hard invariants every change must preserve.