Skip to main content
Glama
tarunlnmiit

inbox-to-action-mcp

by tarunlnmiit

inbox-to-action

CI codecov PyPI version Python 3.11+ License: MIT GitHub stars

Published on PyPI and listed on the Official MCP Registry (io.github.tarunlnmiit/inbox-to-action), Glama (deployable release, Quality A), and Smithery (MCPB bundle). Registry manifests (server.json, glama.json) ship in the repo.

inbox-to-action MCP server

One command. Your inbox triaged, summarized, drafted, and turned into tasks — in a single agentic pass.

Install

pip install inbox-to-action          # or: pipx install inbox-to-action
uvx inbox-to-action run --mock       # zero-install trial (uv)
pip install 'inbox-to-action[mcp]'   # + MCP server for Claude Code
docker run --rm ghcr.io/tarunlnmiit/inbox-to-action   # MCP server (stdio)

Try it with zero setup: inbox-to-action run --mock (bundled sample inbox).

📖 Full documentation → docs/install · providers · Gmail OAuth · multi-account · integrations · MCP & Skill · config · troubleshooting · testing checklist. Quick version: SETUP.md.


Related MCP server: inbox-mcp

Why this exists

Most people process their inbox with four separate tools: an email client to read, a task manager to capture to-dos, a calendar to block time, and (increasingly) an AI summarizer to make sense of long threads. Every message gets handled four times.

inbox-to-action collapses all four into one agentic pass. Run one command and get a unified triage report, drafted replies saved to Gmail, and extracted tasks — without ever leaving the terminal, and without ever sending an email automatically.

🔒 Drafts only — never sends

This tool cannot send email. It requests only the Gmail readonly + compose scopes; there is no send scope and no send API call anywhere in the codebase (enforced by a test). Replies are saved as Gmail drafts for you to review and send.

Email bodies flow into the LLM prompt, so a hostile email could try to steer its own classification or a drafted reply (prompt injection). Because every draft is saved for human review and nothing is ever sent automatically, the worst case is a draft you choose not to send. See SECURITY.md.

🌐 What leaves your machine

inbox-to-action reads your email. Where your email content goes for classification depends on the LLM provider you pick — and the default (openrouter) is a cloud provider, so an out-of-the-box run sends your subjects + bodies to a third party.

PROVIDER=

Email content goes to

Key

ollama

Nowhere — fully local 🔒

none

claude / host

Your existing Claude Code / Anthropic session (keyless)

none

openrouter (default) · openai · nim · anthropic

Third-party cloud ☁️

API key

Want privacy? Use ollama (local) or claude (keyless) so nothing is transmitted to a third party. --telegram / --todoist also push subjects/tasks off-box (opt-in). Full breakdown → PRIVACY.md.

Note: triage-report.md and tasks.md are written to your working directory and contain private email content. If you run inside a git repo, add them to .gitignore.

What it does

  1. Fetches unread email from Gmail (last 24h by default).

  2. Classifies each into action_needed · fyi · newsletter · noise.

  3. Summarizes long threads (>500 words) into two lines.

  4. Extracts tasks with deadlines → local tasks.md (optional Todoist via --todoist).

  5. Drafts replies for action_needed mail → saved as Gmail drafts.

  6. Flags emails that need a calendar block.

Final output: a single triage-report.md with a section per category, drafted-reply previews, a tasks summary, and a calendar list.

Architecture — the agent loop

The model's own classification of each email drives which tools fire next — the pipeline is not hardcoded. The same tool functions back the CLI agent and the MCP server.

flowchart TD
  CLI[main.py · typer] --> AGENT[agent.py · agentic loop]
  AGENT -->|model picks tools per email| T1[classify_email]
  AGENT --> T2[summarize_thread]
  AGENT --> T3[extract_tasks]
  AGENT --> T4[draft_reply → Gmail draft]
  AGENT --> T5[flag_for_calendar]
  T1 & T2 & T3 & T4 & T5 --> LLM[llm_client.py · pluggable providers]
  LLM --> P1[OpenRouter / Ollama / NIM / OpenAI<br/>OpenAI-compatible HTTP]
  LLM --> P2[Anthropic · official SDK · keyless via ant auth login]
  AGENT --> REPORT[report.py → triage-report.md]
fetch → for each email:  classify ─┬─ action_needed → extract_tasks + draft_reply + flag_calendar
                                   ├─ fyi / newsletter / noise → record only
                                   └─ (long thread) → summarize
                          → render triage-report.md

Quick start (2 minutes)

git clone https://github.com/tarunlnmiit/inbox-to-action.git && cd inbox-to-action
python3 -m venv .venv && source .venv/bin/activate
pip install -e '.[mcp]'        # installs the `inbox-to-action` command
cp .env.example .env

This installs an inbox-to-action console command (and the python -m inbox_to_action.mcp_server entry point used by Claude Code / Glama).

Free-first: run end-to-end on zero spend

Pick whichever keyless/free path you like — all run the full pipeline at no cost:

Option A — claude CLI (keyless, fastest; uses your Claude Code login):

PROVIDER=claude inbox-to-action run --mock     # no API key; needs `claude` on PATH

Option B — Ollama (truly keyless, fully local):

ollama serve            # in another terminal
ollama pull llama3.1
PROVIDER=ollama inbox-to-action run --mock     # uses bundled sample inbox

Option C — OpenRouter free model (free signup key):

# put OPENROUTER_API_KEY in .env (free models, $0 spend)
inbox-to-action run --mock                      # default PROVIDER=openrouter

Option D — inside Claude Code (keyless, Claude Code is the LLM): see below.

--mock uses the bundled sample inbox so you can see a full report with zero Gmail setup. Drop --mock once you've authorized Gmail. Free OpenRouter models are often rate-limited; the client auto-rotates a fallback list and retries with backoff.

Real inbox

# 1. Create OAuth credentials in Google Cloud Console (Desktop app),
#    download client_secret.json into the project, then:
inbox-to-action auth                 # one-time consent (read + compose only)
inbox-to-action run --since 24h --no-drafts   # safe first pass: report only, no writes
inbox-to-action run --since 24h      # triage the last day (creates Gmail drafts)
inbox-to-action run --since 3d --max 40 --todoist
  • --no-drafts — classify, summarize, extract tasks, write the report, but create no Gmail drafts. Recommended for a first run.

  • --max N — cap emails per account (default 25) to bound cost/volume.

  • Automated no-reply senders (security alerts, notifications) never get a drafted reply — the report notes them instead.

Telegram summary (--telegram)

Push a concise summary to your phone after each run — counts, action-needed subjects (with draft-ready status), extracted tasks, and a link to your Gmail Drafts.

# 1. In Telegram, message @BotFather → /newbot → copy the bot token.
# 2. Message your new bot once (say "hi"), then open:
#    https://api.telegram.org/bot<token>/getUpdates  → copy "chat":{"id": ...}.
# 3. Put both in .env:
#    TELEGRAM_BOT_TOKEN=...   TELEGRAM_CHAT_ID=...
inbox-to-action run --since 24h --telegram

Off by default (opt-in flag). A send failure never breaks the run. Privacy: this sends email subjects + extracted tasks to Telegram's servers (into your own chat). It's notification only — it never sends email.

Multiple accounts (Gmail + Google Workspace)

Declare accounts in config.json — one merged report, each email tagged with its account. Personal Gmail and Workspace both use the Gmail path (Workspace may need your admin to allow the OAuth app).

{
  "accounts": [
    { "id": "personal", "kind": "gmail", "label": "Personal Gmail" },
    { "id": "work",     "kind": "gmail", "label": "Workspace" }
  ]
}
inbox-to-action auth --account personal   # authorize each account once
inbox-to-action auth --account work
inbox-to-action run --since 24h           # fetches + triages across all accounts

Multiple personal Gmail accounts can reuse one client_secret.json — each gets its own cached token (~/.config/inbox-to-action/tokens/<id>.json). With no accounts block, the tool uses a single default Gmail account (backwards compatible).

Use inside Claude Code (keyless)

When run inside Claude Code, Claude Code is the LLM — no provider key needed. Two integration paths ship in this repo:

MCP server

Exposes IO-only tools (fetch_emails, save_gmail_draft, append_tasks, write_report). Claude Code does the classify/summarize/extract/draft reasoning itself and calls these.

# after `pip install -e '.[mcp]'`
claude mcp add inbox-to-action -- python -m inbox_to_action.mcp_server

This is the same stdio server that MCP registries (e.g. Glama) build from the bundled Dockerfile (CMD python -m inbox_to_action.mcp_server).

Skill

Copy skills/inbox-to-action/ into your Claude Code skills directory, then type /inbox-to-action. The skill instructs Claude Code to fetch, reason, draft, and write the report — keyless.

Anthropic (keyless via ant auth login)

The Anthropic provider uses the official SDK with a zero-arg client, so it picks up your ant auth login OAuth profile — no ANTHROPIC_API_KEY required:

ant auth login
PROVIDER=anthropic inbox-to-action run --mock   # default model: claude-opus-4-8

Configuration

All keys live in .env (.env.example is committed). Switch providers with PROVIDER: openrouter (default) · ollama · nim · openai · anthropic · claude · host.

Configure triage (make it yours)

The default buckets are generic — newsletters and job alerts are treated as no-action. Override that with config.json (copy config.example.json). Two layers:

  • rules — deterministic field → category overrides applied before the LLM (fast, free, exact). First match wins. fieldsender | subject | body | any.

  • triage_instructions — freeform guidance injected into the classifier prompt for nuance the model interprets.

{
  "triage_instructions": "I'm job hunting in ML/AI — treat relevant job alerts as action_needed.",
  "rules": [
    { "field": "sender",  "contains": "hirist.tech", "category": "action_needed" },
    { "field": "subject", "contains": "invoice",     "category": "noise" }
  ]
}
cp config.example.json config.json   # edit to taste (config.json is gitignored)
inbox-to-action run --since 24h                 # auto-loads ./config.json
inbox-to-action run --config /path/to/other.json

Quick override without a file: TRIAGE_INSTRUCTIONS="treat job alerts as action_needed".

Tests

pytest --cov=.        # 100+ tests, ~89% coverage, incl. the never-send security test

Docs

  • docs/ — full guides with screenshots: install, every LLM provider, Gmail OAuth, multi-account, integrations, MCP & Skill, config, troubleshooting, testing checklist.

  • SETUP.md — 5-minute quickstart.

  • PRIVACY.md — what leaves your machine, per provider.

  • SECURITY.md — never-send invariant + vulnerability disclosure.

  • CHANGELOG.md — version history.

  • CONTRIBUTING.md — dev setup + the never-send rule.

  • CLAUDE.md — project map for Claude Code.

Built with

This project demonstrates the contract skills:

  • Agentic orchestration — model-driven, per-email tool selection (no hardcoded pipeline).

  • Function calling — typed tool schemas (agent.TOOL_SCHEMAS) shared by the CLI agent and MCP server.

  • Multi-API integration — Gmail + LLM + Todoist in one flow.

  • Pluggable LLM providers — one llm_client swaps OpenRouter / Ollama / NIM / OpenAI / Anthropic.

  • Claude Code integration — first-class MCP server and Skill, both keyless.

License

MIT — see LICENSE.

Available Tools

4 tools
append_tasksA

Append tasks to tasks.md. Each item: {text, deadline?}.

ParametersJSON Schema
NameRequiredDescriptionDefault
pathNotasks.md
itemsYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A3.5/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries full burden but only states 'Append tasks' – it does not disclose whether the file is created if missing, how existing content is handled, or what the return value is. Behavioral traits like side effects or error conditions are absent.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is extremely concise at two short sentences, with no wasted words. Every part is necessary and front-loaded: first sentence states action, second sentence specifies format. Ideal for quick comprehension.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the simple operation (append tasks to a file) and the availability of an output schema, the description is minimally adequate. However, it omits details like file creation behavior and error handling, which could be important for agent decision-making. It covers the essential purpose but lacks completeness for a robust tool experience.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema has 0% description coverage and defines items as an array of objects with 'additionalProperties: true', which is very permissive. The description provides critical structure by specifying 'Each item: {text, deadline?}' – this adds meaningful constraints beyond the schema and clarifies the intended format. However, the 'path' parameter is not explained beyond the schema default.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the verb 'Append', the target resource 'tasks.md', and the format of each item ('{text, deadline?}'). It is specific and leaves no ambiguity about what the tool does. The sibling tools are unrelated, so no confusion arises.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No explicit guidance is provided on when to use this tool versus alternatives. Sibling tools are for different purposes, but the description does not include any contextual cues or prerequisites, leaving the agent to infer usage from naming alone.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fetch_emailsA

Fetch unread emails (JSON list). Use mock=True for the fixture demo.

Returns a JSON array of {id, sender, subject, body, thread_id, received}. Reason over these yourself: classify each, summarize long ones, extract tasks and draft replies for action_needed items, then call the IO tools.

ParametersJSON Schema
NameRequiredDescriptionDefault
mockNo
sinceNo24h

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A3.8/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden. It discloses the output format ('JSON array of {id, sender, ...}') and implies a read-only operation ('fetch'). It also instructs the agent to process the emails, adding behavioral context.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is four sentences and reasonably concise, but contains extraneous workflow instructions ('Reason over these yourself...') that could be omitted or placed elsewhere. It is front-loaded but includes instructions better suited for a prompt.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's simplicity (2 optional parameters) and the presence of an output schema, the description captures the main purpose and return format. However, missing explanation for the 'since' parameter and lack of error handling details reduce completeness.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%. The description only mentions the 'mock' parameter ('Use mock=True for the fixture demo'), but does not explain the 'since' parameter, which has a default of '24h'. The agent is left to infer its purpose.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states 'Fetch unread emails (JSON list)', using a specific verb and resource. This distinguishes it from sibling tools like append_tasks, save_gmail_draft, and write_report, which perform different operations.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides a concrete usage scenario ('Use mock=True for the fixture demo') and implies a workflow ('then call the IO tools'). However, it does not explicitly state when not to use this tool or compare it to alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

save_gmail_draftC

Save a reply as a Gmail DRAFT (never sends). Returns the draft id.

ParametersJSON Schema
NameRequiredDescriptionDefault
toYes
bodyYes
subjectYes
thread_idNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

C2.8/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided. Description only states it never sends and returns draft id. Missing details: overwrite behavior, permissions, rate limits, thread_id usage for replies.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Single sentence, front-loaded with key action and side effect. Could be slightly expanded to cover parameter roles without losing conciseness.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Tool has 4 parameters, optional thread_id, and an output schema. Description omits crucial context: reply vs new draft, thread_id function, limits, and response details.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0% (no parameter descriptions). The description does not explain any parameter beyond their names. 'thread_id' is especially ambiguous.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Verbs 'Save' and 'never sends' clearly define the action. Resource 'Gmail DRAFT' is specific. Differentiates from siblings like fetch_emails (read) and write_report (different output).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No explicit when-to-use or when-not-to-use guidance. No mention of prerequisites like authentication or Gmail account. No comparison to alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

write_reportB

Write the final triage report markdown to disk.

ParametersJSON Schema
NameRequiredDescriptionDefault
pathNotriage-report.md
markdownYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

B3.1/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description must fully disclose behavior. It only says 'write to disk' without details on overwrites, permissions, error handling, or side effects.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, front-loaded sentence with no unnecessary words. Every word earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple write tool, the description covers the core action but omits details like file overwrite behavior. An output schema exists, so return value is not needed. Still, behavioral gaps reduce completeness.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 0% schema description coverage, the description adds no meaning beyond the schema. It does not explain what the markdown string should contain or how the path defaults work.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the verb 'write' and the resource 'final triage report markdown', with target 'disk'. It distinguishes from siblings like fetch_emails and append_tasks.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No explicit guidance on when to use vs alternatives, but the task is well-defined and siblings are unrelated. The 'final' qualifier implies use at report completion, but not explicitly stated.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 4 tool updatesv1.0.0
    • First observedappend_tasks
    • First observedfetch_emails
    • First observedsave_gmail_draft
    • First observedwrite_report

TDQS

A3.6/5.0

Scored across 4 tools

Disambiguation5/5

Each tool has a clearly distinct purpose: fetching emails, appending tasks, saving drafts, and writing reports. There is no overlap or ambiguity between them.

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern in snake_case (append_tasks, fetch_emails, save_gmail_draft, write_report), making it easy to predict their function.

Tool Count5/5

With 4 tools, the server is well-scoped for its purpose of email triage and task extraction. Each tool serves a necessary function without being excessive or insufficient.

Completeness4/5

The tool set covers the core workflow of fetching, drafting, and recording tasks/reports. Minor gaps exist (e.g., no tool to mark emails as read or delete tasks), but these are acceptable for a demo/triage system.

Maintenance

ActivityStale
ResponsivenessNo issues

Related MCP Connectors

  • Programmable email inbox for AI agents — JMAP, PoW auth, stdio MCP server.

  • Email infrastructure for AI agents — send, receive, search, and reply to email over MCP.

  • Email inboxes for AI agents: send, receive, reply, search, and manage threaded email over MCP.

  • Your agent needs a mailbox of its own — to receive, thread, draft and send, with attachments, without borrowing your personal inbox or your company's SMTP. **What you can ask for** • "Create an inbox for this agent and tell me its address." • "Read the new messages in this thread and draft a reply." • "Send this message with the attachment and wait for the response." • "Search this inbox for everything from that domain." • "Show delivery metrics and the events on this inbox." **How to use it** Point any MCP client at https://mcp.aisa.one/mail/mcp and sign in with OAuth — there is no key to create or paste. 49 tools: create and delete inboxes, list and read messages, raw message bodies, attachments, threads, drafts and draft attachments, send and reply, message search, inbox events, metrics, and list entries — reads and writes. **Why this rather than the source** A real inbox an agent owns, rather than an SMTP credential it borrows from a human. **It is also a door to the rest** The same login reaches 26 sources and 580+ operations. Find the contact elsewhere in the catalogue, then write to them from here — without adding a second server. **What it costs** Finding and inspecting an operation is free. Running one is billed per call at API prices, with no seat and no monthly minimum, and every call takes max_price_usd so an agent cannot overspend by accident. **Where else it reaches** https://mcp.aisa.one/sales/mcp finds the person to write to.

Related MCP Servers

  • A
    license
    B
    quality
    F
    maintenance
    MCP server that connects Claude with Gmail to read, send, delete, and manage messages and labels via the Google Gmail API.
    3
    MIT
  • A
    license
    A
    quality
    D
    maintenance
    A privacy-first MCP server for inbox triage, enabling masked email reading, calendar upsert, reply drafting, and Slack digests using Google and Slack credentials.
    10
    MIT
  • F
    license
    A
    quality
    D
    maintenance
    Local stdio MCP server enabling Claude to send Gmail, search emails with attachment metadata, and download attachments to Google Drive.
    2
    3
    -
  • A
    license
    Not graded
    quality
    B
    maintenance
    A local MCP server that gives Claude access to multiple Gmail accounts simultaneously for search, read, draft, send, label, and a scheduled cross-inbox digest, with enforced send policies per mailbox.
    MIT