MailAtlas
MailAtlas makes email accessible to AI agents by turning files and live mailboxes into cleaned text, HTML, assets, metadata, exportable artifacts, and local sent-message records.
MailAtlas has local email I/O paths:
- ingest email files already on disk with
ingest - receive Gmail or IMAP messages into the local workspace with
receive - keep a live mailbox current with
receive watch - compose and send outbound email through providers you configure at runtime with
send - expose local documents, outbound audit records, drafts, and gated receive/send actions through an optional MCP server
An mbox file is a mailbox file on disk. It is not the same thing as IMAP receive.
MailAtlas produces:
- cleaned body text
- normalized HTML snapshots when the message contains HTML
- extracted inline images and attachments
- document metadata and provenance
- JSON, Markdown, HTML, and PDF exports from stored documents
- Gmail API receive with local cursors and no mailbox mutation
- incremental IMAP receive into the same local store
- outbound
.emlsnapshots, body files, attachment copies, provider status, and retry metadata
MailAtlas is a library and CLI for parsing, receiving, storing, exporting, sending through configured providers, and auditing email for AI agents, retrieval systems, analytics pipelines, and archival systems. It is not a hosted deliverability service, inbox client, or cloud mailbox connector.
Why MailAtlas
- Turn raw email into cleaned text, HTML, inline images, file attachments, and metadata.
- Preserve provenance, forwarded chains, inline images, and regular attachments.
- Apply configurable cleaning for boilerplate, wrappers, footer noise, and link-only lines.
- Export JSON, Markdown, HTML, and PDF artifacts from stored documents.
- Receive Gmail messages with a read-only OAuth token and store them in the same local workspace.
- Receive selected IMAP folders without storing mailbox credentials in the local store.
- Send through SMTP or Cloudflare Email Service using runtime credentials without storing provider secrets.
- Keep a local audit trail of outbound drafts, dry runs, sends, failures, BCC recipients, and attachments.
- Start with the built-in filesystem and SQLite store, then copy the resulting files and metadata into your own storage stack if needed.
Project Status
MailAtlas is currently alpha. Expect the CLI, stored schema, and release tooling to keep improving, but the repository is set up for public contribution with CI, release artifacts, package smoke checks, and a separate synthetic sample-data corpus.
Install
pip
python3.12 -m venv .venv
source .venv/bin/activate
python -m pip install mailatlas
mailatlas doctor
uv
python3.12 -m pip install uv
uv tool install mailatlas
mailatlas doctor
brew
brew tap mailatlas/mailatlas
brew install mailatlas
mailatlas doctor
If Homebrew resolves a different formula named mailatlas, use:
brew install mailatlas/mailatlas/mailatlas
From source
Use a source checkout when you want to work on the core package or contribute to the project:
python3.12 -m venv .venv
source .venv/bin/activate
make bootstrap-python
mailatlas doctor
Run make help to see the local package command surface.
MCP server
Install the optional MCP extra when you want an MCP-compatible client to inspect MailAtlas data or draft email:
python -m pip install "mailatlas[mcp]"
mailatlas mcp --root .mailatlas
The MCP server exposes read tools for stored documents and outbound audit records, plus
mailatlas_draft_email. The live mailatlas_send_email tool and mailbox receive tools are hidden
unless you explicitly opt in.
mailatlas_list_documents and mailatlas_list_outbound accept limit and offset pagination
arguments. They return count and has_more alongside the page of results.
For MCP clients that accept server configuration as JSON, put MailAtlas settings in the server entry instead of exporting shell variables globally:
{
"mcpServers": {
"mailatlas": {
"command": "mailatlas",
"args": ["mcp", "--root", ".mailatlas", "--allow-receive"],
"env": {
"MAILATLAS_GMAIL_TOKEN_STORE": "auto"
}
}
}
}
Use --allow-send only when the MCP client should be able to send live email:
mailatlas mcp --root .mailatlas --allow-send
Use the same provider environment variables as mailatlas send for SMTP, Cloudflare, or Gmail.
Provider secrets are consumed at runtime and are not written to the local store.
Mailbox receive tools are also hidden by default. Enable them only when the MCP client should be able to contact a provider and write private email into the local workspace:
mailatlas mcp --root .mailatlas --allow-receive
The older process environment switches still work for MCP client configs that prefer env over
arguments: MAILATLAS_MCP_ALLOW_SEND=1, MAILATLAS_MCP_ALLOW_RECEIVE=1, and
MAILATLAS_MCP_RECEIVE_ON_READ=1. Set MAILATLAS_MCP_RECEIVE_ON_READ=1 only if read tools should
run one receive pass before listing documents. Without that setting, MCP read tools use only messages
already stored locally.
Verify The Install
mailatlas doctor
mailatlas doctor runs a temporary self-check that verifies ingest, storage, and JSON export. It
also checks PDF export when Chrome or Chromium is available, and reports a warning instead of
failing if the browser is missing.
Local Store
By default, MailAtlas writes to .mailatlas in the current directory:
store.dbraw/html/assets/exports/outbound/- receive account, cursor, and run rows in
store.db
Set MAILATLAS_HOME once if you want MailAtlas to reuse a different root automatically:
export MAILATLAS_HOME="$PWD/.mailatlas"
You can also override the root per command with --root.
Next Steps
- Use the Quickstart walkthrough for the file-based path.
- Use IMAP receive when MailAtlas should connect to a live mailbox.
- Use the CLI overview for the full command surface.
- Use mailatlas/sample-data for synthetic
.emland.mboxfixtures. - Use mailatlas/examples for runnable demos and integration examples.
Core Use Cases
- Build a retrieval corpus from mailbox exports.
- Feed agents cleaned email text without losing links to raw messages and attachments.
- Generate reviewable PDF artifacts from stored HTML or cleaned text fallback.
- Normalize inbound email for analytics, retention, or archival processing.
- Inspect and test parser behavior against known synthetic fixtures.
CLI Examples
Auto-detect and ingest an mbox archive:
git clone https://github.com/mailatlas/sample-data
mailatlas ingest sample-data/fixtures/mbox/atlas-demo.mbox
IMAP receive is incremental by folder and stores only non-secret cursor state:
export MAILATLAS_IMAP_HOST=imap.example.com
export MAILATLAS_IMAP_USERNAME=user@example.com
export MAILATLAS_IMAP_ACCESS_TOKEN=oauth-access-token
mailatlas receive \
--provider imap \
--folder INBOX \
--folder Newsletters
MailAtlas consumes the access token you already have. It does not run a browser login flow or act as your OAuth client.
Run foreground polling when you want IMAP folders to stay current:
mailatlas receive watch \
--provider imap \
--folder INBOX \
--interval 60
Parser cleanup is configurable:
mailatlas ingest sample-data/fixtures/eml/atlas-founder-forward.eml \
--no-strip-forwarded-headers \
--no-strip-boilerplate
Render and audit an outbound message without contacting a provider:
mailatlas send \
--dry-run \
--from agent@example.com \
--to user@example.com \
--subject "Build complete" \
--text "The build passed."
Send through SMTP by providing credentials at runtime:
export MAILATLAS_SEND_PROVIDER=smtp
export MAILATLAS_SMTP_HOST=smtp.example.com
export MAILATLAS_SMTP_USERNAME=agent@example.com
export MAILATLAS_SMTP_PASSWORD=app-password
mailatlas send \
--from agent@example.com \
--to user@example.com \
--subject "Build complete" \
--text "The build passed."
For personal Gmail addresses, prefer Gmail API OAuth instead of SMTP app passwords. Create a Google OAuth desktop client, then authorize MailAtlas once. The default Gmail auth capability is send-only for compatibility:
python -m pip install "mailatlas[keychain]"
mailatlas auth gmail \
--client-id "$MAILATLAS_GMAIL_CLIENT_ID" \
--client-secret "$MAILATLAS_GMAIL_CLIENT_SECRET" \
--email user@gmail.com
mailatlas auth status gmail
To receive Gmail, request the read-only receive capability:
mailatlas auth gmail \
--client-id "$MAILATLAS_GMAIL_CLIENT_ID" \
--client-secret "$MAILATLAS_GMAIL_CLIENT_SECRET" \
--email user@gmail.com \
--capability receive
Use --capability send,receive when the same local token should support both Gmail send and Gmail
receive.
Then send through the Gmail API:
mailatlas send \
--provider gmail \
--from user@gmail.com \
--to user@gmail.com \
--subject "Gmail API test" \
--text "Sent with Gmail API OAuth."
Receive a bounded Gmail pass into the local workspace:
mailatlas receive \
--provider gmail \
--label INBOX \
--limit 50
Run foreground polling when you want the workspace to stay current:
mailatlas receive watch \
--provider gmail \
--label INBOX \
--interval 60
Inspect local receive accounts, cursors, and recent runs:
mailatlas receive status
MailAtlas stores Gmail OAuth tokens outside the workspace by default and never writes them to
store.db, raw snapshots, logs, or JSON send/receive results. Received raw messages, normalized
bodies, assets, and exports are local private data. Revoke the local token with:
mailatlas auth logout gmail
When the keychain extra is installed, local CLI auth stores Gmail OAuth token material in the
operating system keychain by default. Without that extra, MailAtlas falls back to a user config
token file. Use --token-file for throwaway tests, --token-store file to force the config file,
or --token-store keychain to require keychain storage. MAILATLAS_GMAIL_TOKEN_FILE selects a
file store when no token store is passed explicitly.
Backend applications should store Gmail refresh tokens in their own encrypted credential store and
pass short-lived access tokens to SendConfig(provider="gmail", gmail_access_token="...") or
ReceiveConfig(gmail_access_token="...").
Python API Example
from mailatlas import MailAtlas, OutboundMessage, ParserConfig, ReceiveConfig, SendConfig
atlas = MailAtlas(
db_path=".mailatlas/store.db",
workspace_path=".mailatlas",
parser_config=ParserConfig(strip_boilerplate=True, stop_at_footer=True),
)
parsed = atlas.parse_eml(
"sample-data/fixtures/eml/atlas-founder-forward.eml",
)
refs = atlas.ingest_eml(
[
"sample-data/fixtures/eml/atlas-market-map.eml",
"sample-data/fixtures/eml/atlas-inline-chart.eml",
],
)
imap_receive_result = atlas.receive(
ReceiveConfig(
provider="imap",
imap_host="imap.example.com",
imap_username="user@example.com",
imap_password="app-password",
imap_folders=("INBOX", "Newsletters"),
)
)
receive_result = atlas.receive(
ReceiveConfig(
gmail_access_token="ya29...",
gmail_label="INBOX",
limit=50,
)
)
pdf_path = atlas.export_document(
refs[0].id,
format="pdf",
)
dry_run = atlas.send_email(
OutboundMessage(
from_email="agent@example.com",
to=("user@example.com",),
subject="Build complete",
text="The build passed.",
idempotency_key="build-123",
),
SendConfig(provider="smtp", dry_run=True),
)
gmail_send = atlas.send_email(
OutboundMessage(
from_email="user@gmail.com",
to=("user@gmail.com",),
subject="Gmail API test",
text="Sent with an OAuth access token.",
),
SendConfig(provider="gmail", gmail_access_token="ya29..."),
)
Default Storage Layout
MailAtlas writes ordinary files to the filesystem and indexes them in SQLite by default:
raw/for original message byteshtml/for normalized HTML bodies when presentassets/for extracted inline and attached filesexports/for JSON, HTML, and PDF file exportsoutbound/raw/,outbound/text/,outbound/html/, andoutbound/attachments/for outbound audit artifactsstore.dbfor the SQLite index, receive accounts, receive cursors, receive runs, IMAP receive cursors, and outbound send records
These are ordinary files and metadata rows. If you are embedding MailAtlas inside a service, you
can move them into your own blob store and database. HTML and PDF exports sanitize stored HTML,
remove active content and remote auto-loading subresources, preserve ordinary HTTP(S) and mailto:
links, and embed only allowlisted extracted raster images. PDF export uses headless Chrome or
Chromium with an isolated profile and falls back to generated HTML from cleaned text when needed.
Raw messages, raw HTML, Markdown, JSON, and extracted assets remain untrusted input for downstream
consumers.
Markdown export prints to stdout by default with absolute local asset paths, or writes a
document.md plus copied assets/ bundle when you pass --out <directory>.
Outbound provider secrets are read from CLI flags, environment variables, or explicit Python
SendConfig values at runtime. They are not written to SQLite, raw snapshots, logs, or JSON output.
MailAtlas vs Alternatives
| Option | What it does well | Where MailAtlas is stronger |
|---|---|---|
| Inbox connectors | Convenient ad hoc question answering | Repeatable ingestion, exported files, and traceable source records |
| Generic parsers | Basic MIME parsing | Cleaned text, HTML snapshots, assets, metadata conventions |
| One-off scripts | Fast for a narrow task | Better repeatability, packaging, examples, docs, and release path |
Docs And Examples
- Documentation
- Installation guide
- Quickstart walkthrough
- IMAP receive
- CLI overview
- Examples repository
- Sample data repository
- Support
- Security policy
- Changelog
- Releasing
- Code of Conduct
- Contributing
Development
make test
make smoke-release
make doctor
Metadata
Release files for mailatlas 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| mailatlas-0.3.0.tar.gz | 102.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mailatlas-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 175.5 kB
Release files / mailatlas-0.3.0.tar.gz
| Download URL | mailatlas-0.3.0.tar.gz |
|---|---|
| Size | 102.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
5500b0a1a8c84e825eb4bc859429bcdbc671c5216f95b28c44bb000a9c4567e6
|
|
BLAKE2b-256 checksum How to use checksums |
5de533057ead5ddfd37ae6d4c232cdd072c62b9a42e2c3789a0d9e71b2cf1c15
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 21, 2026.
Transparency logRelease files / mailatlas-0.3.0-py3-none-any.whl
| Download URL | mailatlas-0.3.0-py3-none-any.whl |
|---|---|
| Size | 73.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
26780534c9c6b38f4d734b17d8ae20fe67211267edb97f626730d7f2a7fc044d
|
|
BLAKE2b-256 checksum How to use checksums |
f58fd8cbb78d73a2a07112d8c4fb7466e86b91a608fb731b18ddf89954027062
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 21, 2026.
Transparency log