Mail Intake
mail_intake
Mail Intake — provider-agnostic, read-only email ingestion framework.
Mail Intake is a contract-first library designed to ingest, parse, and normalize email data from external providers (such as Gmail) into clean, provider-agnostic domain models.
The library is intentionally structured around clear layers, each exposed as a first-class module at the package root:
- adapters: provider-specific access (e.g. Gmail)
- auth: authentication providers and credential lifecycle management
- credentials: credential persistence abstractions and implementations
- parsers: extraction and normalization of message content
- ingestion: orchestration and high-level ingestion workflows
- models: canonical, provider-agnostic data representations
- config: explicit global configuration
- exceptions: library-defined error hierarchy
The package root acts as a namespace, not a facade. Consumers are expected to import functionality explicitly from the appropriate module.
Installation
Install using pip:
pip install mail-intake
Or with Poetry:
poetry add mail-intake
Mail Intake is pure Python and has no runtime dependencies beyond those required by the selected provider (for example, Google APIs for Gmail).
Basic Usage
Minimal Gmail ingestion example (local development):
from mail_intake.ingestion import MailIntakeReader
from mail_intake.adapters import MailIntakeGmailAdapter
from mail_intake.auth import MailIntakeGoogleAuth
from mail_intake.credentials import PickleCredentialStore
store = PickleCredentialStore(path="token.pickle")
auth = MailIntakeGoogleAuth(
credentials_path="credentials.json",
store=store,
scopes=["https://www.googleapis.com/auth/gmail.readonly"],
)
adapter = MailIntakeGmailAdapter(auth_provider=auth)
reader = MailIntakeReader(adapter)
for message in reader.iter_messages("from:recruiter@example.com"):
print(message.subject, message.from_email)
Iterating over threads:
for thread in reader.iter_threads("subject:Interview"):
print(thread.normalized_subject, len(thread.messages))
Extensibility Model
Mail Intake is designed to be extensible via public contracts exposed through its modules:
- Users MAY implement their own mail adapters by subclassing
adapters.MailIntakeAdapter - Users MAY implement their own authentication providers by subclassing
auth.MailIntakeAuthProvider[T] - Users MAY implement their own credential persistence layers by
implementing
credentials.CredentialStore[T]
Users SHOULD NOT subclass built-in adapter implementations. Built-in adapters (such as Gmail) are reference implementations and may change internally without notice.
Public API Surface
The supported public API consists of the following top-level modules:
- mail_intake.ingestion
- mail_intake.adapters
- mail_intake.auth
- mail_intake.credentials
- mail_intake.parsers
- mail_intake.models
- mail_intake.config
- mail_intake.exceptions
Classes and functions should be imported explicitly from these modules. No individual symbols are re-exported at the package root.
Design Guarantees
- Read-only access: no mutation of provider state
- Provider-agnostic domain models
- Explicit configuration and dependency injection
- No implicit global state or environment reads
- Deterministic, testable behavior
- Distributed-safe authentication design
Mail Intake favors correctness, clarity, and explicitness over convenience shortcuts.
Core Philosophy
Mail Intake is built as a contract-first ingestion pipeline:
- Layered Decoupling: Adapters handle transport, Parsers handle format normalization, and Ingestion orchestrates.
- Provider Agnosticism: Domain models and core logic never depend on provider-specific (e.g., Gmail) API internals.
- Stateless Workflows: The library functions as a read-only pipe, ensuring side-effect-free ingestion.
Documentation Design
Follow these "AI-Native" docstring principles across the codebase:
For Humans
- Namespace Clarity: Always specify which module a class or function belongs to.
- Contract Explanations: Use the
adaptersandauthbase classes to explain extension requirements.
For LLMs
- Dotted Paths: Use full dotted paths in docstrings to help agents link concepts across modules.
- Typed Interfaces: Provide
.pyistubs for every public module to ensure perfect context for AI coding tools. - Canonical Exceptions: Always use
: descriptionpairs inRaisesblocks to enable structured error analysis.