# agentmailkit

> MCP server and CLI that runs scheduled, LLM-written email digests on the local machine. Jobs read local files, git trees, RSS, arXiv and weather, render an HTML email, and deliver through Gmail or SMTP. Nothing sends unless a caller passes `dry_run=false`.

## What it is

Two files define an email: a JSON job (id, cron schedule, sources, theme, delivery) and a markdown prompt. One fixed pipeline runs them: gather sources, render prompt, generate, gate, theme, deliver, post. Python 3.9+ for the CLI; Python 3.10+ for the MCP server, which is the MCP SDK's floor.

Five example jobs ship inside the wheel, so `list_jobs` returns results on a fresh install with no config file.

## Install

```bash
pip install 'agentmailkit[mcp]'
```

## Register

Claude Code:

```bash
claude mcp add agentmailkit -- agentmailkit mcp
```

Codex, in `~/.codex/config.toml`:

```toml
[mcp_servers.agentmailkit]
command = "agentmailkit"
args = ["mcp"]
```

Any other MCP client: command `agentmailkit`, args `["mcp"]`, transport stdio. Add `-C /path/to/agentmailkit.json` to the args to pin a config file; otherwise config is discovered from the server's working directory exactly as the CLI discovers it.

## Tools

- `list_jobs()` - returns every configured job with `id`, `schedule`, `sources`, `delivery`, `render`, `model`, `status`. No arguments. Call this first; every other tool needs a `job_id` from it.
- `preview_job(job_id: str, model: str | None = None)` - renders one job to a local HTML file and returns `subject`, `path`, `chars`, `truncated`, `body` (first 20000 chars), `would_deliver_to`. Cannot send: delivery is rewritten to the file backend before the pipeline runs. Pass `model="echo"` for an offline, key-free structural preview.
- `run_job(job_id: str, dry_run: bool = True)` - runs one job through the real pipeline and returns the run receipt. **`dry_run=false` sends real email from the configured inbox.** The default `true` stops before delivery.
- `list_plugins(kind: str | None = None)` - returns registered plugin names by kind. `kind` is one of `source`, `model`, `gate`, `delivery`, `render`, `post`; omit it for all six.

## Example

```
list_jobs()
-> ["curiosity", "daily-brief", "morning-brief", "repo-pulse", "research-digest"]
   each with schedule ("0 7 * * *"), sources ("weather=weather:Brooklyn#2", ...),
   delivery ({"backend": "stdout", "to": ""}), render, model, status

preview_job(job_id="morning-brief", model="echo")
-> {"job": "morning-brief", "subject": "Morning Brief - 2026-08-26",
    "path": "out/mcp-preview/2026-08-26-morning-brief.html", "chars": 19069,
    "sent": false, "would_deliver_to": {"backend": "stdout", "to": ""}}
```

That sequence needs no API key, no config file and no network delivery. Only escalate to `run_job` once a preview looks right, and only pass `dry_run=false` when the user has asked for real mail to be sent.

## When not to use it

- Reading, searching or replying to an inbox. agentmailkit only composes and sends outbound digests; it never reads mail.
- One-off transactional email from an application. Use an email API directly.
- Anything that must run in the cloud on the user's behalf. The design point is that the job runs where the user's files are.
- Autonomous action. The engine is deterministic and does not decide what to do; the model only writes the prose inside a fixed pipeline.

## Links

- Source: https://github.com/ariaxhan/agentmailkit
- Setup runbook for agents: https://github.com/ariaxhan/agentmailkit/blob/main/AGENTS.md
- Docs: https://ariaxhan.com/projects/agentmailkit/
- PyPI: https://pypi.org/project/agentmailkit/
- MCP registry name: io.github.ariaxhan/agentmailkit
