{
  "markdown": "# inbox-to-action\n\n<!-- mcp-name: io.github.tarunlnmiit/inbox-to-action -->\n\n[![CI](https://github.com/tarunlnmiit/inbox-to-action/actions/workflows/ci.yml/badge.svg)](https://github.com/tarunlnmiit/inbox-to-action/actions/workflows/ci.yml)\n[![codecov](https://codecov.io/gh/tarunlnmiit/inbox-to-action/graph/badge.svg)](https://codecov.io/gh/tarunlnmiit/inbox-to-action)\n[![PyPI version](https://img.shields.io/pypi/v/inbox-to-action)](https://pypi.org/project/inbox-to-action/)\n[![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue)](https://www.python.org/)\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)\n[![GitHub stars](https://img.shields.io/github/stars/tarunlnmiit/inbox-to-action?style=social)](https://github.com/tarunlnmiit/inbox-to-action)\n\nPublished on [PyPI](https://pypi.org/project/inbox-to-action/) and listed on the\n[Official MCP Registry](https://registry.modelcontextprotocol.io) (`io.github.tarunlnmiit/inbox-to-action`),\n[Glama](https://glama.ai/mcp/servers/tarunlnmiit/inbox-to-action) (deployable release, Quality A),\nand [Smithery](https://smithery.ai/servers/tarungupta-y12/inbox-to-action) (MCPB bundle).\nRegistry manifests (`server.json`, `glama.json`) ship in the repo.\n\n[![inbox-to-action MCP server](https://glama.ai/mcp/servers/tarunlnmiit/inbox-to-action/badges/card.svg)](https://glama.ai/mcp/servers/tarunlnmiit/inbox-to-action)\n\n> One command. Your inbox triaged, summarized, drafted, and turned into tasks — in a single agentic pass.\n\n## Install\n\n```bash\npip install inbox-to-action          # or: pipx install inbox-to-action\nuvx inbox-to-action run --mock       # zero-install trial (uv)\npip install 'inbox-to-action[mcp]'   # + MCP server for Claude Code\ndocker run --rm ghcr.io/tarunlnmiit/inbox-to-action   # MCP server (stdio)\n```\n\nTry it with zero setup: `inbox-to-action run --mock` (bundled sample inbox).\n\n📖 **Full documentation → [docs/](docs/README.md)** — [install](docs/01-install.md) · [providers](docs/02-providers.md) · [Gmail OAuth](docs/03-gmail-oauth.md) · [multi-account](docs/04-multi-account.md) · [integrations](docs/05-integrations.md) · [MCP & Skill](docs/06-mcp-and-skill.md) · [config](docs/07-config-and-triage.md) · [troubleshooting](docs/08-troubleshooting.md) · [testing checklist](docs/09-testing-checklist.md). Quick version: [SETUP.md](SETUP.md).\n\n<p align=\"center\">\n  <img src=\"docs/demo.gif\" alt=\"inbox-to-action — one command triages the inbox into a report, drafts, and tasks\" width=\"820\">\n</p>\n\n---\n\n## Why this exists\n\nMost people process their inbox with **four** separate tools: an email client to read,\na task manager to capture to-dos, a calendar to block time, and (increasingly) an\nAI summarizer to make sense of long threads. Every message gets handled four times.\n\n`inbox-to-action` collapses all four into **one agentic pass**. Run one command and get\na unified triage report, drafted replies saved to Gmail, and extracted tasks — without\never leaving the terminal, and **without ever sending an email automatically**.\n\n## 🔒 Drafts only — never sends\n\nThis tool **cannot send email**. It requests only the Gmail `readonly` + `compose`\nscopes; there is no `send` scope and no send API call anywhere in the codebase\n(enforced by a test). Replies are saved as **Gmail drafts** for you to review and send.\n\nEmail bodies flow into the LLM prompt, so a hostile email could try to steer its own\nclassification or a drafted reply (prompt injection). Because every draft is saved for\n**human review** and nothing is ever sent automatically, the worst case is a draft you\nchoose not to send. See [SECURITY.md](SECURITY.md).\n\n## 🌐 What leaves your machine\n\n`inbox-to-action` reads your email. **Where your email content goes for classification\ndepends on the LLM provider you pick** — and the **default (`openrouter`) is a cloud\nprovider**, so an out-of-the-box run sends your subjects + bodies to a third party.\n\n| `PROVIDER=` | Email content goes to | Key |\n|---|---|---|\n| `ollama` | **Nowhere — fully local** 🔒 | none |\n| `claude` / `host` | Your existing Claude Code / Anthropic session (keyless) | none |\n| `openrouter` **(default)** · `openai` · `nim` · `anthropic` | **Third-party cloud** ☁️ | API key |\n\n**Want privacy?** Use `ollama` (local) or `claude` (keyless) so nothing is transmitted\nto a third party. `--telegram` / `--todoist` also push subjects/tasks off-box (opt-in).\nFull breakdown → **[PRIVACY.md](PRIVACY.md)**.\n\n> Note: `triage-report.md` and `tasks.md` are written to your working directory and\n> contain private email content. If you run inside a git repo, add them to `.gitignore`.\n\n## What it does\n\n1. **Fetches** unread email from Gmail (last 24h by default).\n2. **Classifies** each into `action_needed` · `fyi` · `newsletter` · `noise`.\n3. **Summarizes** long threads (>500 words) into two lines.\n4. **Extracts** tasks with deadlines → local `tasks.md` (optional Todoist via `--todoist`).\n5. **Drafts** replies for `action_needed` mail → saved as Gmail **drafts**.\n6. **Flags** emails that need a calendar block.\n\nFinal output: a single **`triage-report.md`** with a section per category, drafted-reply\npreviews, a tasks summary, and a calendar list.\n\n## Architecture — the agent loop\n\nThe model's own classification of each email drives which tools fire next — the pipeline\nis **not** hardcoded. The same tool functions back the CLI agent and the MCP server.\n\n```mermaid\nflowchart TD\n  CLI[main.py · typer] --> AGENT[agent.py · agentic loop]\n  AGENT -->|model picks tools per email| T1[classify_email]\n  AGENT --> T2[summarize_thread]\n  AGENT --> T3[extract_tasks]\n  AGENT --> T4[draft_reply → Gmail draft]\n  AGENT --> T5[flag_for_calendar]\n  T1 & T2 & T3 & T4 & T5 --> LLM[llm_client.py · pluggable providers]\n  LLM --> P1[OpenRouter / Ollama / NIM / OpenAI<br/>OpenAI-compatible HTTP]\n  LLM --> P2[Anthropic · official SDK · keyless via ant auth login]\n  AGENT --> REPORT[report.py → triage-report.md]\n```\n\n```\nfetch → for each email:  classify ─┬─ action_needed → extract_tasks + draft_reply + flag_calendar\n                                   ├─ fyi / newsletter / noise → record only\n                                   └─ (long thread) → summarize\n                          → render triage-report.md\n```\n\n## Quick start (2 minutes)\n\n```bash\ngit clone https://github.com/tarunlnmiit/inbox-to-action.git && cd inbox-to-action\npython3 -m venv .venv && source .venv/bin/activate\npip install -e '.[mcp]'        # installs the `inbox-to-action` command\ncp .env.example .env\n```\n\nThis installs an `inbox-to-action` console command (and the `python -m\ninbox_to_action.mcp_server` entry point used by Claude Code / Glama).\n\n### Free-first: run end-to-end on zero spend\n\nPick whichever keyless/free path you like — all run the full pipeline at no cost:\n\n**Option A — `claude` CLI (keyless, fastest; uses your Claude Code login):**\n```bash\nPROVIDER=claude inbox-to-action run --mock     # no API key; needs `claude` on PATH\n```\n\n**Option B — Ollama (truly keyless, fully local):**\n```bash\nollama serve            # in another terminal\nollama pull llama3.1\nPROVIDER=ollama inbox-to-action run --mock     # uses bundled sample inbox\n```\n\n**Option C — OpenRouter free model (free signup key):**\n```bash\n# put OPENROUTER_API_KEY in .env (free models, $0 spend)\ninbox-to-action run --mock                      # default PROVIDER=openrouter\n```\n\n**Option D — inside Claude Code (keyless, Claude Code is the LLM):** see below.\n\n`--mock` uses the bundled sample inbox so you can see a full report with **zero Gmail\nsetup**. Drop `--mock` once you've authorized Gmail. Free OpenRouter models are often\nrate-limited; the client auto-rotates a fallback list and retries with backoff.\n\n### Real inbox\n\n```bash\n# 1. Create OAuth credentials in Google Cloud Console (Desktop app),\n#    download client_secret.json into the project, then:\ninbox-to-action auth                 # one-time consent (read + compose only)\ninbox-to-action run --since 24h --no-drafts   # safe first pass: report only, no writes\ninbox-to-action run --since 24h      # triage the last day (creates Gmail drafts)\ninbox-to-action run --since 3d --max 40 --todoist\n```\n\n- `--no-drafts` — classify, summarize, extract tasks, write the report, but create\n  **no** Gmail drafts. Recommended for a first run.\n- `--max N` — cap emails per account (default 25) to bound cost/volume.\n- Automated **no-reply** senders (security alerts, notifications) never get a drafted\n  reply — the report notes them instead.\n\n### Telegram summary (`--telegram`)\n\nPush a concise summary to your phone after each run — counts, action-needed subjects\n(with draft-ready status), extracted tasks, and a link to your Gmail Drafts.\n\n```bash\n# 1. In Telegram, message @BotFather → /newbot → copy the bot token.\n# 2. Message your new bot once (say \"hi\"), then open:\n#    https://api.telegram.org/bot<token>/getUpdates  → copy \"chat\":{\"id\": ...}.\n# 3. Put both in .env:\n#    TELEGRAM_BOT_TOKEN=...   TELEGRAM_CHAT_ID=...\ninbox-to-action run --since 24h --telegram\n```\n\nOff by default (opt-in flag). A send failure never breaks the run.\n**Privacy:** this sends email subjects + extracted tasks to Telegram's servers (into\nyour own chat). It's notification only — it never sends email.\n\n### Multiple accounts (Gmail + Google Workspace)\n\nDeclare accounts in `config.json` — one merged report, each email tagged with its\naccount. Personal Gmail and Workspace both use the Gmail path (Workspace may need\nyour admin to allow the OAuth app).\n\n```json\n{\n  \"accounts\": [\n    { \"id\": \"personal\", \"kind\": \"gmail\", \"label\": \"Personal Gmail\" },\n    { \"id\": \"work\",     \"kind\": \"gmail\", \"label\": \"Workspace\" }\n  ]\n}\n```\n\n```bash\ninbox-to-action auth --account personal   # authorize each account once\ninbox-to-action auth --account work\ninbox-to-action run --since 24h           # fetches + triages across all accounts\n```\n\nMultiple personal Gmail accounts can reuse one `client_secret.json` — each gets its\nown cached token (`~/.config/inbox-to-action/tokens/<id>.json`). With no `accounts`\nblock, the tool uses a single default Gmail account (backwards compatible).\n\n## Use inside Claude Code (keyless)\n\nWhen run inside Claude Code, **Claude Code is the LLM** — no provider key needed.\nTwo integration paths ship in this repo:\n\n### MCP server\nExposes IO-only tools (`fetch_emails`, `save_gmail_draft`, `append_tasks`, `write_report`).\nClaude Code does the classify/summarize/extract/draft reasoning itself and calls these.\n\n```bash\n# after `pip install -e '.[mcp]'`\nclaude mcp add inbox-to-action -- python -m inbox_to_action.mcp_server\n```\n\nThis is the same stdio server that MCP registries (e.g. [Glama](https://glama.ai)) build from\nthe bundled `Dockerfile` (`CMD python -m inbox_to_action.mcp_server`).\n\n### Skill\nCopy `skills/inbox-to-action/` into your Claude Code skills directory, then type\n`/inbox-to-action`. The skill instructs Claude Code to fetch, reason, draft, and write\nthe report — keyless.\n\n## Anthropic (keyless via `ant auth login`)\n\nThe Anthropic provider uses the official SDK with a zero-arg client, so it picks up your\n`ant auth login` OAuth profile — **no `ANTHROPIC_API_KEY` required**:\n\n```bash\nant auth login\nPROVIDER=anthropic inbox-to-action run --mock   # default model: claude-opus-4-8\n```\n\n## Configuration\n\nAll keys live in `.env` (`.env.example` is committed). Switch providers with `PROVIDER`:\n`openrouter` (default) · `ollama` · `nim` · `openai` · `anthropic` · `claude` · `host`.\n\n### Configure triage (make it *yours*)\n\nThe default buckets are generic — newsletters and job alerts are treated as no-action.\nOverride that with `config.json` (copy `config.example.json`). Two layers:\n\n- **`rules`** — deterministic `field → category` overrides applied **before** the LLM\n  (fast, free, exact). First match wins. `field` ∈ `sender | subject | body | any`.\n- **`triage_instructions`** — freeform guidance injected into the classifier prompt for\n  nuance the model interprets.\n\n```json\n{\n  \"triage_instructions\": \"I'm job hunting in ML/AI — treat relevant job alerts as action_needed.\",\n  \"rules\": [\n    { \"field\": \"sender\",  \"contains\": \"hirist.tech\", \"category\": \"action_needed\" },\n    { \"field\": \"subject\", \"contains\": \"invoice\",     \"category\": \"noise\" }\n  ]\n}\n```\n\n```bash\ncp config.example.json config.json   # edit to taste (config.json is gitignored)\ninbox-to-action run --since 24h                 # auto-loads ./config.json\ninbox-to-action run --config /path/to/other.json\n```\n\nQuick override without a file: `TRIAGE_INSTRUCTIONS=\"treat job alerts as action_needed\"`.\n\n## Tests\n\n```bash\npytest --cov=.        # 100+ tests, ~89% coverage, incl. the never-send security test\n```\n\n## Docs\n\n- **[docs/](docs/README.md)** — full guides with screenshots: install, every LLM provider, Gmail OAuth, multi-account, integrations, MCP & Skill, config, troubleshooting, testing checklist.\n- [SETUP.md](SETUP.md) — 5-minute quickstart.\n- [PRIVACY.md](PRIVACY.md) — what leaves your machine, per provider.\n- [SECURITY.md](SECURITY.md) — never-send invariant + vulnerability disclosure.\n- [CHANGELOG.md](CHANGELOG.md) — version history.\n- [CONTRIBUTING.md](CONTRIBUTING.md) — dev setup + the never-send rule.\n- [CLAUDE.md](CLAUDE.md) — project map for Claude Code.\n\n## Built with\n\nThis project demonstrates the contract skills:\n\n- **Agentic orchestration** — model-driven, per-email tool selection (no hardcoded pipeline).\n- **Function calling** — typed tool schemas (`agent.TOOL_SCHEMAS`) shared by the CLI agent and MCP server.\n- **Multi-API integration** — Gmail + LLM + Todoist in one flow.\n- **Pluggable LLM providers** — one `llm_client` swaps OpenRouter / Ollama / NIM / OpenAI / Anthropic.\n- **Claude Code integration** — first-class MCP server **and** Skill, both keyless.\n\n## License\n\nMIT — see [LICENSE](LICENSE).\n",
  "bytes": 13814,
  "sha": "053da3fc54b476952a69db7bb2edac50cf92b62ce797ccdaa356569e4184b19f",
  "repo_slug": "tarunlnmiit/inbox-to-action",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_tarunlnmiit_inbox_to_action_54a58993/readme"
}