Skip to content

feat: report the delivered Message-Id from send_email and forward_email - #249

Merged
Wh1isper merged 1 commit into
Wh1isper:mainfrom
khibarin:fix/report-sent-message-id
Sep 7, 2026
Merged

Wh1isper merged 1 commit into
Wh1isper:mainfrom
khibarin:fix/report-sent-message-id

Conversation

@khibarin

@khibarin khibarin commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Problem

Composition assigns an RFC 5322 Message-Id to every outgoing message
(emails/classic.py), but no submission result carries it. send_email and
forward_email return only Email sent successfully to <recipients>, so a
caller that keeps its own record of sent mail has to choose between storing an
empty identifier and inventing one. save_to_mailbox already reports the
identifier it appended; sends did not.

Change

Carry the identifier from the SMTP delivery effect through to both tool
responses:

  • DeliveryMutationOutcome and SendMutationOutcome gain an optional
    message_id, alongside the existing AppendMutationOutcome.message_id.
  • EmailClient.send_email_with_outcome attaches it exactly where it already
    attaches the accepted message — after an accepted DATA phase.
  • _complete_send reports it identically on every sent-copy path, so an
    independent Sent-copy failure cannot erase the identifier of a message that
    was demonstrably delivered.
  • A clean send appends . Message-Id: <...> to the success line, matching the
    wording save_to_mailbox already uses. A partial delivery reports the same
    value in its own message-id section 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

  • Provider: an accepted DATA phase reports the Message-Id that appears in the
    serialized message; a lost DATA result reports none.
  • Application: the identifier survives a failed Sent copy for both send and
    forward; a provider timeout and a withheld message report none.
  • MCP tools: send and forward name the identifier on success; a partial delivery
    names it in the tagged result; an ambiguous delivery contains no <...@...>
    at all.
  • E2E (GreenMail): the identifier reported by send_email and forward_email
    equals the Message-ID the recipient actually received.

Docs and spec

docs/tools.md documents the reported identifier for both tools and its use for
threading a follow-up. spec/07 states the rule normatively and extends
acceptance 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, and mkdocs build --strict
are 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 (two test_config.py cases assume a C: drive; two require symlink and
owner-assignment privileges). make test-e2e was not run locally — Docker is
unavailable in this environment — so the updated GreenMail assertions rely on CI.

🤖 Generated with Claude Code

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

codecov Bot commented Sep 7, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 84.6%. Comparing base (30f640b) to head (5b2bb70).

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.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@Wh1isper
Wh1isper merged commit b8fe19f into Wh1isper:main Sep 7, 2026
17 checks passed
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>
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