- Add hand-written wiki (index, overview, how-to, extending, dev) following the platform anatomy - Remove stale nested docs/lib/mail_intake and regenerate the flat lib reference to match docforge.nav.yml - Regenerate MCP bundle with standardized docstrings
106 lines
2.5 KiB
Markdown
106 lines
2.5 KiB
Markdown
# 🧩 Extending Mail Intake
|
|
|
|
Mail Intake is designed to be extended through its **public contracts**.
|
|
Implement your own adapter, auth provider, or credential store to support a
|
|
new provider or storage backend.
|
|
|
|
---
|
|
|
|
## 📬 Custom adapters
|
|
|
|
Subclass `MailIntakeAdapter` to integrate a new provider. Adapters perform
|
|
read-only transport and return provider-native payloads:
|
|
|
|
```python
|
|
from mail_intake.adapters import MailIntakeAdapter
|
|
|
|
class ExchangeAdapter(MailIntakeAdapter):
|
|
def __init__(self, auth_provider):
|
|
self._auth = auth_provider
|
|
|
|
def fetch_messages(self, query):
|
|
# call the provider API, return native payloads
|
|
...
|
|
|
|
def fetch_threads(self, query):
|
|
...
|
|
```
|
|
|
|
> **Do not** subclass built-in adapters like `MailIntakeGmailAdapter` — they
|
|
> are reference implementations and may change internally without notice.
|
|
|
|
---
|
|
|
|
## 🔐 Custom auth providers
|
|
|
|
Subclass `MailIntakeAuthProvider[T]` to own a different credential flow:
|
|
|
|
```python
|
|
from mail_intake.auth import MailIntakeAuthProvider
|
|
|
|
class ExchangeAuth(MailIntakeAuthProvider[exchange_credentials]):
|
|
def get_credentials(self):
|
|
return self._store.load()
|
|
|
|
def refresh(self):
|
|
...
|
|
```
|
|
|
|
Auth providers remain decoupled from adapter logic — they only manage
|
|
credentials.
|
|
|
|
---
|
|
|
|
## 🗄️ Custom credential stores
|
|
|
|
Implement the `CredentialStore[T]` contract for a new persistence backend:
|
|
|
|
```python
|
|
from mail_intake.credentials import CredentialStore
|
|
|
|
class S3CredentialStore(CredentialStore):
|
|
def save(self, credentials):
|
|
...
|
|
|
|
def load(self):
|
|
...
|
|
|
|
def delete(self):
|
|
...
|
|
```
|
|
|
|
The store abstraction keeps tokens out of config and rotates safely.
|
|
|
|
---
|
|
|
|
## 🧪 Wiring a custom backend
|
|
|
|
```python
|
|
from mail_intake.ingestion import MailIntakeReader
|
|
|
|
auth = ExchangeAuth(store=S3CredentialStore(...))
|
|
adapter = ExchangeAdapter(auth_provider=auth)
|
|
reader = MailIntakeReader(adapter)
|
|
|
|
for message in reader.iter_messages("query"):
|
|
print(message.subject)
|
|
```
|
|
|
|
The reader only depends on the adapter contract, so the rest of the pipeline
|
|
keeps working unchanged.
|
|
|
|
---
|
|
|
|
## ✅ Extension checklist
|
|
|
|
1. Implement the **public contract** — never subclass built-in adapters.
|
|
2. Keep transport in the adapter, parsing in parsers, auth in the provider.
|
|
3. Return the provider-native payload and let parsers normalize it.
|
|
4. Inject dependencies explicitly — no global state or env reads.
|
|
|
|
---
|
|
|
|
## 📚 Read Next
|
|
|
|
- [How to Use](02_how_to_use.md) — the built-in Gmail flow.
|
|
- [Development](04_development.md) — running tests and docs. |