MailPilot
English | 中文
纯 Python 命令行邮件客户端 —— 收、发、读、搜、标记 —— 内置邮件服务器,并提供完整的智能体(function-calling)API。
GPL-3.0-or-later 许可。零运行时依赖:SMTP、IMAP、POP3 客户端,asyncio 实现的 SMTP+POP3 服务器,SQLite 本地存储,MIME 解析,以及 NTLM 所需的 DES/MD4 等全部 SASL 认证,均基于 Python 标准库自行实现。
功能特性
- 全部主流协议:SMTP(发送)、IMAP4(收取/搜索/标志/文件夹)、POP3(收取)——全面支持 SSL 与 STARTTLS。
- 全部主流认证方式:SASL
PLAIN、LOGIN、CRAM-MD5、XOAUTH2/OAUTHBEARER、NTLM(内置纯 Python DES + MD4),以及 POP3 的APOP。auto模式按服务器通告自动选择。 - 服务商预设:gmail、outlook、qq、163、126、yahoo、icloud、zoho、aliyun、sina——一个参数填好全部服务器与端口。
- 内置邮件服务器:没配服务器?
mailpilot serve即可启动本地 SMTP + POP3 服务器(asyncio、零系统依赖),支持按邮箱认证、CRAM-MD5/APOP,并带中继防护。 - 本地 SQLite 存储:每封收取/发出的邮件都可离线搜索、标记、移动、删除。
- MIME 处理完善:multipart/alternative、RFC 2047 中文头、附件(列出 + 保存)、HTML→纯文本兜底。
- 智能体 API:每个操作都有对应的 OpenAI function-calling
TOOLSschema +dispatch(),LLM 智能体能像人一样收发、搜索、标记邮件。
安装
要求 Python ≥ 3.10。运行时无任何第三方依赖,全部标准库。
# 推荐:直接装入现有 conda 环境(不需要 venv)
conda activate dev
pip install -e .[dev] # [dev] 只额外装 pytest/ruff/mypy
只使用 CLI/API 的话,pip install -e . 即可。
数据与文件位置
所有运行时数据集中在一个目录:默认 ~/.mailpilot/。
| 文件 | 用途 | 权限 |
|---|---|---|
~/.mailpilot/config.json |
账号凭据(邮箱、授权码/token、服务器地址、端口) | 0600(保存时强制,仅属主可读写) |
~/.mailpilot/mailpilot.db |
SQLite 存储:每封收取/发出的邮件——原始 RFC822 报文、解析后的头/正文、标志、附件 | 0600(首次运行后收紧) |
目录解析优先级(从高到低):
config.json内的data_dir字段- 环境变量
MAILPILOT_DATA_DIR - 默认
~/.mailpilot/
邮件数据库同时保存完整原始报文与解析后的字段(主题、发收件人、日期、标志、text/html 正文),因此可离线搜索,附件可随时重新提取。任何真实凭据或邮件内容都不会出现在代码、测试或文档中。
整体迁移(config + 邮件):
export MAILPILOT_DATA_DIR=/Volumes/SecureUSB/mailpilot
或在配置文件里设 "data_dir": "/path"。
配置账号(服务商预设)
mailpilot config add qq you@qq.com --password <SMTP授权码> --auth login --provider qq
mailpilot config test -a qq # 探测 IMAP + POP3 + SMTP 认证
交互式 OAuth 登录(Gmail / Outlook)
Gmail、Outlook 这类 XOAUTH2 账号无需手动找 token,一条命令走浏览器授权:
# 一次性设置你的 OAuth client_id(桌面应用类型)
mailpilot config set-extra gmail-账号名 client_id <你的-client-id>
# 弹出浏览器登录,成功后 token 自动写回配置
mailpilot config login you@gmail.com --provider gmail
mailpilot config login -a 已有账号名 # 对已有账号重新授权
浏览器打开 Google/Microsoft 授权页 → 登录 → 本地回环端口自动接收跳转,
access/refresh token 落盘 ~/.mailpilot/config.json(0600)。access token
过期会在下次使用时自动用 refresh token 续期。
收取、列览、阅读、搜索、标记
mailpilot fetch -a qq -n 20 # 拉取新邮件进本地库
mailpilot list --unread # 按时间倒序
mailpilot search "关键词"
mailpilot read 42 # 头部 + 正文
mailpilot read 42 --save-attachments ./att
mailpilot mark 42 --flags seen,flagged
mailpilot folders
发送
mailpilot send -t bob@example.com -s "你好" -m "正文" \
--attach ./report.pdf:report-2026.pdf
运行内置服务器
# 无认证模式,localhost,存入 ~/.mailpilot/mailpilot.db
mailpilot serve --smtp-port 2525 --pop-port 1110 --domain localhost
# 带认证的邮箱(--user 可重复)
mailpilot serve --user alice@localhost:secret1 --user bob@localhost:secret2
随后任何 SMTP/POP3 客户端都可连 127.0.0.1:2525 / 127.0.0.1:1110。本地域投递无需认证;向其他域中继需要认证,否则拒绝(550)。
脚本化 JSON 输出
所有命令都接受 --json,统一返回 {"success", "data", "error", "metadata"}:
mailpilot list --json --unread | jq '.data[0].subject'
智能体集成(OpenAI Function Calling)
from mailpilot.agent.tools import TOOLS, dispatch
# 1. 把 TOOLS 加进模型的工具列表。
# 2. 模型发起工具调用时路由:
result = dispatch("mailpilot_send", {
"to": ["bob@example.com"],
"subject": "智能体发来的问候",
"body": "通过 mailpilot 工具调用发送。",
})
print(result) # {"success": True, "data": {...}, "error": None, "metadata": {...}}
可用工具:mailpilot_send、mailpilot_list、mailpilot_read、mailpilot_search、mailpilot_mark、mailpilot_delete、mailpilot_move、mailpilot_fetch、mailpilot_folders、mailpilot_account_add、mailpilot_account_list、mailpilot_account_test。
查看 schema:mailpilot api --schema。
Python API
from mailpilot import ToolResult, send_mail, list_messages, read_message, search_messages, mark_messages
result = send_mail(to=["bob@example.com"], subject="Hi", body="Hello")
if result.success:
print(result.data["message_id"])
所有 API 函数返回 ToolResult dataclass(success、data、error、metadata、.to_dict()、__bool__ 真值判断)。
配置
账号存于 ~/.mailpilot/config.json(0600)。完整布局与迁移方法见上文数据与文件位置。运行期覆盖:
MAILPILOT_DATA_DIR— 数据目录mailpilot serve --db <path>— 内置服务器的自定义数据库路径
项目结构
mailpilot/
├── core/ # config(账号/预设)、errors(ToolResult)、
│ # auth(PLAIN/LOGIN/CRAM-MD5/XOAUTH2/NTLM/APOP)、
│ # _des + _md4(NTLM 所需纯 Python 密码学原语)
├── mail/ # smtp_client、imap_client、pop_client、
│ # parser(MIME 构建/解析/渲染)、store(SQLite)
├── serve/ # 内置 asyncio SMTP + POP3 服务器
├── agent/ # OpenAI function-calling TOOLS + dispatch
└── cli/ # argparse CLI(config/send/fetch/list/read/search/…)
tests/ # pytest 套件,含客户端↔内置服务器端到端回环
开发
conda activate dev # 用现有环境,无需 venv
pip install -e .[dev]
pytest # 59 个测试
ruff check . && ruff format .
mypy mailpilot
备注
- QQ/163/126 邮箱使用"授权码"而非登录密码——通过
--password传入。凭据只存于~/.mailpilot/config.json(0600),绝不落入代码、测试或文档。 - XOAUTH2 token 可用
mailpilot config test -a acct --oauth2-token <token>或Account.oauth2_token提供(Gmail/Outlook OAuth 流程)。 - 内置服务器把投递的邮件存进同一个 SQLite 库,因此
mailpilot fetch --protocol pop3可以对它完整回环——测试智能体时不必碰真实邮箱。 - 开发直接使用 conda 环境(如
conda activate dev && pip install -e .[dev])——无需 virtualenv。
许可
GPL-3.0-or-later(见 LICENSE)。
MailPilot
English | 中文
Pure-Python command-line mail client — send, fetch, read, search, mark — with a built-in mail server and a full agent (function-calling) API.
GPL-3.0-or-later licensed. Zero runtime dependencies: everything (SMTP, IMAP, POP3 clients, an asyncio SMTP+POP3 server, SQLite storage, MIME parsing, SASL auth incl. DES/MD4 for NTLM) is built on the Python standard library.
Features
- All mainstream protocols: SMTP (send), IMAP4 (fetch/search/flags/folders), POP3 (fetch) — SSL and STARTTLS everywhere.
- All mainstream auth methods: SASL
PLAIN,LOGIN,CRAM-MD5,XOAUTH2/OAUTHBEARER,NTLM(with a pure-Python DES + MD4), and POP3APOP.automode tries what the server advertises. - Provider presets: gmail, outlook, qq, 163, 126, yahoo, icloud, zoho, aliyun, sina — one flag fills all host/port settings.
- Built-in mail server: no server configured? Run
mailpilot serveto start a local SMTP + POP3 server (asyncio, zero system dependencies) with optional per-mailbox auth, CRAM-MD5/APOP support and relay protection. - Local SQLite store: every fetched/sent message is searchable, flaggable, movable, deletable — offline.
- MIME done right: multipart/alternative, RFC 2047 CJK headers, attachments (list + save), HTML→text fallback.
- Agent API: an OpenAI function-calling
TOOLSschema +dispatch()for every operation, so an LLM agent can read/write/search/mark mail exactly like a human user.
Installation
Requires Python ≥ 3.10. No runtime dependencies — everything is standard library.
# Recommended: install into your existing conda env (no venv needed)
conda activate dev
pip install -e .[dev] # [dev] adds pytest/ruff/mypy only
pip install -e . alone is enough for CLI/API usage.
Data & file locations
All runtime data lives in one directory: ~/.mailpilot/ by default.
| File | Purpose | Permissions |
|---|---|---|
~/.mailpilot/config.json |
Account credentials (email, auth code/token, server hosts, ports) | 0600 (owner-only, enforced on save) |
~/.mailpilot/mailpilot.db |
SQLite store: every fetched/sent message — raw RFC822 source, parsed headers/body, flags, attachments | 0600 (tightened after first run) |
Location resolution order (highest first):
data_dirfield insideconfig.json- Environment variable
MAILPILOT_DATA_DIR - Default
~/.mailpilot/
The mail database stores the full raw message plus parsed fields (subject, from/to, date, flags, text/html bodies), so searches work offline and attachments can be re-extracted any time. No message content is kept anywhere else — no code, no tests, no README ever contain real credentials or message data.
To move everything (config + mail) elsewhere:
export MAILPILOT_DATA_DIR=/Volumes/SecureUSB/mailpilot
Or set "data_dir": "/path" in the config file.
Configure an account (provider preset)
Configure an account (provider preset)
mailpilot config add qq you@qq.com --password <SMTP-auth-code> --auth login --provider qq
mailpilot config test -a qq # probes IMAP + POP3 + SMTP auth
Interactive OAuth login (Gmail / Outlook)
XOAUTH2 accounts (Gmail, Outlook) need no manual token hunting — one command runs the browser consent flow:
# One-time: set your OAuth client_id (Desktop app type)
mailpilot config set-extra <account-name> client_id <your-client-id>
# Opens the browser; tokens are saved back into config.json
mailpilot config login you@gmail.com
mailpilot config login -a <existing-account> # re-authorize an account
The provider's consent page opens in your default browser; a temporary local
loopback port catches the redirect, then access/refresh tokens are persisted
to ~/.mailpilot/config.json (0600). An expired access token is refreshed
automatically with the stored refresh token on next use.
Fetch, list, read, search, mark
mailpilot fetch -a qq -n 20 # same as before # pull new mail into local store
mailpilot list --unread # newest first
mailpilot search "Foxmail"
mailpilot read 42 # headers + body
mailpilot read 42 --save-attachments ./att
mailpilot mark 42 --flags seen,flagged
mailpilot folders
Send
mailpilot send -t bob@example.com -s "Hello" -m "Body text" \
--attach ./report.pdf:report-2026.pdf
Run the built-in server
# Open (no auth) on localhost, storing into ~/.mailpilot/mailpilot.db
mailpilot serve --smtp-port 2525 --pop-port 1110 --domain localhost
# Auth-protected mailboxes (repeat --user)
mailpilot serve --user alice@localhost:secret1 --user bob@localhost:secret2
Then point any SMTP/POP3 client at 127.0.0.1:2525 / 127.0.0.1:1110. Local-domain delivery is accepted without auth; relaying to other domains requires authentication and is otherwise denied (550).
JSON output for scripting
Every command accepts --json and returns {"success", "data", "error", "metadata"}:
mailpilot list --json --unread | jq '.data[0].subject'
Agent Integration (OpenAI Function Calling)
from mailpilot.agent.tools import TOOLS, dispatch
# 1. Pass TOOLS to your model's tool list.
# 2. When the model calls a tool, route it:
result = dispatch("mailpilot_send", {
"to": ["bob@example.com"],
"subject": "Hi from the agent",
"body": "Sent via mailpilot tool call.",
})
print(result) # {"success": True, "data": {...}, "error": None, "metadata": {...}}
Available tools: mailpilot_send, mailpilot_list, mailpilot_read, mailpilot_search, mailpilot_mark, mailpilot_delete, mailpilot_move, mailpilot_fetch, mailpilot_folders, mailpilot_account_add, mailpilot_account_list, mailpilot_account_test.
Inspect the schema yourself: mailpilot api --schema.
Python API
from mailpilot import ToolResult, send_mail, list_messages, read_message, search_messages, mark_messages
result = send_mail(to=["bob@example.com"], subject="Hi", body="Hello")
if result.success:
print(result.data["message_id"])
All API functions return a ToolResult dataclass (success, data, error, metadata, .to_dict(), truthiness via __bool__).
Configuration
Accounts live in ~/.mailpilot/config.json (0600). See Data & file locations above for the full layout and how to relocate it. Per-run overrides:
MAILPILOT_DATA_DIR— data directorymailpilot serve --db <path>— custom database path for the built-in server
Project structure
mailpilot/
├── core/ # config (accounts/presets), errors (ToolResult),
│ # auth (SASL PLAIN/LOGIN/CRAM-MD5/XOAUTH2/NTLM/APOP),
│ # _des + _md4 (pure-Python crypto primitives for NTLM)
├── mail/ # smtp_client, imap_client, pop_client,
│ # parser (MIME build/parse/render), store (SQLite)
├── serve/ # built-in asyncio SMTP + POP3 server
├── agent/ # OpenAI function-calling TOOLS + dispatch
└── cli/ # argparse CLI (config/send/fetch/list/read/search/…)
tests/ # pytest suite incl. end-to-end client↔built-in-server loops
Development
conda activate dev # your existing env; no venv needed
pip install -e .[dev]
pytest # 59 tests
ruff check . && ruff format .
mypy mailpilot
Notes
- QQ/163/126 mailboxes use "authorization codes" (授权码) instead of the account password — pass it via
--password. Credentials are stored only in~/.mailpilot/config.json(0600); they never appear in code, tests, or docs. - XOAUTH2 tokens can be supplied with
mailpilot config test -a acct --oauth2-token <token>orAccount.oauth2_tokenfor Gmail/Outlook OAuth flows. - The built-in server stores delivered mail in the same SQLite store, so
mailpilot fetch --protocol pop3round-trips against it — useful for testing agents without touching a real mailbox. - Development uses a conda env directly (e.g.
conda activate dev && pip install -e .[dev]) — no virtualenv required.
License
GPL-3.0-or-later (see LICENSE).
Release files for cycleuser-mailpilot 0.0.4
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| cycleuser_mailpilot-0.0.4.tar.gz | 69.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| cycleuser_mailpilot-0.0.4-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 137.1 kB
Release files / cycleuser_mailpilot-0.0.4.tar.gz
| Download URL | cycleuser_mailpilot-0.0.4.tar.gz |
|---|---|
| Size | 69.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c0cc1e23efdce14f24bd327f0dee01180a85e6d2923d664e9af87f582b8c85b5
|
|
BLAKE2b-256 checksum How to use checksums |
24612a37c1268c0d02de31e5f094599fa1883c3fa82f6612e4700bcdd5f4a85b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.12
|
Release files / cycleuser_mailpilot-0.0.4-py3-none-any.whl
| Download URL | cycleuser_mailpilot-0.0.4-py3-none-any.whl |
|---|---|
| Size | 67.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c50c0dd1d5a4490178b5a5637221fe1a6d99a70c8042abe04b53445cf0eca69d
|
|
BLAKE2b-256 checksum How to use checksums |
98f5074962554838ad0301f6ebf71df5a06f9492b101874501673923756a0bca
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.12
|