inbox-mcp
# inbox-mcp
A privacy-preserving **MCP server** (built on [FastMCP](https://github.com/jlowin/fastmcp))
that lets a coding agent (Claude Code / Codex / any MCP client) triage your inbox
end to end: **read & mask email → upsert calendar events → draft replies → post a
digest to Slack** — all driven by your own Google + Slack credentials.
Every email body, header and snippet is run through a local **masking pipeline**
*before* it ever reaches the model: secrets are irreversibly redacted, contact PII
is swapped for reversible tokens, and only the human-facing write paths
(calendar / Slack / reply draft) restore the real values. Names go to your own
model; passwords, API keys and card numbers never come back at all.
```
Gmail ──► [ mask ] ──► agent reasons over safe text ──► Calendar (upsert)
├──► Gmail reply DRAFT (never sent)
└──► Slack digest (one per run)
```
## Features
- **Unified tools** with service prefixes: `gmail_*` (search / read / label /
reply-draft), `calendar_*` (idempotent upsert + list), `slack_*` (post a report).
- **Masking-first**: a Gitleaks-style secret pass → allowlist → PII tokenizer →
in-memory vault, all in-process. See [docs/masking.md](docs/masking.md).
- **Idempotent calendar upserts** keyed by `iCalUID`, so re-running never
double-books. Timed, all-day and multi-day events supported.
- **Reply drafts only** — a threaded draft syncs to your mail client; the server
never sends.
- **Apple Mail deep-links** (`message://`) so a digest line opens the original mail.
- **Configurable calendar routing** — define any number of categories via
`GOOGLE_CALENDAR_ID_<KEY>` environment variables; nothing is hardcoded.
- **No telemetry, no external services** beyond Google + Slack. Secrets stay in a
local, gitignored env file.
## How it works
The server is stateless transport over the Google + Slack APIs plus the masking
layer. It is meant to be **registered with an interactive agent** and driven by a
prompt (e.g. a daily inbox-triage routine). Scheduling that prompt is left to you
or your automation daemon — this repo ships the tools, not a scheduler. See
[docs/operating-handoff.md](docs/operating-handoff.md).
## Requirements
- Python ≥ 3.13, managed with [`uv`](https://docs.astral.sh/uv/)
- A Google OAuth **Desktop-app** `credential.json` with the Gmail API + Calendar
API enabled (scopes `gmail.modify` + `calendar`)
- A Slack **Bot User OAuth token** (`xoxb-…`) with `chat:write`, invited to your
target channel
- *(optional)* `presidio-analyzer` + a spaCy model for full PERSON/LOCATION NER
## Quick start
```sh
# 1. install
uv sync # add `--extra nlp` for Presidio (English-only) PERSON/LOCATION masking; names are unmasked by default
# 2. configure (secrets live OUTSIDE the repo)
cp .env.example ~/.config/inbox-mcp/.env
chmod 600 ~/.config/inbox-mcp/.env
$EDITOR ~/.config/inbox-mcp/.env # paths, Slack token, calendar IDs
# 3. run the server (first run opens a browser for Google consent → token.json)
uv run inbox-mcp
# 4. tests
uv run pytest -q
```
### Register with an agent
Point the agent at this directory; no secrets go in the registration (the server
loads them from `~/.config/inbox-mcp/.env`):
```sh
claude mcp add inbox_mcp -- uv run --directory /path/to/inbox-mcp inbox-mcp
# or
codex mcp add inbox_mcp -- uv run --directory /path/to/inbox-mcp inbox-mcp
```
## Documentation
| Doc | What |
|-----|------|
| [docs/configuration.md](docs/configuration.md) | OAuth, Slack, calendar IDs, env vars, registration |
| [docs/tools.md](docs/tools.md) | Every tool: inputs, outputs, read-only vs write |
| [docs/masking.md](docs/masking.md) | The privacy pipeline and how to extend it |
| [docs/operating-handoff.md](docs/operating-handoff.md) | Running this to process a real inbox (CWD, `operating/`, scheduling) |
| [docs/daily-run-prompt.template.md](docs/daily-run-prompt.template.md) | A generic inbox-triage prompt to copy & personalize |
| [docs/reply-style-guide.template.md](docs/reply-style-guide.template.md) | A generic reply-voice guide to copy & personalize |
| [docs/scheduler-inject.template.md](docs/scheduler-inject.template.md) | The wrapper your scheduler injects each run (pointer + guardrails + completion sentinel) |
## Security
- Secrets (`credential.json`, `token.json`, `.env`) are **gitignored** and belong
in `~/.config/inbox-mcp/` (chmod 600) — never in the repo or agent config.
- Masking runs **before** any email content reaches the model; secrets are
redacted irreversibly and never restored.
- Reply drafts are **never auto-sent**.
- Calendar writes target **secondary** calendars you configure, not your primary.
## License
[MIT](LICENSE).
## Contributing
See [CONTRIBUTING.md](CONTRIBUTING.md).
TDQS
Scored across 10 tools
Most tools have clearly distinct purposes: search vs. read vs. label vs. calendar vs. Slack. Slight overlap exists between gmail_search_emails and gmail_search_email_ids (both search) and between gmail_apply_labels and gmail_bulk_label_matching (both label), but descriptions clarify the intended use cases.
All tool names use lowercase snake_case with domain prefixes (gmail_, calendar_, slack_) and a verb_noun structure. Minor deviations like 'bulk_label_matching' are still readable and follow the general pattern, so the set feels consistent.
10 tools is a well-scoped size for an inbox automation server covering email search, read, labeling, draft creation, calendar upsert/list, and Slack posting. Each tool earns its place and the count is neither thin nor bloated.
The tool surface covers the core workflow of searching and reading emails, applying labels, drafting replies, upserting calendar events, listing them, and posting to Slack. Minor gaps exist such as no email send (only draft), no calendar delete, and no Slack read, but these don't block the primary intended workflow.