brainstate

leeguooooo/mail-use · source

Snapshot 0 · 2026-11-04T12:47:27Z · JavaScript · MIT

Content fingerprint8a87fd52e18bd1fc3da3179b0022079c0abee1fe115a781814e03c7dc7126257

GitHub description at capture

Email for AI agents — read, search, send and triage Gmail / QQ / 163 / any IMAP from the CLI or MCP. Part of the *-use family.

README at capture

# mail-use

**Email, as something an AI agent can actually operate.** One CLI over Gmail, QQ,
163, Outlook and any IMAP/SMTP box — every command speaks JSON, every destructive
one is dry-run until you pass `--confirm`.

```bash
mail-use code --json          # the newest verification code, one live pass
mail-use email recent --format compact --json
mail-use email delete --from newsletter@shop.com --confirm --json
```

Part of the [`*-use` family](#the--use-family) — small tools that each give an agent
hands on one real thing.

### Why this and not an IMAP snippet

- **A stable JSON contract, not scraped text.** Every response carries
  `success: boolean`, and failures carry `error_code` from a fixed set
  (`auth_failed`, `folder_not_found`, `imap_error`, …). Documented in
  [`docs/CLI_JSON_CONTRACT.md`](docs/CLI_JSON_CONTRACT.md).
- **It tells you when it might be wrong.** Cached reads report `from_cache`,
  `cache_age_seconds` and `cache_stale`, and a snapshot old enough to mean "nothing
  is syncing" is refused in favour of a live fetch. An empty inbox is never a silent
  "nothing arrived".
- **Destructive by consent only.** `delete` / `mark` / `move` / `send` return a
  dry-run preview — grouped per account and folder, with sample subjects — and change
  nothing until `--confirm`. `--all-folders` skips Sent/Drafts/Junk/Trash unless asked.
- **Built for token budgets.** `--format compact` projects each email to the ten
  fields worth scanning (~30% smaller than the full shape), `--with-preview` folds a
  body snippet into the list call, and batch `show` reuses one IMAP connection.
- **Fast enough to call in a loop.** A persistent daemon pools IMAP connections and
  syncs to local SQLite in the background: five sequential `email list` calls go from
  25s to 0.83s. See [the table below](#persistent-daemon-5-30-faster-cli-calls).
- **MCP too.** `mail-use mcp config --json` prints a paste-ready entry; the server
  exposes 16 tools with the same dry-run defaults.

> Renamed from **Mailbox** to **mail-use**. The command is now `mail-use`; `mailbox` still
> works as an alias, and your config in `~/.config/mailbox` is untouched.

## Supported providers

163 / 126 · QQ · Gmail · Outlook / Hotmail · any custom IMAP+SMTP server.

Search behaves differently per provider and the CLI says so: Gmail searches bodies
server-side via `X-GM-RAW`, while QQ/163/Outlook have broken IMAP TEXT search, so
`--query` falls back to matching subject + sender only. Use `--from` / `--subject`
there for predictable results.

## Install

### One line, no npm, no Node

```bash
curl -fsSL https://raw.githubusercontent.com/leeguooooo/mail-use/main/install.sh | sh
mail-use --help
```

Downloads the prebuilt binary for your platform (macOS arm64/x64, Linux x64/arm64) from the
[latest GitHub Release](https://github.com/leeguooooo/mail-use/releases/latest), verifies its
checksum, and installs to `~/.local/bin`. Pin a version with `MAIL_USE_VERSION=v2.11.2`, or
change the dir with `MAIL_USE_INSTALL_DIR=...`. A missing `.sha256` aborts the install
(`MAIL_USE_INSECURE=1` overrides; a mismatch is always fatal). Each tarball also carries
signed build provenance: `gh attestation verify mail-use-<target>.tar.gz --repo leeguooooo/mail-use`.

The installer also drops a `mailbox` symlink next to it, so scripts written against the old
name keep working.

There is no npm package. Distribution is GitHub Release binaries only — that keeps
releases free of `NPM_TOKEN` and 2FA prompts, and keeps install free of a Node toolchain.
The pre-rename `@leeguoo/mailbox-cli` packages on npm are frozen and no longer updated.

### Upgrading

```bash
mail-use upgrade --check     # is there a newer release? (`mail-use 3.3.1 -> 3.3.2`)
mail-use upgrade --check --json  # the same check as JSON: name, current, latest, update_available, skills, install_channel, checked_only
mail-use upgrade             # (--json only changes the output format) CLI only: download, verify sha256, check the new binary's version, swap atomically, restart the daemon
mail-use upgrade --skills    # the same, then refresh this tool's own skill copies (skill-only when the CLI is current)
mail-use upgrade --tag v3.1.0   # pin an exact release (also the way to roll back)
```

Exit code `0` means the command worked (an update being available is not an error); `2`
means the check, the download or the verification failed (the installed binary is left
as it was); `1` means it refused because Homebrew, npm or a source checkout owns this
install (it prints that manager's command instead), or a skill could not be refreshed.

A plain `upgrade` only lists the skill copies it finds, with the command that would refresh
each. `upgrade --skills` refreshes them: a Claude Code plugin
(`claude plugin update mail-use@leeguooooo-plugins`, or printed if `claude` isn't on PATH),
a git checkout linked into `~/.agents/skills`, `~/.claude/skills` or `~/.codex/skills`
(`git pull --ff-only`, never forced), or a copied folder from `npx skills add` (it prints
`npx skills update mail-use` for you to run).

Any other command checks for a new release at most once a day (cached in
`${XDG_CACHE_HOME:-~/.cache}/mail-use/update-check.json`, 2 s timeout, failures silent) and,
when one exists, prints a single line to **stderr** — stdout stays clean JSON:
`mail-use 3.3.2 is available (you have 3.3.1). Upgrade: mail-use upgrade`. Set `CI`,
`MAIL_USE_NO_UPDATE_CHECK` or `USE_NO_UPDATE_CHECK` to turn it off.

When the daemon is running it notices new releases for you: one unauthenticated GET to
the GitHub releases API per day (`MAILBOX_UPDATE_CHECK_HOURS`, `0` disables), surfaced as
`update` in `mail-use daemon status --json`. It only ever *reports* — it never downloads
or installs anything.

`upgrade` is never automatic and never runs on its own: a tool that silently replaces
its own executable is a supply-chain surprise, not a convenience. It refuses to install
a tarball whose published `.sha256` is missing or doesn't match, or whose binary does not
report the requested version, and refuses to run at all from a Homebrew, npm or dev-checkout
install. Re-running the `curl … install.sh | sh`
line does the same job.

### As an AI Skill (Claude Code / Cursor / etc.)

```bash
# Project scope — installs into ./.claude/skills/mail-use (or ./.cursor/skills/...):
npx skills add leeguooooo/mail-use --skill mail-use

# User scope — installs into ~/.claude/skills/mail-use:
npx skills add leeguooooo/mail-use --skill mail-use -g
```

The skill assumes the CLI is on `PATH` (install via the `curl … install.sh | sh` above).
For the biggest speedup also run `mail-use daemon install` once.

### MCP server (Claude Desktop / Code / Cursor)

```bash
mail-use mcp config --json   # prints a paste-ready mcpServers entry
```

### From source (development)

```bash
pnpm install
pnpm test

# build a local platform binary into dist/mail-use
pnpm build:binary
```

If a test run is interrupted (editor task killed, agent session closed), its
Vitest workers can be left behind and hold memory. Check with
`pgrep -fl vitest` and kill what remains.

## Configure accounts

```bash
mkdir -p ~/.config/mailbox
cp examples/accounts.example.json ~/.config/mailbox/auth.json
```

Config locations:

- Credentials: `~/.config/mailbox/auth.json`
- Other settings: `~/.config/mailbox/config.toml`

## Apple Mail, both ways

Agents use mail-use, people use Apple Mail; both talk to the same server, so an account only
has to be set up once:

```bash
mail-use apple-mail status    # which mailboxes each side has
mail-use apple-mail           # mail-use → Apple Mail: opens System Settings on the profile, click Install
mail-use apple-mail import    # Apple Mail → mail-use: paste one app password / authorization code per mailbox
mail-use account add          # neither side has it yet: start here
```

Apple Mail keeps its passwords to itself (and Gmail added through Google sign-in has none), so
importing needs one authorization code per mailbox; macOS only installs profiles through System
Settings, so exporting needs one click. Mailboxes already in Apple Mail are skipped, so nothing
gets duplicated. Step-by-step guide (Chinese), where to get each provider's code, and FAQ:
[docs/guides/apple-mail.md](docs/guides/apple-mail.md).

## Common commands

```bash
# CLI help
mail-use --help

# newest verification code across all accounts, one live pass
mail-use code --json

# list accounts
mail-use account list --json

# list unread emails (cache by default; --from filters cache-side)
mail-use email list --unread-only --limit 20 --json
mail-use email list --account-id my_account_id --from "newsletter" --json

# show one email (response includes list_unsubscribe when the header is set)
mail-use email show 123456 --account-id my_account_id --json

# mark read (use --dry-run to validate first)
mail-use email mark 123456 --read --account-id my_account_id --folder INBOX --dry-run --json
mail-use email mark 123456 --read --account-id my_account_id --folder INBOX --confirm --json

# delete
mail-use email delete 123456 --account-id my_account_id --folder INBOX --confirm --json

# bulk mutate by sender or subject (no UID list needed)
mail-use email mark --from "support@npmjs.com" --read --confirm --account-id my_account_id --json
mail-use email delete --from "newsletter" --account-id my_account_id --json    # dry-run preview
mail-use email delete --subject "[ad]" --account-id my_account_id --confirm --json
```

### Cache + sync

- Cache DB default: `~/.local/share/mailbox/email_sync.db`
- Listing uses cache by default where possible. Add `--live` to force IMAP.

```bash
mail-use sync status --json
mail-use sync force --json
mail-use sync init
mail-use sync daemon
```

## Persistent daemon (5-30× faster CLI calls)

Each one-shot invocation otherwise spends 1-3s on TCP+TLS+IMAP LOGIN. With the daemon
running, calls reuse pooled connections and a background SQLite sync means `email list`
usually doesn't touch IMAP at all.

The `curl … install.sh | sh` installer sets this up for you when accounts are already
configured (`MAIL_USE_NO_DAEMON=1` opts out). Otherwise:

```bash
mail-use daemon install      # autostart at login (macOS launchd / Linux systemd-user)
mail-use daemon status --json
mail-use daemon reload       # drop pooled connections after editing auth.json
```

Measured on Gmail INBOX, M2 MacBook over residential WAN:

| Operation | No daemon | Daemon (`--live`) | Daemon (cached) |
|---|---|---|---|
| Single `email list` | 5.0s | 1.0s | 0.17s |
| `email folders` | 5.0s | 0.85s | n/a |
| 5 sequential `email list` | 25s | 5.3s | **0.83s** |
| 3 parallel `email show` | ~15s | 2.7s | **0.88s** |

### Resource footprint (many agent sessions on one machine)

The daemon is **one process per user**, shared by every agent session through a Unix
socket — so more sessions do not mean more IMAP connections. Measured idle on macOS
with 3 accounts connected:

| | |
|---|---|
| Idle CPU | ~0.15% |
| Idle RSS | 3-15 MB |
| Connections | max 3 per account (`MAILBOX_POOL_MAX`), reaped back to 1 after 10 min idle |
| 12 concurrent calls | 1.6s wall clock, pool stayed at 1 connection per account |

Knobs, if the defaults do not suit you:

| Env | Default | Effect |
|---|---|---|
| `MAILBOX_POOL_MAX` | `3` | Max concurrent IMAP connections per account |
| `MAILBOX_POOL_IDLE_MS` | `600000` | Close connections idle this long (`0` disables reaping) |
| `MAILBOX_POOL_KEEP_WARM` | `1` | Connections per account kept warm through reaping |
| `MAILBOX_NO_DAEMON` | unset | `1` makes the CLI skip the daemon entirely |
| `MAILBOX_UPDATE_CHECK_HOURS` | `24` | Daemon's passive update check (`0` disables it and the CLI's daily notice) |
| `MAIL_USE_NO_UPDATE_CHECK` / `USE_NO_UPDATE_CHECK` | unset | Any value turns off the CLI's daily "new version" notice |

## AI usage guide

Mail classification belongs to the calling AI. `mail-use cleanup --limit 50 --json`
returns live metadata, body previews and gids for review; use `--offset` for the
next page and `email show <gid...>` for full content. The AI selects specific gids
and executes them with `email move`, `mark`, `flag` or `delete`. `cleanup` and
`inbox` never classify or choose actions, and legacy category cleanup is rejected.

If you're integrating this CLI into an AI agent, start here:

- `docs/AI_SKILL_MAIL_USE.md`

## OpenClaw integration

This repo includes an OpenClaw skill at `skills/mail-use/SKILL.md`.

OpenClaw loads skills from:
- `<workspace>/skills`
- `~/.openclaw/skills`

Quick link helper (symlink into `~/.openclaw/skills`):

```bash
./scripts/link_openclaw_skill.sh
```

Force replace an existing link:

```bash
./scripts/link_openclaw_skill.sh --force
```

To use this repo without copying files, add the repo skills directory to
`skills.load.extraDirs` in `~/.openclaw/openclaw.json`:

```json
{
  "skills": {
    "load": {
      "extraDirs": [
        "/path/to/mcp-email-service/skills"
      ]
    }
  }
}
```

OpenClaw handles channel delivery and scheduling; mail-use returns structured
JSON outputs and optional text summaries.

Verify OpenClaw picked up the skill:

```bash
openclaw skills list --eligible
openclaw skills check
```

## The `*-use` family

Small, composable CLIs that give an AI agent hands on one real thing. Same shape
everywhere: `curl … install.sh | sh` to install, `npx skills add leeguooooo/<name>`
to teach your agent, JSON on stdout.

| Repo | Gives your agent |
|---|---|
| [chrome-use](https://github.com/leeguooooo/chrome-use) | A real browser — logged-in sessions, forms, scraping, screenshots |
| [mail-use](https://github.com/leeguooooo/mail-use) | Email — read, search, send, triage across Gmail / QQ / 163 / any IMAP |
| [iphone-use](https://github.com/leeguooooo/iphone-use) | A real iPhone — tap, type, screenshot, pull on-device data |
| [wechat-use](https://github.com/leeguooooo/wechat-use) | WeChat on macOS — send messages, query contacts and history |
| [discord-use](https://github.com/leeguooooo/discord-use) | Discord — messages, channels, forums, webhooks (REST-only, Rust) |
| [cookie-use](https://github.com/leeguooooo/cookie-use) | Many logged-in accounts per site — capture, switch, apply sessions |
| [profile-use](https://github.com/leeguooooo/profile-use) | Your personal profile, safely — fill signup / KYC / checkout forms |
| [bitwarden-use](https://github.com/leeguooooo/bitwarden-use) | Bitwarden / Vaultwarden — headless passkey (FIDO2) login |
| [chatgpt-use](https://github.com/leeguooooo/chatgpt-use) | Your ChatGPT subscription as a coding-agent backend — no API key |
| [computer-use](https://github.com/leeguooooo/computer-use) | The macOS desktop itself |
| [pixcake-use](https://github.com/leeguooooo/pixcake-use) | Read-only PixCake probing — snapshot / diff / SQLite inspection |

## Contract

- `docs/CLI_JSON_CONTRACT.md`
---

> Built by **leeguooooo** — field notes on AI agents, reverse engineering & Cloudflare Workers at **[blog.misonote.com](https://blog.misonote.com)** · follow on **[X @leeguooooo](https://x.com/leeguooooo)**

## Author

Built by **郭立 (Guo Li / leeguoo)** — [leeguoo.com](https://leeguoo.com/about) · [GitHub](https://github.com/leeguooooo) · [X](https://x.com/leeguooooo) · more tools in the [*-use family](https://github.com/leeguooooo/plugins). <!-- profile-use: allow; existing public author attribution -->