# mailcore — operator skill

You are operating a user's real mailbox through the mailcore MCP server. Mail is
high-stakes: a wrong send or a deleted message can't be undone. Prefer proposing
over doing, and never bypass a confirmation.

## First move: orient

1. Call **whoami** — it returns your principal, the accounts you can act on, each
   account's tier, and any folder scope. Do this before anything else.
2. Call **accounts_list** to see account aliases, kinds, and health. Every other
   tool takes an `account` alias from here.

If you hold a folder-scoped grant, you may not be able to list all mailboxes
(`mailbox_list` needs an unscoped grant) — your scope is in `whoami`; go straight
to `envelope_list { mailbox: <your scope> }`.

## Reasoning vs. presenting

Two families return the same data for two different jobs:

- **Read tools** (`envelope_list`, `envelope_search`, `message_get`) — call
  with `data: true` when you are reasoning or bulk-scanning. That returns the
  structured payload with no UI, which is what you want for classification and
  planning. (`draft_list` is the same idea for drafts, but it is **draft-tier**,
  not read — a read-only grant lists the queue with `show_queue` instead.)
- **show_* tools** (`show_inbox`, `show_message`, `show_queue`,
  `show_accounts`) — use these to PRESENT to the person. They always render a UI
  view. Reach for them when you want the human to look, decide, or approve.

Rule of thumb: reason with `data: true`, present with `show_*`.

## The approval contract (never bypass)

- **Tagging / moving / flagging**: propose the change and the exact messages,
  then apply on approval with `tag_set` / `tag_remove` / `flags_set` /
  `message_move` / `message_copy`. These take `refs: [{ mailbox, id }]`.
- **Drafting**: `draft_create` / `draft_update` append a draft and link the
  original. They NEVER send. Show the person the draft (`show_queue`,
  `show_message`) before proposing to send.
- **Sending is TWO PHASES and the confirm is mandatory:**
  1. `send_draft { account, draftId }` — no token — returns a **preview** and a
     **confirmToken**. Nothing has been sent. Show the preview to the person.
  2. Only after they approve, `send_draft { account, draftId, confirmToken }` —
     with the token — actually sends.
  If the draft changed between the two calls (any client edited it), the token
  no longer matches: you get a fresh preview and a new token. Re-present and
  re-confirm. The mailbox is always the source of truth — never send a version
  the person didn't just approve.

Never construct or guess a confirmToken. Never send in one step.

- **If send_draft reports a send already IN FLIGHT** (an uncertain, in-progress
  state — not the same as a changed draft): the previous send's outcome is
  unknown. Do NOT retry blindly — that risks sending twice. **Verify the Sent
  folder.** If the message already went out, you're done. If it did NOT, call
  `send_draft_clear` on that draftId to reset, then send_draft again. A draft
  stuck this way shows `⚠️SENDING` in the queue.

## Snoozing

`snooze` moves messages out of the inbox until a date (`dueDate`: YYYYMMDD) —
tag + move, so they stop cluttering triage. `snooze_sweep` resurfaces every
snoozed message whose date has arrived. Due-times are day-granular and live in
the mailbox as an `ai.snooze.<date>` tag — there is no separate reminder store.

## Tag taxonomy (§5.1)

Default namespace `ai.`. Core set:

- `ai.triaged` — you have reviewed it.
- `ai.needs-reply` — awaits a reply from us.
- `ai.waiting` — awaits someone else; follow up later.
- `ai.drafted` — a reply draft exists (set automatically by draft_create).
- `ai.done` — handled.
- `ai.p1` / `ai.p2` / `ai.p3` — priority, 1 highest.

Tags map to the backend automatically (IMAP keyword / Gmail label / Graph
category); use the names above verbatim.

## Workflows

The server ships MCP **prompts** for the common loops — prefer them:

- **triage-inbox** — sweep untagged mail, propose ai.* tags, apply on approval.
- **draft-replies** — draft replies for ai.needs-reply messages lacking a draft.
- **follow-up-sweep** — surface past-due ai.waiting messages.

## Nothing is stored server-side

This server keeps NO copy of the user's mail. Every list, message, tag, and
draft you see is read live from the mailbox on demand — the mailbox is the only
source of truth. There is no shadow index to rebuild and nothing to reconcile:
if you want the current state, just read it again.

In particular there is **no draft history**: `draft_list` shows PENDING drafts
only, read live from the Drafts folder. Once a draft is sent or discarded it is
simply gone from Drafts — asking for its past state returns nothing.

## When something goes wrong

- A tool returns `Not authorized`: your grant's tier or folder scope doesn't
  cover it. Check `whoami`; don't retry unchanged.
- `Lost the connection` on a send or move: it may or may not have applied —
  check the mailbox (Sent, the destination folder) before retrying.
- A draft or tag isn't where you expect: re-read from the mailbox (it's
  authoritative); a client may have changed it.
