Skip to content

Add archive_email — combine move and mark-read in one atomic call#67

Merged
MadLlama25 merged 1 commit into
MadLlama25:mainfrom
Richyread:pr/archive-email
Jul 6, 2026
Merged

Add archive_email — combine move and mark-read in one atomic call#67
MadLlama25 merged 1 commit into
MadLlama25:mainfrom
Richyread:pr/archive-email

Conversation

@Richyread

@Richyread Richyread commented May 4, 2026

Copy link
Copy Markdown
Contributor

Working through a Fastmail-rail port of an email-processing skill, I noticed every "archive into folder X" site needed two MCP calls: move_email + mark_email_read. The pair has awkward partial-failure semantics (the move succeeds but the mark-read doesn't, or vice versa), and the two-call pattern adds latency and complexity at every archive site in the calling code.

archive_email folds both operations into a single Email/set patch: remove all current mailbox memberships, add the target mailbox, and set keywords/$seen=true. JMAP guarantees per-email patch atomicity, so either the whole archive lands or none of it does — no "moved but not marked read" middle state to handle.

Scope is deliberately narrow: it's "archive into a folder + mark read", which is what most rule-based filing flows want. Trashing has a different convention (trashed mail often stays unread because Trash doesn't surface in folder lists or unread counts), so delete_email remains the right call there — archive_email intentionally doesn't cover that case.

Smoke-tested against a real account: round-tripping a marketing email out to a supplier folder and back to Inbox worked cleanly on both legs, $seen stayed set as expected, no errors. All 176 existing unit tests still pass.


This PR was drafted with Claude Opus 4.7; I reviewed and tested each commit before opening.

Working through a Fastmail-rail port of an email-processing skill, I
noticed every "archive into folder X" site needed two MCP calls:
move_email + mark_email_read. The pair has awkward partial-failure
semantics (the move succeeds but the mark-read doesn't, or vice versa),
and the two-call pattern adds latency and complexity at every archive
site in the calling code.

archive_email folds both operations into a single Email/set patch:
remove all current mailbox memberships, add the target mailbox, and
set keywords/$seen=true. JMAP guarantees per-email patch atomicity,
so either the whole archive lands or none of it does — no "moved but
not marked read" middle state to handle.

Scope is deliberately narrow: it's "archive into a folder + mark read",
which is what most rule-based filing flows want. Trashing has a
different convention (trashed mail often stays unread because Trash
doesn't surface in folder lists or unread counts), so delete_email
remains the right call there — archive_email intentionally doesn't
cover that case.

Smoke-tested against a real account: round-tripping a marketing email
out to a supplier folder and back to Inbox worked cleanly on both
legs, $seen stayed set as expected, no errors. All 176 existing unit
tests still pass.
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.

2 participants