Skip to main content
Glama
ArgonTechSolutions

Agentic Mail Bridge

README.md
# Agentic Mail Bridge

This is a local, stdio-only MCP server for explicitly labeled Gmail/Google Workspace and Purelymail accounts. It is designed for supervised reading, drafting, and sending across several work identities without a default sender.

Licensed under [MIT](LICENSE). It is an ArgonTechSolutions project, but it runs locally and does not operate a hosted mailbox service.

## Security model

- Account credentials remain in an age-encrypted vault.
- On Windows, each device keeps its age identity in a DPAPI `CurrentUser`-protected local file; other platforms use the native OS keyring. Enrollment moves only public requests and encrypted vault bundles.
- Mail bodies and attachments are untrusted input. Bodies are bounded, labeled as untrusted, and attachments are metadata-only.
- Local state stores only provider cursors, draft hashes, timestamps, delivery receipts, and bounded delivery authorizations.
- Sending is bridge-enforced: a send needs a current authorization ID that only the local CLI can create after an interactive user confirmation. An agent cannot infer it from wording in a prompt.
- Sends target an existing provider draft and re-check its SHA-256 immediately before delivery. Unknown SMTP outcomes are never retried automatically.
- Scheduled triage can create drafts, but cannot send. Mark-as-read automation requires a named exact read rule.
- The bridge opens no HTTP server. OAuth uses a temporary `127.0.0.1` callback only during local Gmail enrollment.

## Build and check

```powershell
npm install --cache .npm-cache
npm run check
```

`npm run check` performs typechecking, the fake-provider test suite, and a production build. Do not run `npm audit fix` automatically; review production findings before changing dependency versions.
The current production audit note is recorded in `docs/dependency-audit.md`.

## Initialize the bridge vault

```powershell
npm run build
node .\dist\cli.js device init --name windows-pc
node .\dist\cli.js device request --out enrollment-laptop.json
```

This creates the bridge's own local, age-encrypted vault. It does not create, import, or expose a separate secrets repository. Approve/import requests only through a trusted route. Revoking a device does not erase old encrypted vault copies, so rotate provider credentials if a device was compromised.

## Enroll providers

Gmail/Workspace:

```powershell
node .\dist\cli.js account add-gmail --label personal --client-json C:\secure\google-oauth-client.json
```

Purelymail:

```powershell
node .\dist\cli.js account add-purelymail --label work
```

Each command is local and supervised. No real credentials, OAuth files, app passwords, message content, or vault files belong in Git or chat. The bridge stores enrolled account credentials in its encrypted vault. An optional `--secrets-client <name>` mode is available only to people who already operate a separate local SOPS/age secrets store; it is not required and is not set up by this project. See `docs/google-oauth.md`, `docs/purelymail.md`, and `docs/device-enrollment.md`.

## Register with Codex

Build first, then print a scoped snippet:

```powershell
powershell -ExecutionPolicy Bypass -File .\scripts\print-codex-config.ps1
```

The script only prints configuration; it does not edit global Codex configuration, open a browser, create a schedule, or restart a session. Review the output before adding it manually.

## Review and authorize delivery

Creating a draft never sends it. Before sending, an agent must first call `mail_review_draft` and use the returned current draft ID and SHA-256. For a one-time send, the user reviews that exact provider draft locally and creates a short-lived authorization:

```powershell
node .\dist\cli.js delivery approve --account demo --draft-id <provider-draft-id>
```

The command prints the normalized current draft, asks for an interactive confirmation, then returns a `once_...` authorization ID. Supply that ID explicitly to the agent together with the reviewed draft. It is valid only for that account, provider draft, and exact current content hash, and can be used once.

For genuinely low-stakes recurring mail, the user can explicitly create a narrow, expiring rule:

```powershell
node .\dist\cli.js delivery preapprove --account demo --recipient person@example.com --subject-prefix "Status: " --max-sends 3 --expires-at "2026-09-07T00:00:00.000Z"
```

This returns a `rule_...` authorization ID. It permits at most the stated number of sends, only from the stated account, to exactly that one recipient, with no CC/BCC, and with the stated subject prefix. It deliberately does not authorize arbitrary recipients or allow an agent to discover or create rules. Treat the returned ID as an explicit capability: provide it to an agent only when you intend that bounded behavior.

If Gmail changes a draft through autosave, `mail_review_draft` returns the canonical Gmail draft ID and new hash. Re-review and create a new one-time authorization; never substitute a Gmail message ID for a draft ID.

## Scheduled triage

Use the exact baseline in `docs/scheduled-triage.md`. Start interactively and read-only. Create schedules and enable live read rules only after separate approval and manual review.