Email MCP Server
An MCP server that exposes IMAP/SMTP email accounts to AI assistants as 52 tools for reading, searching, sending, organizing, scheduling, and analyzing mail across multiple accounts.
Accounts & folders — list configured accounts, list mailboxes with unread counts, create/rename/delete folders
Read mail — list, search (keyword, sender, recipient, size, attachments, dates), fetch one or many emails (max 20), reconstruct threads, check read/flag/label status, find which real folder holds a virtual-folder message
Attachments — download by filename (base64, ≤5 MB)
Send mail — send, reply (with/without reply-all), forward, save drafts, send drafts
Labels — provider-aware (ProtonMail folders, Gmail labels, IMAP keywords): list, add, remove, create, delete
Organize — move, delete (trash or permanent), mark read/unread/flag/unflag, bulk actions on up to 100 emails
Scheduling — queue future emails, list pending/sent/failed, cancel
Templates — list templates and apply one via preview, draft, or send with variable substitution
Contacts & analytics — extract frequent contacts, email volume/top-sender/daily-trend stats, connection health and quota
Calendar & reminders — extract ICS events from mail, analyze an email for scheduling, add to local calendar, list calendars/events, create and list Reminders (macOS-centric)
Watcher & alerts — IMAP IDLE watcher status, AI triage presets, hooks config, diagnose/test desktop notifications, configure alert routing at runtime
Provides comprehensive email capabilities for Gmail accounts via IMAP/SMTP, including reading, sending, managing labels, scheduling emails, and experimental OAuth2 support.
Enables email management for GMX accounts via IMAP/SMTP, with features like reading, sending, and organizing emails.
Enables email management for iCloud accounts via IMAP/SMTP, with features like reading, sending, and organizing emails.
Enables email management for ProtonMail accounts via IMAP/SMTP, with features like reading, sending, and organizing emails.
Enables email management for Zoho accounts via IMAP/SMTP, with features like reading, sending, and organizing emails.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Email MCP Serversummarize my unread emails from today"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Email MCP Server
An MCP (Model Context Protocol) server providing comprehensive email capabilities via IMAP and SMTP.
Enables AI assistants to read, search, send, manage, schedule, and analyze emails across multiple accounts. Exposes 52 tools, 7 prompts, and 6 resources over the MCP protocol with OAuth2 support (experimental), email scheduling, calendar extraction, analytics, provider-aware label management, real-time IMAP IDLE watcher with AI-powered triage, instant verification-code catching with clipboard copy, customizable presets and static rules, and a guided setup wizard.
Built on the MCP TypeScript SDK v2 (spec revision 2026-07-28), it serves over stdio for local clients or Streamable HTTP for networked access — existing stdio configurations keep working unchanged.
Highlights
Feature | email-mcp | Typical MCP email |
Multi-account | ✅ | ❌ |
Send / reply / forward | ✅ | ✅ |
Drafts & templates | ✅ | ❌ |
Labels & bulk ops | ✅ provider-aware | ❌ |
Schedule future emails | ✅ | ❌ |
Real-time IMAP IDLE watcher | ✅ | ❌ |
Verification codes → clipboard | ✅ concealed + auto-clear | ❌ |
AI triage with presets | ✅ | ❌ |
Desktop & webhook alerts | ✅ | ❌ |
Calendar (ICS) extraction | ✅ | ❌ |
Email analytics | ✅ | ❌ |
OAuth2 (Gmail / M365) | ✅ experimental | ❌ |
Guided setup wizard | ✅ auto-detect | ❌ |
Streamable HTTP transport | ✅ | ❌ |
Related MCP server: imap-mcp
Table of Contents
Security
All connections use TLS/STARTTLS encryption
Passwords are never logged; audit trail records operations without credentials
Token-bucket rate limiter prevents abuse (one bucket per account, sharing a global
rate_limit)OAuth2 XOAUTH2 authentication for Gmail and Microsoft 365 (experimental)
Attachment downloads capped at 5 MB with base64 encoding
Background
Most MCP email implementations provide only basic read/send. This server aims to be a full-featured email client for AI assistants, covering the entire lifecycle: reading, composing, managing, scheduling, and analyzing email — all from a single MCP server.
Key design decisions:
XDG-compliant config — TOML at
~/.config/email-mcp/config.tomlMulti-account — Operate across multiple IMAP/SMTP accounts simultaneously
Layered services — Business logic is decoupled from MCP wiring for testability
Provider auto-detection — Gmail, Outlook, Yahoo, iCloud, Fastmail, ProtonMail, Zoho, GMX
Install
Requires Node.js ≥ 24.
# Run directly (no install needed)
npx @codefuturist/email-mcp setup
# or
pnpm dlx @codefuturist/email-mcp setup
# Or install globally
npm install -g @codefuturist/email-mcp
# or
pnpm add -g @codefuturist/email-mcpnpm install -g fails with EACCES when npm's global prefix is a root-owned
directory (common on a system Node install) — the npx/pnpm dlx form above
avoids this entirely by not installing anything globally. If you do want a
permanent global install, fix npm's prefix instead of reaching for sudo
(npm config set prefix ~/.npm-global and add that to PATH, or use a Node
version manager like nvm/fnm/volta) — running npm install -g as root
routinely leaves root-owned files mixed into a user-owned node_modules tree,
which causes harder-to-diagnose permission errors on the next global install.
Homebrew (native binary)
The prebuilt, dependency-free binary — no Node.js at runtime — from the codefuturist tap:
brew install codefuturist/tap/email-mcpPublished as a cask on every stable release; works on macOS (arm64 + x64) and on Linuxbrew ≥ 4.3 (binary-only casks).
Native binary (Bun)
A dependency-free single binary (minified + bytecode-precompiled, ~32 ms cold start, no Node.js at runtime) can be compiled with Bun:
bun run build:binary # native binary → build/email-mcp
cp build/email-mcp ~/.local/bin/
bun run build:binaries # release matrix: darwin-arm64/x64, linux-x64/arm64,
# windows-x64 → build/release/*.{tar.gz,zip} + sha256 checksumsPrebuilt binaries for every release are built by CI with GoReleaser Pro
(.goreleaser.yaml) for all common platforms — Linux glibc + musl, macOS, and
Windows, each x64 + arm64 where Bun supports it — provenance-attested (verify with
gh attestation verify <asset> --repo codefuturist/email-mcp) and shipped with
GitHub releases —
installable via mise: mise use -g ubi:codefuturist/email-mcp.
The same binary can also be built locally from the published npm package (no
checkout needed; ≥ 0.5.1, which carries the imapflow TLS fix at runtime —
older versions need the repo's pnpm patch). Note that a plain bun add -g
shim executes dist/main.js under node via its shebang, so the compile step
is what buys the ~11 ms cold start:
bun add -g @codefuturist/email-mcp
bun build --compile --minify --bytecode \
"${BUN_INSTALL:-$HOME/.bun}/install/global/node_modules/@codefuturist/email-mcp/dist/main.js" \
--outfile ~/.local/bin/email-mcpAn installed binary keeps itself current: email-mcp update checks the latest
release and — after a confirmation, a sha256 check against the release's
checksums file, and (when the gh CLI is present) a provenance-attestation
check — atomically replaces itself. --check only reports (exit code 1 =
update available), --yes skips the prompt for scripts. npm- and mise-managed
installs are recognized and pointed at npm install -g …@latest /
mise upgrade instead of being overwritten.
server install from the binary points the launchd login item at it. Requires the
bundled imapflow patch (patches/) — imapflow passes servername: false to
tls.connect for IP hosts, which Bun's stricter node:tls rejects.
Docker
No Node.js required — just Docker.
# Latest stable release
docker pull ghcr.io/codefuturist/email-mcp:latest
# Pin to an exact version (immutable)
docker pull ghcr.io/codefuturist/email-mcp:0.2.3
# Auto-update patches within a minor version
docker pull ghcr.io/codefuturist/email-mcp:0.2
# Track a major version (won't cross breaking-change boundary)
docker pull ghcr.io/codefuturist/email-mcp:0
# Pin to an exact git commit (immutable, CI traceability)
docker pull ghcr.io/codefuturist/email-mcp:sha-abc1234
# Or build from source
docker build -t ghcr.io/codefuturist/email-mcp .Tag convention: Tags follow bare semver (no
vprefix), matching Docker ecosystem standards (e.g.node:24,nginx:1.25). Thelatesttag is only updated on stable releases, never pre-releases.
Note: By default, the server uses stdio transport. Config must be created on the host first (via
npx @codefuturist/email-mcp setupor manually) and mounted into the container.
Usage
Setup
# Add an email account interactively (recommended)
# On macOS and Linux this offers to store the password in the OS keychain,
# so config.toml only carries a `keychain:<account>` reference.
email-mcp account add
# Or use the legacy alias
email-mcp setup
# Or create a template config manually
email-mcp config initThe setup wizard auto-detects server settings, tests connections, saves config, and outputs the MCP client config snippet.
Test Connections
email-mcp test # all accounts
email-mcp test personal # specific accountConfigure Your MCP Client
The snippets below use the default stdio transport, best for local desktop clients. For networked or remote access over Streamable HTTP, see Streamable HTTP (networked) below.
Recommended — use the guided installer (auto-detects Claude Desktop, Cursor, Windsurf; for VS Code use the manual snippet below):
email-mcp installOr add manually using the snippets below.
Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"email": {
"command": "npx",
"args": ["-y", "@codefuturist/email-mcp", "stdio"]
}
}
}Option 1 — Extensions gallery (easiest):
Open the Extensions view (⇧⌘X / Ctrl+Shift+X)
Search
@mcp email-mcpClick Install (user-wide) or right-click → Install in Workspace
Option 2 — Workspace config (.vscode/mcp.json, committed to source control):
{
"servers": {
"email": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@codefuturist/email-mcp", "stdio"]
}
}
}Option 3 — User config (settings.json, applies to all workspaces):
Open the Command Palette → Preferences: Open User Settings (JSON) and add:
{
"mcp": {
"servers": {
"email": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@codefuturist/email-mcp", "stdio"]
}
}
}
}Edit ~/.cursor/mcp.json:
{
"mcpServers": {
"email": {
"command": "npx",
"args": ["-y", "@codefuturist/email-mcp", "stdio"]
}
}
}Edit ~/.codeium/windsurf/mcp_config.json:
{
"mcpServers": {
"email": {
"command": "npx",
"args": ["-y", "@codefuturist/email-mcp", "stdio"]
}
}
}Edit ~/.config/zed/settings.json:
{
"context_servers": {
"email": {
"command": {
"path": "npx",
"args": ["-y", "@codefuturist/email-mcp", "stdio"]
}
}
}
}Add to ~/.vibe/config.toml:
[[mcp_servers]]
name = "email-mcp"
transport = "stdio"
command = "npx"
args = ["-y", "@codefuturist/email-mcp", "stdio"]To pass credentials directly instead of using a config file, use the env field:
[[mcp_servers]]
name = "email-mcp"
transport = "stdio"
command = "npx"
args = ["-y", "@codefuturist/email-mcp", "stdio"]
env = { "EMAIL_ACCOUNTS" = "<your-accounts-json>" }MCP tools are exposed as email-mcp_<tool_name> (e.g. email-mcp_list_emails). Restart Vibe after editing the config.
Run the server in a container — mount your config directory read-only:
docker run --rm -i \
-v ~/.config/email-mcp:/home/node/.config/email-mcp:ro \
-v email-mcp-state:/home/node/.local/state/email-mcp \
-v email-mcp-cache:/home/node/.cache/email-mcp \
ghcr.io/codefuturist/email-mcpThe config mount can stay read-only, but state and cache must be persisted.
The image declares both as volumes, so Docker keeps them in anonymous volumes
if you omit the flags — naming them, as above, is what makes them survive a
docker run --rm. State holds the scheduled-email queue: without it, a restart
drops every email scheduled but not yet sent. Cache holds the local mirror,
which is regenerable, so losing that only costs a re-sync.
For MCP client configuration (e.g. Claude Desktop):
{
"mcpServers": {
"email": {
"command": "docker",
"args": [
"run", "--rm", "-i",
"-v", "~/.config/email-mcp:/home/node/.config/email-mcp:ro",
"-v", "email-mcp-state:/home/node/.local/state/email-mcp",
"-v", "email-mcp-cache:/home/node/.cache/email-mcp",
"ghcr.io/codefuturist/email-mcp"
]
}
}
}{
"mcpServers": {
"email": {
"command": "npx",
"args": ["-y", "@codefuturist/email-mcp", "stdio"],
"env": {
"MCP_EMAIL_ADDRESS": "you@gmail.com",
"MCP_EMAIL_PASSWORD": "your-app-password",
"MCP_EMAIL_IMAP_HOST": "imap.gmail.com",
"MCP_EMAIL_SMTP_HOST": "smtp.gmail.com"
}
}
}
}Always-on server (detached)
The stdio transport lives only as long as an MCP client keeps it open — which also
means the IMAP watcher (and verification catching) stop with it. email-mcp server
runs the Streamable HTTP server as a background service instead, iMCP-style:
email-mcp server start # detached on http://127.0.0.1:8080/mcp
email-mcp server start --port 3199 # any http flags pass through (--host, --token, …)
email-mcp server start --attach # foreground instead (Ctrl-C stops)
email-mcp server status # pid, address, /healthz, uptime
email-mcp server logs -n 50 # tail the daemon log
email-mcp server restart # reuses the previous flags
email-mcp server stop # graceful SIGTERM (add --force for SIGKILL)The binding lives in the config file, so a plain server start is deterministic:
[settings.server]
host = "127.0.0.1"
port = 8080
path = "/mcp"
token = "" # bearer token; empty = no auth (loopback only)
allowed_hosts = [] # Host-header allowlist for reverse proxiesPrecedence: CLI flags → EMAIL_MCP_HTTP_* env → [settings.server] → defaults —
for email-mcp http, server start, and the launchd login item alike. Edit it
interactively with config edit server (the token renders masked). --insecure
is deliberately flag-only and cannot be persisted.
Start at login (macOS): email-mcp server install writes a launchd login item
(com.email-mcp.server, RunAtLoad + KeepAlive) running email-mcp http with no
flags — the config file is its single source of truth across reboots and crashes.
server status recognizes the launchd mode; server uninstall removes it.
State lives in $XDG_STATE_HOME/email-mcp/daemon.json, logs in server.log next to
it (rotated at 5 MB). stop only ever signals a process whose command line is
verifiably this server (PID-reuse safe). Point HTTP-capable MCP clients at
http://127.0.0.1:8080/mcp; binding non-loopback hosts still requires a token.
Streamable HTTP (networked)
By default the server speaks MCP over stdio, ideal for local desktop clients (see the snippets above). For networked or remote access, run it as a Streamable HTTP server instead:
email-mcp http --port 8080This serves MCP at http://127.0.0.1:8080/mcp, with a health probe at GET /healthz.
Flags & environment variables
Flag | Environment variable | Default | Description |
|
|
| Address to bind |
|
|
| Port to listen on |
|
|
| HTTP path serving MCP |
|
| — | Require |
|
| loopback names + bind host | Comma-separated |
| — |
| Allow binding a non-loopback host without a token |
Security
Token auth — When a token is set, every request must send
Authorization: Bearer <token>.Non-loopback bind guard — Binding a non-loopback host (e.g.
0.0.0.0) without a token is refused unless--insecureis passed. Use--insecureonly when TLS and authentication are terminated by an upstream reverse proxy.DNS-rebinding protection — The
Hostheader is validated against an allowlist (loopback names plus the bind host by default). SetEMAIL_MCP_HTTP_ALLOWED_HOSTSto your public domain, or*to disable the check when a proxy already enforces it.
Client configuration
For clients that support the Streamable HTTP transport:
{
"mcpServers": {
"email": {
"type": "streamable-http",
"url": "http://127.0.0.1:8080/mcp"
}
}
}When a token is configured, add an Authorization: Bearer <token> header if your client supports custom headers.
Docker
docker compose --profile http upRequires EMAIL_MCP_HTTP_TOKEN to be set; the image EXPOSEs port 8080.
CLI Commands
email-mcp [command]
Commands:
stdio Run as MCP server over stdio (default)
http Run as MCP server over Streamable HTTP (networked)
server start [--attach] Start an always-on HTTP server (detached by default)
server stop [--force] Stop the detached server
server status Show pid, address, health, uptime (exit 1 if stopped)
server restart Stop and start again (reuses previous flags)
server logs [-n N] Show the last N daemon log lines
server install Install as macOS login item (launchd, survives reboots)
server uninstall Remove the login item and stop the server
account list List all configured accounts
account add Add a new email account interactively
account edit [name] Edit an existing account
account delete [name] Remove an account
setup Alias for 'account add'
test Test connections for all or a specific account
open <id> [--print] Open an email in the native mail client via its message: link
(macOS; --print or other platforms print the URL instead)
install Register email-mcp with MCP clients interactively
install status Show registration status for detected clients
install remove Unregister email-mcp from MCP clients
config show Show config (passwords masked)
config edit [section] Interactive settings editor (general, server, send, attachments,
scheduler, watcher, verification, cache, hooks, alerts)
config validate Check syntax, schema, typo'd keys, and cross-setting consistency
completion zsh|bash|fish Print shell completion script (accounts and sections complete dynamically)
config path Print config file path
config init Create template config
scheduler check Process pending scheduled emails
scheduler list Show all scheduled emails
scheduler install Install OS-level scheduler (launchd/crontab)
scheduler uninstall Remove OS-level scheduler
scheduler status Show scheduler installation status
cache status [--json] Show cache size, contents, coverage and freshness
cache clear [--account N] Delete cached mail/attachments and free the disk space
(--json supported by status, clear and verify)
cache verify Check the cache for corruption and reclaim orphaned files
update [--check|--yes] Self-update the binary to the latest GitHub release
help Show helpShell completion
# zsh (one line in ~/.zshrc)
source <(email-mcp completion zsh)
# bash
source <(email-mcp completion bash)
# fish
email-mcp completion fish > ~/.config/fish/completions/email-mcp.fishCompletions cover every command, subcommand, and flag — and complete account
names from your config (email-mcp test <TAB>, account edit <TAB>) and
settings sections (config edit <TAB>) dynamically.
Checking logs
The background daemon logs to $XDG_STATE_HOME/email-mcp/server.log. View it
with a tail/follow/filter viewer — no need to find the file:
email-mcp logs # last 40 lines (colored by level)
email-mcp logs -n 200 # more lines
email-mcp logs -f # live follow (Ctrl-C to stop)
email-mcp logs --level warn # only WARN and ERROR (continuation lines kept)
email-mcp logs --grep imap # only lines matching a substring
email-mcp logs -f --level error # watch for errors as they happenemail-mcp server logs is an alias with the same options. Color auto-disables
when piped or under NO_COLOR; on a systemd host, journalctl --user -u email-mcp is also available.
Reading and sending mail from the CLI
Beyond running as an MCP server, email-mcp is a usable mail client in its own
right — no MCP client and no coding required. Commands default to your first
configured account (pass --account <name> to pick another), print clean,
colored tables (color auto-disables when piped or under NO_COLOR), and tab
complete (see Shell completion).
email-mcp list # recent INBOX messages (● unread, ★ flagged, 📎 attachment)
email-mcp list --unread --limit 50 # filters
email-mcp list --mailbox Archive --search invoice
email-mcp read 42 # full message (headers, body, attachment names + sizes)
email-mcp read 42 --mark-read
email-mcp search "from bank" # IMAP search
email-mcp mailboxes # folders with unread/total counts
email-mcp flag 42 # flag / --off to unflag
email-mcp attach 42 report.pdf ~/Downloads # save an attachment
email-mcp delete 42 --yes # delete (asks to confirm without --yes)
email-mcp reply 42 --body "on it" --all
email-mcp send --to a@b.c,d@e.f --subject "Hi" --body "..." --attach ./file.pdfEvery command accepts --help; the read commands (list, read, search,
mailboxes) accept --json for machine-readable output you can pipe to jq:
email-mcp list --json | jq '.items[] | {id, subject, from: .from.address}'
email-mcp read 42 --json | jq .attachmentsAttachment sizes on send/reply are checked against the configured limit before anything leaves your machine (see Storing account secrets for keeping credentials out of plaintext).
Configuration
Located at $XDG_CONFIG_HOME/email-mcp/config.toml (default: ~/.config/email-mcp/config.toml). Validate it with email-mcp config validate (TOML syntax, schema, typo'd keys with did-you-mean, cross-setting consistency; exit 1 on errors — CI-friendly) and edit it interactively with email-mcp config edit [section] — every save validates first and backs up the previous file to config.toml.bak. Note: programmatic saves rewrite the file without TOML comments (the backup keeps them).
[settings]
rate_limit = 10 # max emails per minute per account
[settings.send]
max_attachment_mb = 25 # global cap on the base64-encoded attachment total
save_to_sent = false # file a copy of each sent message in the IMAP Sent folder
[settings.attachments]
auto_download = true # save attachments locally when calendar events are created from emails
[settings.attachment_download]
# dir = "/data/attachments" # unset (default) disables download_attachment's saveToDownloadDir
allowed_extensions = ["pdf", "png", "jpg", "jpeg", "webp", "tiff", "txt", "md", "doc", "docx", "odt", "xls", "xlsx", "csv"]
max_bytes = 26214400 # 25 MB
[[accounts]]
name = "personal"
email = "you@gmail.com"
full_name = "Your Name"
password = "keychain:personal" # or "env:VAR", or a plaintext app password
# max_attachment_mb = 25 # optional per-account override (beats global + provider limits)
# sent_mailbox = "INBOX.Sent Messages" # optional, see "Sent-folder copies" below
# save_to_sent = true # optional per-account override of the Gmail-skip guess
[accounts.imap]
host = "imap.gmail.com"
port = 993
tls = true
[accounts.smtp]
host = "smtp.gmail.com"
port = 465
tls = true
starttls = false
verify_ssl = true
[accounts.smtp.pool]
enabled = true
max_connections = 1
max_messages = 100Storing account secrets
The password field (and OAuth2 client_secret / refresh_token) accepts an
indirection so credentials need not sit in the file as plaintext:
Value | Resolved from |
| OS keychain — macOS Keychain ( |
| macOS Keychain with an explicit service name |
| the environment variable |
any other string | used literally (plaintext — backward compatible) |
The config file itself is always written 0600 (owner-only), and so is its
.bak.
macOS Keychain (multi-account): store each account's secret once —
email-mcp account set-secret personal # store a NEW secret (prompts; nothing echoed or in argv)
email-mcp account migrate-secret personal # move an EXISTING plaintext password into the keychainset-secret prompts for the value and stores it; migrate-secret reads the
account's current plaintext password from config.toml, stores it in the
keychain, verifies it, then rewrites the line to password = "keychain:personal".
Either way you end up with password = "keychain:personal". Each account is a separate Keychain
item under the shared email-mcp service, so multiple accounts coexist.
Secrets are read back at startup over the security CLI; only the service and
account names are ever passed as arguments — the secret travels on stdout only.
At rest the secret lives in your login keychain, encrypted and unlocked by
your macOS login (Touch ID at the login level). A running email-mcp daemon
reads it once at startup; it does not prompt for Touch ID on every IMAP
reconnect — per-access biometric prompts are deliberately not used for an
unattended background service. (True per-access Touch ID would require the
data-protection keychain and a native helper.)
OAuth2 (experimental)
Note: OAuth2 support is experimental. Token refresh and provider-specific flows may require additional testing in your environment.
[[accounts]]
name = "work"
email = "you@company.com"
full_name = "Your Name"
[accounts.oauth2]
provider = "google" # or "microsoft"
client_id = "your-client-id"
client_secret = "your-client-secret"
refresh_token = "your-refresh-token"
[accounts.imap]
host = "imap.gmail.com"
port = 993
tls = true
[accounts.smtp]
host = "smtp.gmail.com"
port = 465
tls = true
[accounts.smtp.pool]
enabled = true
max_connections = 1
max_messages = 100Environment Variables
For single-account setups (overrides config file):
Variable | Default | Description |
| required | Email address |
| required | Password or app password |
| required | IMAP server hostname |
| required | SMTP server hostname |
|
| Account name |
| — | Display name |
| Login username | |
|
| IMAP port |
|
| IMAP TLS |
|
| SMTP port |
|
| SMTP TLS |
|
| SMTP STARTTLS |
|
| Verify SSL certificates |
|
| Enable SMTP transport pooling |
|
| Max pooled SMTP connections |
|
| Max messages per pooled connection |
|
| Max sends per minute |
|
|
|
|
|
|
|
| Days of sent-email history to keep; |
| — | Directory |
| (built-in list — see below) | Comma-separated extension allowlist for saved attachments |
|
| Max attachment size |
Local Cache
Reads are served from a local SQLite mirror of your mail (plus a
content-addressed store for attachments) under
${XDG_CACHE_HOME:-~/.cache}/email-mcp/. It makes listing, searching, and
re-reading mail fast, and it keeps mail readable when the mail server is
unreachable. It is on by default.
The cache is never authoritative. Every row can be re-fetched from IMAP, so deleting it costs a resync and never loses mail — you can remove the directory at any time, as the XDG cache convention allows.
Seeing and reclaiming what it holds
email-mcp cache status # size vs budget, per-mailbox coverage, last sync
email-mcp cache status --json # same, for scripts
email-mcp cache clear # free the space (asks first; mail is re-fetched)
email-mcp cache clear --account work --yes
email-mcp cache verify # integrity check + reclaim orphaned filescache status reports the index size and attachment bytes separately, how many
messages have full text cached, and how long ago each mailbox was reconciled.
check_health exposes the same numbers to an AI client, plus the read hit ratio.
How freshness is decided
Correctness comes first: a stale answer to "any new mail?" is a wrong answer.
Before serving a mailbox from the cache, the server either trusts a reconcile
from the last 10 seconds, or spends one STATUS round trip comparing the
server's UIDVALIDITY/UIDNEXT/HIGHESTMODSEQ/MESSAGES against what the
mirror recorded — the IMAP equivalent of a conditional request. If anything
moved, it resynchronises before answering (bounded; past the bound you get the
cached answer marked stale). If the server is unreachable you get the cached
answer marked offline.
Message bodies and attachments are immutable for a given UIDVALIDITY/UID, so
once stored they are served with no revalidation at all. Writes made through
this server (flagging, moving, deleting) resynchronise the affected mailboxes
immediately, which matters on servers without CONDSTORE where a flag change
is otherwise invisible.
Settings
Setting | Default | What it does |
|
|
|
|
| Synced in the background. Others mirror lazily once read |
|
| How far back the first sync of a mailbox reaches |
|
| Bodies kept per mailbox. |
|
| Budget for index + attachments combined |
|
| Seconds between background reconciles |
Past the budget, eviction runs cheapest-to-rebuild-first: attachment files
(least recently read first), then message bodies, then envelopes older than
window_days. Envelopes are kept as long as possible, because they are what
listings and search need.
Coverage, not just freshness. The cache only answers a listing when it
holds every message the server reports for that mailbox. Set window_days to
something other than 0 and the mirror becomes a slice — it then cannot report
a truthful total or know whether more pages exist, so reads fall back to the
live path until it catches up. That is also why envelope eviction clears the
mailbox's sync watermarks: the next sync rebuilds coverage instead of leaving a
permanent, invisible hole.
Data at rest
Cached mail — including message bodies and attachment content — is stored
unencrypted, in a directory and files readable only by your user
(0700/0600). This matches what desktop mail clients do; the recommended
protection is full-disk encryption (FileVault, LUKS). If you would rather not
have bodies and attachments on disk at all, set body_messages = 0, which
keeps the cache metadata-only.
Gmail note: do not add [Gmail]/All Mail to mailboxes. Gmail presents the
same message in every label folder, so mirroring All Mail alongside INBOX
stores the same mail twice.
Email Scheduling
The scheduler enables future email delivery with a layered architecture:
MCP auto-check — Processes the queue on server startup and every 60 seconds while the MCP server is running
CLI —
email-mcp scheduler checkfor manual or cron-based processingOS-level daemon —
email-mcp scheduler installsets up launchd (macOS) or crontab (Linux) to run every minute, independently of the MCP server
Important — the daemon must be installed for reliable delivery. Without it, scheduled emails only fire while an AI client is actively connected. Your machine also needs to be running at the scheduled time; if it's asleep or off, the daemon will process overdue emails on next wake/startup. Failed sends are retried up to 3 times before being marked
failed.
Setting up the daemon
# Install (macOS launchd / Linux crontab — runs every minute)
email-mcp scheduler install
# Verify it's running
email-mcp scheduler status
# View pending / sent / failed scheduled emails
email-mcp scheduler list
# Trigger a manual check immediately
email-mcp scheduler check
# Remove the daemon
email-mcp scheduler uninstallScheduled emails are stored as JSON files in ~/.local/state/email-mcp/scheduled/ with status-based locking. Each entry tracks attempts (max 3) and the last error, so you can inspect failures with scheduler list.
Real-time Watcher & AI Hooks
The IMAP IDLE watcher monitors configured mailboxes in real-time using persistent IDLE connections (separate from tool connections). When new emails arrive:
Static rules — Pattern-match on from/to/subject → apply labels, flag, or mark read instantly (no AI)
AI triage — Remaining emails are analyzed via MCP sampling with a customizable preset prompt
Notify mode — Falls back to logging if AI triage is disabled
Configure in config.toml:
[settings.watcher]
enabled = true
folders = ["INBOX"]
idle_timeout = 1740 # 29 minutes (IMAP spec max is 30)
[settings.hooks]
on_new_email = "triage" # "triage" | "notify" | "none"
preset = "inbox-zero" # "inbox-zero" | "gtd" | "priority-focus" | "notification-only" | "custom"
auto_label = true # apply AI-suggested labels
auto_flag = true # flag urgent emails
batch_delay = 5 # seconds to batch before triage
# User context — appended to preset's AI prompt
custom_instructions = """
I'm a software engineer. Emails from @mycompany.com are always high priority.
Newsletters I read: TL;DR, Hacker Newsletter.
"""
# Static rules — run BEFORE AI, skip AI if matched
[[settings.hooks.rules]]
name = "GitHub Notifications"
match = { from = "*@github.com" }
actions = { labels = ["Dev"], mark_read = true }
[[settings.hooks.rules]]
name = "Newsletter Archive"
match = { from = "*@substack.com|*@buttondown.email" }
actions = { labels = ["Newsletter"] }
[[settings.hooks.rules]]
name = "VIP Contacts"
match = { from = "ceo@company.com|cto@company.com" }
actions = { flag = true, labels = ["VIP"] }Presets
Preset | Focus | Suggested Labels |
| Aggressive categorization + archiving | Newsletter, Notification, Updates, Finance, Social, Promo |
| Getting Things Done contexts | @Action, @Waiting, @Reference, @Someday, @Delegated |
| Simple priority classification (default) | (none — just priority + flag) |
| No AI triage, just log | (none) |
| User defines full system prompt | User-defined |
Static Rules
Static rules use glob-style patterns (*@github.com) with | as OR separator (*@github.com|*@gitlab.com). All conditions within a match are AND'd. First matching rule wins.
Available actions: labels (string array), flag (boolean), mark_read (boolean), alert (boolean — forces desktop notification).
Alerts
Urgency-based multi-channel notification routing — grab attention for important emails even when you're not looking at the chat. All channels are opt-in and disabled by default.
Priority | Desktop | Sound | MCP Log Level | Webhook |
| ✅ Banner | 🔊 Alert |
| ✅ |
| ✅ Banner | 🔇 Silent |
| ✅ |
| ❌ | ❌ |
| ❌ |
| ❌ | ❌ |
| ❌ |
[settings.hooks.alerts]
desktop = true # OS-level notifications (macOS/Linux/Windows)
sound = true # play sound for urgent emails
urgency_threshold = "high" # minimum priority to trigger desktop alert
webhook_url = "https://ntfy.sh/my-email-alerts" # optional: Slack, Discord, ntfy.sh, etc.
webhook_events = ["urgent", "high"]Supported platforms: macOS (Notification Center via osascript), Linux (notify-send), Windows (PowerShell toast). Zero npm dependencies — uses native OS commands.
Notification setup by platform:
Desktop notifications use osascript (built-in). The terminal app running the MCP server needs notification permission:
Open System Settings → Notifications & Focus
Find your terminal app (Terminal, iTerm2, VS Code, Cursor, etc.)
Enable Allow Notifications and choose Banners or Alerts
Ensure Focus / Do Not Disturb is not blocking notifications
Use check_notification_setup to diagnose and test_notification to verify.
Requires notify-send from libnotify. For sound alerts, paplay is also needed:
# Ubuntu / Debian
sudo apt install libnotify-bin pulseaudio-utils
# Fedora
sudo dnf install libnotify pulseaudio-utils
# Arch
sudo pacman -S libnotifyDesktop notifications require a running display server (X11/Wayland) — they will not work in headless/SSH sessions.
Uses PowerShell toast notifications (built-in):
Open Settings → System → Notifications
Ensure Notifications is turned on
Set Focus Assist to allow notifications
If using Windows Terminal, ensure its notifications are enabled
AI-configurable: The AI can check, test, and configure notifications at runtime:
check_notification_setup— diagnose platform support and show setup instructionstest_notification— send a test notification to verify everything worksconfigure_alerts— enable/disable desktop, sound, threshold, webhook (with optional persist to config file)
Webhook payload:
{
"event": "email.urgent",
"account": "work",
"sender": { "name": "John CEO", "address": "ceo@company.com" },
"subject": "Q4 Review Due Today",
"priority": "urgent",
"labels": ["VIP"],
"rule": "VIP Contacts",
"timestamp": "2026-02-18T11:30:00Z"
}Static rules can force desktop notifications with alert = true, regardless of urgency threshold:
[[settings.hooks.rules]]
name = "VIP Contacts"
match = { from = "ceo@company.com" }
actions = { flag = true, alert = true, labels = ["VIP"] }Features:
Auto-reconnect — Exponential backoff (1s → 60s) on connection failures
Batching — Groups arrivals within a configurable delay to reduce AI calls
Rate limiting — Max 10 sampling calls per minute
Graceful degradation — Falls back to notify mode if client doesn't support sampling
Resource subscriptions — Pushes
notifications/resources/updatedfor unread counts
Verification codes (instant OTP catch)
When a mail carrying a verification code (OTP/2FA) or sign-in magic link arrives, email-mcp copies it to your clipboard within ~1–2 seconds and shows a notification — “✅ Code 482913 from GitHub — Copied to clipboard, clears in 60s”. Press ⌘V / Ctrl+V, done.
Detection is scoring-based (English + German), not a bare digit regex: keyword proximity
(verification, Bestätigungscode, expires, …) minus negative context (order numbers,
invoices, tracking, Zoom passcodes, phone numbers, prices, dates). A miss beats a wrong
code on your clipboard.
[settings.watcher]
enabled = true # REQUIRED for ambient catching (IMAP IDLE push)
[settings.verification]
enabled = true # on by default
auto_copy = true # copy caught codes/links to the clipboard
confirm_copy = false # ask (native dialog) before touching the clipboard
notify = true # desktop notification on catch
copy_links = true # also catch magic links (only when no code found)
link_action = "open" # "open" = offer to open links in the browser, "copy" = clipboard
clear_after_seconds = 60 # conditional auto-clear (0 = never)
max_age_minutes = 10 # ignore stale messages (IDLE replays)
accounts = [] # limit to specific accounts (empty = all)
sender_allowlist = [] # e.g. ["*@github.com", "*@google.com"]
sender_denylist = []Ask-first mode: with confirm_copy = true, a native macOS dialog ("Code 482913 from
GitHub — Copy to clipboard?") appears before anything touches your clipboard. Sign-in
links get a three-button dialog by default (link_action = "open"): Open launches
the link in your default browser, Copy puts it on the clipboard, Cancel does
nothing — opening is always confirmed, since auto-launching a browser from mail content
would be a phishing hazard. Set link_action = "copy" for the silent clipboard behavior.
Dialogs are macOS-only (same mechanism as the calendar confirmation); elsewhere these
modes degrade to notification-only.
Clipboard hygiene (macOS): codes are written via a JXA osascript with the
org.nspasteboard.ConcealedType marker — the same convention 1Password/Bitwarden use — so
well-behaved clipboard managers (Maccy, Paste, …) never record them. The auto-clear is
conditional: the clipboard is only cleared if it still holds the caught value, so anything
you copied in the meantime is never stomped. Linux uses wl-copy/xclip, Windows uses
PowerShell Set-Clipboard (no concealment convention exists there). Zero npm dependencies.
For agents: get_verification_code fetches the newest code on demand — during an
automated signup, call it with wait_seconds = 30 and it returns the code seconds after
the mail lands (event-driven with the watcher, 5 s polling without). If nothing arrives in
time, it returns found: false with a hint — just call it again.
Security notes: the clipboard is shared OS state — any app can read it while the code
is there (that window is bounded by clear_after_seconds). The code itself is never
written to server logs or the audit log; it does appear in get_verification_code results,
which is the point of that tool. Kill switches: enabled = false (feature off) or
auto_copy = false (notification only). Environment overrides: MCP_EMAIL_VERIFICATION_*
(_ENABLED, _AUTO_COPY, _CONFIRM_COPY, _NOTIFY, _COPY_LINKS, _LINK_ACTION,
_CLEAR_AFTER_SECONDS, _MAX_AGE_MINUTES, _ACCOUNTS, _SENDER_ALLOWLIST,
_SENDER_DENYLIST).
Use check_clipboard_setup to diagnose the pipeline (add test_write = true for a
harmless copy → read-back → clear round-trip).
API
Tools (52)
Structured output:
list_emails,search_emails,get_email_status,list_mailboxes, andget_verification_codealso return machine-readable results (outputSchema+structuredContent) alongside the human-readable text, for clients that consume typed tool output.
Email bodies: HTML and other formats
Nearly all real mail is HTML — in one sampled mailbox, 165 of 169 messages were
single-part text/html — so the read tools convert it rather than handing back
markup.
get_email, get_emails, and get_thread take a format:
| Body you get |
| HTML converted to readable text: links become |
| As |
| The raw body part, HTML included |
Bodies are capped by default — 10 000 characters for get_email, 5 000 per
message for get_emails and get_thread — and a hint reports how much was
left out. Pass maxLength: 0 for no cap. Each side of the body (text, HTML) is
read up to 512 KB, shared across its parts.
Other formats behave as follows:
multipart/alternative— both halves are fetched, sobodyTextandbodyHtmlare both available. A plain part too small to hold the same content as its HTML sibling is treated as a "view this in an HTML reader" placeholder and skipped.multipart/mixedwith several text parts — concatenated in order, so a note split either side of an inline image (Apple Mail) or a per-message digest section is not truncated to its first part.text/plain; format=flowed(RFC 3676) — soft line wraps are undone, so paragraphs read as paragraphs rather than hard-wrapped at 72 columns;DelSpand space-stuffing are honoured.Inline (
cid:) images — listed as attachments withpartId,contentId, andinline, and downloadable viadownload_attachment'spartId. To embed one when sending, pass{path, cid, contentType}inattachmentsand reference it as<img src="cid:…">.Bounces (
multipart/report) — the machine-readablemessage/delivery-statuspart is surfaced separately from the body.Calendar invites — handled by
extract_calendar/add_to_calendar, honouring the part's declared charset.Encrypted mail (S/MIME, PGP/MIME) — not decrypted; the body says so plainly instead of appearing empty.
Attached messages (
message/rfc822) — never mistaken for the body, but used as the body when the message carries no other text (forward-as-attachment, digests).
Attachment download directory. download_attachment normally returns the
file base64-encoded (≤5 MB). Set [settings.attachment_download] dir (or
MCP_EMAIL_ATTACHMENT_DOWNLOAD_DIR) to a directory and pass
saveToDownloadDir: true to instead write the file there and get back its
path — useful for larger files, or when the caller just wants the attachment
on disk rather than inlined into the conversation. The option is unavailable
(the call fails) until dir is set — there is no default location. Only
allowed_extensions are written (default: common document/image types; see
the config example above); max_bytes caps the size, independent of the
5 MB base64 cap. Pass saveAs to choose the filename written there (a plain
basename — no path separators); otherwise the attachment's own filename is
sanitized and used. Running in Docker, point dir at a mounted volume shared
with whatever reads the files back, e.g. in docker-compose.yml:
services:
email-mcp:
environment:
MCP_EMAIL_ATTACHMENT_DOWNLOAD_DIR: /data/attachments
volumes:
- attachments:/data/attachments
other-service:
volumes:
- attachments:/data/attachments:ro
volumes:
attachments:Sent-folder copies. SMTP delivers mail; it does not file it. [settings.send] save_to_sent defaults to false; turn it on for an IMAP host that does not
capture sends itself. Gmail does capture sends server-side, so an account
whose IMAP server advertises the X-GM-EXT-1 capability is skipped
automatically even with the setting on — filing a copy there would give you
two of everything. Set the account's own save_to_sent to force the decision
either way (true files anyway, false never files, regardless of the
global setting or the Gmail guess).
The copy carries the same Message-ID the server accepted, is flagged
\Seen, and lands in the folder the server marks \Sent (or a folder named
Sent, created if absent). On a mailbox that carries several sent-shaped
folders — a leftover Sent beside the one actually in use — that guess can
land on an empty one; set the account's sent_mailbox to the full path
(as list_mailboxes reports it) to settle it. The copy omits Bcc, which the
MIME composer strips unconditionally.
A failure to file never fails the send — the mail has already left, and
saying otherwise would invite a duplicate send — but send_draft keeps the
draft in that case rather than deleting the only remaining copy of the
message. send_email, reply_email, forward_email and send_draft all
report the outcome in their result's sentCopy field: {kind: "filed", path},
{kind: "skipped"} (setting off, or Gmail's own capture), or
{kind: "failed", error}.
Outbound HTML (html: true) is sent as multipart/alternative with a
generated plain-text alternative. Forwards quote the original as converted
text; pass html: true to forward_email to also carry the original HTML.
Read (14)
Tool | Description |
| List all configured email accounts |
| List folders with unread counts and special-use flags |
| Paginated email listing with date, sender, subject, and flag filters; marks bulk mail; optional body preview |
| Read full email content with attachment metadata and bulk-mail classification (HTML converted to readable text by default) |
| Fetch full content of multiple emails in a single call (max 20); same bulk markers |
| Get read/flag/label state of an email without fetching the body |
| Search by keyword across subject, sender, and body; same bulk markers and optional preview as |
| Download an email attachment by filename or MIME part id, base64-encoded or (if configured) saved to the server's attachment download directory |
| Discover the real folder(s) an email resides in (resolves virtual folders) |
| Extract unique contacts from recent email headers |
| Reconstruct a conversation thread via References/In-Reply-To; same bulk markers |
| List available email templates |
| Email analytics — volume, top senders, daily trends |
| Connection health, latency, quota, and IMAP capabilities |
Bulk-mail classification
list_emails, search_emails, get_email, get_emails and get_thread mark
list and machine-generated mail, so an inbox can be triaged without reading
bodies:
Marker | Meaning | Derived from |
📰 | Mailing list or subscription |
|
🤖 | Machine-generated notification |
|
(none) | Ordinary person-to-person mail | no bulk headers present |
A newsletter also reports its unsubscribe URI when the sender offers one, and
flags 1-click when RFC 8058 one-click unsubscribe is supported. The headers
are requested as part of the listing fetch, so classifying a whole page costs no
extra round trip.
Classification is header-derived and therefore deterministic — no model call and no heuristics on subject text. A sender that omits the headers is reported as personal mail rather than guessed at. These are sender-asserted headers, not verified ones — treat the marker as a signal to weigh, not a fact to defer to.
Body previews
list_emails and search_emails accept preview: true to include roughly 200
characters of each message body. It is off by default: it fetches 1500 extra
bytes per message and lengthens the tool output, which is a cost the caller
should choose rather than inherit.
The preview is decoded from one body part rather than from message source —
IMAP section 1, or 1.1 when section 1 is itself a multipart. Because the fetch
stops at a byte count rather than at an encoding boundary, decoding tolerates
input cut mid-stream: base64 is truncated to a whole group, a half-written
quoted-printable escape is dropped, and the replacement character left by a
severed UTF-8 sequence is trimmed. Charsets beyond UTF-8 are handled — real mail
still ships iso-8859-1.
Markup is stripped whether or not the sender declared it, since bulk senders
routinely put HTML and stylesheets in a part labelled text/plain. Entities and
zero-width preheader padding are decoded, and a preview that reduces to markup
residue is omitted: no preview beats a misleading one.
Write (9)
Tool | Description |
| Send a new email (plain text or HTML, CC/BCC) |
| Reply with proper threading (In-Reply-To, References) |
| Forward with original content quoted |
| Save an email draft to the Drafts folder |
| Send an existing draft and remove from Drafts |
| Apply a template with variable substitution |
| Schedule an email for future delivery |
| List scheduled emails by status |
| Cancel a pending scheduled email |
Manage (7)
Tool | Description |
| Move email between folders |
| Move to Trash or permanently delete |
| Mark as read/unread, flag/unflag |
| Batch operation on up to 100 emails |
| Create a new mailbox folder |
| Rename an existing mailbox folder |
| Permanently delete a mailbox and contents |
Labels (5)
Tool | Description |
| Discover available labels (auto-detects provider strategy) |
| Add a label to an email (ProtonMail folders, Gmail X-GM-LABELS, or IMAP keywords) |
| Remove a label from an email |
| Create a new label |
| Delete a label |
Watcher & Alerts (6)
Tool | Description |
| Show IMAP IDLE connections, folders being monitored, and last-seen UIDs |
| List available AI triage presets with descriptions and suggested labels |
| Show current hooks configuration — preset, rules, and custom instructions |
| Update alert/notification settings at runtime |
| Diagnose desktop notification support and provide setup instructions |
| Send a test notification to verify OS permissions are configured |
Verification (3)
Tool | Description |
| Find (or wait up to 45 s for) the newest OTP/2FA code or sign-in magic link, with optional clipboard copy |
| Diagnose clipboard integration (platform tools, concealed-write support) with optional round-trip test |
| Update verification-catching settings at runtime (incl. starting/stopping the catcher), optional persist to config file |
Calendar & Reminders (8)
Tool | Description |
| Extract ICS/iCalendar events from an email |
| Analyze an email to detect events and reminder-worthy content |
| Add an email event to the local calendar (macOS Calendar.app; on Linux hands the .ics to xdg-open); event notes link back to the source email |
| Create a reminder in macOS Reminders.app from an email; reminder notes link back to the source email |
| List all available local calendars (macOS Calendar.app; elsewhere a single placeholder) |
| Check whether the local calendar is accessible (macOS probes for real; elsewhere always reports accessible) |
| List events from the local calendar in a date range (macOS only; returns empty elsewhere) |
| List reminders from Reminders.app (macOS only) |
Prompts (7)
Prompt | Description |
| Categorize and prioritize unread emails with suggested actions |
| Summarize an email conversation thread |
| Draft a context-aware reply to an email |
| Compose a new email from provided context and instructions |
| Extract actionable tasks from email threads |
| Summarize upcoming calendar events from emails |
| Suggest emails to archive, delete, or unsubscribe from |
Resources (6)
Resource | URI | Description |
Accounts |
| List of configured accounts |
Mailboxes |
| Folder tree for an account |
Unread |
| Unread email summary |
Templates |
| One email template by name; the resource lists the available names |
Stats |
| Email statistics snapshot |
Scheduled |
| Pending scheduled emails |
Provider Auto-Detection
Provider | Domains |
Gmail | gmail.com, googlemail.com |
Outlook / Hotmail | outlook.com, hotmail.com, live.com, outlook.de, outlook.co.uk |
Yahoo Mail | yahoo.com, yahoo.co.uk, yahoo.de, ymail.com |
iCloud | icloud.com, me.com, mac.com |
Fastmail | fastmail.com, fastmail.fm |
ProtonMail Bridge | proton.me, protonmail.com, protonmail.ch, pm.me |
Zoho Mail | zoho.com, zohomail.com |
GMX | gmx.com, gmx.de, gmx.net, gmx.at, gmx.ch |
Architecture
src/
├── main.ts — Entry point and subcommand routing
├── server.ts — MCP server factory
├── logging.ts — MCP protocol logging bridge
├── cli/ — Interactive CLI commands
│ ├── account-commands.ts — Account CRUD (list, add, edit, delete)
│ ├── setup.ts — Legacy setup alias → account add
│ ├── test.ts — Connection tester
│ ├── config-commands.ts — Config management (show, edit, path, init)
│ ├── install-commands.ts — MCP client registration (install, status, remove)
│ ├── cache-commands.ts — Local-mirror CLI (status, clear, verify)
│ └── scheduler.ts — Scheduler CLI
├── config/ — Configuration layer
│ ├── xdg.ts — XDG Base Directory paths
│ ├── schema.ts — Zod validation schemas
│ ├── providers.ts — Provider auto-detection + OAuth2 endpoints (experimental)
│ └── loader.ts — Config loader (TOML + env vars)
├── cache/ — Local SQLite mirror
│ ├── schema.ts — Mirror schema + migrations
│ ├── store.ts — Read/write API over the mirror
│ └── sync-engine.ts — Background reconcile + body backfill
├── connections/
│ └── manager.ts — Lazy persistent IMAP/SMTP with OAuth2 (experimental)
├── services/ — Business logic
│ ├── imap.service.ts — IMAP operations
│ ├── label-strategy.ts — Provider-aware label strategy (ProtonMail/Gmail/IMAP keywords)
│ ├── smtp.service.ts — SMTP operations
│ ├── template.service.ts — Email template engine
│ ├── oauth.service.ts — OAuth2 token management (experimental)
│ ├── calendar.service.ts — ICS/iCalendar parsing
│ ├── scheduler.service.ts — Email scheduling queue
│ ├── watcher.service.ts — IMAP IDLE real-time watcher with auto-reconnect
│ ├── hooks.service.ts — AI triage via MCP sampling + static rules + auto-labeling/flagging
│ ├── notifier.service.ts — Multi-channel notification dispatcher (desktop/sound/webhook)
│ ├── clipboard.service.ts — Concealed clipboard writes for caught codes (zero deps)
│ ├── dialog.service.ts — Native confirm dialogs + browser hand-off (macOS)
│ ├── verification-catcher.service.ts — Instant OTP/magic-link catch on new mail
│ ├── presets.ts — Built-in hook presets (inbox-zero, gtd, priority-focus, etc.)
│ └── event-bus.ts — Typed EventEmitter for internal email events
├── tools/ — MCP tool definitions (52)
├── prompts/ — MCP prompt definitions (7)
├── resources/ — MCP resource definitions (6)
├── safety/ — Audit trail and rate limiter
└── types/ — Shared TypeScript typesRoadmap
Ideas raised but deliberately not built yet, kept here so they aren't lost or quietly re-proposed:
AgentMail as an alternative provider. AgentMail offers API-key-only, programmatic
@agentmail.toinboxes — no IMAP/SMTP host/port/TLS/credential setup, useful for spinning up throwaway agent-owned addresses. It would be additive (a second provider alongside real IMAP/SMTP accounts, not a replacement), but is a product/architecture decision — a new vendor dependency and tool surface — rather than a bug fix or gap-fill, so it's parked here rather than auto-built. Revisit if there's real demand for agent-provisioned inboxes rather than access to existing mailboxes.
Maintainers
Release prerequisites
release.yml checks these before publishing anything, so a missing one is a
fast red run rather than a half-published release:
Secret | Needed for |
| GoReleaser Pro license — |
| PAT with |
| npm publish |
| Docker Hub push (GHCR uses |
The Homebrew cask ships the unsigned Bun binary, so its postflight clears
the macOS quarantine bit — macOS only, since Linux has no xattr(1) and the
cask DSL's system_command raises on a non-zero exit. brew uninstall --zap
trashes the config, state, cache and data directories plus the two launch
agents.
Contributing
PRs accepted. Please conform to the standard-readme specification when editing this README.
# Development workflow
pnpm install
pnpm typecheck # type check
pnpm check # lint and format
pnpm build # build
pnpm start # runTesting
pnpm test # unit tests
pnpm test:integration # against a throwaway GreenMail server (Docker required)
pnpm smoke # most MCP tools against a real configured account (see caveats below)The integration suite runs each tool against a throwaway GreenMail server
(spun up via Testcontainers). Attachment coverage
(email-attachments.integration.test.ts) drives the real MCP tool layer and
locks in the size-awareness contract: every send reports each file's size and
the raw≈base64 total, the cap is enforced on the encoded total (what the
wire carries), an over-cap send fails closed (nothing is delivered), and
forwards carry the original attachments unless includeAttachments: false.
The effective cap is resolved per account: an explicit per-account
max_attachment_mb wins outright; otherwise the stricter of the global
[settings.send] cap and the provider's documented limit applies (Gmail,
Yahoo, and Proton 25 MB; Outlook.com, iCloud, and GMX 20 MB; Fastmail 50 MB).
pnpm smoke drives all tools over stdio the way a client does, which reaches
what GreenMail cannot: provider quirks, real-world MIME, and the macOS
Calendar bridges. It needs a working account, so it is not part of CI. Writes
stay inside a scratch folder and a draft it creates itself; it does not touch
Calendar, Reminders, notifications, or reply_email, which would send mail to
a real correspondent. Pass E2E_ACCOUNT=<name> to choose an account, otherwise
it uses the first one configured.
License
Available Tools
52 toolsadd_labelAdd labelAIdempotent
Add a label to an email. For ProtonMail, this copies the email into the corresponding Labels/ folder. For Gmail and standard IMAP, this sets a keyword flag on the message.
| Name | Required | Description | Default |
|---|---|---|---|
| label | Yes | Label name to add (e.g., "Important", "Project-X") | |
| account | Yes | Account name from list_accounts | |
| emailId | Yes | Email ID (UID) from list_emails | |
| mailbox | Yes | Mailbox containing the email (must be a real folder) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare mutation (readOnlyHint=false), idempotency (idempotentHint=true), and non-destructiveness (destructiveHint=false). The description adds valuable provider-specific behavior: ProtonMail copies the message into a Labels/<name> folder, while Gmail and standard IMAP set a keyword flag. It stops short of covering error handling when the label is absent or permission requirements.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tight sentences: the first states the action, the second explains provider-dependent side effects. Every sentence is front-loaded and earns its place with no redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema and full schema coverage, the description plus annotations are nearly sufficient. It explains the action and provider behavior but omits what happens if the specified label does not exist (error vs. auto-create), which would be useful for a mutation tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so all four parameters are already well documented in the schema itself. The description adds no additional parameter-level meaning, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb 'Add' and resource 'label' scoped to 'an email', clearly distinguishing it from sibling tools like create_label, remove_label, and delete_label that operate on the label entity itself rather than applying a label to a message.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies usage—use this when you want to attach a label to an email—but does not explicitly state when to choose it over alternatives like mark_email, move_email, or create_label, nor does it mention prerequisites such as the label already existing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
add_to_calendarAdd to calendarA
Add an email event to the local calendar (macOS Calendar.app / Linux via xdg-open). Automatically extracts event data from the email: ICS attachments, meeting URL (Zoom/Teams/Meet), conference dial-in / ID / passcode, attendees, and email body excerpt. All relevant email attachments (PDFs, docs, etc.) are saved locally and linked in the event notes. A native confirmation dialog is shown on macOS before the event is written. Returns one of: added | cancelled | timed_out | no_display.
| Name | Required | Description | Default |
|---|---|---|---|
| account | Yes | Account name | |
| confirm | No | Show native confirmation dialog before adding (default: true) | |
| mailbox | No | Mailbox path (default: INBOX) | INBOX |
| email_id | Yes | Email UID | |
| alarm_minutes | No | Minutes before event to show an alert (default: 15) | |
| calendar_name | No | Target calendar name (empty = default calendar) | |
| save_attachments | No | Save non-ICS email attachments locally and link them in the event notes (default: the [settings.attachments] auto_download setting) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the readOnlyHint=false/destructiveHint=false annotations: it discloses that a native confirmation dialog gates the write on macOS, that non-ICS attachments are written to local disk and linked in notes, that platform behavior differs (Calendar.app vs xdg-open), and it enumerates every return outcome including cancelled and timed_out.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
One dense, front-loaded paragraph with no filler; the verb and platform lead, followed by extraction behavior, side effects, and return values. Slightly long, but every clause carries information an agent needs.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With 7 parameters and no output schema, the description compensates by enumerating the four possible return values and describing the side effects (attachment writes, confirmation dialog, platform dispatch). Nothing needed to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% so the baseline is 3, but the description adds real meaning: it clarifies that confirm maps to a macOS-native dialog before the write, and that save_attachments covers any non-ICS attachment with local persistence plus note linking. alarm_minutes and calendar_name are left to the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource — 'Add an email event to the local calendar' — and names the underlying platform (macOS Calendar.app / Linux via xdg-open). This clearly separates it from siblings like extract_calendar, analyze_email_for_scheduling, and create_reminder, which do not write to the calendar from an email.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The implicit context (an email containing event data) is conveyed through the extraction behavior, so an agent can infer when to reach for it. However, it never explicitly names alternatives such as extract_calendar or analyze_email_for_scheduling, nor states when NOT to use this tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
analyze_email_for_schedulingAnalyze email for schedulingARead-only
Analyze an email to detect calendar events and/or reminder-worthy content. Returns structured analysis so the AI can decide whether to call add_to_calendar, create_reminder, both, or neither. Use this as the first step before creating any scheduling resource.
| Name | Required | Description | Default |
|---|---|---|---|
| account | Yes | Email account name | |
| mailbox | No | Mailbox containing the email | INBOX |
| email_id | Yes | Email ID from list_emails_metadata |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, so the no-side-effect profile is covered. The description usefully clarifies that the output is an analysis the agent interprets rather than an action, but says nothing about cost/latency (this appears to invoke analysis) or how the structured result should be consumed.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences with the detection scope front-loaded, then routing guidance, then an explicit ordering rule. No filler or restatement of the tool name.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, and since the entire value of this tool is the structured analysis it returns, the description should sketch what that structure contains (event vs reminder signals, confidence). It also omits any relationship to the extract_calendar sibling, which is a real gap for a pre-step tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and the required account/email_id plus optional mailbox are fully documented in the schema. The description adds no parameter-level meaning beyond 'an email', so the schema carries all semantic weight.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (analyze) and resource (email) plus the scope of detection: calendar events and/or reminder-worthy content. It is clearly distinguishable from the create-side siblings (add_to_calendar, create_reminder), but it never addresses the closely named sibling extract_calendar, leaving overlap ambiguity.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
'Use this as the first step before creating any scheduling resource' gives explicit ordering guidance and names the downstream alternatives (add_to_calendar, create_reminder, both, neither). It stops short of saying when this analysis is unnecessary or how it differs from extract_calendar.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
apply_templateApply templateA
Apply an email template with variable substitution. Use action "preview" to see the result, "draft" to save as draft, or "send" to send immediately.
| Name | Required | Description | Default |
|---|---|---|---|
| to | No | Recipient addresses (required for send, optional for draft) | |
| action | No | What to do with the composed email | preview |
| account | Yes | Account name from list_accounts | |
| template | Yes | Template name from list_templates | |
| variables | Yes | Variable values as key-value pairs, e.g. { topic: 'Q1 Review' } |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=false, destructiveHint=false, and openWorldHint=true, so the safety profile is covered. The description adds that 'send' transmits immediately, but says nothing about permissions, failure behavior, or whether preview/draft have side effects. Modest value beyond annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tight, front-loaded sentences with no filler. The second sentence partially duplicates the action enum from the schema, which is a small redundancy, but overall the text is efficient.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With 100% schema coverage and annotations covering the safety profile, the description need not re-explain parameters or return values. It covers the three action modes, which is the key behavioral fork. Minor gaps remain around send/draft side effects but are acceptable given the structured data.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents account, template, variables, to, and the action enum. The description adds only the 'variable substitution' concept, which is largely redundant with the schema's own variable example. Baseline 3 is correct when the schema does the heavy lifting.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Apply an email template with variable substitution'), which is clearer than the sibling send_email/save_draft by naming the template mechanism. It does not, however, explicitly contrast itself with those siblings, so an agent still infers the boundary.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The second sentence explains what each 'action' value does, which is mode-selection guidance. But it largely restates the enum already in the schema and gives no when-to-use-this-tool-vs-alternatives (e.g. apply_template+send vs send_email). Usage is implied, not stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
bulk_actionBulk actionADestructive
Batch operation on multiple emails by UID list. Supports mark_read, mark_unread, flag, unflag, move, and delete. Max 100 IDs per call. Returns success/failure counts.
| Name | Required | Description | Default |
|---|---|---|---|
| ids | Yes | Array of email UIDs (max 100). Get UIDs from list_emails or search_emails. | |
| action | Yes | Bulk action to perform | |
| account | Yes | Account name from list_accounts | |
| mailbox | No | Source mailbox containing the emails | INBOX |
| destination | No | Destination mailbox — required when action is 'move' |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=false and destructiveHint=true, so the safety profile is covered. The description adds real value on top: it names delete as one of the destructive actions, discloses the 100-ID batching limit, and states that success/failure counts are returned, which hints at partial-failure behavior. It stops short of saying whether failures are atomic/rolled back or what auth is required.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, front-loaded with the core purpose, then supported actions, then limits and return shape. No redundant or filler language.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 5-param mutation tool with no output schema, the description covers actions, batching limits, and return summary, while annotations carry the destructive profile and the schema carries parameter detail. Minor gap: no statement about partial-failure/atomicity semantics or the destination requirement for 'move' (left to the schema).
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and the schema already documents every parameter, including the move-requires-destination rule and the UID array bounds. The description restates the action list and the 100 max without adding syntax or format meaning, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (batch operation) and resource (multiple emails by UID list) and enumerates exactly which actions are supported. The 'bulk'/'multiple emails' framing cleanly separates it from the single-item siblings move_email, delete_email, and mark_email without needing to name them.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Batching semantics imply the use case (act on many UIDs in one call rather than looping single-item tools), and the max-100 cap signals a practical boundary. However, it never states when to prefer this over repeated single-email calls, nor any exclusions or prerequisites, so guidance remains implicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
cancel_scheduledCancel scheduledADestructive
Cancel a scheduled email. Removes it from the queue and deletes the associated draft.
| Name | Required | Description | Default |
|---|---|---|---|
| schedule_id | Yes | Schedule ID to cancel |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=false and destructiveHint=true, so the safety profile is covered. The description adds a genuine side-effect disclosure beyond annotations: the scheduled item is removed from the queue and the associated draft is deleted, which is non-obvious and important. It stops short of stating reversibility or failure modes.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences, front-loaded with the action and immediately followed by the destructive side effect. No filler or redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a one-parameter destructive mutation with full annotation coverage and no output schema, the description supplies what matters most: the queue removal and draft deletion. It could be complete with a note on whether cancellation is permanent or error behavior, but the essentials are present.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% for the single schedule_id parameter, so the schema already documents it fully. The description adds no format, source, or constraint detail beyond the schema, making the baseline 3 appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('Cancel') and resource ('scheduled email'), clearly distinguishing it from siblings like delete_email and list_scheduled. It does not explicitly name an alternative sibling by name, but the action is unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives no when-to-use context, prerequisites, or named alternatives. Usage is only implied by the tool name ('scheduled'), with no guidance on how it differs from delete_email or when a scheduled item cannot be cancelled.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
check_calendar_permissionsCheck calendar permissionsARead-only
Check whether the local calendar is accessible. On macOS, verifies Calendar.app access (requires Privacy & Security → Calendars permission). Returns granted status and step-by-step setup instructions if access is denied.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, so safety is covered. The description adds real value beyond that: platform-specific behavior (macOS Privacy & Security → Calendars), the fact that it returns a granted status, and that it returns setup instructions on failure — useful for a diagnostic tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, front-loaded with the core purpose, followed by platform behavior and return content. No filler or restatement of the tool title.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema and no parameters, the description carries the full burden and does so: it describes the return value (granted status plus step-by-step fix instructions) and the platform caveat, leaving no gap an agent needs to call and interpret this correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes no parameters, so the baseline is 4; there is nothing for the description to clarify beyond what the empty schema already implies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource: checking whether the local calendar is accessible, and scopes it to the platform (macOS Calendar.app). This clearly separates it from other diagnostic siblings like check_health or check_clipboard_setup, which target different subsystems.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies usage (diagnosing calendar access before attempting calendar operations) and mentions the privacy permission needed, but it never states explicitly when to call this versus alternatives such as list_calendars or extract_calendar, nor any when-not conditions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
check_clipboard_setupCheck clipboard setupARead-only
Diagnose the clipboard integration used for caught verification codes: platform tools, concealed-write support, current [settings.verification] values, and the watcher prerequisite. With test_write=true it performs a harmless clipboard round-trip (writes a test string, reads it back, clears it).
| Name | Required | Description | Default |
|---|---|---|---|
| test_write | No | Write/read/clear a test string to verify the pipeline end-to-end |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes beyond the annotations by naming the concrete behaviors the diagnosis inspects and by disclosing that test_write=true performs a write/read/clear round-trip rather than a pure read. No mention of error modes, permission requirements, or what the diagnostic report contains, so it stops short of 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, front-loaded with the purpose, then the one conditional behavior. Every clause carries information (the four inspected areas, the opt-in write path) and nothing is padding.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema and no annotation detail beyond the safety hints, the description should carry more of the result contract – what the diagnostic actually reports or what a failing check looks like. It names the inspected inputs but leaves the output shape entirely unstated.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already defines test_write as 'Write/read/clear a test string to verify the pipeline end-to-end'. The description restates that as a 'harmless clipboard round-trip', adding only the reassurance of harmlessness – a marginal increment over the structured field.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (diagnose) and a narrow resource (the clipboard integration for caught verification codes), then enumerates exactly what is inspected: platform tools, concealed-write support, [settings.verification] values, and the watcher prerequisite. This is clearly distinguishable from siblings like configure_verification or check_notification_setup.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is only implied – the agent can infer this is the pre-flight/repair check for verification-code capture, and the description explains what test_write=true does, but it never says when to reach for this versus configure_verification or get_verification_code, nor any prerequisite condition for running it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
check_healthCheck healthARead-only
Check connection health, quota, and capabilities for email accounts, plus local cache size, coverage and hit ratio. Useful for diagnosing issues.
| Name | Required | Description | Default |
|---|---|---|---|
| account | No | Account name (checks all accounts if omitted) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already establish readOnlyHint=true and destructiveHint=false, so safety is covered. The description adds real value beyond them by itemizing what is actually inspected (connection health, quota, capabilities, cache size, coverage, hit ratio), which tells the agent this is a broad diagnostic probe rather than a narrow status read.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences with no filler, and the enumeration of checked dimensions is front-loaded before the purpose statement. Slightly dense but every clause earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-optional-param, read-only diagnostic with no output schema, the description adequately conveys what areas are reported on. It would be marginally stronger if it said whether the check performs live network calls or reads cached state.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
There is a single optional parameter and schema description coverage is 100%, with the schema stating 'Account name (checks all accounts if omitted)'. The description adds nothing about the parameter, so the baseline 3 applies since the schema carries the full burden.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description gives a specific verb ('check') and enumerates the concrete resources inspected: connection health, quota, capabilities, cache size, coverage and hit ratio. That scope clearly separates it from siblings like get_email_status or get_watcher_status, though it never names an alternative explicitly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
'Useful for diagnosing issues' implies a diagnostic context but does not state when to prefer this tool over get_email_status, get_watcher_status or check_notification_setup, nor any exclusion conditions. Usage is implied rather than prescribed.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
check_notification_setupCheck notification setupARead-only
Diagnose desktop notification support on this platform. Checks if required OS tools are available and provides setup instructions to enable notification permissions (macOS, Linux, Windows).
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, so the safety profile is covered. The description adds useful context beyond annotations by disclosing that it probes OS-level tool availability and returns setup instructions, but it stops short of characterizing the diagnostic result (pass/fail granularity, error modes) — no output schema exists, so this is a real gap.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, zero waste, front-loaded with the primary action ('Diagnose desktop notification support') before the elaboration. Every clause earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter, read-only diagnostic with no output schema, the description covers what it checks and where it applies. The one missing piece is what the caller gets back (per-platform pass/fail detail vs. free-form instructions), which matters more here because no output schema documents the return value.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so the baseline is 4; there is nothing for the description to disambiguate. It correctly implies the tool is platform-scoped and argument-free.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource ('Diagnose desktop notification support', 'Checks if required OS tools are available') plus the platform scope (macOS, Linux, Windows). It is clear on its own, but it does not name or contrast with neighboring tools such as test_notification, configure_alerts, or check_clipboard_setup, so an agent must infer the boundary itself.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied rather than stated: the natural read is 'run this to diagnose whether notifications can work before trying test_notification', but the description never says when to call it versus those siblings or what to do with the result. There are no explicit exclusions or prerequisites.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
configure_alertsConfigure alertsA
Update alert/notification settings at runtime. Changes take effect immediately. Use save=true to persist changes to the config file. Omit any field to leave it unchanged.
| Name | Required | Description | Default |
|---|---|---|---|
| save | No | Persist changes to config.toml (default: runtime only) | |
| sound | No | Enable/disable sound alerts for urgent emails | |
| desktop | No | Enable/disable desktop notifications | |
| webhook_url | No | Webhook URL for external notifications (empty string to disable) | |
| webhook_events | No | Which urgency levels trigger webhook dispatch | |
| urgency_threshold | No | Minimum urgency level to trigger desktop notifications |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With annotations only declaring readOnlyHint=false and destructiveHint=false, the description carries real added weight: changes apply immediately, the default is runtime-only, and omitting a field leaves it untouched (a partial-update contract). It does not address failure modes or whether a restart is ever required, keeping it short of a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, front-loaded with the core action and then the persistence and partial-update rules. Every sentence carries a distinct, actionable fact with no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a six-parameter, all-optional mutation tool with no output schema, the description covers the essentials an agent needs: immediacy of effect, persistence control, and partial-update behavior. It is slightly thin on interaction effects (e.g., what happens to webhook_events when the URL is cleared) but adequate overall.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3 regardless. The description nonetheless adds a genuine semantic rule not spelled out in the schema — "omit any field to leave it unchanged" — clarifying that all six optional parameters behave as a merge rather than a full overwrite, and it reinforces the save semantics.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource ("Update alert/notification settings"), clearly distinguishing this from read-oriented siblings like check_notification_setup and test_notification. It stops short of naming an alternative tool by name, so it does not reach the full sibling-differentiation credit.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives one concrete usage rule — "Use save=true to persist changes to the config file" — which tells the agent how to choose behavior. However, it never states when to reach for this tool versus configure_verification, check_notification_setup, or test_notification, so the routing guidance is only implied.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
configure_verificationConfigure verification catchingA
Update [settings.verification] at runtime — changes take effect immediately, including starting/stopping the ambient catcher via enabled. Use save=true to persist to config.toml (previous file is backed up). Omit every field to see the current configuration. Registered in read-only mode too: this writes config, never the mailbox.
| Name | Required | Description | Default |
|---|---|---|---|
| save | No | Persist changes to config.toml (default: runtime only) | |
| notify | No | Desktop notification when something is caught | |
| enabled | No | Master switch for verification catching | |
| accounts | No | Accounts to watch (empty array = all) | |
| auto_copy | No | Copy caught codes/links to the clipboard | |
| copy_links | No | Also catch sign-in magic links | |
| link_action | No | Caught links: offer to open in the browser, or copy like a code | |
| confirm_copy | No | Ask (native dialog) before touching the clipboard | |
| max_age_minutes | No | Ignore messages older than this | |
| sender_denylist | No | Sender globs to ignore | |
| sender_allowlist | No | Sender globs, e.g. ["*@github.com"] (empty = all senders) | |
| clear_after_seconds | No | Auto-clear clipboard after N seconds (0 = never) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the annotations (readOnlyHint=false, destructiveHint=false) by disclosing that changes take effect immediately, that save=true persists to config.toml with a backup of the previous file, that enabled starts/stops the ambient catcher, and that the tool is available in read-only mode yet only writes config, never the mailbox. This gives the agent a full behavioral picture of a mutation with a rollback safety net.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three tightly packed sentences, front-loaded with the primary action and effect, followed by persistence, read behavior, and scope caveats. Each sentence carries distinct, useful information with minimal waste.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 12-parameter mutation tool with no output schema, the description covers effect timing, persistence/backup, read mode, and scope boundaries, which is close to complete. It could still note the relationship to sibling configuration tools, but nothing essential for correct invocation is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents all 12 parameters, establishing the baseline of 3. The description reinforces the meaning of a couple of key params (save, enabled) but adds little semantic detail the schema does not already carry.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (Update) and resource ([settings.verification]) and conveys the domain (verification catching), which an agent can clearly separate from siblings like configure_alerts or get_verification_code. It does not explicitly name or contrast an alternative tool, so it stops short of the top band.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives concrete operating guidance: use save=true to persist, and omit every field to read the current configuration. It establishes clear usage context for the write-vs-read patterns but offers no when-not guidance or explicit routing to a sibling tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_labelCreate labelA
Create a new label. For ProtonMail, creates a folder under Labels/. For standard IMAP keywords, labels are auto-created on first use — this is a no-op.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Label name (e.g., "Project-X"). For nested labels use "/" separator (e.g., "Work/Urgent"). | |
| account | Yes | Account name from list_accounts |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only cover the safety profile (readOnlyHint=false, destructiveHint=false). The description adds real behavioral context beyond that: ProtonMail stores it as a folder under Labels/, and for standard IMAP keywords the call is a no-op because labels are auto-created on first use — a critical 'this may do nothing' disclosure.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, front-loaded with the primary action and then the backend-specific behavior. Every sentence carries information; nothing is redundant with the title.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple two-parameter creation tool with no output schema, the description covers the key ambiguity (per-backend behavior and the no-op case). It lacks only sibling routing guidance to be fully complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, and the schema itself documents both parameters including the '/' nesting convention and the list_accounts reference. The description adds no parameter-level meaning, so the baseline of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Create a new label') and goes further by distinguishing the two backends it can act on. It does not, however, explicitly contrast itself with the sibling 'add_label' (apply) vs 'create_label' (define), so an agent must infer that boundary.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied rather than stated. The No-op caveat for standard IMAP keywords hints at when this call is pointless, but there is no explicit guidance like 'use add_label to apply an existing label to a message' or prerequisites beyond the account param.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_mailboxCreate mailboxAIdempotent
Create a new mailbox (folder). Use '/' as separator for nested folders (e.g., 'Work/Projects'). Use list_mailboxes to see existing folders.
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes | Folder path to create (e.g., 'Archive/2026' or 'Projects') | |
| account | Yes | Account name from list_accounts |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=false, idempotentHint=true, and destructiveHint=false, covering the safety and idempotency profile. The description adds useful context about nested folder syntax but does not describe what happens if the folder already exists, whether parent folders are auto-created, or any permission requirements.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, front-loaded with the core action, followed by syntax guidance and a helpful cross-reference. Every sentence earns its place with no redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple creation tool with full schema coverage and relevant annotations, the description covers purpose, path syntax, and a useful sibling reference. A minor gap remains around conflict handling and whether parent directories are created automatically, but annotations mitigate the idempotency concern.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description adds meaning beyond the schema by explicitly specifying the '/' separator for nested folders and giving an example ('Work/Projects'), which clarifies how the 'path' parameter can be structured.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('Create') and resource ('mailbox (folder)'), and immediately clarifies that mailbox means folder. It distinguishes itself from siblings like list_mailboxes and delete_mailbox by explicitly naming the read-only alternative.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Points to list_mailboxes for checking existing folders, which is a clear contextual alternative. However, it does not explicitly state when not to use this tool or outline prerequisites (e.g., required permissions).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_reminderCreate reminderA
Create a reminder in macOS Reminders.app from an email. Shows a native confirmation dialog before adding. The reminder notes include a message: deep link that reopens the source email in Mail.app. Use for action items, deadlines, and follow-up tasks extracted from emails. Use analyze_email_for_scheduling first to let the AI decide if a reminder is appropriate.
| Name | Required | Description | Default |
|---|---|---|---|
| notes | No | Reminder body/notes (defaults to auto-built from email; an open-email deep link is appended either way) | |
| title | No | Reminder title (defaults to email subject) | |
| account | Yes | Email account name | |
| confirm | No | Show native confirmation dialog before adding (default: true) | |
| mailbox | No | Mailbox containing the email | INBOX |
| due_date | No | ISO 8601 due date (e.g. 2026-02-20T10:00:00). Leave empty for no due date. | |
| email_id | Yes | Email ID from list_emails_metadata | |
| priority | No | Reminder priority | none |
| list_name | No | Reminders list name (default list if omitted) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Adds real behavior beyond annotations: a native confirmation dialog appears before adding, and notes always get an appended deep link that reopens the source email. It does not state permission/authorization requirements or what happens if the Reminders list is missing, so it falls short of a full behavioral picture.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Four short sentences, each carrying a distinct load: what it does, the confirmation behavior, the notes side effect, and the routing precondition. Front-loaded with purpose and zero filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 9-parameter mutation tool with no output schema, the description covers purpose, side effects, and prerequisites, which is most of what an agent needs. It stops short of describing the result returned or list-not-found failure modes.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so all 9 parameters are already documented. The description mostly restates schema facts (notes auto-built, deep link appended, title defaults to subject) rather than adding new meaning, so baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (create), resource (reminder), target app (macOS Reminders.app), and source (an email). An agent can immediately distinguish it from list_reminders and analyze_email_for_scheduling.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly says when to use it ('action items, deadlines, and follow-up tasks extracted from emails') and names the gating alternative ('Use analyze_email_for_scheduling first'). This is a concrete when/when-not routing instruction.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_emailDelete emailADestructive
Delete an email. By default moves to Trash. Set permanent=true for permanent deletion (⚠️ irreversible). The mailbox must be a real folder. Use find_email_folder first if the email was found in a virtual folder.
| Name | Required | Description | Default |
|---|---|---|---|
| account | Yes | Account name from list_accounts | |
| emailId | Yes | Email ID to delete (from list_emails) | |
| mailbox | No | Mailbox containing the email | INBOX |
| permanent | No | ⚠️ Permanently delete (skip Trash) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true, but the description adds materially: the default is a recoverable move to Trash, permanent=true is irreversible (flagged with a warning glyph), and the target mailbox must be a real folder. It does not cover auth/permission needs or success return, keeping it below a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, zero filler, with the default behavior stated first and the destructive escalation second. Every sentence carries information an agent needs to call the tool safely.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 4-parameter mutation with annotations and no output schema, the description covers reversibility, the destructive option, and the folder prerequisite. It is slightly thin on what a successful deletion returns and on account/emailId sourcing, but nothing critical to correct invocation is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so 3 is the baseline, but the description adds semantics not in the schema: the mailbox must be a real (non-virtual) folder, which explains why find_email_folder may be needed. It also reinforces the irreversible nature of the permanent flag beyond the schema's brief note.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ("Delete an email") and immediately qualifies the operation's scope: default soft-delete to Trash versus permanent removal. This distinguishes it from siblings like delete_label, delete_mailbox, and bulk_action, which operate on different resources.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives a concrete prerequisite and routes the agent to a sibling tool ("Use find_email_folder first if the email was found in a virtual folder") and explains the condition that selects the permanent path. It does not mention when to prefer bulk_action for multiple messages, so it stops short of full alternative coverage.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_labelDelete labelADestructive
Delete a label. For ProtonMail, deletes the label folder. For standard IMAP keywords, labels cannot be deleted server-wide — use remove_label on individual emails.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Label name to delete | |
| account | Yes | Account name from list_accounts |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already flag destructiveHint=true and readOnlyHint=false, so safety is covered. The description adds real value beyond them by disclosing provider-dependent behavior — that on IMAP the operation effectively fails and a different tool must be used. It doesn't state whether the delete is reversible or what happens to emails bearing the label, which keeps it out of 5 territory.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, zero waste, with the primary action front-loaded and the provider caveat following immediately. Every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a two-param destructor with full schema coverage, no output schema, and annotations carrying the safety profile, the description covers everything an agent needs: what it does, the provider dependency, and the fallback. Nothing essential is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% with both params (name, account) documented, so the schema does the heavy lifting. The description adds nothing about syntax, naming constraints, or the account param. Baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (delete) and resource (label), and explicitly distinguishes the two backends: ProtonMail deletes the label folder, standard IMAP keywords can't be deleted server-wide. An agent can separate this from add_label/create_label/remove_label without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly names the alternative (remove_label on individual emails) and the exact condition that routes to it (standard IMAP keywords, no server-wide delete). This is a genuine when-not clause, not just implied context.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_mailboxDelete mailboxADestructive
⚠️ DESTRUCTIVE: Permanently delete a mailbox and ALL its contents. This cannot be undone. Use list_mailboxes to verify the folder path.
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes | Folder path to delete (⚠️ all emails inside will be lost) | |
| account | Yes | Account name from list_accounts |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true and readOnlyHint=false, so the safety profile is known. The description adds material context beyond that: the deletion is permanent, irreversible, and destroys all contained emails. It stops short of stating permission requirements or failure behavior for non-empty mailboxes.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tight sentences with the destructive warning front-loaded and the precondition second. Every clause carries weight and nothing is redundant.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a two-parameter mutation with annotations covering destructiveness and no output schema, the description supplies the irreversibility and content-loss facts an agent needs before calling. Missing only auth/permission expectations and what happens on a non-empty or invalid path.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so both parameters are already documented, including the warning on 'path'. The description only echoes the folder-path concept and the list_mailboxes source of truth, adding no syntax or format detail beyond the schema. Baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (delete) and resource (mailbox) plus scope ('ALL its contents'), which cleanly separates it from delete_email, delete_label, and rename_mailbox. The permanence qualifier makes the operation unmistakable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives one procedural pointer — verify the folder path with list_mailboxes first — which is genuinely useful. However, it offers no guidance on when to choose this over delete_email or bulk_action, and no explicit when-not conditions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
download_attachmentDownload attachmentARead-only
Download an email attachment by filename or part id. First use get_email to see available attachments with their filenames and partIds (partId is unambiguous — use it for duplicate filenames and inline cid images). Returns base64-encoded content for files ≤5MB. saveToDownloadDir is unavailable — no attachment download directory is configured on this server.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | Email ID (UID) from list_emails or get_email | |
| partId | No | MIME part id from get_email metadata (takes precedence over filename) | |
| saveAs | No | Filename to save as under the download directory (basename only, no path separators). Only used with saveToDownloadDir; defaults to the attachment’s own filename. | |
| account | Yes | Account name from list_accounts | |
| mailbox | No | Mailbox containing the email | INBOX |
| filename | No | Exact attachment filename (from get_email metadata) | |
| saveToDownloadDir | No | Write the attachment to the server-configured download directory instead of returning it base64-encoded. Fails if no download directory is configured. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, so safety is covered. The description adds valuable behavioral context: base64-encoded return, a ≤5MB size limit, and the unavailability of saveToDownloadDir. It does not explain what happens for files larger than 5MB, so it is strong but not exhaustive.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three tight sentences, front-loaded with the primary action. Each sentence contributes unique operational information: the get_email prerequisite, the return format and size limit, and the disabled save option.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 7-parameter read-only tool with no output schema, the description supplies the key context an agent needs: how to discover attachments, what the return value looks like, the size limit, and the disabled save directory. It omits the behavior for attachments larger than 5MB, leaving a minor gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description adds decision-relevant meaning beyond the schema by highlighting partId precedence for duplicate filenames and inline cid images, and by declaring saveToDownloadDir unavailable, which directly affects parameter usage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('Download') and resource ('email attachment'), and clarifies the two selection mechanisms ('filename or part id'). It also points to get_email for discovering attachments, which implicitly separates its role from the retrieval tool.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly instructs the agent to first use get_email, and advises using partId for duplicate filenames and inline cid images. It also states that saveToDownloadDir is unavailable, giving clear when-not-to-use guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
extract_calendarExtract calendarBRead-only
Extract calendar events (ICS/iCalendar) from an email. Returns structured event data including time, location, attendees, and status.
| Name | Required | Description | Default |
|---|---|---|---|
| account | Yes | Account name | |
| mailbox | No | Mailbox path (default: INBOX) | INBOX |
| email_id | Yes | Email UID |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, so the safety profile is covered. The description adds genuine value by disclosing the return shape (time, location, attendees, status), but it says nothing about behavior when no calendar part exists or how parsing failures surface.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tight sentences: the first states what is extracted and from where, the second lists the returned fields. Nothing is redundant and the essential information is front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a read-only, three-parameter tool with full schema coverage and annotations covering safety, the description is nearly complete: it names the source and the returned fields. It only omits behavior for emails without an ICS part, which is a minor gap given the low complexity.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, with account, mailbox, and email_id all documented in the schema itself, so the baseline is 3. The description adds no parameter-level meaning beyond what the schema already provides.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb (Extract) and resource (calendar events / ICS from an email), and the parenthetical format hint (ICS/iCalendar) pins down what is being parsed. It implicitly distinguishes itself from extract_contacts by resource type, though it never names that near-parallel sibling to route the agent explicitly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no guidance on when to use this tool versus alternatives such as get_email, extract_contacts, or add_to_calendar, nor any stated preconditions (e.g. the email must actually contain an ICS part). The 'from an email' phrasing only weakly implies the required email_id context.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
extract_contactsExtract contactsARead-only
Extract unique contacts from recent email headers. Returns contacts sorted by frequency (most frequent first). Useful for finding frequent correspondents or building an address book.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Number of recent emails to scan (default: 100, max: 500) | |
| account | Yes | Account name from list_accounts | |
| mailbox | No | Mailbox to scan (default: INBOX) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare the read-only, non-destructive safety profile, so the description's job is to add operational context — and it does: results are de-duplicated and sorted by frequency descending, drawn from recent email headers. It omits how contacts are identified (name vs. address) and any result-size behavior, which keeps it out of the 5 band.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, zero filler, with the core action front-loaded and the return-ordering detail placed second. Every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a no-output-schema read tool with full annotation and schema coverage, the description supplies the essential missing pieces: output ordering, de-duplication, and the source of contacts. A note on whether contacts include display names, or how many results to expect for a given limit, would fully close the gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, including defaults and bounds for limit, account, and mailbox, so the schema carries the parameter burden. The description only implies the 'recent' window that limit controls, adding no syntax or format detail beyond the schema — the correct baseline is 3.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Extract unique contacts from recent email headers'), and the uniqueness/recency scope distinguishes it from every sibling in the list, none of which touches contact extraction. An agent can immediately tell what this does.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives a clear use context ('finding frequent correspondents or building an address book'), which tells the agent when this tool is the right pick. It stops short of naming any alternative or a when-not condition, so it doesn't reach the top band.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
find_email_folderFind email folderARead-only
Find a real mailbox folder an email belongs to. Required before move_email or delete_email when the email was found in a virtual folder (e.g., "All Mail", "Starred"). Returns the first real folder path found, to use as sourceMailbox; on a label-based server a message may also sit in other folders.
| Name | Required | Description | Default |
|---|---|---|---|
| account | Yes | Account name from list_accounts | |
| emailId | Yes | Email ID (UID) from list_emails | |
| sourceMailbox | No | Mailbox where the email is currently visible (e.g., "All Mail") | INBOX |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, so safety is covered. The description adds genuine behavioral context beyond that: it returns only the FIRST real folder path, that path is meant as sourceMailbox, and on label-based servers the message may live in other folders too. This caveat about incomplete results is valuable.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, front-loaded with purpose and the precondition, then the return semantics. Every sentence earns its place; slightly dense but no waste.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, and the description compensates by describing what is returned (first real folder path) and its intended use as sourceMailbox, plus the label-server caveat. Nothing essential for correct invocation is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so baseline is 3. The description goes beyond the schema by explaining the role of sourceMailbox ('where the email is currently visible' in a virtual folder) and tying the returned value back to that parameter, adding real semantic linkage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Find a real mailbox folder an email belongs to') and explicitly contrasts 'real' folders against 'virtual' ones. This is distinguishable from siblings like list_mailboxes and list_labels without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly names the triggering condition ('when the email was found in a virtual folder') and the dependent siblings ('Required before move_email or delete_email'). An agent knows exactly when to call this instead of acting directly.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
forward_emailForward emailA
Forward an email to new recipients with optional additional message. The original is converted to readable text and "> "-quoted below your note; its attachments are re-attached unless includeAttachments is false. Set html=true to also send an HTML part carrying the original HTML in a blockquote.
| Name | Required | Description | Default |
|---|---|---|---|
| cc | No | CC recipients | |
| to | Yes | Forward to these recipients | |
| body | No | Additional message above the forwarded content | |
| html | No | Also send an HTML part with the original HTML preserved in a blockquote (default: false — text only) | |
| account | Yes | Account name from list_accounts | |
| emailId | Yes | Email ID to forward (from list_emails or get_email) | |
| mailbox | No | Mailbox where the original email is | INBOX |
| includeAttachments | No | Re-attach the original attachments (default: true) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations cover safety (destructiveHint=false, readOnlyHint=false, openWorldHint=true), so the description correctly focuses on mechanics: how the original is quoted, attachment re-attachment, and the html flag's effect. It omits any delivery-failure or rate-limit context but adds substantive transformation details beyond annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three tight sentences, front-loaded with the core action, then the transformation rule, then the html opt-in. No filler or redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Covers the key behavioral surprises (quote wrapping, attachment default, html toggle) that an agent couldn't infer from the name alone. No output schema exists, but this tool realistically returns only send status, and no usage exclusions are stated — a minor gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents every parameter including html and includeAttachments. The description reinforces html and includeAttachments semantics but adds no format/syntax guidance beyond what the schema provides. Baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (Forward) and resource (email) with the key transformation described — original converted to quoted text with attachments re-attached. Clearly distinguishable from siblings like send_email (new message) and reply_email (to original sender).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implicitly signals when to use this over send_email/reply_email by describing forwarding semantics, but never explicitly names alternatives or states when-not-to-use. Guidance is implied through behavior rather than stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_emailGet emailARead-only
Get the full content of a specific email by ID. Does NOT mark the email as seen (uses IMAP BODY.PEEK — non-destructive). format: text (default) = HTML converted to readable text with markdown; stripped = text minus quoted replies and signatures; full = raw body part (HTML mail returns raw HTML). Body is capped at 10000 chars by default — pass maxLength (0 = unlimited) to change. Set markRead=true only when you want to explicitly mark the email as read. A Bulk: line appears for list or machine-generated mail (📰 newsletter / 🤖 automated), derived from RFC headers, with the unsubscribe URI when the sender offers one.
| Name | Required | Description | Default |
|---|---|---|---|
| format | No | Body format: text=readable text with markdown links (default), stripped=text without quoted replies or signatures, full=raw body part | text |
| account | Yes | Account name from list_accounts | |
| emailId | Yes | Email ID from list_emails or search_emails | |
| mailbox | No | Mailbox path (default: INBOX) | INBOX |
| markRead | No | Explicitly mark the email as read after fetching (default: false — reading is non-destructive by default) | |
| maxLength | No | Truncate body at this many characters (default 10000; 0 = unlimited). A hint shows how many characters remain. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the readOnlyHint/destructiveHint annotations: it discloses the IMAP BODY.PEEK mechanism, that the email is NOT marked seen, the 10000-char cap with the maxLength=0 escape, and the derived 'Bulk:' line with unsubscribe URI. These are exactly the side-effects and output quirks an agent needs.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose and the non-destructive guarantee, then parameter guidance, then the Bulk note. Dense but each sentence carries information; only the final Bulk/marketing line is somewhat peripheral.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description carries the return-shape burden and does so: it explains body formats, truncation behaviour and the remaining-characters hint, and the Bulk header derivation. An agent can call this correctly and interpret the result without further docs.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the 3 baseline applies, but the description adds real meaning: what 'stripped' actually removes (quoted replies and signatures), what 'text' converts to (HTML with markdown links), and that maxLength=0 means unlimited. It enriches semantics beyond the enum labels.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Get the full content of a specific email by ID'), which cleanly separates it from list_emails/get_emails (plural, summary) and get_thread (whole conversation). The scope word 'full' signals single-message retrieval.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It clarifies when to set markRead=true and how format choices map to intent, which is genuine usage guidance. But it never names an alternative sibling (e.g. get_thread for a conversation, get_emails for summaries), so the agent must infer when this tool is the right pick versus its neighbours.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_emailsGet multiple emailsARead-only
Fetch the full content of multiple emails in a single call (max 20). More efficient than calling get_email repeatedly when triaging or summarising several emails. Does NOT mark emails as seen. Defaults to format="text" (HTML stripped) for compact, AI-friendly output. Each message carries the same 📰 newsletter / 🤖 automated marker as get_email.
| Name | Required | Description | Default |
|---|---|---|---|
| ids | Yes | Email IDs to fetch (max 20). Obtain IDs from list_emails or search_emails. | |
| format | No | Body format (default: text — HTML converted to readable text with markdown links). Use stripped to also remove quoted replies. | text |
| account | Yes | Account name from list_accounts | |
| mailbox | No | Mailbox path (default: INBOX) | INBOX |
| maxLength | No | Truncate each email body at this many characters (default 5000; 0 = unlimited). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, but the description adds genuinely useful behavior beyond them: it explicitly states emails are NOT marked as seen, and it explains the default text format strips HTML for compact AI-friendly output. This preempts the common concern that reading mail mutates read state.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Four sentences, each earning its place, with the core purpose (batch fetch, max 20) front-loaded. No filler or repetition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a batch-read tool with safety fully covered by annotations and no output schema, the description covers the essentials: batch cap, format default, side-effect behavior, and the newsletter/automated markers returned per message. Return-shape details are reasonably signaled.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so ids, format, account, mailbox, and maxLength are all already documented. The description restates the max-20 cap and the text default, adding only the 'AI-friendly' rationale, so baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Fetch the full content of multiple emails in a single call') and quantifies the batch limit (max 20). It explicitly positions itself against the sibling get_email, so an agent can distinguish batch vs. single fetch without opening either schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Names the alternative ('More efficient than calling get_email repeatedly') and the condition that selects this tool ('when triaging or summarising several emails'). The 'Does NOT mark emails as seen' line implicitly redirects state-changing intent to mark_email, giving the agent clear routing guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_email_statsGet email statsARead-only
Get email statistics and analytics for a mailbox. Shows volume, top senders, daily trends, and read/flagged counts.
| Name | Required | Description | Default |
|---|---|---|---|
| period | No | Time period: day, week, or month | week |
| account | Yes | Account name | |
| mailbox | No | Mailbox path (default: INBOX) | INBOX |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true/destructiveHint=false, so the safety profile is covered. The description adds real value by disclosing the returned content (volume, top senders, trends, read/flagged counts), which is important since no output schema exists; it could add a bit more on period/default behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tight sentences: purpose first, returned metrics second. No filler, well front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a read-only analytics tool with annotations covering safety and a fully documented schema, the description is nearly complete; enumerating return content compensates for the absent output schema. Only minor gaps (period default, response format) remain, which the schema largely covers.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% (account, mailbox with default, period enum all documented), so the baseline is 3. The description adds no parameter-level detail such as period semantics or default scope beyond what the schema already states.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('Get email statistics and analytics for a mailbox') and enumerates the metrics returned (volume, top senders, daily trends, read/flagged counts). No sibling tool provides statistics/analytics, so it is unambiguously differentiated from the operational email tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied by the clear purpose (fetch analytics for a mailbox) but the description offers no explicit when-to-use guidance, prerequisites, or reference to alternatives. There is no direct stats sibling to route against, so the omission is mild but real.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_email_statusGet email statusARead-only
Get the current read/flag/label state of an email without fetching its body. Much cheaper than get_email when you only need to check whether an email is unread, flagged, or which labels it has. Also useful to confirm the result of a mark_email call. Does NOT mark the email as seen.
| Name | Required | Description | Default |
|---|---|---|---|
| account | Yes | Account name from list_accounts | |
| emailId | Yes | Email ID from list_emails or search_emails | |
| mailbox | No | Mailbox path (default: INBOX) | INBOX |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | |
| date | Yes | |
| from | Yes | |
| seen | Yes | |
| labels | Yes | |
| flagged | Yes | |
| mailbox | Yes | |
| subject | Yes | |
| answered | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, so the safety profile is covered; the description reinforces it with 'Does NOT mark the email as seen' and adds cost context ('Much cheaper than get_email'). It discloses the no-side-effect trait clearly, though it stops short of describing caching, consistency, or rate behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, each earning its place: what it returns, why it beats get_email, and the no-side-effect guarantee. Front-loaded with the core purpose.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a read-only status tool with full schema coverage and an output schema, the description covers everything an agent needs: what state is returned, when to prefer it, and that it has no marking side effect. No material gap remains.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so account, emailId, and mailbox are already documented in the schema. The description adds no syntax or semantics beyond that, which matches the baseline 3 when the schema does the heavy lifting.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Get the current read/flag/label state of an email') and explicitly scopes what it does not do ('without fetching its body'). It cleanly distinguishes itself from siblings get_email and mark_email by naming both.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Names the alternative (get_email) with the selection condition ('when you only need to check whether an email is unread, flagged, or which labels it has'), and adds a second use case (confirming a mark_email result). When-to-use is explicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_hooks_configGet hooks configARead-only
Get the current AI hooks configuration including preset, rules, and custom instructions.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, so safety is covered. The description adds useful content disclosure (preset, rules, custom instructions) but says nothing about auth requirements, whether the config is cached, or how defaults are represented when no config exists.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
One sentence, zero filler, with the resource front-loaded before the payload enumeration. Nothing could be removed without losing information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter read tool with no output schema, the description does the important work of naming what comes back (preset, rules, custom instructions). It is adequate, though it does not describe the shape or defaults of that returned configuration.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so the baseline is 4 per the rubric. There is no parameter syntax the description needs to explain, and it correctly avoids inventing any.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('Get') and resource ('AI hooks configuration') and enumerates the payload ('preset, rules, and custom instructions'). No sibling tool overlaps with hooks configuration, so the lack of explicit sibling differentiation is not a real gap, but the description stops short of the crispness a 5 requires.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no when-to-use guidance, no mention of prerequisites, and no alternatives named. An agent can infer that this reads the hooks config, but the description provides no routing logic whatsoever.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_threadGet threadARead-only
Reconstruct a full email conversation thread by following References and In-Reply-To headers. Returns all related messages. Does NOT mark emails as seen. format: text (default) = HTML converted to readable text with markdown; stripped = text minus quoted replies; full = raw body part. Each body is capped at 5000 chars by default — pass maxLength (0 = unlimited) to change. Use newestFirst=true to show the most recent message in full and older messages as header-only summaries. Use get_email first to obtain the message_id. Each message carries a Bulk line when it is list or machine-generated mail.
| Name | Required | Description | Default |
|---|---|---|---|
| format | No | Body format: text=readable text with markdown links (default), stripped=text without quoted replies or signatures, full=raw body part | text |
| account | Yes | Account name from list_accounts | |
| mailbox | No | Mailbox to search (default: INBOX) | INBOX |
| maxLength | No | Truncate each message body at this many characters (default 5000; 0 = unlimited). A hint shows how many characters remain. | |
| message_id | Yes | Message-ID header value (from get_email) | |
| newestFirst | No | When true, shows the newest message in full and older messages as header-only summaries. Ideal for AI triage of long threads where only the latest reply matters. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, and the description adds genuinely non-obvious behavior on top: 'Does NOT mark emails as seen,' per-body truncation at 5000 chars with a remaining-character hint, and a Bulk line flagging list/machine-generated mail. That is real behavioral context beyond the structured fields.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
It is dense but front-loaded: purpose first, then the non-mutation guarantee, then format/limit/ordering semantics, then the prerequisite. Every sentence carries information, though the format explanation is packed tightly enough that a reader must parse carefully.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema and six parameters, the description still covers what comes back (all related messages, truncated bodies, Bulk annotations) and the ordering/format knobs an agent needs. Nothing required to invoke it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description goes further: it clarifies that 'text' converts HTML with markdown links, that stripped removes quoted replies, that maxLength 0 means unlimited, and exactly how newestFirst alters per-message rendering. Only mailbox semantics are left entirely to the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource ('Reconstruct a full email conversation thread') plus the mechanism (following References and In-Reply-To headers), which is unique among siblings like get_email and get_emails. An agent can distinguish this thread-reconstruction tool from single-message fetchers without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives an explicit prerequisite ('Use get_email first to obtain the message_id') and a situational cue for newestFirst ('Ideal for AI triage of long threads'). It does not explicitly state when to prefer get_email or get_emails over this tool, so it stops short of full alternative routing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_verification_codeGet verification codeA
Find the newest verification code (OTP/2FA) or sign-in magic link in recent mail, using false-positive-resistant scoring (EN/DE). Use wait_seconds (max 45) to wait for a mail still in flight; if nothing arrives, simply call again. Works without the watcher via polling — with [settings.watcher] enabled, hits land within ~1 second. Set copy_to_clipboard=true to also place the value on the clipboard (concealed from clipboard managers, auto-cleared). The code/link is returned in the result by design.
| Name | Required | Description | Default |
|---|---|---|---|
| account | No | Account name from list_accounts (omit to scan all accounts) | |
| mailbox | No | Mailbox to scan (default: INBOX) | INBOX |
| wait_seconds | No | Wait up to N seconds for a new mail before giving up (default: 0) | |
| lookback_minutes | No | How far back to scan (default: 15) | |
| copy_to_clipboard | No | Also copy the found value to the user's clipboard |
Output Schema
| Name | Required | Description |
|---|---|---|
| code | No | |
| from | No | |
| hint | No | |
| kind | No | |
| link | No | |
| found | Yes | |
| copied | Yes | |
| account | No | |
| mailbox | No | |
| service | No | |
| subject | No | |
| confidence | No | |
| age_seconds | No | |
| watcher_active | Yes | |
| clipboard_concealed | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The annotations (readOnlyHint=false, destructiveHint=false) leave the non-read-only side effect unexplained, and the description fills that gap: copy_to_clipboard writes to the user's clipboard, is concealed from clipboard managers, and is auto-cleared. It also discloses latency expectations (max 45s wait, ~1s with watcher) and retry semantics. It doesn't mention auth/permission requirements, so not a full 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The core purpose leads the first sentence and each following sentence carries operational information (waiting, retry, watcher, clipboard). The closing sentence about the code being returned 'by design' is marginally redundant given the output schema exists, which keeps it from a perfect score.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a five-parameter, zero-required tool with full schema coverage and an output schema, the description covers timing, retry, performance modes, side effects, and result format adequately. The one thin spot is that it references '[settings.watcher]' without directing the agent to the related configuration sibling.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3; the description goes beyond it by explaining clipboards concealment and auto-clearing for copy_to_clipboard, plus the retry meaning of wait_seconds. It adds real semantic value rather than merely restating schema text, though account/mailbox/lookback_minutes get no extra description.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource ('Find the newest verification code (OTP/2FA) or sign-in magic link in recent mail') and even names the detection method. This is clearly distinct from generic siblings like search_emails, get_email, or get_emails, so an agent can route to it without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives concrete operating conditions: use wait_seconds for mail still in flight, simply call again if nothing arrives, and it contrasts polling vs. watcher-enabled behavior (~1 second hits). What's missing is an explicit pointer to a sibling (e.g., configure_verification for watcher setup) or a statement of when NOT to use this tool, so it stops short of 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_watcher_statusGet watcher statusBRead-only
Get the status of IMAP IDLE watcher connections and recent activity.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare readOnlyHint=true and destructiveHint=false, so safety is covered externally. The description adds that the status concerns IMAP IDLE watcher connections and recent activity, but says nothing about return format, whether connections may be actively reconnecting, or what 'activity' includes. Modest added value over annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single, front-loaded sentence with no redundancy. Every word earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-param read-only status tool without an output schema, the description is adequate but thin. It does not describe what the returned status contains, which matters more when there is no output schema to fall back on.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Zero parameters, so per the rubric this is a baseline 4. The schema is trivially empty and the description does not need to document any inputs.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (get) and resource (status of IMAP IDLE watcher connections) with added scope 'recent activity'. However, it does not differentiate itself from the many other status/health siblings (get_email_status, check_health), leaving some ambiguity about when this tool is the right one.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No guidance on when to use this versus get_email_status or check_health. The sibling set is large and includes several 'status' tools, so the absence of any routing guidance is a real gap.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_accountsList accountsARead-only
List all configured email accounts. Call this first to discover available account names for use with other tools.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, so the safety profile is covered and the description doesn't contradict it. It adds the useful framing that this is a discovery/prerequisite call, but says nothing about behavior when no accounts are configured or the shape/multiplicity of results.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences, zero filler, with the action front-loaded and the usage instruction immediately following. Every clause earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a trivial zero-parameter list tool with no output schema, the description covers what it does and when to call it. It could mention what a returned entry looks like, but that gap is minor given the simplicity of the tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so the baseline is 4 and there are no parameter semantics for the description to clarify. Nothing is missing on this dimension.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('List all configured email accounts'), which is a distinct concept from the sibling list_mailboxes/list_labels tools. It doesn't explicitly contrast itself with those siblings, but the resource noun is unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly prescribes ordering and intent: 'Call this first to discover available account names for use with other tools.' An agent knows exactly when to reach for this tool relative to every other tool in the set.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_calendarsList calendarsARead-only
List all available local calendars (macOS Calendar.app / Linux default). Use the returned calendar names with add_to_calendar to target a specific calendar.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, so the safety profile is covered. The description adds the platform scope note, which is beyond the annotations, but says nothing about empty-result behavior or ordering.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tight sentences with zero waste. The core purpose is front-loaded and the follow-up sentence is actionable rather than filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a parameterless read tool with a trivial schema and no output schema, the description covers what is listed and how the result is consumed. Only minor gaps remain, such as return ordering or empty-result behavior.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so there is nothing for the description to disambiguate. Baseline 4 applies; the description cannot add parameter meaning that does not exist.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('List all available local calendars') and scopes it with platform context ('macOS Calendar.app / Linux default'). An agent can distinguish this from event/reminder siblings like list_events and list_reminders without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly tells the agent why to call it: to obtain calendar names for use with add_to_calendar. That is clear usage context, though it states no exclusions or when-not-to-use condition.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_emailsList emailsARead-only
List emails in a mailbox with optional filters. Returns paginated results with metadata (read/unread 🔵, flagged ⭐, replied ↩️, attachments 📎, labels 🏷️). List or machine-generated mail is marked 📰 newsletter / 🤖 automated from its RFC headers, with the unsubscribe URI when the sender offers one — personal mail carries no marker. Use get_email to fetch full body content. ProtonMail note: labels are represented as IMAP folders — use list_labels to discover them, then list_emails with mailbox="Labels/X" to find labeled emails.
| Name | Required | Description | Default |
|---|---|---|---|
| from | No | Filter by sender address or name | |
| page | No | Page number | |
| seen | No | Filter: true=read only, false=unread only | |
| since | No | Show emails after this date (ISO 8601) | |
| before | No | Show emails before this date (ISO 8601) | |
| account | Yes | Account name from list_accounts | |
| flagged | No | Filter: true=flagged only, false=unflagged only | |
| mailbox | No | Mailbox path (default: INBOX) | INBOX |
| preview | No | Include a ~200 character body preview per message. Off by default: it fetches extra bytes per message and lengthens the output. | |
| subject | No | Filter by subject keyword | |
| answered | No | Filter: true=replied, false=not yet replied | |
| pageSize | No | Results per page | |
| has_attachment | No | Filter: true=has attachments, false=no attachments |
Output Schema
| Name | Required | Description |
|---|---|---|
| page | Yes | |
| cache | No | |
| count | Yes | |
| total | Yes | |
| emails | Yes | |
| account | Yes | |
| hasMore | Yes | |
| mailbox | Yes | |
| pageSize | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already establish the safety profile (readOnlyHint=true, destructiveHint=false), so the bar is lower, yet the description goes well beyond: it documents pagination, the emoji marker legend for message state, and the newsletter/automated classification derived from RFC headers including the unsubscribe URI. This is behavior an agent could not infer from annotations or schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the core purpose, then the output markers, then the alternatives and the ProtonMail caveat — a sensible ordering with no filler sentences. The dense emoji legend is information-carrying but slightly heavy for a description that must also stay scannable.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With 13 parameters, all schema-documented, plus an output schema, the description does the right thing by not re-explaining returns. It covers pagination, filtering, sibling routing, and the ProtonMail label quirk. The only residual is that the relationship to get_emails/search_emails is left unstated.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3. The description earns above baseline by adding a non-obvious convention the schema lacks: ProtonMail labels are IMAP folders reached via mailbox="Labels/X", which meaningfully extends how the mailbox parameter should be used.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('List emails in a mailbox') plus its scope ('with optional filters'), and explicitly routes full-body retrieval to get_email. It does not, however, differentiate itself from the confusingly similar siblings get_emails and search_emails, which an agent must disambiguate.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives concrete usage instructions: call get_email for full bodies, and for ProtonMail run list_labels first then list_emails with mailbox="Labels/X". That is real when/how guidance. It stops short of saying when to prefer this over search_emails or get_emails, so no explicit exclusion is offered.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_eventsList eventsARead-only
List local calendar events with optional filters. Search by title, date range, or calendar name. Use to check for existing events before creating new ones, or to verify a recently added event. Returns event id, title, start/end time, location, and calendar name.
| Name | Required | Description | Default |
|---|---|---|---|
| to | No | Show events on or before this date (ISO 8601, e.g. 2026-02-28). Defaults to 30 days from now. | |
| from | No | Show events on or after this date (ISO 8601, e.g. 2026-02-19). Defaults to 7 days ago. | |
| limit | No | Maximum number of results (default: 20) | |
| title | No | Filter events whose title contains this text (case-insensitive) | |
| calendar_name | No | Restrict to a specific calendar by name |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, so the safety profile is covered. The description adds genuine value beyond that by enumerating the returned fields (id, title, start/end, location, calendar name), which matters since there is no output schema. It does not disclose ordering or pagination behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose and filters are front-loaded, then usage guidance, then return shape — a sensible order with no filler. Slightly verbose in listing return fields, but each sentence carries distinct information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Covers purpose, filters, usage triggers, and return values, and the annotations cover safety, so an agent has what it needs to call this correctly. Minor gaps remain around result ordering and whether the limit truncates silently, but with a fully described schema and no output schema this is close to complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so every parameter including defaults, ISO format, and the limit bounds is already documented in the schema. The description restates the filter axes (title, date range, calendar name) without adding syntax or semantics beyond them, and never mentions the row limit. Baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('List local calendar events') plus the filter dimensions available, which distinguishes it from siblings like list_calendars (enumerates calendars) and extract_calendar (parses events out of email). An agent can pick this tool without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives two concrete when-to-use triggers: checking for existing events before creating one, and verifying a recently added event. It stops short of naming alternatives or exclusions (e.g., when to prefer extract_calendar or add_to_calendar), so it is clear context without full routing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_labelsList labelsARead-only
List available labels for an email account. Auto-detects the label system: ProtonMail folder-labels, Gmail X-GM-LABELS, or IMAP keywords. ProtonMail note: labels are represented as IMAP folders under the Labels/ prefix. Use list_emails with mailbox="Labels/" to find emails tagged with a ProtonMail label.
| Name | Required | Description | Default |
|---|---|---|---|
| account | Yes | Account name from list_accounts |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnlyHint annotation it discloses real behavior: automatic detection of the label backend (ProtonMail folder-labels, Gmail X-GM-LABELS, IMAP keywords) and the ProtonMail folder-label representation. It omits pagination and return shape, but adds meaningful context that annotations do not cover.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Four short sentences, front-loaded with the core action, then backend behavior, then the ProtonMail caveat and the routing hint. No filler sentences.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a read-only single-param list tool with no output schema, the description covers purpose, backend variance, and the ProtonMail edge case that would otherwise trip an agent. It stops short of describing what the returned entries look like, the only notable gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% with a single 'account' parameter already described as coming from list_accounts; the description only alludes to 'an email account' and adds no syntax or format detail beyond the schema. Baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Specific verb+resource ('List available labels for an email account') with the scope narrowed by naming the three label systems it handles. It also distinguishes itself from folder-oriented siblings by noting that ProtonMail labels surface as IMAP folders under Labels/.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives concrete follow-up usage: pair the result with list_emails and mailbox="Labels/<name>", which is exactly the workflow an agent needs. It does not explicitly say when to prefer list_mailboxes or list_labels, so no full when-not guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_mailboxesList mailboxesARead-only
List all mailbox folders for an account with unread counts and special-use flags. Use list_accounts first to get the account name.
| Name | Required | Description | Default |
|---|---|---|---|
| account | Yes | Account name from list_accounts |
Output Schema
| Name | Required | Description |
|---|---|---|
| count | Yes | |
| account | Yes | |
| mailboxes | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, so the safety profile is covered. The description adds useful output context (unread counts, special-use flags), but says nothing about ordering, pagination, or what happens for accounts with no mailboxes.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences with zero filler; the capability statement leads and the prerequisite follows. Every clause earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, return-value detail is handled elsewhere, and annotations cover the read-only nature. The prerequisite to call list_accounts closes the main gap; only edge cases like empty accounts or result ordering are unaddressed.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% for the single 'account' parameter, so the schema already documents it. The description only reinforces provenance ('from list_accounts'), adding little beyond the schema's own wording.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Specific verb+resource (list mailbox folders) scoped to a single account, and it names what the result carries: unread counts and special-use flags. That distinguishes it from sibling listers like list_accounts and list_labels, though it does not explicitly contrast itself with create_mailbox/rename_mailbox beyond the obvious naming.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives an explicit prerequisite ordering: 'Use list_accounts first to get the account name,' which tells the agent the required call sequence. It stops short of stating when-not to use it or naming alternative folder-inspection tools such as find_email_folder.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_presetsList presetsARead-only
List all available AI triage presets with their descriptions and suggested labels.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, so the safety profile is covered. The description adds the shape of the returned items (descriptions and suggested labels), which is modestly useful, but says nothing about ordering, filtering, or whether presets are built-in versus user-created.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single front-loaded sentence with no filler or repetition. Every clause (what is listed, and what each item includes) earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no input parameters, no output schema, and annotations covering safety, the definition is nearly sufficient as-is. It partially compensates for the missing output schema by naming the returned fields, though the list's scope and ordering remain unspecified.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so there is nothing for the description to disambiguate; the baseline for a no-argument tool applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource (list presets) and goes further by naming what each entry contains: descriptions and suggested labels. It is clearly distinguishable from the email/calendar siblings, though it never explicitly contrasts itself with adjacent tools like list_templates or list_labels.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no guidance on when an agent should call this versus list_templates or list_labels, nor what an 'AI triage preset' is used for downstream. Usage must be inferred entirely from the tool name.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_remindersList remindersARead-only
List reminders from macOS Reminders.app with optional filters. Search by title or list name. By default only shows incomplete reminders. Use to check for existing reminders before creating new ones, or to verify a recently added reminder. Returns reminder id, title, due date, completion status, priority, and list name.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum number of results (default: 20) | |
| title | No | Filter reminders whose title contains this text (case-insensitive) | |
| list_name | No | Restrict to a specific Reminders list by name | |
| include_completed | No | Include completed reminders (default: false) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already establish readOnlyHint=true and destructiveHint=false, so safety is covered. The description adds valuable behavior beyond them: the default that only incomplete reminders are returned, and an enumeration of returned fields, which matters since no output schema exists.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Four short sentences with no filler, front-loading the source, filters, and the default-completion behavior before use cases. The return-field sentence is slightly list-like but earns its place given the absence of an output schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description usefully enumerates returned fields (id, title, due date, completion status, priority, list name), and covers default filtering and search modes. It is complete for correct invocation, with only pagination behavior (beyond the limit param) left to the schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents limit, title, list_name, and include_completed including defaults and bounds. The description only restates filter capability ('Search by title or list name') and the incomplete-by-default behavior, adding little syntax or format detail. Baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('List reminders from macOS Reminders.app') plus its scope ('with optional filters' and 'Search by title or list name'). An agent can immediately distinguish it from the write-side sibling create_reminder without opening either schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly names two use cases: checking for existing reminders before creating new ones, and verifying a recently added reminder. This gives clear when-to-use context, though it does not name the sibling tool (create_reminder) directly or state exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_scheduledList scheduledBRead-only
List scheduled emails. Shows pending, sent, or all scheduled emails.
| Name | Required | Description | Default |
|---|---|---|---|
| status | No | Filter by status (default: pending) | pending |
| account | No | Filter by account name (all accounts if omitted) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, so safety is covered. The description adds little beyond restating statuses already defined in the schema, and it conspicuously omits 'failed' even though the schema enum includes it. No return format or pagination behavior is described.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences with the core action front-loaded. The second sentence is somewhat redundant, but overall there is no wasted text.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple, read-only list tool with full schema coverage and no output schema, the description is sufficient to invoke correctly. Minor gaps (omitted 'failed' status, no return-value detail) are acceptable given the schema and annotations.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so both parameters are fully documented in the schema. The description mentions status values but adds no syntax, default, or account-filter detail beyond what the schema already provides, fitting the baseline of 3.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('List') and resource ('scheduled emails'), making the operation immediately clear. It does not differentiate itself from siblings like list_emails or get_email_status, but the resource scope is unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides no when-to-use or when-not-to-use guidance and names no alternatives (e.g., list_emails, schedule_email, cancel_scheduled). The implied use case is obvious only from the tool name and description.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_templatesList templatesARead-only
List all available email templates. Templates are TOML files in ~/.config/email-mcp/templates/ with {{variable}} placeholders for subject and body.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, so the safety profile is covered. The description adds useful context about where templates live and their TOML/placeholder format, but discloses nothing about return shape or ordering beyond that.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences with zero waste. The core action is front-loaded and the second sentence earns its place by describing template format and location.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple zero-parameter list tool whose annotations already carry the safety profile, this is largely complete. The only minor gap is the absence of any detail about what the listing returns (names only vs. contents), though no output schema exists.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so the baseline is 4. There is no parameter syntax or semantics to document, and the description adds no parameter-related detail (none is needed).
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (List) and resource (email templates) clearly. An agent can distinguish it from mutation siblings like apply_template, though it does not explicitly name or contrast with the closest sibling list_presets.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
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. The purpose of discovering templates before applying them is only implied, and there is no routing to or away from related siblings like apply_template or list_presets.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
mark_emailMark emailAIdempotent
Change email flags — mark as read/unread, flag/unflag. Idempotent: marking an already-read email as read is a no-op.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | Email ID (UID) from list_emails or search_emails | |
| action | Yes | Action: read, unread, flag (star), or unflag (unstar) | |
| account | Yes | Account name from list_accounts | |
| mailbox | No | Mailbox containing the email | INBOX |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare idempotentHint=true, so the 'idempotent' sentence largely restates structured data; the concrete example ('already-read → no-op') adds modest clarity about semantics. It says nothing beyond that: no auth/permission needs, no disclosure of whether the change is reversible, and no note on side effects for other clients or integrations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences, purpose first, behavioral caveat second; no filler and nothing that doesn't earn its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple four-parameter flag-mutation tool with a fully documented schema, the description covers purpose and idempotency adequately. It omits any mention of the response/result (no output schema exists) and any permission context, which is a minor but real gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, with the action enum documented as 'read, unread, flag (star), or unflag (unstar)'. The description merely repeats the same action vocabulary without adding format, constraints, or interaction details, so the baseline of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('change email flags') and enumerates the exact resource operations (read/unread, flag/unflag), so an agent immediately knows what it does. It implicitly distinguishes itself from siblings like move_email, delete_email, and add_label, but never names or contrasts an alternative explicitly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The action list implies what the tool is for, and the idempotency note hints at safe re-invocation. However, there is no guidance on when to use this vs add_label/remove_label (labels) or move_email, nor any stated permissions or prerequisites, leaving routing to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
move_emailMove emailAIdempotent
Move an email to a different mailbox folder. The sourceMailbox must be a real folder, not a virtual one like "All Mail". Use find_email_folder first if the email was discovered in a virtual folder.
| Name | Required | Description | Default |
|---|---|---|---|
| account | Yes | Account name from list_accounts | |
| emailId | Yes | Email ID to move (from list_emails) | |
| sourceMailbox | Yes | Current mailbox (e.g., INBOX) | |
| destinationMailbox | Yes | Target mailbox (e.g., Archive). Use list_mailboxes to see options. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=false, idempotentHint=true, destructiveHint=false, so the safety profile is covered. The description adds a non-obvious failure condition (virtual source folders are rejected) and the recovery path via find_email_folder, which is real behavioral value beyond the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, no filler. The operation is stated first, then the constraint and the corrective tool, so the critical caveat is front-loaded where an agent will read it.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 4-required-param mutation tool with full schema coverage and no output schema, the description covers the main pitfall an agent would hit. It does not mention destination-folder existence checks or what happens on partial failure, minor gaps given the schema carries the rest.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds a semantic constraint on sourceMailbox (must be a real folder, not virtual) that the schema only shows as a plain example string. destinationMailbox resolution is still left to the 'list_mailboxes' hint in the schema rather than the description.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource+scope: 'Move an email to a different mailbox folder.' An agent can distinguish this from sibling mutations like mark_email, delete_email, or apply_template without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives a concrete prerequisite ('sourceMailbox must be a real folder, not a virtual one like "All Mail"') and routes to an alternative tool ('Use find_email_folder first if the email was discovered in a virtual folder'). It does not cover when-not-to-use (e.g., moving vs. deleting/archiving semantics), so it stops short of the full 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
remove_labelRemove labelAIdempotent
Remove a label from an email. For ProtonMail, this removes the email from the label folder. For Gmail and standard IMAP, this removes a keyword flag.
| Name | Required | Description | Default |
|---|---|---|---|
| label | Yes | Label name to remove | |
| account | Yes | Account name from list_accounts | |
| emailId | Yes | Email ID (UID) from list_emails | |
| mailbox | Yes | Mailbox containing the email (must be a real folder) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=false, idempotentHint=true and destructiveHint=false. The description adds valuable provider-specific mechanics: ProtonMail removes the email from the label folder, while Gmail/IMAP removes a keyword flag. It does not contradict any annotation and explains what actually changes, though it omits auth needs and error behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tightly written sentences with the core action front-loaded. Every sentence carries information about the operation's scope or provider behavior, with no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 4-parameter mutation tool with annotations covering safety and a fully documented schema, the description is nearly complete: it explains the operation's effect across providers. It stops short of covering edge cases like removing a label that is not present or required permissions.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so all four required parameters are already documented in the schema. The description adds no syntax, format, or constraint details beyond what the schema provides, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource ('Remove a label from an email') and scopes it to a single email, which distinguishes it from siblings like delete_label or add_label. It does not explicitly name an alternative, so it stops short of the 5 calibration where a sibling is named.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no when-to-use guidance, no mention of alternatives such as delete_label vs mark_email, and no prerequisites. The description only states what the tool does, matching the MID calibration where a clear verb but no routing guidance earns a 2.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
rename_mailboxRename mailboxA
Rename an existing mailbox (folder). Use list_mailboxes to see current folder paths.
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes | Current folder path | |
| account | Yes | Account name from list_accounts | |
| new_path | Yes | New folder path |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare readOnlyHint=false and destructiveHint=false, so the safety profile is already covered and the description does not contradict it. The description adds nothing beyond that, though, such as whether emails inside the folder are preserved or whether parent paths must exist.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tight sentences with the core action front-loaded and the helper reference second. No filler, fully front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a rename operation with full schema coverage and existing annotations, plus no output schema, the description is largely sufficient. It could note the consequence for contained emails, but nothing critical to invocation is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so account, path, and new_path are already documented. The description adds no format or syntax detail (e.g., hierarchy separators or how new_path is interpreted), so the baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('Rename') and resource ('an existing mailbox (folder)'), and clarifies the folder synonym. An agent can distinguish it from create_mailbox and delete_mailbox purely from the description.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides a helpful pointer to list_mailboxes for discovering valid current paths, which is genuine usage context. However, it gives no when/when-not guidance relative to alternatives like create_mailbox or delete_mailbox, leaving selection logic implied.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
reply_emailReply emailA
Reply to an email with proper threading (In-Reply-To & References headers). Use get_email first to read the original. The original message is quoted below the reply by default (Thunderbird/Outlook style) — set quoteOriginal=false for the bare body only.
| Name | Required | Description | Default |
|---|---|---|---|
| body | Yes | Reply body content | |
| html | No | Send as HTML | |
| account | Yes | Account name from list_accounts | |
| emailId | Yes | Email ID to reply to (from list_emails or get_email) | |
| mailbox | No | Mailbox where the original email is | INBOX |
| replyAll | No | Reply to all recipients | |
| attachments | No | Files to attach: plain paths, or {path, cid, contentType, filename} objects for inline images. Sizes are checked before sending; the base64-encoded total must stay under the account's effective limit (global cap 25 MB; known provider or per-account limits may be stricter). | |
| quoteOriginal | No | Append the original message as a quoted block below the reply (Thunderbird/Outlook style: "On DATE, NAME wrote:" + > prefix for text replies, or <blockquote type="cite"> for HTML). Disable to send the bare reply body only. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare write/open-world/non-destructive; the description adds real behavioral context: the reply is threaded via headers, and the original is quoted below by default in Thunderbird/Outlook style with an explicit opt-out. It does not cover permission requirements, delivery/return behavior, or error cases, but adds meaningful value beyond the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three tight sentences: purpose/threading first, then the prerequisite, then the default-vs-opt-out behavior. Every sentence earns its place with no redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a write tool with no output schema but full annotation and 100% schema coverage, the description supplies the key behavioral nuances (threading, quoting default). Minor gaps remain regarding permissions and send-confirmation behavior, but nothing essential for correct invocation is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so all eight parameters are already documented, including quoteOriginal. The description echoes the quoteOriginal default and framing rather than adding syntax or constraints beyond the schema, which matches the baseline 3 when the schema does the heavy lifting.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource ('Reply to an email') and immediately differentiates the operation by its threading semantics (In-Reply-To & References headers), which no sibling like send_email or forward_email shares. An agent can select this over send_email/forward_email without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives an explicit prerequisite ('Use get_email first to read the original') and clarifies default vs. alternative behavior (quoted reply by default, quoteOriginal=false for bare body). It stops short of naming the sibling tools it replaces or stating when-not to use it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
save_draftSave draftA
Save an email draft to the Drafts folder. Compose over time, then use send_draft to send it. Use list_emails with the Drafts mailbox to see saved drafts.
| Name | Required | Description | Default |
|---|---|---|---|
| cc | No | CC recipients | |
| to | No | Recipient email addresses (can be empty for drafts) | |
| bcc | No | BCC recipients | |
| body | Yes | Email body content | |
| html | No | Send as HTML (default: plain text) | |
| account | Yes | Account name from list_accounts | |
| subject | Yes | Email subject | |
| references | No | Full References chain for threading (original references plus its Message-ID) | |
| in_reply_to | No | Message-ID for threading (from get_email) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=false and destructiveHint=false, so the agent knows this is a non-destructive write. The description adds nothing beyond that: it does not say whether saving overwrites/replaces an existing draft, whether repeated saves create duplicates, or what identifying handle comes back for later sending.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, zero filler, with the core action front-loaded and the follow-on/retrieval guidance ordered logically after it.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 9-parameter create tool with no output schema, the description covers the workflow (save, then send, then list). Its one notable gap is the return value: with no output schema, the agent is not told whether a draft identifier is returned, and must instead infer retrieval via list_emails.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so all nine parameters (including threading fields in_reply_to and references) are already documented in the schema. The description adds no syntax, format, or constraint detail beyond what the schema provides, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource (save an email draft) plus the destination (Drafts folder), and explicitly names the sibling send_draft as the follow-on action, distinguishing persistence from sending.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Clearly frames the use case ('compose over time, then use send_draft to send it') and routes to list_emails with the Drafts mailbox for retrieval. It lacks an explicit when-not clause (e.g., use send_email when you already have the final content), so it stops short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
schedule_emailSchedule emailA
Schedule an email to be sent at a specific time in the future. The email is queued locally and sent automatically when the time arrives.
| Name | Required | Description | Default |
|---|---|---|---|
| cc | No | CC recipients | |
| to | Yes | Recipient email addresses | |
| bcc | No | BCC recipients | |
| body | Yes | Email body | |
| html | No | Send as HTML (default: false) | |
| account | Yes | Account name to send from | |
| send_at | Yes | When to send (ISO 8601 datetime, e.g. '2025-02-20T09:00:00Z') | |
| subject | Yes | Email subject | |
| references | No | Full References chain (the original References plus its Message-ID). Most clients thread on this, so pass it whenever scheduling a reply. | |
| in_reply_to | No | Message-ID to reply to |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=false and destructiveHint=false, so the write nature is covered. The description adds genuinely useful non-obvious behavior ('queued locally and sent automatically when the time arrives'), but omits critical operational facts: whether the scheduled send survives app restart, what happens if send_at is in the past, and whether/how it can be cancelled.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tight sentences with zero filler; the timing constraint is front-loaded in the first sentence and the delivery mechanism follows. Nothing is redundant or padded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 10-parameter mutation tool with no output schema, the description covers purpose and queue behavior adequately, and the schema handles parameters. What is missing is lifecycle context: how this relates to list_scheduled/cancel_scheduled and what guarantees the local queue provides.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% with 10 well-documented parameters (including threading guidance on references/in_reply_to), so the schema carries the parameter burden. The description adds no syntax or format detail beyond what is already there, making the baseline 3 appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Schedule an email') plus the defining constraint ('at a specific time in the future'), which separates it from the immediate send_email sibling. It does not name that sibling explicitly, so the routing distinction is implied rather than stated.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The phrase 'in the future' implies when to reach for this over send_email, but the description never says so outright and offers no exclusions or prerequisites. Related siblings (list_scheduled, cancel_scheduled) are never referenced, so the agent must infer the workflow.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_emailsSearch emailsARead-only
Search emails by keyword across subject, sender, and body — and, where the local mirror is enabled, attachment filenames (searching "report" finds an email carrying Q3-report.pdf). Omit query (or pass an empty string) to use it as a pure filter — e.g. find all emails from a specific sender without a keyword. Supports additional filters for sender, recipient, date range (since/before — INTERNALDATE; sent_since/sent_before — Date: header), attachments, size, and reply status. At least one of query/from/to/since/before/sent_since/sent_before/has_attachment/larger_than/smaller_than/answered must be set; otherwise the call errors (use list_emails to browse without filters). Results carry the same 📰 newsletter / 🤖 automated markers as list_emails.
| Name | Required | Description | Default |
|---|---|---|---|
| to | No | Filter by recipient address | |
| from | No | Filter by sender address (substring match) | |
| page | No | Page number | |
| query | No | Search keyword across subject, sender, and body (omit to use filters only) | |
| since | No | Only emails received on/after this date (ISO 8601, e.g. 2026-05-11T00:00:00Z) | |
| before | No | Only emails received before this date (ISO 8601) | |
| account | Yes | Account name from list_accounts | |
| mailbox | No | Mailbox path (default: INBOX) | INBOX |
| preview | No | Include a ~200 character body preview per message. Off by default: it fetches extra bytes per message and lengthens the output. | |
| answered | No | Filter: true=replied, false=not replied | |
| pageSize | No | Results per page | |
| sent_since | No | Only emails with Date: header on/after this date (ISO 8601) | |
| larger_than | No | Minimum email size in KB | |
| sent_before | No | Only emails with Date: header before this date (ISO 8601) | |
| smaller_than | No | Maximum email size in KB | |
| has_attachment | No | Filter: true=has attachments, false=no attachments |
Output Schema
| Name | Required | Description |
|---|---|---|
| page | Yes | |
| cache | No | |
| count | Yes | |
| total | Yes | |
| emails | Yes | |
| account | Yes | |
| hasMore | Yes | |
| mailbox | Yes | |
| pageSize | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, and the description adds real context beyond them: the attachment-filename search is conditional on the local mirror being enabled, results carry the same newsletter/automated markers as list_emails, and an unset-filter call is an error rather than a no-op. It does not cover pagination limits or result ordering, but the error semantics and mirror dependency are meaningful disclosures.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The keyword-search capability is front-loaded, followed by filter guidance, the precondition, and the sibling reference, in a logical order with no filler sentences. It is on the long side, but each sentence conveys a distinct constraint an agent would otherwise have to discover by failing a call.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 16-parameter search tool with an output schema present, the description need not explain return values, and it covers the remaining risk areas: filter precondition, query-optional behavior, searchable fields, date-field semantics, and the list_emails fallback. An agent has everything needed to invoke it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description earns extra credit: it explains that an omitted/empty query turns the tool into a pure filter, gives the 'report' → Q3-report.pdf example for attachment matching, and crucially distinguishes since/before (INTERNALDATE) from sent_since/sent_before (Date: header), a semantic split the schema does not spell out.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Search emails by keyword across subject, sender, and body') plus the scope of extra coverage (attachment filenames when the local mirror is on). It explicitly positions itself against list_emails for the unfiltered browse case, so an agent can distinguish the two without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives concrete when-to-use cases (filter-only search by omitting query, example of finding a sender's mail without a keyword), states the hard precondition that at least one of the listed filters must be set or the call errors, and names the alternative for the no-filter case (list_emails). Nothing is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
send_draftSend draftADestructive
Send an existing draft email and remove it from Drafts. The draft is fetched, sent via SMTP, then deleted. Use list_emails with the Drafts mailbox to find draft IDs.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | Draft email UID (from list_emails on Drafts mailbox) | |
| account | Yes | Account name from list_accounts | |
| mailbox | No | Drafts folder path (auto-detected if omitted) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=false and destructiveHint=true, so the safety profile is covered; the description earns credit for specifying exactly what is destroyed and in what order ('fetched, sent via SMTP, then deleted'). It omits failure semantics — e.g. whether the draft survives if the SMTP send fails — which is the one behavioral gap.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences with zero filler: what it does, the mechanism/ordering, and the discovery step. The destructive consequence is front-loaded in the first sentence rather than buried.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description carries return-value burden and it does describe the observable outcome (draft sent, then deleted). Predictable unknowns remain — failure/rollback behavior and whether a response body is returned — so it is strong but not exhaustive for a destructive, state-changing tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so id, account and mailbox are already documented (including the '(auto-detected if omitted)' note for mailbox). The description only restates the 'draft IDs' notion and adds no format, range, or default detail beyond the schema; baseline 3 is correct.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('Send an existing draft email') and adds the consequential side effect ('remove it from Drafts'). The word 'existing' plus the pointer to list_emails distinguishes it from send_email (compose-new) and save_draft (create/update), so an agent can route correctly without opening other schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit prerequisite workflow ('Use list_emails with the Drafts mailbox to find draft IDs'), which is the key precondition for this tool. It does not, however, state when to prefer send_email (new message) over this one, or what happens on send failure, so it stops short of full when/when-not guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
send_emailSend emailB
Send a new email. Supports plain text or HTML body, CC, and BCC.
| Name | Required | Description | Default |
|---|---|---|---|
| cc | No | CC recipients | |
| to | Yes | Recipient email addresses | |
| bcc | No | BCC recipients | |
| body | Yes | Email body content | |
| html | No | Send as HTML (default: plain text) | |
| account | Yes | Account name from list_accounts | |
| subject | Yes | Email subject | |
| attachments | No | Files to attach: plain paths, or {path, cid, contentType, filename} objects for inline images. Sizes are checked before sending; the base64-encoded total must stay under the account's effective limit (global cap 25 MB; known provider or per-account limits may be stricter). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=false, destructiveHint=false, and openWorldHint=true, so the mutating, outward-facing nature is covered structurally. The description adds nothing behavioral on top: it doesn't say the send is immediate/irreversible, what happens on partial recipient failure, or any rate/auth constraints, and the attachment size limits it could have surfaced live only in the schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences, zero filler, with the core action front-loaded and the capability summary following. Nothing redundant or padded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For an 8-parameter tool with no output schema, the description is adequate but thin: it omits the required 'account' sourcing, the attachments capability, and any differentiation from the seven-plus other send/dispatch siblings. With annotations covering safety, the gap is not severe, but a caller routing between send_email, send_draft, and schedule_email gets no help here.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so every parameter including the nuanced attachments object (cid, contentType, filename, size limits) is already documented. The description's mention of text/HTML, CC, and BCC merely mirrors fields the schema defines, matching the baseline of 3 when the schema carries the load.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Send a new email') and enumerates the supported composition features (plain text/HTML, CC, BCC), so the agent knows exactly what the tool produces. It does not, however, distinguish itself from nearby siblings like send_draft, reply_email, forward_email, or schedule_email, which also dispatch mail.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The word 'new' implicitly separates this from reply_email/forward_email/send_draft, and usage is implied for first-time composition. But there is no explicit when-to-use statement, no mention of the draft or scheduled alternatives, and no prerequisites (e.g., that 'account' must come from list_accounts).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
test_notificationTest notificationA
Send a test desktop notification to verify that OS permissions are correctly configured. Use check_notification_setup first to diagnose any issues.
| Name | Required | Description | Default |
|---|---|---|---|
| sound | No | Include a sound alert in the test notification |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=false and destructiveHint=false, so the agent knows this triggers an action but is non-destructive. The description adds that the effect is a real desktop notification gated by OS permissions, which is useful context, but says nothing about expected output, failure modes, or whether the notification is visible to the user only.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two compact sentences with zero filler; the purpose comes first and the prerequisite guidance follows. Nothing is repeated from the title or schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-optional-parameter tool with no output schema and annotations covering the safety profile, the description supplies the essential context: what it does, what it depends on, and what to run first. The only gap is what a successful versus failed verification looks like.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The single 'sound' parameter is fully documented in the schema (100% coverage), including its default. The description adds nothing about it, so the schema does all the work and the baseline of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Specific verb ('Send') plus resource ('test desktop notification') and the stated goal (verify OS permissions are correctly configured). It is clearly distinguishable from the sibling check_notification_setup, which it explicitly references as the diagnostic counterpart.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It states the context of use (verifying OS permission configuration) and names a prerequisite tool, 'Use check_notification_setup first to diagnose any issues.' That is explicit routing guidance. It does not state when not to use it, which keeps it short of a 5.
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.
52 tool updates
v0.6.0- Changed
add_label1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
add_to_calendar3 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - removed
Input schema / properties / save_attachments / defaultRemoved value: -true - changed
Input schema / properties / save_attachments / descriptionPrevious value: -"Save non-ICS email attachments locally and link them in the event notes"New value: +"Save non-ICS email attachments locally and link them in the event notes (default: the [settings.attachments] auto_download setting)"
- Changed
analyze_email_for_scheduling1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
apply_template1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
bulk_action1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
cancel_scheduled1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
check_calendar_permissions1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Added
check_clipboard_setup - Changed
check_health1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
check_notification_setup1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
configure_alerts1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Added
configure_verification - Changed
create_label1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
create_mailbox1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
create_reminder2 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - changed
Input schema / properties / notes / descriptionPrevious value: -"Reminder body/notes (defaults to auto-built from email)"New value: +"Reminder body/notes (defaults to auto-built from email; an open-email deep link is appended either way)"
- Changed
delete_email1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
delete_label1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
delete_mailbox1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
download_attachment5 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Input schema / properties / partIdAdded value: +{ + "description": "MIME part id from get_email metadata (takes precedence over filename)", + "type": "string" +} - added
Input schema / properties / saveAsAdded value: +{ + "description": "Filename to save as under the download directory (basename only, no path separators). Only used with saveToDownloadDir; defaults to the attachment’s own filename.", + "type": "string" +} - added
Input schema / properties / saveToDownloadDirAdded value: +{ + "default": false, + "description": "Write the attachment to the server-configured download directory instead of returning it base64-encoded. Fails if no download directory is configured.", + "type": "boolean" +} - changed
Input schema / requiredPrevious value: -[ - "account", - "id", - "filename" -]New value: +[ + "account", + "id" +]
- Changed
extract_calendar1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
extract_contacts1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
find_email_folder1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
forward_email5 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - changed
Input schema / properties / cc / items / patternPrevious value: -"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"New value: +"^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$" - added
Input schema / properties / htmlAdded value: +{ + "default": false, + "description": "Also send an HTML part with the original HTML preserved in a blockquote (default: false — text only)", + "type": "boolean" +} - added
Input schema / properties / includeAttachmentsAdded value: +{ + "default": true, + "description": "Re-attach the original attachments (default: true)", + "type": "boolean" +} - changed
Input schema / properties / to / items / patternPrevious value: -"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"New value: +"^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
- Changed
get_email6 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - changed
Input schema / properties / format / defaultPrevious value: -"full"New value: +"text" - changed
Input schema / properties / format / descriptionPrevious value: -"Body format: full=raw (default), text=plain text (strips HTML), stripped=plain text without quoted replies or signatures"New value: +"Body format: text=readable text with markdown links (default), stripped=text without quoted replies or signatures, full=raw body part" - added
Input schema / properties / maxLength / defaultAdded value: +10000 - changed
Input schema / properties / maxLength / descriptionPrevious value: -"Truncate body at this many characters. A hint shows how many characters remain."New value: +"Truncate body at this many characters (default 10000; 0 = unlimited). A hint shows how many characters remain." - changed
Input schema / properties / maxLength / minimumPrevious value: -100New value: +0
- Changed
get_email_stats1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
get_email_status2 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "properties": { + "answered": { + "type": "boolean" + }, + "date": { + "type": "string" + }, + "flagged": { + "type": "boolean" + }, + "from": { + "type": "string" + }, + "id": { + "type": "string" + }, + "labels": { + "items": { + "type": "string" + }, + "type": "array" + }, + "mailbox": { + "type": "string" + }, + "seen": { + "type": "boolean" + }, + "subject": { + "type": "string" + } + }, + "required": [ + "id", + "mailbox", + "subject", + "from", + "date", + "seen", + "flagged", + "answered", + "labels" + ], + "type": "object" +}
- Changed
get_emails5 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - changed
Input schema / properties / format / descriptionPrevious value: -"Body format (default: text — strips HTML for efficient AI reading). Use stripped to also remove quoted replies."New value: +"Body format (default: text — HTML converted to readable text with markdown links). Use stripped to also remove quoted replies." - added
Input schema / properties / maxLength / defaultAdded value: +5000 - changed
Input schema / properties / maxLength / descriptionPrevious value: -"Truncate each email body at this many characters."New value: +"Truncate each email body at this many characters (default 5000; 0 = unlimited)." - changed
Input schema / properties / maxLength / minimumPrevious value: -100New value: +0
- Changed
get_hooks_config1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
get_thread6 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - changed
Input schema / properties / format / defaultPrevious value: -"full"New value: +"text" - changed
Input schema / properties / format / descriptionPrevious value: -"Body format: full=raw (default), text=plain text (strips HTML), stripped=plain text without quoted replies or signatures"New value: +"Body format: text=readable text with markdown links (default), stripped=text without quoted replies or signatures, full=raw body part" - added
Input schema / properties / maxLength / defaultAdded value: +5000 - changed
Input schema / properties / maxLength / descriptionPrevious value: -"Truncate each message body at this many characters. A hint shows how many characters remain."New value: +"Truncate each message body at this many characters (default 5000; 0 = unlimited). A hint shows how many characters remain." - changed
Input schema / properties / maxLength / minimumPrevious value: -100New value: +0
- Added
get_verification_code - Changed
get_watcher_status1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
list_accounts1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
list_calendars1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
list_emails3 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Input schema / properties / previewAdded value: +{ + "default": false, + "description": "Include a ~200 character body preview per message. Off by default: it fetches extra bytes per message and lengthens the output.", + "type": "boolean" +} - changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "properties": { + "account": { + "type": "string" + }, + "cache": { + "additionalProperties": false, + "properties": { + "offline": { + "const": true, + "type": "boolean" + }, + "source": { + "const": "cache", + "type": "string" + }, + "stale": { + "const": true, + "type": "boolean" + } + }, + "required": [ + "source" + ], + "type": "object" + }, + "count": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "emails": { + "items": { + "additionalProperties": false, + "properties": { + "answered": { + "type": "boolean" + }, + "bulk": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "newsletter", + "automated" + ], + "type": "string" + }, + "listId": { + "type": "string" + }, + "oneClick": { + "type": "boolean" + }, + "unsubscribe": { + "type": "string" + } + }, + "required": [ + "kind", + "oneClick" + ], + "type": "object" + }, + "date": { + "type": "string" + }, + "flagged": { + "type": "boolean" + }, + "from": { + "additionalProperties": false, + "properties": { + "address": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "address" + ], + "type": "object" + }, + "hasAttachments": { + "type": "boolean" + }, + "id": { + "type": "string" + }, + "labels": { + "items": { + "type": "string" + }, + "type": "array" + }, + "preview": { + "type": "string" + }, + "seen": { + "type": "boolean" + }, + "subject": { + "type": "string" + }, + "to": { + "items": { + "additionalProperties": false, + "properties": { + "address": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "address" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "id", + "subject", + "from", + "to", + "date", + "seen", + "flagged", + "answered", + "hasAttachments", + "labels" + ], + "type": "object" + }, + "type": "array" + }, + "hasMore": { + "type": "boolean" + }, + "mailbox": { + "type": "string" + }, + "page": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "pageSize": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "total": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "account", + "mailbox", + "total", + "page", + "pageSize", + "hasMore", + "count", + "emails" + ], + "type": "object" +}
- Changed
list_events1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
list_labels1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
list_mailboxes2 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "properties": { + "account": { + "type": "string" + }, + "count": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "mailboxes": { + "items": { + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "path": { + "type": "string" + }, + "specialUse": { + "type": "string" + }, + "totalMessages": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "unseenMessages": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "name", + "path" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "account", + "count", + "mailboxes" + ], + "type": "object" +}
- Changed
list_presets1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
list_reminders1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
list_scheduled1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
list_templates1 field changed- removed
Input schema / $schemaRemoved value: -"http://json-schema.org/draft-07/schema#"
- Changed
mark_email1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
move_email1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
remove_label1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
rename_mailbox1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
reply_email3 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Input schema / properties / attachmentsAdded value: +{ + "description": "Files to attach: plain paths, or {path, cid, contentType, filename} objects for inline images. Sizes are checked before sending; the base64-encoded total must stay under the account's effective limit (global cap 25 MB; known provider or per-account limits may be stricter).", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "additionalProperties": false, + "properties": { + "cid": { + "description": "Content-ID for inline embedding — reference as <img src=\"cid:....\"> in an HTML body", + "type": "string" + }, + "contentType": { + "description": "Explicit MIME type when the file extension would misdetect", + "type": "string" + }, + "filename": { + "description": "Filename shown to the recipient (default: the file basename)", + "type": "string" + }, + "path": { + "description": "Local file path", + "type": "string" + } + }, + "required": [ + "path" + ], + "type": "object" + } + ] + }, + "type": "array" +} - added
Input schema / properties / quoteOriginalAdded value: +{ + "default": true, + "description": "Append the original message as a quoted block below the reply (Thunderbird/Outlook style: \"On DATE, NAME wrote:\" + > prefix for text replies, or <blockquote type=\"cite\"> for HTML). Disable to send the bare reply body only.", + "type": "boolean" +}
- Changed
save_draft5 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - changed
Input schema / properties / bcc / items / patternPrevious value: -"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"New value: +"^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$" - changed
Input schema / properties / cc / items / patternPrevious value: -"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"New value: +"^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$" - added
Input schema / properties / referencesAdded value: +{ + "description": "Full References chain for threading (original references plus its Message-ID)", + "items": { + "type": "string" + }, + "type": "array" +} - changed
Input schema / properties / to / items / patternPrevious value: -"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"New value: +"^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
- Changed
schedule_email2 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Input schema / properties / referencesAdded value: +{ + "description": "Full References chain (the original References plus its Message-ID). Most clients thread on this, so pass it whenever scheduling a reply.", + "items": { + "type": "string" + }, + "type": "array" +}
- Changed
search_emails9 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Input schema / properties / beforeAdded value: +{ + "description": "Only emails received before this date (ISO 8601)", + "type": "string" +} - added
Input schema / properties / fromAdded value: +{ + "description": "Filter by sender address (substring match)", + "type": "string" +} - added
Input schema / properties / previewAdded value: +{ + "default": false, + "description": "Include a ~200 character body preview per message. Off by default: it fetches extra bytes per message and lengthens the output.", + "type": "boolean" +} - changed
Input schema / properties / query / descriptionPrevious value: -"Search keyword (omit or leave empty to use filters only)"New value: +"Search keyword across subject, sender, and body (omit to use filters only)" - added
Input schema / properties / sent_beforeAdded value: +{ + "description": "Only emails with Date: header before this date (ISO 8601)", + "type": "string" +} - added
Input schema / properties / sent_sinceAdded value: +{ + "description": "Only emails with Date: header on/after this date (ISO 8601)", + "type": "string" +} - added
Input schema / properties / sinceAdded value: +{ + "description": "Only emails received on/after this date (ISO 8601, e.g. 2026-05-11T00:00:00Z)", + "type": "string" +} - changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "properties": { + "account": { + "type": "string" + }, + "cache": { + "additionalProperties": false, + "properties": { + "offline": { + "const": true, + "type": "boolean" + }, + "source": { + "const": "cache", + "type": "string" + }, + "stale": { + "const": true, + "type": "boolean" + } + }, + "required": [ + "source" + ], + "type": "object" + }, + "count": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "emails": { + "items": { + "additionalProperties": false, + "properties": { + "answered": { + "type": "boolean" + }, + "bulk": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "newsletter", + "automated" + ], + "type": "string" + }, + "listId": { + "type": "string" + }, + "oneClick": { + "type": "boolean" + }, + "unsubscribe": { + "type": "string" + } + }, + "required": [ + "kind", + "oneClick" + ], + "type": "object" + }, + "date": { + "type": "string" + }, + "flagged": { + "type": "boolean" + }, + "from": { + "additionalProperties": false, + "properties": { + "address": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "address" + ], + "type": "object" + }, + "hasAttachments": { + "type": "boolean" + }, + "id": { + "type": "string" + }, + "labels": { + "items": { + "type": "string" + }, + "type": "array" + }, + "preview": { + "type": "string" + }, + "seen": { + "type": "boolean" + }, + "subject": { + "type": "string" + }, + "to": { + "items": { + "additionalProperties": false, + "properties": { + "address": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "address" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "id", + "subject", + "from", + "to", + "date", + "seen", + "flagged", + "answered", + "hasAttachments", + "labels" + ], + "type": "object" + }, + "type": "array" + }, + "hasMore": { + "type": "boolean" + }, + "mailbox": { + "type": "string" + }, + "page": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "pageSize": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "total": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "account", + "mailbox", + "total", + "page", + "pageSize", + "hasMore", + "count", + "emails" + ], + "type": "object" +}
- Changed
send_draft1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
- Changed
send_email5 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Input schema / properties / attachmentsAdded value: +{ + "description": "Files to attach: plain paths, or {path, cid, contentType, filename} objects for inline images. Sizes are checked before sending; the base64-encoded total must stay under the account's effective limit (global cap 25 MB; known provider or per-account limits may be stricter).", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "additionalProperties": false, + "properties": { + "cid": { + "description": "Content-ID for inline embedding — reference as <img src=\"cid:....\"> in an HTML body", + "type": "string" + }, + "contentType": { + "description": "Explicit MIME type when the file extension would misdetect", + "type": "string" + }, + "filename": { + "description": "Filename shown to the recipient (default: the file basename)", + "type": "string" + }, + "path": { + "description": "Local file path", + "type": "string" + } + }, + "required": [ + "path" + ], + "type": "object" + } + ] + }, + "type": "array" +} - changed
Input schema / properties / bcc / items / patternPrevious value: -"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"New value: +"^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$" - changed
Input schema / properties / cc / items / patternPrevious value: -"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"New value: +"^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$" - changed
Input schema / properties / to / items / patternPrevious value: -"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"New value: +"^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
- Changed
test_notification1 field changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
49 tool updates
v0.2.3- First observed
add_label - First observed
add_to_calendar - First observed
analyze_email_for_scheduling - First observed
apply_template - First observed
bulk_action - First observed
cancel_scheduled - First observed
check_calendar_permissions - First observed
check_health - First observed
check_notification_setup - First observed
configure_alerts - First observed
create_label - First observed
create_mailbox - First observed
create_reminder - First observed
delete_email - First observed
delete_label - First observed
delete_mailbox - First observed
download_attachment - First observed
extract_calendar - First observed
extract_contacts - First observed
find_email_folder - First observed
forward_email - First observed
get_email - First observed
get_email_stats - First observed
get_email_status - First observed
get_emails - First observed
get_hooks_config - First observed
get_thread - First observed
get_watcher_status - First observed
list_accounts - First observed
list_calendars - First observed
list_emails - First observed
list_events - First observed
list_labels - First observed
list_mailboxes - First observed
list_presets - First observed
list_reminders - First observed
list_scheduled - First observed
list_templates - First observed
mark_email - First observed
move_email - First observed
remove_label - First observed
rename_mailbox - First observed
reply_email - First observed
save_draft - First observed
schedule_email - First observed
search_emails - First observed
send_draft - First observed
send_email - First observed
test_notification
TDQS
Scored across 52 tools
Many tools target the same resource with overlapping actions—e.g., list_emails vs search_emails, get_email vs get_emails vs get_email_status, and mark_email vs bulk_action—so an agent could easily misselect despite detailed descriptions. Calendar/reminder tools (extract_calendar, analyze_email_for_scheduling, add_to_calendar) also have blurry boundaries. Some overlap exists but descriptions help.
Tool names consistently use snake_case and mostly follow a verb_noun pattern (list_emails, send_email, create_label, delete_mailbox). A few deviations like bulk_action (noun phrase) and list_scheduled (missing noun) are minor. The convention is predictable overall.
52 tools is far beyond the 3–15 typical for a well-scoped server and exceeds the 50+ threshold for extreme mismatch. Even accounting for the broad email+calendar+reminders scope, the surface is heavily overloaded with many niche configuration and diagnostic tools.
Email lifecycle is thoroughly covered: list/get/search/send/reply/forward/move/delete/mark/label/draft/schedule/templates. However, calendar and reminder operations lack update/delete tools, and account management (add/remove) is absent, leaving some dead ends.
Maintenance
Related MCP Connectors
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.
Hosted email MCP for AI agents with inboxes, send/receive, memory, recovery, and credits.
An MCP server that provides email capabilities, hosted on Alpic platform
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceAn MCP server that enables AI models to read, search, and send emails via IMAP and SMTP protocols. It supports various providers like Gmail and Outlook, allowing for tasks such as retrieving unread messages, searching by sender, and managing mailbox folders.-
- AlicenseBqualityCmaintenanceAn MCP server that exposes IMAP operations as tools for AI assistants, enabling email management including listing mailboxes, reading, searching, moving, flagging emails, and creating drafts.721 npmMIT
- AlicenseAqualityDmaintenanceAn MCP server providing comprehensive email capabilities via IMAP and SMTP, enabling AI assistants to read, search, send, manage, schedule, and analyze emails across multiple accounts.49663 npmLGPL 3.0
- AlicenseNot gradedqualityAmaintenanceEnables AI assistants to read, search, send, manage, schedule, and analyze emails across multiple IMAP/SMTP accounts via MCP, with features like OAuth2, calendar extraction, analytics, and real-time email watching.461 npmLGPL 3.0