email-notify
Allows sending emails through Gmail's SMTP server using an authorization code.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@email-notifysend a test email to admin@example.com"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
email-notify
基于 Docker 的轻量邮件发送微服务。同时对外提供两种调用方式:
REST HTTP 接口(给传统应用)
MCP(Model Context Protocol)服务(给 AI 智能体,外部 AI 远端连 Streamable HTTP / 本地 AI 用 stdio)
底层 SMTP 凭据通过环境变量注入,两种接口共用同一份发送逻辑。
特性
双接口单进程:一个 Starlette + uvicorn 进程同时承载 REST 与 MCP,复用
mailer.py低内存:MCP 用
stateless_http=True,会话内存随连接数零增长;mem_limit: 96m兜底轻镜像:基于
python:3.12-alpine,非 root 用户运行、文件系统read_only双传输:MCP 同时支持 Streamable HTTP(远端 AI)和 stdio(本地 AI,如 Claude Desktop)
统一鉴权:单一
API_KEY同时保护/api/send与/mcp,/healthz放行生产可用:
restart: unless-stopped、日志轮转、内置 healthcheck
Related MCP server: Cloudflare Email MCP
目录结构
email-notify/
├── sendmail.py # 原始脚本(保留,向后兼容,可独立运行)
├── app/
│ ├── __init__.py
│ ├── server.py # Starlette: REST 路由 + MCP 挂载 + Bearer 中间件
│ ├── mcp_tools.py # FastMCP 服务 + send_email 工具定义
│ ├── mcp_stdio.py # stdio 传输入口(本地 AI 用)
│ ├── mailer.py # SMTP 发送逻辑(REST 与 MCP 共用,env 驱动)
│ └── auth.py # Bearer Token 校验
├── requirements.txt # mcp[cli] + uvicorn(版本钉死)
├── Dockerfile # python:3.12-alpine,uvicorn 启动
├── docker-compose.yml # 资源限制 + 健康检查 + 日志轮转
├── .env.example
└── README.md快速开始
1. 准备配置
cp .env.example .env
# 生成一个 32 字节随机 API_KEY
openssl rand -hex 32编辑 .env,填入真实 SMTP 配置和生成的 API_KEY:
SMTP_SERVER=smtp.163.com
SMTP_PORT=25
SENDER_MAIL=xxx@163.com
SENDER_PW=xxx
API_KEY=<上面 openssl 生成的串>2. 构建并启动
docker compose up -d --build3. 验证
# 健康检查(无需鉴权)
curl http://localhost:8000/healthz
# {"status":"ok"}接口 1:REST HTTP
POST /api/send — 发送邮件
请求头
名称 | 必填 | 说明 |
| 是 |
|
| 是 |
|
请求体
字段 | 类型 | 必填 | 说明 |
|
| 是 | 收件人数组,元素需含 |
|
| 是 | 主题,非空 |
|
| 是 | HTML 正文,非空 |
响应
状态码 | 响应体 | 含义 |
|
| 发送成功 |
|
| 参数校验失败 / 非 JSON 体 |
|
| Token 缺失或错误 |
|
| SMTP 环境变量未配置 |
|
| SMTP 连接 / 认证 / 投递失败 |
示例
curl -X POST http://localhost:8000/api/send \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"recipients": ["dev@example.com"],
"subject": "测试邮件",
"message_body": "<h1>Hello</h1><p>from email-notify</p>"
}'GET /healthz — 健康检查
无需鉴权,返回 {"status":"ok"},不发起 SMTP 连接。
接口 2:MCP(给 AI 智能体)
服务对外暴露一个 MCP 工具:
工具 | 入参 | 返回 |
|
|
|
AI 客户端有两种连法。
方式 A:Streamable HTTP(远端 AI 推荐)
MCP 端点:http://<host>:8000/mcp
调用时在 HTTP 头里带上 Authorization: Bearer <API_KEY>(与 REST 接口共用同一个 token)。
⚠️ 远程访问必须配置 Host 白名单:SDK 默认只允许本机(
127.0.0.1/localhost)连接。外部 AI 远程连入时,需在.env里设置MCP_ALLOWED_HOSTS,否则会得到421 Invalid Host header。两种取值:
指定主机:
MCP_ALLOWED_HOSTS=notify.example.com:*,10.0.0.5:*(推荐,支持host:*通配端口)全放开:
MCP_ALLOWED_HOSTS=*(关闭 Host 校验,仅靠 Bearer Token 鉴权,适合 Docker / 反代 / 内网)详见下方环境变量;遇到 421 可参考"常见问题"一节。
在支持自定义 HTTP Header 的 MCP 客户端里,把 authentication 配成 Bearer Token 即可。例如用官方 SDK 写客户端:
import asyncio
from mcp.client.session import ClientSession
from mcp.client.streamable_http import streamablehttp_client
async def main():
async with streamablehttp_client(
"http://localhost:8000/mcp",
headers={"Authorization": "Bearer <API_KEY>"},
) as (read, write, _):
async with ClientSession(read, write) as session:
await session.initialize()
result = await session.call_tool("send_email", {
"recipients": ["dev@example.com"],
"subject": "线上告警",
"message_body": "<h1>CPU 超过 90%</h1>",
})
print(result.structuredContent)
asyncio.run(main())用 MCP Inspector 快速调试:
npx -y @modelcontextprotocol/inspector,填入 URLhttp://localhost:8000/mcp与 Bearer Token。
方式 B:stdio(本地 AI,如 Claude Desktop)
stdio 模式把服务作为子进程拉起,无需鉴权(靠进程隔离),所有 SMTP 配置从环境变量传入。
入口:python -m app.mcp_stdio
Claude Desktop 配置示例(claude_desktop_config.json):
{
"mcpServers": {
"email-notify": {
"command": "python",
"args": ["-m", "app.mcp_stdio"],
"env": {
"SMTP_SERVER": "smtp.163.com",
"SMTP_PORT": "25",
"SENDER_MAIL": "xxx@163.com",
"SENDER_PW": "xxx"
}
}
}
}也可直接用镜像跑:
command换成docker,args换成["run","-i","--rm","-e","SMTP_SERVER=...","<image>","python","-m","app.mcp_stdio"]。
环境变量
变量 | 说明 | 示例 |
| SMTP 服务器地址 |
|
| SMTP 端口 |
|
| 发件人邮箱 |
|
| 发件人密码 / 授权码(163/QQ 等需用授权码) |
|
| REST 与 MCP 共用的 Bearer Token,建议 ≥ 32 字节 |
|
| MCP 允许的 Host 头白名单(逗号分隔,支持 |
|
注意:加密模式按
SMTP_PORT自动选择——465/994走隐式 SSL(SMTP_SSL),25/587走 STARTTLS。云服务器默认封出站 25 端口,建议用465。
运维
常用命令
docker compose up -d --build # 构建并后台启动
docker compose logs -f # 跟踪日志
docker compose restart # 重启
docker compose down # 停止并删除容器
docker compose ps # 查看健康状态资源占用
指标 | 典型值 | 上限 |
运行时内存 (RSS) | 50-70 MB |
|
镜像大小 | ~80 MB | - |
启动时间 | < 2s | - |
MCP 会话内存增长 | 无 | 无( |
日志
json-file驱动,轮转10m × 3 份(最多 30MB 落盘)uvicorn access log + 应用业务日志全部打到 stdout,由 docker 收集
调参与扩展
提升 REST 并发
uvicorn 默认单 worker。SMTP 是阻塞 I/O,已在 Starlette 里通过 anyio.to_thread.run_sync 丢到线程池,单进程即可并发处理多个发送。如需更高吞吐,把 Dockerfile CMD 的 --workers 改为 2(需同步调大 mem_limit 到 160m)。
新增 MCP 工具
在 app/mcp_tools.py 加一个 @mcp.tool() 函数即可,MCP 的 HTTP 与 stdio 两条链路自动获得新工具,无需改动 server.py。
安全说明
.env已在.gitignore中,切勿提交真实凭据单一 Bearer Token:
/api/send和/mcp都要校验;stdio 模式免鉴权(本地子进程)非 root 容器 + 只读文件系统(
read_only: true+tmpfs: /tmp)建议:生产环境在前面加反向代理(nginx / traefik)做 TLS 终结和限流
MCP HTTP 安全:建议绑定内网或经反代暴露,不要直接公网无防护开放
常见问题
通常是 IPv6 优先但无 IPv6 路由 + 云厂商封出站 25 端口(腾讯云/阿里云/AWS 默认都封 25)叠加导致。解决:把 SMTP_PORT 改成 465(SSL,云厂商不封):
SMTP_PORT=465代码已按端口自动选择模式:465/994 走隐式 SSL(SMTP_SSL),25/587 走 STARTTLS。无需改代码。
若 465 也连不通,可能是服务器缺 SSL 证书校验链或防火墙问题;可临时改
.env用 587(STARTTLS)尝试。
端口和加密模式不匹配。SMTP_PORT=465 必须配合隐式 SSL(代码已自动处理);若你强行用 25 走 SSL 或 465 走 STARTTLS 会报此错。保持端口默认映射即可:465/994 → SSL,25/587 → STARTTLS。
SENDER_PW 应填邮箱服务商的授权码,不是登录密码。163/126/QQ/Gmail 等均需在邮箱后台单独生成。
MCP 端点与 REST 共用 API_KEY。确认客户端的 Authorization: Bearer <API_KEY> 头与 .env 里的值完全一致。
SDK 默认开启 DNS rebinding 防护,只允许本机(127.0.0.1/localhost)连接。两种解决方式,在 .env 里设置 MCP_ALLOWED_HOSTS:
# 方式1:指定主机白名单(推荐,精确控制,支持 host:* 通配端口)
MCP_ALLOWED_HOSTS=notify.example.com:*
MCP_ALLOWED_HOSTS=notify.example.com:*,10.0.0.5:*
# 方式2:全放开(关闭 Host 校验,仅靠 Bearer Token 鉴权,适合 Docker / 反代 / 内网)
MCP_ALLOWED_HOSTS=*改完 docker compose up -d 重启即可。注意方式1里的 Host 是客户端发来的 Host: 头值(通常是 域名:端口),不是 URL 路径。
mem_limit: 96m 过低。检查 docker compose logs,临时调到 128m 排查。
可以。sendmail.py 保留未动,可独立 python sendmail.py 运行。新服务能力在 app/ 包内,互不干扰。
设计取舍
决策 | 选择 | 理由 |
Web 框架 | Starlette + uvicorn | 单一 ASGI 进程,能同时挂 MCP(async)与 REST;轻量 |
MCP 传输 | Streamable HTTP + stdio | 远端 AI 用 HTTP,本地 AI 用 stdio;共用同一份 tool 定义 |
MCP 模式 |
| 会话内存零增长,契合低内存诉求 |
Worker 数 | 1 | 阻塞 SMTP 用线程池并发,单 worker 把内存压到最低 |
鉴权 | 单一 | REST 与 MCP 共用,少一个 env 变量 |
健康检查 | 不连 SMTP | 防止探针触发外部连接 |
邮件连接 | 每次新建 | 低内存优先,避免常驻连接池 |
License
MIT
This server cannot be deployed
Maintenance
Related MCP Connectors
Email infrastructure for AI agents — send, receive, search, and reply to email over MCP.
Hosted email for AI agents: create inboxes, send, receive, and reply over MCP with scoped API keys
Send transactional emails for AI agents via SMTP. Templates included.
Email inboxes for AI agents: send, receive, reply, search, and manage threaded email over MCP.
Related MCP Servers
- AlicenseCqualityBmaintenanceA Model Context Protocol server that enables AI models to send emails via simple tool calls, supporting both plain text and HTML formats with JWT authentication.111 npm14ISC
- AlicenseNot gradedqualityDmaintenanceEnables AI agents to send emails via Cloudflare's Email Service. Provides both MCP server integration for AI tools and a REST API for traditional applications with support for HTML content, attachments, and secure authentication.2MIT
- AlicenseNot gradedqualityDmaintenanceA production-ready MCP server that empowers AI agents to securely send emails via SMTP, supporting plain text, HTML, and attachments.MIT
- AlicenseNot gradedqualityBmaintenanceMCP server that provides email communication tools (send email, health check, list providers) via SMTP, accessible over HTTP/SSE for remote AI clients like Claude Desktop.0MIT