docspluginsextensionsemail

email

IMAP/SMTP as a capability — the agent can search and read your mail, send and reply, and stand up subscriptions that wake it when matching mail arrives. Works with any provider.

What the agent can do

  • Triage → fetch → act — search returns envelope summaries; fetch downloads a chosen message to staging as .md and .eml; send composes or replies in-thread.
  • Browse folders — list the mailbox's folders, filtered by the configured allowlist.
  • Scope is enforced server-side — the sender allow/deny lists are pushed into the IMAP query, so out-of-scope mail never comes back.

Getting mailbox credentials

Give the agent a dedicated mail account — credentials are stored on disk, so keep it separate from your personal account. You'll need the IMAP and SMTP host and port, and a password; providers with 2FA require an app-specific password.

Secrets

secrets.json

json · config/ext/email/
{
  "EMAIL_ADDRESS": "agent@example.com",
  "EMAIL_PASSWORD": "app-specific-password",
  "IMAP_HOST": "imap.gmail.com",
  "IMAP_PORT": 993,
  "SMTP_HOST": "smtp.gmail.com",
  "SMTP_PORT": 465
}

Configuration

Config splits into inbound (search and subscribe) and outbound (send), each with an approval mode and scope lists. The mode is a ladder: disabled doesn't register the tool, approval prompts per call, enabled runs within scope without prompting.

capabilities.json

json · extensions.email slice
{
  "extensions": {
    "email": {
      "enabled": true,
      "channel": "email",
      "inbound": { "default": "approval", "senders": ["@acme.com"] },
      "outbound": { "default": "approval", "recipients": [] }
    }
  }
}
FieldTypeDefaultEffect
inbound.defaultdisabled | approval | enabledapprovalApproval policy for search and subscribe.
inbound.sendersstring[][]Sender allowlist — exact (alice@acme.com) or domain (@acme.com). Empty means any. Enforced in the IMAP query.
inbound.blockedstring[][]Sender denylist; same syntax.
outbound.defaultdisabled | approval | enabledapprovalApproval policy for send.
outbound.recipientsstring[][]Recipient allowlist. Empty means any.
outbound.blockedstring[][]Recipients the agent may never send to.

Finer knobs tune scope and approval bypass: inbound.folders, inbound.window_days, inbound.max_results, the always_allow lists on each side (senders/recipients that skip the prompt), and inbound.require_auth (require DKIM/DMARC alignment on subscriptions).

Tools

Search emails via IMAP, returning envelope summaries (id, from, to, subject, date, snippet).

Parameters

  • optional from[string]Filter by sender address.
  • optional to[string]Filter by recipient address.
  • optional subject[string]Filter by subject (substring match).
  • optional body[string]Filter by body content.
  • optional folder[string, default INBOX]IMAP folder name.

Returns

ID: EMAIL_ID
From: FROM
To: TO
Date: DATE
Subject: SUBJECT
Snippet: SNIPPET
when one block per match, separated by blank lines
(Showing N of TOTAL matches)
when appended when result count exceeds max_results
No emails found.
when zero matches
Search failed: ERROR
when IMAP error

email__fetch(ids?, emailId?, folder?)

Download emails to staging as .md (parsed text with headers, attachment summary, image stats) and .eml (raw MIME).

Parameters

  • optional ids[string[]]Email IDs to fetch (batch). Use this or emailId.
  • optional emailId[string]Single email ID (alternative to ids).
  • optional folder[string, default INBOX]IMAP folder name.

Returns

Fetched N email(s) to /staging/in/
  ID: ID | From: FROM | Subject: SUBJECT
    FILENAME.md, FILENAME.eml

Use Read to access .md files. Use .eml for raw MIME / attachments.
when success
Rejected N email(s):
  ID: ID — REASON
when partial success with rejections
Missing required field: ids or emailId
when no id provided
Fetch failed: ERROR
when IMAP error

email__send(to, subject, body, replyToMessageId?)

Compose and send an email. Reply in-thread by passing replyToMessageId.

Parameters

  • to[string]Recipient email address.
  • subject[string]Email subject.
  • body[string]Plain text email body.
  • optional replyToMessageId[string]RFC Message-ID to reply to (from an email__fetch result).

Returns

Email sent. Message-ID: MESSAGE_ID
when success
Invalid arguments: ERROR
when validation failure
Email sending is disabled for this agent.
when outbound.default = disabled
Send failed: ERROR
when SMTP error

email__list_folders()

List all available IMAP mailbox folders.

Returns

FOLDER_PATH
FOLDER_PATH — DISPLAY_NAME
…
when one per line, optionally showing folder name and special-use attribute
No folders found.
when empty mailbox
Failed to list folders: ERROR
when IMAP error

email__subscribe(schedule, instructions, from?, subject?, folder?, id?, timezone?)

Watch for new emails matching criteria. Matches arrive on the configured channel as new turns with the agent's standing instructions.

Parameters

  • schedule[string]"realtime" for IMAP IDLE push, or a cron expression like "*/15 * * * *".
  • instructions[string]Instructions delivered with each matching email.
  • optional from[string]Filter by sender address.
  • optional subject[string]Filter by subject.
  • optional folder[string, default INBOX]IMAP folder to watch.
  • optional id[string]Custom subscription ID (auto-generated if omitted).
  • optional timezone[IANA timezone]Timezone for cron schedule. Defaults to agent timezone.

Returns

Subscription created:
  ID: ID
  Folder: FOLDER
  Schedule: SCHEDULE
  Target: PARTICIPANT
  Watermark: WATERMARK
when success
Missing required fields: schedule, instructions
when incomplete input
Subscriptions require a participant context.
when called outside an active conversation
Invalid cron expression: ERROR
when malformed cron

email__unsubscribe(id)

Remove an email subscription.

Parameters

  • id[string]Subscription ID to remove.

Returns

Subscription "ID" removed.
when success
Missing required field: id
when no id provided
Subscription not found: ID
when unknown id

email__list_subscriptions()

List all email subscriptions and their status.

Returns

ID: ID
  Folder: FOLDER
  Schedule: SCHEDULE
  Target: TARGET
  Status: active|paused
  Watermark: WATERMARK
  Created: ISO_TIMESTAMP
  Criteria: CRITERIA_JSON
when one block per subscription, separated by blank lines
No email subscriptions.
when none registered

Subscriptions

With a dedicated channel, the agent can subscribe to matching mail — either real-time (IMAP IDLE) or on a schedule. New mail is delivered to that channel with each subscription's standing instructions, and the agent acts on it without being asked. Without a channel, the subscription tools are hidden and the on-demand tools still work.

Notes & gotchas

🔒 SECURITY
Start outbound.default at disabled and widen with a narrow outbound.recipients only when the agent needs to send; keep inbound.default at approval unless you're deliberately opening reads. Broad subscriptions widen the agent's input surface.
⚠ HEADS UP
Credentials live on disk in secrets.json. Use a dedicated mailbox and an app-specific password.