Skip to content

Tags: Wh1isper/mcp-email-server

Tags

1.9.1

Toggle 1.9.1's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
Fix managed attachment content policy propagation (#253)

Co-authored-by: YAAI <261597362+yaai-bot@users.noreply.github.com>

1.9.0

Toggle 1.9.0's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
fix: support explicit recipient glob allowlists (#250)

1.8.0

Toggle 1.8.0's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
feat: report the delivered Message-Id from send_email and forward_ema…

…il (#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>

1.7.0

Toggle 1.7.0's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
fix: deny recipient-bound operations for empty allowlists (#248)

1.6.2

Toggle 1.6.2's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
Restore a container image build and refresh the locked dependencies t…

…hat carry advisories (#245)

* Restore a container image build

A Dockerfile lived in this repository until #203, which removed it together
with the publishing job in the release workflow. The last image on ghcr is
therefore 0.16.0 from 2026-07-13, while the project has since moved to 1.6.x.
Anyone running the server as a container is stuck on that image, and it now
reports 176 fixable advisories -- 18 of them in pillow, which arrived through
the gradio dependency this project has since dropped.

This restores a build, in two stages so the runtime layer carries neither uv
nor the build inputs.

Two details worth keeping:

- The virtualenv stays at /app/.venv. Deployments that run the server over
  streamable-http rather than stdio address the interpreter directly, and that
  path was the de-facto interface of the previous image.
- tini stays as the init process, as before, so the children the server spawns
  are reaped rather than accumulating over long-running IMAP sessions.

Verified by building the image and driving it over streamable-http: it starts,
answers `initialize` and `tools/list` with all 18 tools, and `list_mailboxes`
and `list_emails_metadata` return real data against a live IMAP account.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KgNBBghEfVPSevVBzT2CSc

* Refresh the locked dependencies that carry advisories

A scan of an image built from the current lockfile reports 38 advisories with
a fix available. They all sit in transitive pins that the lockfile holds below
the fixed releases; the declared ranges in pyproject.toml already allow them.

  cryptography      46.0.4  -> 50.0.1
  python-multipart  0.0.20  -> 0.0.32
  PyJWT             2.11.0  -> 2.13.0
  starlette         0.48.0  -> 1.6.0
  mcp               1.26.0  -> 1.29.1
  aiosmtplib         4.0.2  -> 5.1.2
  pydantic-settings 2.12.0  -> 2.15.0
  python-dotenv      1.1.1  -> 1.2.3
  pygments          2.19.2  -> 2.21.0
  idna                3.10  -> 3.19

That takes the same image from 38 fixable advisories to 3, the remainder being
setuptools and msgpack, which do not move inside the current constraints.

Deliberately a targeted `uv lock --upgrade-package ...` rather than a blanket
`uv lock --upgrade`. The blanket form also lifts typer and its click
dependency, and 10 tests then fail -- eight in test_managed_cli.py, one in
test_stdio_protocol.py, one in test_web_ui_server.py -- all around CLI option
handling. Those packages carry no advisories, so there was no reason to move
them.

Verified: `pytest` passes in full, 1457 passed / 37 skipped, identical to the
run on the unmodified lockfile.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KgNBBghEfVPSevVBzT2CSc

* feat(container): restore verified GHCR releases

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Wh1isper <9573586@qq.com>

1.6.1

Toggle 1.6.1's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
fix(metadata): enforce exact datetime filtering (#244)

IMAP BEFORE and SINCE discard time and timezone, so datetime queries could
return imprecise results or an empty same-day interval.

Require timezone-aware boundaries, widen IMAP date criteria into conservative
candidate searches, and reapply the exact INTERNALDATE interval before total,
ordering, and pagination. Keep the MCP contract, documentation, deterministic
coverage, and GreenMail evidence aligned with the corrected behavior.

Assisted-by: YAAI <261597362+yaai-bot@users.noreply.github.com>

1.6.0

Toggle 1.6.0's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
feat: add semantic IMAP tags and embedded attachment content (#242)

* feat: add semantic email tags and attachment content

Add account-scoped semantic IMAP tags across legacy and managed configuration, metadata reads, mutations, CLI, and the management UI. Add an independent opt-in MCP embedded attachment content path for clients without a shared filesystem while preserving local download behavior.

Derived from and cherry-picked from 68a5c59.

Co-authored-by: alonso <acuna007@gmail.com>

* fix(managed): migrate schema v3 catalogs to v4

Preserve existing managed accounts, policy, bindings, and secrets when opening the schema introduced before semantic tags and embedded attachment content. Validate the exact v3 catalog, add the new default-disabled fields in one transaction, and record v4 only after schema and invariant checks pass.

Assisted-by: YAAI <261597362+yaai-bot@users.noreply.github.com>

---------

Co-authored-by: alonso <acuna007@gmail.com>

1.5.2

Toggle 1.5.2's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
Fix bare Message-IDs in reply threading headers (#239)

Co-authored-by: YAAI <261597362+yaai-bot@users.noreply.github.com>

1.5.1

Toggle 1.5.1's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
fix(imap): handle omitted empty search responses (#237)

Recognize iCloud's exact completion-only empty SEARCH response while keeping all other malformed UID payloads fail-closed. Add protocol/provider regressions, boundary tests, and troubleshooting documentation.\n\nCloses #236

1.5.0

Toggle 1.5.0's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
feat: add forward_email tool (#184)

* feat: add forward_email tool

Forward an existing message to new recipients, re-attaching the original's
MIME parts. Reworked against the Local Email App V2 application boundaries as
requested in the review of #184, rather than rebasing the previous
app.py/classic-handler implementation.

Application layer (application/mutations.py)
- ForwardCommand (a ComposeCommand carrying source_email_id, source_mailbox and
  include_attachments), ForwardSource/ForwardSourcePart, and ForwardService.
- Three independent provider effects, each preceded by a fresh authority and
  policy resolution: the IMAP source read, SMTP delivery, and the sent copy.
  The recipient allowlist is validated against the pre-open snapshot and again
  against the opened account.
- A failed, denied, cancelled or ambiguous source read aborts before the
  outgoing provider is opened, so a forward can never be delivered without the
  parts it was meant to carry. Sentinel ValueError and MutationProviderError
  propagate out of execute(); only TimeoutError is caught, and it is re-raised
  as MutationProviderError rather than being folded into a delivery outcome.
- Forward bounds reuse APPLICATION_LIMITS: part count, per-part and aggregate
  attachment bytes, and the derived subject. The derived body (caller note plus
  forwarded block) is re-validated through ComposeCommand.validate, so an
  oversized forward is rejected rather than truncated.
- The sent-copy tail of SendService.execute is extracted into a shared
  _complete_send() used by both services, keeping the ambiguity and
  reconciliation semantics in one place. SendService behavior is unchanged.
- Results reuse SendMutationOutcome and existing detail tags.

Provider layer (emails/classic.py)
- EmailClient.fetch_forward_source() reads the source in a single IMAP session,
  enforcing the sender allowlist before the body is fetched so a blocked source
  stays indistinguishable from a missing one. Every failure raises; there is no
  empty-list sentinel that could be mistaken for "no attachments".
- normalize_forwarded_part() round-trips each source part through compat32
  before it is attached. Source parts are parsed under policy=default, while
  compose_message builds compat32 containers that are later flattened under
  SMTP, SMTPUTF8, compat32 for the IMAP append, and again by aiosmtplib.
  Attaching a structured part directly loses the RFC 2231 charset label and
  re-encodes the parameter as unknown-8bit, and does so only when the source
  header needs refolding. The round-trip freezes part headers as opaque
  strings, so every send path emits identical bytes.
- Parts are re-attached rather than rebuilt from get_payload(decode=True),
  which preserves message/rfc822 subtrees, zero-byte parts, Content-ID,
  Content-Transfer-Encoding and Content-Type parameters.
- compose_message() and send_email_with_outcome() accept keyword-only
  extra_parts; existing call sites and behavior are untouched.
- When forwarded parts would place raw 8-bit octets on a session that did not
  advertise 8BITMIME, the transaction fails before MAIL with
  smtp-8bitmime-required, mirroring the existing smtp-utf8-unsupported
  pre-flight rejection. send_email_with_outcome has no general 7-bit downgrade;
  that gap is pre-existing and left for a separate change.

Transport and adapter
- ClassicMutationProvider.fetch_forward_source()/forward(), both bounded, with
  the same capability_unavailable guard as send for IMAP-only accounts.
- forward_email is annotated as a non-read-only, non-idempotent, open-world
  mutation, matching send_email, and the catalog fixture is regenerated.
- smtp-8bitmime-required is added to the public send detail allowlist so the
  reason survives redaction.

Docs and spec
- docs/tools.md, docs/guides.md and docs/configuration.md cover the tool, its
  annotations, the plain-text quoting limitation and the SMTP requirement.
- spec/07 gains a Forward subsection and acceptance criterion 07.12; spec/12
  extends the corresponding verification row.

Tests: +83 unit and contract tests plus GreenMail stdio E2E coverage that
verifies the delivered message and the sent copy over imaplib rather than
trusting the server's own response.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix: tighten forward policy and size bounds

* test: preserve forward source privacy precedence

---------

Co-authored-by: Jack Koch <jack@jbkjr.me>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Wh1isper <9573586@qq.com>