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.tomltoscripts/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 |
| ---------------------------- | ------------------------- |
| EMAIL_OAUTH2_CLIENT_ID | OAuth2 client ID |
| EMAIL_OAUTH2_CLIENT_SECRET | OAuth2 client secret |
| EMAIL_OAUTH2_REFRESH_TOKEN | OAuth2 refresh token |
| EMAIL_OAUTH2_TOKEN_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 |
| MAIL_OPERATION_ERROR | 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.
| Request fields | | | -------------- | --- | | (none) | |
| 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 user@example.com | 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 boss@example.com" }
}
Send email
{
"requestId": "send",
"schemaVersion": "1.0",
"data": {
"to": ["user@example.com"],
"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 (
EMAIL_APP_PASSWORD) - OAuth2 requires all four
EMAIL_OAUTH2_*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.tomlexists and is valid TOML - Check
email,imap.host,smtp.hostare 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.
微信扫一扫