IMAP/SMTP 信箱 MCP server — 把任意標準信箱(Gmail/Outlook/自架…)包成 MCP tools;支援本地 stdio 與遠端 HTTP/OAuth 部署
Project description
mcp-email — IMAP / SMTP 信箱 MCP server(本機 stdio + 遠端 HTTP / OAuth)
純 Python 實作(mcp_email.py),把任意支援 IMAP / SMTP 的標準信箱(Gmail / Outlook / Yahoo / iCloud / Zoho / 公司 Exchange / 自架 mail server …)包成 MCP tools 給 Claude / Claude Code / Cowork / 任何 MCP host 用。
三種啟動模式(參考 m2k-calendar-tools 的部署架構):
| 模式 | 指令 | 適用場景 | 認證 |
|---|---|---|---|
| stdio(預設) | mcp-email |
本機單人(Claude Desktop / Code) | 環境變數 / .env / email_configure(原有方式,完全不變) |
| HTTP | mcp-email --http |
內網多人共用(Claude Code / Desktop 遠端連線) | 每請求 Authorization: Basic,伺服器不存帳密 |
| OAuth | mcp-email --oauth --issuer … |
claude.ai Connectors(手機 app / 網頁版) | OAuth 2.1 + 無狀態加密 token,伺服器不存帳密 |
能做什麼(16 個 tools)
| Tool | 用途 |
|---|---|
email_configure |
Runtime 動態切換 SMTP / IMAP 帳密、port、TLS、附件上限、收件人白名單 |
email_test_connection |
一鍵測 SMTP + IMAP 是否能登入 |
email_send |
寄信:HTML + 純文字、to/cc/bcc、檔案/Base64 附件、Reply-To、自訂 headers、retry |
email_reply |
回覆某封信:自動帶 Re: 主旨與 In-Reply-To/References 串接對話,可 reply_all、引用原文 |
email_forward |
轉寄某封信:Fwd: 主旨、附上原信抬頭與內容,預設一併轉寄原附件 |
email_list_folders |
IMAP 列出所有 mailbox 名稱(中文名稱自動解碼) |
email_list_messages |
列出 folder 內訊息 header(支援 IMAP search syntax;含 date_iso) |
email_get_message |
抓單封信完整內容(body text/html、附件 metadata 含 index、date_iso、選擇是否標 SEEN) |
email_get_attachment |
下載單封信裡某個附件的實際內容(base64),以 filename 或 index 指定;有大小上限保護 |
email_mark |
加/移除 IMAP flag(\Seen / \Flagged 等) |
email_delete |
標記 \Deleted 並 expunge(UID EXPUNGE) |
email_create_folder |
建立 folder(支援中文,自動 modified UTF-7;已存在不報錯) |
email_move_messages |
搬信:UID MOVE,server 不支援則 COPY + UID EXPUNGE fallback |
email_apply_rules |
規則整理:掃描後依條件 move/mark/delete,dry_run 預覽;單一目的地失敗不中斷整批;比對方式可調(match: substring/regex/exact、case_sensitive、match_mode: first/all) |
email_save_draft |
把一封信存成草稿(IMAP APPEND 到草稿匣,\Draft flag),不寄出,回草稿 uid |
email_send_draft |
把草稿匣裡的某封草稿寄出,成功後從草稿匣刪除 |
Prompts(在支援的 client 顯示成 slash command)
| Prompt | 用途 |
|---|---|
triage_inbox |
分流信箱:列未讀 → 摘要 → 建議動作(參數:folder、limit) |
weekly_cleanup |
每週整理:引導 apply_rules 先 dry-run 再執行的安全流程(參數:folder) |
draft_reply |
讀取指定信件並草擬回覆、確認後才寄(參數:uid、folder、tone) |
對應使用者需求:
- 📤 寄信 HTML + 純文字 ✅(multipart/alternative,純文字 fallback 自動)
- 👥 多收件人 to / cc / bcc ✅(bcc 不會 出現在 header)
- 📎 附件 — 本機檔案路徑 + Base64 ✅(text/* 自動 charset=utf-8)
- 🔧 Runtime 動態設定 ✅(
email_configuretool,任一欄位可單獨更新) - 🔍 連線測試 ✅(SMTP
NOOP+ IMAPNOOP各別測) - ⚡ 高效能 ✅(timeout 可調、SMTP send 失敗 exponential backoff 重試)
安裝
最快:uvx / pip(從 PyPI)
⚠️ 尚未發佈到 PyPI(首次發佈流程見文末〈發佈到 PyPI〉)。發佈後即可用 下列方式安裝;在那之前請用下一節「從原始碼」。
發佈後,用 uv 免手動建環境,首次執行自動抓套件:
uvx mcp-email # 直接跑(uv 會自動安裝到隔離環境)
# 或
pipx install mcp-email
pip install mcp-email # 裝進現有環境,提供 `mcp-email` 指令
MCP host 設定(Claude Desktop / Code / Cursor / Cline … 通用),帳密走 env:
{
"mcpServers": {
"email": {
"command": "uvx",
"args": ["mcp-email"],
"env": {
"IMAP_SERVER": "imap.gmail.com", "IMAP_PORT": "993",
"SMTP_SERVER": "smtp.gmail.com", "SMTP_PORT": "465", "SMTP_USE_SSL": "true",
"EMAIL_USER": "you@gmail.com", "EMAIL_PASS": "<app password>"
}
}
}
}
Claude Code 使用者:repo 內附
.mcp.json,用claude開這個資料夾會自動提示啟用。
從原始碼(開發 / 自己改)
建議用獨立的 virtualenv,避免污染系統 Python(也不要把 .venv/ 提交到 git):
cd ~/Documents/workspace/mcp-email
# 建立並啟用虛擬環境(請用你實際的 Python,例如 Homebrew 的 python3)
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
# 執行時依賴
pip install -r requirements.txt
# 開發 / 測試依賴(含 aiosmtpd 假 SMTP)
pip install -r requirements-dev.txt
# 跑端到端測試(in-process 假 SMTP + 假 IMAP)
python test_e2e.py
# 預期:=== 49/49 passed ===
# 遠端(--http/--oauth)模式測試
python -m pytest test_remote.py -q
⚠️ macOS 注意:別用
/usr/bin/python3(CommandLineTools 內建的 stub)建 venv, 它可能跳出xcode-select安裝提示。請改用 Homebrew 的python3(/opt/homebrew/bin/python3)或 pyenv。.venv/已列入.gitignore, 不會、也不該進版控;clone 後請各自重建。
註冊到 Claude Desktop / Cowork / Code
打開 Claude 的 MCP 設定檔(通常在 ~/Library/Application Support/Claude/claude_desktop_config.json 或對應路徑),加上:
{
"mcpServers": {
"email": {
"command": "python3",
"args": ["/Users/weiting/Documents/workspace/mcp-email/mcp_email.py"],
"env": {
"SMTP_HOST": "smtp.gmail.com",
"SMTP_PORT": "587",
"SMTP_USER": "you@gmail.com",
"SMTP_PASS": "<gmail app password>",
"SMTP_USE_TLS": "true",
"IMAP_HOST": "imap.gmail.com",
"IMAP_PORT": "993",
"IMAP_USER": "you@gmail.com",
"IMAP_PASS": "<gmail app password>",
"IMAP_USE_SSL": "true",
"EMAIL_FROM": "Your Name <you@gmail.com>",
"EMAIL_TIMEOUT_SEC": "30",
"EMAIL_RETRY_MAX": "3"
}
}
}
}
重啟 Claude 後就會看到 email_* 系列 tool 出現。
不設環境變數也可以,啟動後第一次用之前先呼叫 email_configure 設定帳密(runtime 設定不會落地,重啟會清空)。
用環境變數 + wrapper(推薦:設定檔零密碼)
如果你的帳密放在 ~/.secrets(會被 shell source 的機密檔),可以用附帶的 run_server.sh:
它會先 source ~/.secrets 再啟動 server,所以連 GUI 啟動的 Claude Desktop 也吃得到 env、
而設定檔裡完全不用寫密碼。設定改成:
{
"mcpServers": {
"email": { "command": "/絕對路徑/mcp-email/run_server.sh" }
}
}
環境變數別名:server 同時接受兩種命名,~/.secrets 不必改名 —
| 標準名 | 也接受的別名 |
|---|---|
SMTP_HOST / IMAP_HOST |
SMTP_SERVER / IMAP_SERVER |
SMTP_USER / IMAP_USER |
SMTP_USERNAME / IMAP_USERNAME |
SMTP_PASS / IMAP_PASS |
SMTP_PASSWORD / IMAP_PASSWORD |
帳密只需設一組:SMTP 與 IMAP 通常是同一個信箱帳號,所以帳密不必重複設 —
- 用共用的
EMAIL_USER/EMAIL_PASS(或MAIL_*),SMTP / IMAP 兩邊都吃; - 或只設一邊(例如只有
IMAP_USERNAME/IMAP_PASSWORD),另一邊會自動沿用同一組帳密。 - 只有
host/port因協定不同需各自設(如smtp.x.com:587vsimap.x.com:993;自架常是同 host 不同 port)。
只設一個協定也可以:IMAP 與 SMTP 功能完全獨立 —— 只設 IMAP 就能用全部讀信工具
(list_folders / list_messages / get_message / mark / delete);
只設 SMTP 就能用 email_send。要兩種都用才需要兩邊的 host 都設。
遠端使用(本地以外)
以下兩種模式讓同一份 server 部署在內網主機 / NAS / 雲端 VM,多人共用。
共同原則:伺服器只設定主機資訊(IMAP_HOST / SMTP_HOST / port / TLS),
不設定任何人的帳密;每位使用者的帳密隨請求帶入、原樣 pass-through 給
IMAP/SMTP 登入,撤銷 = 使用者自己改密碼或撤掉應用程式專用密碼。
兩種模式都是明文帳密等級的傳輸,正式部署必須放在 HTTPS 反向代理後面
(nginx / Caddy / Synology 反向代理皆可)。放在反向代理後面時,記得設
EMAIL_ALLOWED_HOSTS=你的對外網域,否則 SDK 的 DNS-rebinding 防護會把
請求擋成 421 Invalid Host header。
遠端模式與 stdio 的行為差異(多人共用的安全考量):
email_configure停用並自 tool 列表隱藏(全域設定不容任一使用者改動)path附件預設停用(那是「伺服器」的檔案系統);請改用content_base64, 或由管理員設EMAIL_ATTACHMENT_DIRS白名單開放- 預設
From= 該請求登入的帳號(不吃伺服器端EMAIL_FROM)
HTTP 模式(Basic pass-through,適合內網共用)
# 已安裝(pip / pipx)或直接 uvx,一行起服務:
IMAP_HOST=mail.example.com SMTP_HOST=mail.example.com \
uvx mcp-email --http --host 0.0.0.0 --port 8765
# 從原始碼跑也一樣:python3 mcp_email.py --http --host 0.0.0.0 --port 8765
主機設定也可以放在工作目錄的 .env 檔(KEY=VALUE 每行一組,環境變數優先),
不必每次打在指令前。
每位使用者在自己的 Claude Code 註冊(帳密只存在自己機器上):
claude mcp add --transport http email https://主機/mcp \
--header "Authorization: Basic $(printf '%s' '帳號:密碼' | base64)"
伺服器不保存任何帳密、也絕不回退到環境變數憑證:沒帶 Authorization
標頭的工具呼叫一律被拒絕。
OAuth 模式(claude.ai Connectors:手機 app / 網頁版)
注意:OAuth 模式為單行程設計(進行中的授權交易存在記憶體), 不支援多副本負載平衡;token 本身無狀態,重啟不影響已發 token。
claude.ai 的連線來自 Anthropic 雲端,issuer 必須是公網可達的 HTTPS 網址:
# uvx(自動帶上 OAuth 模式的額外依賴 cryptography):
IMAP_HOST=mail.example.com SMTP_HOST=mail.example.com \
uvx --with cryptography mcp-email --oauth --issuer https://mail-mcp.example.com \
--host 0.0.0.0 --port 8765
# 或既有環境:pip install "mcp-email[oauth]"(= pip install cryptography)
流程:標準 OAuth 2.1(動態註冊 + PKCE)。使用者第一次連接會被導到
/login 輸入信箱帳密(建議應用程式專用密碼),bridge 先以 IMAP 登入驗證,
通過後把憑證用伺服器金鑰 AES-GCM 加密封進 token —— 無狀態設計,
伺服器沒有憑證資料庫。access token 1 小時、refresh token 30 天自動輪替。
claude.ai 端:設定 → 連接器(Connectors)→ 新增自訂連接器 → 貼上
https://mail-mcp.example.com/mcp。
相關環境變數(皆選配):
| 變數 | 用途 |
|---|---|
EMAIL_BRIDGE_KEY |
token 加密金鑰(urlsafe base64 的 32 bytes);沒設就自動產生 .bridge-key 檔(chmod 600)。換金鑰=所有已發 token 立即失效 |
EMAIL_BRIDGE_KEY_FILE / EMAIL_OAUTH_CLIENTS |
金鑰檔 / DCR client 註冊檔路徑(Docker 部署指向 /data) |
EMAIL_DOMAIN |
設了之後,登入帳號沒打 @ 會自動補 @此網域(如 EMAIL_DOMAIN=example.com 時輸入 alice → alice@example.com) |
EMAIL_AUTH_LOG |
登入失敗日誌(固定格式,供 fail2ban 監看封鎖來源) |
EMAIL_ALLOWED_HOSTS |
反向代理後的對外網域白名單(逗號分隔) |
⚠️ 金鑰是整個 OAuth 模式的單點要害:拿到 bridge 金鑰的人可以解開所有
已發 token 內的使用者憑證。金鑰檔 / EMAIL_BRIDGE_KEY 務必限縮讀取權限、
不進版控(repo 的 .gitignore 已排除 .bridge-key),有疑慮就換金鑰
(代價只是所有人重新授權一次)。
內建防護:登入頁同意畫面(顯示發起授權的應用程式名稱+授權碼送達的 網域,讓使用者辨識並攔截釣魚連結)、登入失敗節流(同 IP / 全域滑動視窗, 避免上游信箱服務封鎖 bridge 的 IP)、同授權交易密碼錯誤 5 次作廢、 登入頁安全標頭、auth log 自我輪替。搭配 fail2ban 的 filter 範例:
# /etc/fail2ban/filter.d/mcp-email.conf
[Definition]
failregex = mcp-email-login-fail ip=<HOST>
# jail:logpath 指向 EMAIL_AUTH_LOG 的路徑,maxretry/bantime 視需求
Docker 部署
現成多架構 image(linux/amd64 + linux/arm64,打 v* tag 由 GitHub Actions 自動發佈):
# GHCR(免登入即可 pull)
docker pull ghcr.io/weiting-tw/mcp-email:latest
# Docker Hub(維護者設定 DOCKERHUB_* secret 後才會發佈)
docker pull weiting-tw/mcp-email:latest
或自己建:
docker build -t mcp-email .
# HTTP 模式(把 mcp-email 換成上面的 image 名稱亦可)
docker run -d -p 8765:8765 \
-e IMAP_HOST=mail.example.com -e SMTP_HOST=mail.example.com \
mcp-email
# OAuth 模式(掛 /data volume 保留金鑰與 client 註冊)
docker run -d -p 8765:8765 -v mcp-email-data:/data \
-e IMAP_HOST=mail.example.com -e SMTP_HOST=mail.example.com \
mcp-email --oauth --issuer https://對外網址 --host 0.0.0.0 --port 8765
容器以非 root(uid 10001)執行。OAuth 模式為單行程設計(授權交易存在 記憶體),不支援多副本負載平衡。
HTTPS 反向代理範例(Caddy,兩行搞定自動憑證)
mail-mcp.example.com {
reverse_proxy 127.0.0.1:8765
}
nginx 使用者:proxy_pass http://127.0.0.1:8765; 並保留
proxy_set_header Host $host;;SSE 緩衝不用另外關(server 回應已帶
X-Accel-Buffering: no)。設好代理後記得配 EMAIL_ALLOWED_HOSTS=mail-mcp.example.com。
遠端部署上線前檢查清單
上線 --http / --oauth 前逐項確認,尤其打 ✅ 的三項是實務上最常害人踩雷的:
- HTTPS 反向代理:帳密/token 是明文等級傳輸,一定要放在 TLS 後面,絕不裸跑對外。
-
EMAIL_ALLOWED_HOSTS:設成你的對外網域,否則 SDK 的 DNS-rebinding 防護會回421 Invalid Host header。 - ✅ OAuth 金鑰要持久化:
--oauth一定要掛-v mcp-email-data:/data(或設固定的EMAIL_BRIDGE_KEY)。沒掛 volume→容器每次重啟都重新產生金鑰→所有人 token 全失效、要重新授權,這是最常見的災情。 - ✅ 金鑰檔權限:
.bridge-key(或/data)只給執行帳號讀;拿到金鑰的人可解開所有已發 token 內的使用者憑證。已在.gitignore排除,切勿進版控。 - ✅ 用應用程式專用密碼:宣導使用者在登入頁輸入 App Password,不要用信箱主密碼——外洩時只波及收發信、可單獨撤銷。
- 同意畫面:登入頁會顯示「哪個應用程式要連你的信箱」+「授權碼送達的網域」;提醒使用者只在認得目的地時才繼續(防釣魚)。
- 失敗節流 / fail2ban:設
EMAIL_AUTH_LOG並掛 fail2ban(filter 範例見上),擋帳密暴力猜測。 -
path附件:遠端模式預設停用;若確有需求才用EMAIL_ATTACHMENT_DIRS白名單開放,範圍越小越好。 - token 生命週期已知:access 1 小時(自動續)、refresh 30 天(活躍使用者滾動不過期);使用者改密碼=所有舊 token 失效(需重新授權),這是刻意的撤銷機制。
常見信箱設定
| 服務 | SMTP host | port | TLS | IMAP host | port | SSL | 備註 |
|---|---|---|---|---|---|---|---|
| Gmail | smtp.gmail.com |
587 | TLS | imap.gmail.com |
993 | SSL | 需 App Password |
| Outlook / Microsoft 365 | smtp.office365.com |
587 | TLS | outlook.office365.com |
993 | SSL | 帳號需開 SMTP / IMAP 開關 |
| Outlook.com / Hotmail | smtp-mail.outlook.com |
587 | TLS | outlook.office365.com |
993 | SSL | 同上 |
| Yahoo Mail | smtp.mail.yahoo.com |
587 | TLS | imap.mail.yahoo.com |
993 | SSL | 需開 App Password |
| iCloud Mail | smtp.mail.me.com |
587 | TLS | imap.mail.me.com |
993 | SSL | 需開 App-specific Password |
| Zoho Mail | smtp.zoho.com |
587 | TLS | imap.zoho.com |
993 | SSL | |
| 自架(Postfix + Dovecot) | 視設定 | 587/465 | TLS / SSL | 視設定 | 143/993 | STARTTLS / SSL |
安全提醒:別把純密碼塞進 config 檔案明文。建議:
- Gmail / Yahoo / iCloud / Outlook:用 App Password(兩階段驗證 → 應用程式密碼)
- 公司 Exchange:問 IT 拿 SMTP/IMAP credential,或用 OAuth(這個版本暫未支援 OAuth flow)
- 密碼存 macOS Keychain 並用
security find-generic-password -w動態注入
TLS / SSL 憑證驗證:預設兩邊都 驗 憑證(verify_cert: true)。自架信箱或公司內部用自簽憑證會在連線時 CERTIFICATE_VERIFY_FAILED 報錯。要關掉驗證:
// 環境變數
"SMTP_VERIFY_CERT": "false",
"IMAP_VERIFY_CERT": "false"
或 runtime 用 email_configure:
{ "smtp": { "verify_cert": false }, "imap": { "verify_cert": false } }
關掉後會喪失 MITM 防護,只在自簽 / 測試環境用,連 Gmail / Outlook 之類公網信箱絕對不要關。
附件路徑白名單:因為 email_send 的 path 附件受模型/host 控制,預設不設限時,
理論上可被誘導把任意本機檔案(如 ~/.ssh/id_rsa)夾帶寄出。若要防護,設定允許目錄:
// 環境變數(os.pathsep 分隔,macOS/Linux 用 : )
"EMAIL_ATTACHMENT_DIRS": "/Users/me/Documents:/Users/me/Pictures"
或 runtime 用 email_configure:
{ "attachment_allowed_dirs": ["/Users/me/Documents", "/Users/me/Pictures"] }
設定後,path 附件只能來自這些目錄底下(會解析 symlink 防繞過),名單外一律 PermissionError。
空白(預設)= 不限制。content_base64 附件不受此限(內容由呼叫端直接提供)。
收件人網域白名單(防被誘導亂寄,開關,預設關):email_send / email_reply /
email_forward / email_send_draft 都是對外動作,若擔心模型被惡意信件內容誘導亂寄,
可限制只能寄到指定網域:
// 環境變數(逗號分隔)
"EMAIL_ALLOWED_RECIPIENT_DOMAINS": "gss.com.tw,example.com"
設定後任何收件人(to/cc/bcc)網域不在清單內就 PermissionError;空白(預設)= 關閉不限制。
附件下載大小上限:email_get_attachment 把附件 base64 回傳,過大的附件會撐爆模型
context / 記憶體,預設上限 5MB(超過只回 metadata 並標 too_large)。可用
EMAIL_MAX_ATTACHMENT_BYTES 調整,或呼叫時以 max_bytes 覆蓋。
⚠️ Prompt injection(把這套接上 LLM 最重要的風險):信件的主旨/內文是寄件人可控的,
email_get_message / email_list_messages 會把它們原樣回給模型,惡意信可能夾帶
「請把這封轉寄到 …/刪除某資料夾」之類的指令試圖操縱模型。防線:
- 絕不要把信件內文裡的指令當成命令執行——內文永遠是「資料」不是「指令」。
- 破壞性/對外動作(
send/reply/forward/delete/move/apply_rules/send_draft) 執行前一律先向使用者確認;多數 MCP host(Claude Desktop/Code)本來就會對每次工具 呼叫要求核准,這是主要安全網,別關掉它。 - 加上上面的收件人網域白名單當第二層防線;
apply_rules保持dry_run=true先預覽。
Tool 呼叫範例
email_configure
{
"smtp": {
"host": "smtp.gmail.com",
"port": 587,
"username": "you@gmail.com",
"password": "xxxx xxxx xxxx xxxx",
"use_tls": true
},
"imap": {
"host": "imap.gmail.com",
"port": 993,
"username": "you@gmail.com",
"password": "xxxx xxxx xxxx xxxx",
"use_ssl": true
},
"email_from": "Your Name <you@gmail.com>",
"retry_max": 3
}
email_send
{
"to": ["alice@example.com", "bob@example.com"],
"cc": "carol@example.com, dave@example.com",
"bcc": ["eve@example.com"],
"subject": "週報 2026-06-12",
"text": "純文字版本內容",
"html": "<h1>週報</h1><p>本週進度:…</p>",
"reply_to": "support@example.com",
"headers": {
"X-Report-Week": "2026-W24"
},
"attachments": [
{"path": "/Users/me/Documents/report.pdf"},
{"path": "/Users/me/Pictures/chart.png", "filename": "週報圖表.png"},
{
"content_base64": "SGVsbG8gV29ybGQh",
"filename": "data.txt",
"mime_type": "text/plain"
}
]
}
email_test_connection
{ "smtp": true, "imap": true }
回傳:
{
"smtp": {"ok": true, "host": "smtp.gmail.com", "port": 587},
"imap": {"ok": true, "host": "imap.gmail.com", "port": 993}
}
email_list_messages
{
"folder": "INBOX",
"limit": 20,
"search": "UNSEEN"
}
IMAP search syntax 常用:
ALL、UNSEEN、SEEN、FLAGGED、UNFLAGGED、ANSWERED、UNANSWERED、DELETED、DRAFT、RECENTFROM "alice@example.com"、TO "..."、CC "..."、SUBJECT "週報"SINCE 1-Jan-2026、BEFORE 31-Dec-2026、ON 12-Jun-2026- 邏輯:
UNSEEN FROM "alice"、OR FROM "alice" FROM "bob"、NOT SEEN
中文關鍵字:支援。非 ASCII 關鍵字會自動改用 SEARCH CHARSET UTF-8 + literal
(RFC 3501 標準做法);一次一個中文詞,且含 OR/NOT 時要放在最後一個條件。
若伺服器接受語法但比對不到(實測 Mail2000 不解 RFC2047 編碼的中文標頭),會自動
fallback 成「以剩餘 ASCII 條件縮小範圍 → 抓 header 在客戶端比對」(掃描上限 200 封,
回傳會附 note 說明)。email_apply_rules 的規則比對本來就在客戶端做,中文不受限。
email_get_message
{ "folder": "INBOX", "uid": "12345", "mark_read": false }
email_mark / email_delete
{"folder": "INBOX", "uids": ["12345", "12346"], "flag": "\\Seen", "add": true}
{"folder": "INBOX", "uids": ["12345"]}
驗證測試結果
=== 49/49 passed ===
另有 test_mcp_stdio.py:用真正的 MCP client 把 mcp_email.py 以 stdio 子行程啟動,
跑完整 initialize → list_tools → 呼叫 tool 的 handshake,驗證能被任何 MCP host 載入。
以及 test_remote.py(40 tests):遠端模式的 Basic 標頭解析、每請求憑證覆蓋、
遠端限制(email_configure / path 附件停用、白名單 symlink 繞過)、
OAuth token 加解密與 provider 流程(含失敗節流、狀態清理、DCR 上限淘汰)、
in-process uvicorn 起真正 streamable-http server 的端對端測試——包括
「HTTP Basic 帳密穿透到 IMAP/SMTP 登入」與「DCR→PKCE→/login→token→Bearer
呼叫工具」的完整 OAuth 授權流程。
每次 push / PR 會由 GitHub Actions 在 Python 3.10–3.13 上自動跑兩支測試(見 .github/workflows/test.yml)。
涵蓋三類情境:
SMTP 寄信(in-process aiosmtpd 假 SMTP,真的把信送過去比對)
- header(From / To / Cc / Reply-To / Subject)
- bcc 不外漏(rcpt_tos 有 bcc 但 header 沒)
- multipart:純文字 + HTML 兩個 part 都存在
- 附件:檔案 + base64 兩種來源,filename 含中文(
備註.txt)也對 - retry:port 沒人 listen 時 SMTP 會 raise
ConnectionRefusedError - 密碼不會出現在
email_configure回傳
錯誤路徑 / 安全
- 沒寄件人、沒收件人、附件不存在、壞 base64 各自 raise 對應 error
- html-only 自動補純文字 fallback
- 附件白名單:名單外檔案被
PermissionError擋下、名單內正常附加
IMAP 讀信(FakeIMAP 取代真連線)
list_folders/list_messages(UID 正確解析)/get_message(text+html+附件 metadata)mark(flag 真的寫入)/delete(UID EXPUNGE,及不支援 UIDPLUS 時 fallback)/test_connection(IMAP NOOP)- parser 健壯性:FLAGS/UID 出現在 BODY literal「之後」的回應排序也能正確解析
設計選擇 / 已知限制
- 無 OAuth flow:目前只支援帳密。Gmail / 365 OAuth2 比較複雜,要看後續是否真實需要再加。
- 不維護長連線:每次 IMAP 呼叫都重開連線。簡單可靠,效能上對「偶爾讀信」場景夠用;如果你要做「常駐 polling」可能要改長連線。
- IMAP search 直接傳給伺服器:自由度高但要懂 RFC 3501 syntax;含空格的字串記得加雙引號(如
FROM "alice@x.com")。 - 刪信用 UID EXPUNGE:
email_delete在 server 支援 UIDPLUS(RFC 4315)時只清掉你指定的 uid,不會誤刪資料夾內其他已標\Deleted的信;不支援時才 fallback 到一般EXPUNGE(回傳的method欄位會標明用了哪種)。 - 附件大小:受 SMTP 伺服器限制(Gmail 25MB、Outlook 20MB、Exchange 視設定)。本工具不設上限,超過會直接 SMTP error。
- 遠端模式的多帳號:
--http/--oauth天生多人(每請求帶各自憑證、彼此隔離);stdio 模式仍是一個 instance 一個帳號,多帳號要靠 host 多開幾個 instance(每個給不同 env)。 - OAuth 模式單行程:授權交易存在記憶體,不支援多副本負載平衡;token 本身無狀態,重啟不影響已發 token(金鑰不變的前提下)。
發佈到 PyPI(維護者用)
推 v* tag 會由 GitHub Actions 自動 build + 發佈(.github/workflows/publish.yml,
走 PyPI Trusted Publishing,不需要 API token):
# 1. bump pyproject.toml 的 version 並 commit
# 2. 上 tag 並推出去,Actions 就會發佈
git tag v0.3.0 && git push origin v0.3.0
一次性設定:PyPI 的 mcp-email 專案 → Settings → Publishing → 新增 GitHub publisher
(owner weiting-tw、repository mcp-email、workflow publish.yml、environment pypi),
並在 GitHub repo 的 Settings → Environments 建立名為 pypi 的 environment。
License
MIT。隨便用。
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mcp_email-0.5.0.tar.gz.
File metadata
- Download URL: mcp_email-0.5.0.tar.gz
- Upload date:
- Size: 50.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
147fa6c1b55c09e2d245721f006ff82a845988f923c2d0728da6354847869471
|
|
| MD5 |
b0ebdb7b6757ffedfb726536ba3aa3d8
|
|
| BLAKE2b-256 |
6b2b6e57b6cad154c8648529f30e3ece5311020fc633c75786488ece5a9d3f46
|
Provenance
The following attestation bundles were made for mcp_email-0.5.0.tar.gz:
Publisher:
publish.yml on weiting-tw/mcp-email
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mcp_email-0.5.0.tar.gz -
Subject digest:
147fa6c1b55c09e2d245721f006ff82a845988f923c2d0728da6354847869471 - Sigstore transparency entry: 2218968222
- Sigstore integration time:
-
Permalink:
weiting-tw/mcp-email@864dffb7b92146872a78f088294c87cdf19a9d38 -
Branch / Tag:
refs/tags/v0.5.0 - Owner: https://github.com/weiting-tw
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@864dffb7b92146872a78f088294c87cdf19a9d38 -
Trigger Event:
push
-
Statement type:
File details
Details for the file mcp_email-0.5.0-py3-none-any.whl.
File metadata
- Download URL: mcp_email-0.5.0-py3-none-any.whl
- Upload date:
- Size: 51.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ec0c29fc75909cdcd4502cb81cba6af4ed939064efa6f70c5f50806150a5cb47
|
|
| MD5 |
c1d517257e6c5d1a1a01f4a178135ad6
|
|
| BLAKE2b-256 |
7ddbdfca928cad3d9a1cd40fe2dd6c1e4c246244da99d4d110e503646f36e8b9
|
Provenance
The following attestation bundles were made for mcp_email-0.5.0-py3-none-any.whl:
Publisher:
publish.yml on weiting-tw/mcp-email
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mcp_email-0.5.0-py3-none-any.whl -
Subject digest:
ec0c29fc75909cdcd4502cb81cba6af4ed939064efa6f70c5f50806150a5cb47 - Sigstore transparency entry: 2218968451
- Sigstore integration time:
-
Permalink:
weiting-tw/mcp-email@864dffb7b92146872a78f088294c87cdf19a9d38 -
Branch / Tag:
refs/tags/v0.5.0 - Owner: https://github.com/weiting-tw
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@864dffb7b92146872a78f088294c87cdf19a9d38 -
Trigger Event:
push
-
Statement type: