Skip to content

feat: add AtomicMail toolkit for autonomous agent inboxes - #9130

Open
dom-dalty wants to merge 1 commit into
agno-agi:mainfrom
dom-dalty:feat-atomic-mail-toolkit
Open

feat: add AtomicMail toolkit for autonomous agent inboxes#9130
dom-dalty wants to merge 1 commit into
agno-agi:mainfrom
dom-dalty:feat-atomic-mail-toolkit

Conversation

@dom-dalty

Copy link
Copy Markdown

Summary

Adds an AtomicMail toolkit (AtomicMailTools) that gives an Agno agent its own email inbox. AtomicMail issues inboxes through an autonomous, proof-of-work signup flow — no domain setup or human verification step — and exposes them over JMAP, which makes it a natural fit for agents that need to send and receive mail without a human in the loop.

The toolkit exposes three tools, each with sync and async variants:

  • register_inbox — provision (or idempotently reuse) an inbox via the proof-of-work signup handshake. The resulting API key is cached to ~/.atomicmail/credentials.json (override with credentials_dir or ATOMIC_MAIL_CREDENTIALS_DIR) so the same inbox is reused across runs.
  • send_email — send a plain-text email from the registered inbox.
  • list_inbox — list the most recent messages in the inbox.

A cookbook example (cookbook/91_tools/atomic_mail_tools.py) drives the toolkit end to end from a deterministic, no-LLM Agno Workflow (register → read inbox → send), with the inbox username and recipient supplied via additional_data/env so no address is hardcoded.

Type of change

  • Bug fix
  • New feature
  • Breaking change
  • Improvement
  • Model update
  • Other:

Checklist

  • Code complies with style guidelines
  • Ran format/validation scripts (./scripts/format.sh and ./scripts/validate.sh) — both pass clean
  • Self-review completed
  • Documentation updated (comments, docstrings)
  • Examples and guides: Relevant cookbook examples have been included or updated (if applicable)
  • Tested in clean environment
  • Tests added/updated (if applicable)

Duplicate and AI-Generated PR Check

  • I have searched existing open pull requests and confirmed that no other PR already addresses this (no open or historical PR references AtomicMail; the tool does not yet exist on main)
  • If a similar PR exists, I have explained below why this PR is a better approach
  • Check if this PR was entirely AI-generated (by Copilot, Claude Code, Cursor, etc.) — see disclosure in Additional Notes; this is AI-assisted, human-reviewed and human-tested, not entirely AI-generated

Additional Notes

Testing. pytest libs/agno/tests/unit/tools/test_atomic_mail.py — 18 passing (sync + async, network mocked). Verified end to end against the live AtomicMail API in a freshly rebuilt dev environment: an inbox was registered, listed, and used to send an email that was confirmed received.

A real send-path bug was found and fixed during live testing. send_email built the From header from the capability JWT's inboxId claim, assuming it was a full address. In practice inboxId is only the local-part, so JMAP rejected the message with forbiddenFrom ("From address is malformed"). The fix:

  • derives the full <inboxId>@<allowedFromDomain> address from the capability JWT (both values are present as claims),
  • prefers that freshly-derived address over any stored credential (a credential written before the fix could hold a bare local-part), and
  • adds the JMAP-required identityId to EmailSubmission/set.

A regression test was added that asserts the wire-level From address and identityId, so the mocked-response tests can no longer mask this class of bug.

Protocol note. The proof-of-work handshake runs on every authentication and its solve time is server-dictated (dynamic difficulty; observed anywhere from ~4s to ~160s). This is inherent to AtomicMail's anti-abuse design, not a defect, but callers should expect register_inbox/send_email/list_inbox latency to vary.

Security. No credentials, tokens, or personal addresses are committed. The POW_SCRYPT_SALT constant is a public protocol parameter (identical for every client), not a secret. Credentials are written only at runtime to the user's home directory with 0o600 permissions.

AI disclosure. This PR was developed with AI assistance (Claude Code): the assistant reviewed the initial toolkit, found and fixed the send-path bug, added the workflow cookbook and the regression test, and ran the live end-to-end validation. All changes were human-reviewed and human-tested before submission.

Add AtomicMailTools exposing register_inbox / send_email / list_inbox
(sync and async) over AtomicMail's proof-of-work signup and JMAP API, so an
agent can provision its own inbox and send/receive mail with no human signup.

Includes a deterministic no-LLM workflow cookbook (register -> read -> send)
and unit tests. Fixes a send-path bug found in live testing: the From header
used the bare inboxId local-part, which JMAP rejects as forbiddenFrom. The
full <inboxId>@<allowedFromDomain> address is now derived from the capability
JWT and the required identityId is set on EmailSubmission.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

PR Triage

Missing issue link: Please link the issue this PR addresses using fixes #<issue_number>, closes #<issue_number>, or resolves #<issue_number> in the PR description. If there is no existing issue, please create one first.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant