The SKILL.md body loads into context on every activation, so one-time install/ setup prose was wasted context once emcli is running. Move it out: - New AGENTIC-MANUAL.md: get-the-files bootstrap, binary install (incl. options and build-from-source, folding in the old references/install.md), EMCLI_KEY, account discovery. Fetched only during first-time setup. - SKILL.md trimmed (182→~145 lines) to the recurring path: security model, a short "Files & first run" pointer + per-session preflight, the list/get/ack/send workflow, JSON envelope, command table, enforcement, do/don't. - Remove references/install.md (folded in); fix RELEASING.md pointer. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
7.1 KiB
name: emcli description: Read and send email through emcli, a guard-railed CLI gateway that mediates IMAP/SMTP so the agent never holds mail credentials. Use when a task involves checking or reading an inbox, listing or searching messages, fetching a message or its attachments, marking mail as processed, or sending or replying to email on the user's behalf. Works with Gmail and any IMAP/SMTP account; every command prints a single JSON object. Triggers include: check my email, read the inbox, list/search messages, get or download a message, reply to that email, send an email, process new mail. compatibility: Requires the emcli binary (run scripts/install.sh to fetch it; needs curl or wget and a Linux/macOS/Windows shell) and the EMCLI_KEY environment variable, which the orchestrator provides. Needs network access to the configured mail server. metadata: author: steve version: "0.4.0" homepage: "https://gitea.dcglab.co.uk/steve/emcli"
emcli — email for agents
emcli is a command-line gateway for email. You (the agent) call its agent commands to read
and send mail; the program holds the credentials and enforces the user's rules, so you never see a
password and cannot bypass the limits. Every agent command prints exactly one line of JSON and
sets its exit code to match.
Security model — read this first
- You only run agent commands:
list,get,search,ack,send. Account setup, passwords, whitelists, and config are the user's job (admin commands) — do not run or suggest runningaccount,whitelist,config,init, ordoctorunless the user explicitly asks you to help administer. - Never touch the secret key.
EMCLI_KEYis supplied in the environment by whoever launched you. Do not read it, print it, log it, pass it as an argument, or try to generate one. If it is missing, stop and tell the user (see "Files & first run"). - Some mail is intentionally invisible. The user may restrict which senders you can see and who you can email. Blocked or filtered results are normal — handle them, don't try to work around them (see Enforcement).
Files & first run
This skill ships more than this file. Paths like AGENTIC-MANUAL.md and references/commands.md
are relative to this skill's directory; if one isn't on disk, fetch it from the raw base URL + path:
https://gitea.dcglab.co.uk/steve/emcli/raw/branch/main/skills/emcli/
- First-time setup — installing the binary, the
EMCLI_KEY, finding accounts: readAGENTIC-MANUAL.md. Only needed when emcli isn't set up yet. - Full command detail — every flag, JSON shapes, error codes:
references/commands.md.
Per-session preflight (quick): run emcli version; if it's not found, set up via
AGENTIC-MANUAL.md. Confirm EMCLI_KEY is set without printing it (test -n "$EMCLI_KEY"); if
empty, tell the user their orchestrator must provide it. Then get the account name from the user.
How to read every result
Each command prints one JSON object:
{ "error": false, "error_detail": {}, "data": { } }
Always check error first.
error: false→ usedata.error: true→ readerror_detail.codeanderror_detail.message. The exit code is also non-zero.
With jq:
out=$(emcli list --account gmail --new) || true
echo "$out" | jq -e '.error == false' >/dev/null && echo "$out" | jq '.data.messages'
Error codes you may get back: config, db, network, auth, policy, not_found, usage.
A policy error means the user's rules blocked the action; not_found is returned both for
missing mail and for mail you are not allowed to see — treat them the same.
The core workflow: process new mail
ACC=gmail # the account name the user gave you
# 1. See what's new (unprocessed). Headers only — cheap.
emcli list --account "$ACC" --new --limit 20
# 2. For a message of interest, fetch the full body + attachments.
emcli get --account "$ACC" --uid 70314
# 3. Do the work (summarize, extract, draft a reply, etc.).
# 4. Mark it processed so it stops showing under --new. This is deliberate — do it
# only when you've actually handled the message.
emcli ack --account "$ACC" --uid-list 70314
Acking is the only command that changes state; list/get/search never do. Ack is safe to
repeat and order-independent (you can ack several UIDs: --uid-list 70314,70315,70320).
Sending mail
emcli send --account "$ACC" \
--to alice@example.com --subject "Hello" --body "Plain-text body."
# multiple recipients (repeat or comma-separate), cc/bcc, attachments:
emcli send --account "$ACC" --to a@x.com --to b@x.com --cc boss@x.com \
--subject "Report" --body "see attached" --attach ./report.pdf
# reply that threads correctly off a message you can see:
emcli send --account "$ACC" --to a@x.com --subject "Re: Hi" --body "thanks" \
--reply-to 70314 --folder INBOX
Sending only works on read-write accounts. If you get policy / ro_mode, the account is
read-only — tell the user; do not attempt another account without their say-so.
Command quick reference
| Command | Purpose |
|---|---|
emcli list --account A [--folder F] [--new] [--limit N] [--before U] [--since U] |
Message headers, newest first |
emcli get --account A [--folder F] --uid U |
One full message (body + attachments) |
emcli search --account A [--folder F] [--from X] [--subject-contains X] [--text X] [--since-date D] [--before-date D] |
Server-side search |
emcli ack --account A [--folder F] --uid-list U1,U2 |
Mark message(s) processed |
emcli send --account A --to X [--cc X] [--bcc X] --subject S --body B [--attach P]… [--reply-to U] |
Send / reply |
Defaults: --folder INBOX, --limit 50 (max 500). Dates are RFC 3339 (e.g.
2026-06-01T00:00:00Z). UIDs come from list/search output.
Full reference (every flag, exact JSON shapes for each command, attachment encoding, error
codes, and the enforcement rules): references/commands.md — read it from disk, or fetch it from
the raw base URL in "Files & first run" above if you don't have it locally.
Enforcement awareness — work with the rules
The user configures these; you cannot change them and shouldn't try.
- Read-only (RO) accounts reject
send(policy/ro_mode). - Inbound whitelist / subject filter: mail from disallowed senders (or non-matching subjects)
is invisible — it won't appear in
list/search, andget/ackon it returnnot_found. - Outbound whitelist: if any recipient isn't allowed, the whole send is blocked
(
policy/whitelist_out) — nothing is sent. Don't retry by dropping recipients silently; surface it to the user.
Do / Don't
- ✅ Check
erroron every call; reportpolicy/not_found/authoutcomes plainly to the user. - ✅
getto read, thenackonly after you've truly processed a message. - ✅ Ask the user for the account name; keep bodies plain text.
- ❌ Don't read, print, or invent
EMCLI_KEYor any password. - ❌ Don't run admin commands (
account/whitelist/config/init) unless asked to help set up. - ❌ Don't treat a blocked send or filtered message as a bug to route around — it's the user's policy.