feat: report the delivered Message-Id from send_email and forward_email - #249
Merged
Merged
Conversation
Composition already assigned an RFC 5322 Message-Id to every outgoing message, but no submission result carried it, so a caller keeping its own record of sent mail had to choose between an empty identifier and an invented one. Carry the identifier from the SMTP delivery effect through SendMutationOutcome into both tool responses, mirroring the identifier save_to_mailbox already reports. The identifier is attached only where an accepted DATA phase proves which message the provider took. A rejected, timed-out, or otherwise ambiguous submission still reports none, and a failed or unknown Sent copy never removes the identifier of a message that was demonstrably delivered. A clean send appends "Message-Id: <...>" to the success line; a partial delivery reports the same value in its own message-id section. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #249 +/- ##
=====================================
Coverage 84.5% 84.6%
=====================================
Files 31 31
Lines 9213 9229 +16
Branches 1295 1297 +2
=====================================
+ Hits 7789 7810 +21
+ Misses 992 988 -4
+ Partials 432 431 -1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
alonsoac
pushed a commit
to PBX-Virtual/mcp-email-server
that referenced
this pull request
Sep 14, 2026
…il (Wh1isper#249) Composition already assigned an RFC 5322 Message-Id to every outgoing message, but no submission result carried it, so a caller keeping its own record of sent mail had to choose between an empty identifier and an invented one. Carry the identifier from the SMTP delivery effect through SendMutationOutcome into both tool responses, mirroring the identifier save_to_mailbox already reports. The identifier is attached only where an accepted DATA phase proves which message the provider took. A rejected, timed-out, or otherwise ambiguous submission still reports none, and a failed or unknown Sent copy never removes the identifier of a message that was demonstrably delivered. A clean send appends "Message-Id: <...>" to the success line; a partial delivery reports the same value in its own message-id section. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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.
Problem
Composition assigns an RFC 5322
Message-Idto every outgoing message(
emails/classic.py), but no submission result carries it.send_emailandforward_emailreturn onlyEmail sent successfully to <recipients>, so acaller that keeps its own record of sent mail has to choose between storing an
empty identifier and inventing one.
save_to_mailboxalready reports theidentifier it appended; sends did not.
Change
Carry the identifier from the SMTP delivery effect through to both tool
responses:
DeliveryMutationOutcomeandSendMutationOutcomegain an optionalmessage_id, alongside the existingAppendMutationOutcome.message_id.EmailClient.send_email_with_outcomeattaches it exactly where it alreadyattaches the accepted message — after an accepted
DATAphase._complete_sendreports it identically on every sent-copy path, so anindependent Sent-copy failure cannot erase the identifier of a message that
was demonstrably delivered.
. Message-Id: <...>to the success line, matching thewording
save_to_mailboxalready uses. A partial delivery reports the samevalue in its own
message-idsection of the tagged result.The identifier is never derived from composition. A rejected, timed-out, or
otherwise ambiguous submission reports none, so the response stays honest about
what the server can actually vouch for.
Tests
DATAphase reports theMessage-Idthat appears in theserialized message; a lost
DATAresult reports none.forward; a provider timeout and a withheld message report none.
names it in the tagged result; an ambiguous delivery contains no
<...@...>at all.
send_emailandforward_emailequals the
Message-IDthe recipient actually received.Docs and spec
docs/tools.mddocuments the reported identifier for both tools and its use forthreading a follow-up.
spec/07states the rule normatively and extendsacceptance criterion 6 rather than renumbering the list.
Verification
ruff format --check,ruff check,pyright,pre-commit run -a,uv lock --locked,dev/build_frontend.py --check, andmkdocs build --strictare clean. The full suite passes at 83.71% coverage. Four pre-existing failures
on this Windows checkout are unrelated to this change and fail identically on
main(twotest_config.pycases assume aC:drive; two require symlink andowner-assignment privileges).
make test-e2ewas not run locally — Docker isunavailable in this environment — so the updated GreenMail assertions rely on CI.
🤖 Generated with Claude Code