email-skill
This skill provides script-based email operations for an agent. It includes functionalities for managing mailboxes, reading/searching emails, sending/replying/forwarding emails, and managing attachments, allowing agents to perform comprehensive email-related tasks programmatically.
Features
- IMAP operations: Read, list, mark, move, delete, copy emails
- SMTP operations: Send, reply, forward emails with attachments
- Folder management: Create, delete, rename, list mailboxes
- Dual-format bodies: Supports both plain-text and HTML, with automatic fallback generation
- Attachment handling: Supports base64-encoded attachments
- Multi-account support: Configure multiple accounts
- Authentication: Password or OAuth2 via environment variables (auto-detected)
- Signatures: Automatic signature appending to outgoing emails
- Thread support: Proper In-Reply-To and References header handling
When to use
Use this skill when you need an agent to:
- Check inbox for new emails and summarize them
- Read specific emails and extract content
- Send new emails with attachments
- Reply to or forward emails
- Organize emails by moving/copying between folders
- Create or manage mailbox folders
- Mark emails as read/unread, flagged, spam, or junk
Requirements
- Python 3.11+
- IMAP/SMTP access to your email provider
- Network access to email servers
Configuration
Basic Setup
Configure this skill with ./scripts/config.toml:
- Copy
scripts/config.default.toml to scripts/config.toml
- Edit
config.toml - fill in email address and server addresses
Authentication Setup
Authentication is automatically detected from environment variables. OAuth2 takes priority over password.
Password-based authentication:
| Variable |
Description |
EMAIL_USERNAME |
Login username (optional, defaults to email) |
EMAIL_PASSWORD |
User password or app password |
# Linux/Mac
export EMAIL_USERNAME="me"
export EMAIL_PASSWORD="my-password"
# Windows (PowerShell)
$env:EMAIL_USERNAME="me"
$env:EMAIL_PASSWORD="my-password"
# Windows (CMD)
set EMAIL_USERNAME=me
set EMAIL_PASSWORD=my-password
OAuth2 authentication:
| Variable |
Description |
EMAILOAUTH2CLIENT_ID |
OAuth2 client ID |
EMAILOAUTH2CLIENT_SECRET |
OAuth2 client secret |
EMAILOAUTH2REFRESH_TOKEN |
OAuth2 refresh token |
EMAILOAUTH2TOKEN_URL |
OAuth2 token endpoint URL |
# Linux/Mac
export EMAIL_OAUTH2_CLIENT_ID="xxx"
export EMAIL_OAUTH2_CLIENT_SECRET="xxx"
export EMAIL_OAUTH2_REFRESH_TOKEN="xxx"
export EMAIL_OAUTH2_TOKEN_URL="https://oauth2.example.com/token"
# Windows (PowerShell)
$env:EMAIL_OAUTH2_CLIENT_ID="xxx"
$env:EMAIL_OAUTH2_CLIENT_SECRET="xxx"
$env:EMAIL_OAUTH2_REFRESH_TOKEN="xxx"
$env:EMAIL_OAUTH2_TOKEN_URL="https://oauth2.example.com/token"
# Windows (CMD)
set EMAIL_OAUTH2_CLIENT_ID=xxx
set EMAIL_OAUTH2_CLIENT_SECRET=xxx
set EMAIL_OAUTH2_REFRESH_TOKEN=xxx
set EMAIL_OAUTH2_TOKEN_URL="https://oauth2.example.com/token"
Test
# Linux/Mac
echo '{"requestId":"test","schemaVersion":"1.0","data":{"maxResults":5}}' | python3 scripts/mail_list.py
# Windows (PowerShell)
echo '{"requestId":"test","schemaVersion":"1.0","data":{"maxResults":5}}' | python scripts/mail_list.py
# Windows (CMD)
echo "{\"requestId\":\"test\",\"schemaVersion\":\"1.0\",\"data\":{\"maxResults\":5}}" | python scripts/mail_list.py
Data Exchange Contract
Overview
All scripts follow the same JSON-over-stdin contract:
- Agent sends one JSON object to stdin
- Script writes one JSON object to stdout
- Logs and diagnostics are written to stderr
Request Schema
{
"requestId": "optional-trace-id",
"schemaVersion": "1.0",
"account": "optional-account-name-in-config",
"data": {}
}
Success Response Schema
{
"ok": true,
"requestId": "same-as-request",
"schemaVersion": "1.0",
"data": {}
}
Error Response Schema
{
"ok": false,
"requestId": "same-as-request",
"schemaVersion": "1.0",
"error": {
"code": "ERROR_CODE",
"message": "Human-readable message",
"details": {}
}
}
| Error Code |
Description |
VALIDATION_ERROR |
Invalid input data or parameters |
CONFIG_ERROR |
Configuration file missing or invalid |
AUTH_ERROR |
Authentication failed |
NETWORK_ERROR |
Network connection failed |
MAILOPERATIONERROR |
IMAP/SMTP operation failed |
MAILBOX_ERROR |
Mailbox selection or management failed |
INTERNAL_ERROR |
Unexpected internal error |
Scripts
folder_create.py
Create mailbox folder.
| Request fields |
|
name |
string, required |
| Response fields |
|
account |
Account name used |
name |
Folder name created |
created |
true on success |
folder_delete.py
Delete mailbox folder.
| Request fields |
|
name |
string, required |
| Response fields |
|
account |
Account name used |
name |
Folder name deleted |
deleted |
true on success |
folder_list.py
List mailbox folders.
| Response fields |
|
account |
Account name used |
mailboxes |
Array of folder objects |
folder_rename.py
Rename mailbox folder.
| Request fields |
|
oldName |
string, required |
newName |
string, required |
| Response fields |
|
account |
Account name used |
oldName |
Original folder name |
newName |
New folder name |
renamed |
true on success |
mail_copy.py
Copy email(s) between folders.
| Request fields |
|
uids |
string[] or comma-separated string |
sourceFolder |
optional, default INBOX |
targetFolder |
required |
| Response fields |
|
account |
Account name used |
uids |
UIDs copied |
sourceFolder |
Source folder |
targetFolder |
Target folder |
copied |
true on success |
mail_delete.py
Delete email(s).
| Request fields |
|
uids |
string[] or comma-separated string |
folder |
optional, default INBOX |
expunge |
boolean, default false |
| Response fields |
|
account |
Account name used |
uids |
UIDs deleted |
folder |
Folder name |
deleted |
true on success |
expunged |
true if hard deleted |
mail_forward.py
Forward email with optional additions.
| Request fields |
|
uid |
string, required |
folder |
optional, default INBOX |
to |
string or string[], required |
cc |
optional |
bcc |
optional |
bodyText |
optional, prepended to forwarded content |
bodyHtml |
optional |
attachments |
optional |
| Response fields |
|
account |
Account name used |
forwarded |
true on success |
uid |
Original email UID |
to |
Recipients |
cc |
CC recipients |
subject |
Forwarded subject |
Automatically includes original email and attachments.
mail_mark.py
Mark email(s) with flags.
| Request fields |
|
uids |
string[] or comma-separated string, required |
markType |
read, unread, flag, unflag, spam, notspam, junk, notjunk, required |
folder |
optional, default INBOX |
| Response fields |
|
account |
Account name used |
uids |
UIDs marked |
markType |
Mark type applied |
marked |
true on success |
mail_move.py
Move email(s) between folders.
| Request fields |
|
uids |
string[] or comma-separated string |
sourceFolder |
optional, default INBOX |
targetFolder |
required |
| Response fields |
|
account |
Account name used |
uids |
UIDs moved |
sourceFolder |
Source folder |
targetFolder |
Target folder |
moved |
true on success |
mail_read.py
Read email content and metadata.
| Request fields |
|
uid |
string, required |
folder |
optional, default INBOX |
| Response fields |
|
account |
Account name used |
uid |
Email UID |
subject |
Email subject |
from |
Sender |
to |
Recipients |
cc |
CC recipients |
date |
Email date |
bodyText |
Plain text body |
bodyHtml |
HTML body |
attachments |
Attachment list |
tags |
Combined flags and labels |
Marks message as read after fetch.
mail_reply.py
Reply to email.
| Request fields |
|
uid |
string, required |
folder |
optional, default INBOX |
bodyText |
optional |
bodyHtml |
optional |
replyAll |
boolean, default false |
priority |
high, normal, low |
attachments |
optional |
| Response fields |
|
account |
Account name used |
uid |
Original email UID |
folder |
Original email folder |
sent |
true on success |
to |
Reply recipients |
cc |
CC recipients |
attachmentCount |
Number of attachments |
priority |
Priority level (high, normal, low) |
readReceipt |
true if read receipt requested |
inReplyTo |
In-Reply-To message ID |
references |
References header value |
subject |
Reply subject |
mail_list.py
List emails using IMAP search.
| Request fields |
|
query |
optional, default UNSEEN |
folder |
optional, default INBOX |
maxResults |
optional, default 10 |
| Response fields |
|
account |
Account name used |
folder |
Folder searched |
query |
Search query |
uids |
Matching UIDs |
count |
Results returned |
totalCount |
Total matches |
hasMore |
More results available |
summary |
Email summaries |
| Query |
Description |
UNSEEN |
Unread messages |
FROM [email protected] |
From sender |
SUBJECT "keyword" |
Subject contains |
SINCE 2024-01-01 |
Since date |
ALL |
All messages |
mail_send.py
Send new email.
| Request fields |
|
to |
string or string[], required |
subject |
string, required |
bodyText |
optional |
bodyHtml |
optional |
cc |
optional |
bcc |
optional |
priority |
high, normal, low |
attachments |
optional |
| Response fields |
|
account |
Account name used |
sent |
true on success |
to |
Recipients |
cc |
CC recipients |
bccCount |
Number of BCC recipients |
attachmentCount |
Number of attachments |
priority |
Priority level (high, normal, low) |
readReceipt |
true if read receipt requested |
inReplyTo |
In-Reply-To message ID |
references |
References header value |
subject |
Sent subject |
Examples
List new emails
echo '{"requestId":"test","schemaVersion":"1.0","data":{"maxResults":10}}' | python3 scripts/mail_list.py
Read email by UID
{ "requestId": "read", "schemaVersion": "1.0", "data": { "uid": "123" } }
Search from sender
{
"requestId": "search",
"schemaVersion": "1.0",
"data": { "query": "FROM [email protected]" }
}
Send email
{
"requestId": "send",
"schemaVersion": "1.0",
"data": {
"to": ["[email protected]"],
"subject": "Hello",
"bodyText": "Hello world!"
}
}
Reply to email
{
"requestId": "reply",
"schemaVersion": "1.0",
"data": { "uid": "123", "bodyText": "Thanks!" }
}
Mark and move
{"requestId":"mark","schemaVersion":"1.0","data":{"uids":"123","markType":"read"}}
{"requestId":"move","schemaVersion":"1.0","data":{"uids":"123","targetFolder":"Archive"}}
Troubleshooting
AUTH_ERROR
- Ensure environment variables are set correctly
- For 2FA accounts, use app password (
EMAILAPPPASSWORD)
- OAuth2 requires all four
EMAILOAUTH2* variables
NETWORK_ERROR
- Verify IMAP port 993 (SSL) or 143 (STARTTLS)
- Verify SMTP port 465 (SSL) or 587 (STARTTLS)
- Check firewall settings
CONFIG_ERROR
- Ensure
config.toml exists and is valid TOML
- Check
email, imap.host, smtp.host are configured
Security Warnings
⚠️ SSL Verification: Setting ssl_verify = false in config disables certificate validation and exposes connections to man-in-the-middle attacks. Only disable for local development/testing.
⚠️ IMAP Injection Protection: User-provided search queries are validated against a whitelist of safe commands. Custom queries containing ()"; characters will be rejected.
Debugging
Check stderr for detailed error logs with code, message, and details.