Skip to content

fix: make --read-only block delivery instead of the compose tools - #119

Merged
patrickfreyer merged 6 commits into
mainfrom
carry/95-read-only-blocks-delivery
Oct 15, 2026
Merged

patrickfreyer merged 6 commits into
mainfrom
carry/95-read-only-blocks-delivery

Conversation

@patrickfreyer

Copy link
Copy Markdown
Owner

Opened by Claude (AI assistant) on Patrick's behalf.

Carries over #95 by @herbeckrobin (original commits preserved), rebased onto current main.

Fixes #102

Follow-up edits (separate commit)

  • test_reply_still_drafts now also patches apple_mail_mcp.tools.compose.subprocess.run (returncode 0, stdout b"DRAFT SAVED"). reply_to_email runs its main script through osascript directly, so the test failed on Linux CI with FileNotFoundError and drove the real Mail.app on macOS. It now also asserts the draft result.
  • forward_email refuses with Error: Forwarding is disabled in read-only mode. when server.READ_ONLY is set, as defense in depth in case the --read-only tool removal ever fails. Test covers both the plain and the message (direct osascript) paths.

python -m pytest -q: 136 passed.

Proposed CHANGELOG entry

Changed

  • --read-only now blocks delivery instead of removing the composing tools.
    compose_email and reply_to_email stay registered and refuse mode="send"
    themselves; only forward_email, which has no non-sending mode, is still
    removed. Previously the flag took away the entire compose surface, and with
    it the only tools that accept attachments — leaving no supported way to
    attach a file to a draft in read-only mode.

  • --read-only had no effect on manage_drafts(action="send"). compose.py
    did from apple_mail_mcp.server import READ_ONLY, which binds the value
    False at import time, while __main__ sets server.READ_ONLY afterwards.
    The guard therefore never fired. The module now reads the flag through the
    server module at call time.

🤖 Generated with Claude Code

herbeckrobin and others added 6 commits October 14, 2026 22:19
Two problems, one cause: the flag was applied by deleting tools rather
than by gating the sending path.

1. The guard in manage_drafts(action="send") never fired. compose.py did
   `from apple_mail_mcp.server import READ_ONLY`, binding the value False
   at import, while __main__ assigns server.READ_ONLY afterwards. The
   module now reads the flag through the server module at call time.

2. --read-only removed compose_email and reply_to_email outright, even
   though their "draft" and "open" modes never deliver anything. Those
   two are also the only tools in the compose surface that accept
   attachments, so read-only mode left no supported way to attach a file
   to a draft. They now stay registered and refuse mode="send"
   themselves. forward_email has no non-sending mode and is still
   removed.

Adds regression coverage for both, including one test that fails if the
flag is ever bound at import time again.
…the compose tools

Rebased onto current main; CHANGELOG.md taken from main (entry consolidated at release).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nder read-only

- test_reply_still_drafts now also patches compose.subprocess.run
  (returncode 0, stdout b"DRAFT SAVED"): reply_to_email runs its main
  script through osascript directly, so the test failed on Linux CI with
  FileNotFoundError and drove the real Mail.app on macOS.
- forward_email refuses with an error when server.READ_ONLY is set, as
  defense in depth in case the --read-only tool removal ever fails. Test
  covers both the plain and the message (direct osascript) paths.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@patrickfreyer
patrickfreyer merged commit 9f3b5bc into main Oct 15, 2026
5 checks passed
@patrickfreyer
patrickfreyer deleted the carry/95-read-only-blocks-delivery branch October 15, 2026 02:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

--read-only does not block manage_drafts(action="send") — flag is bound by value at import time

2 participants