@blanklog/mail-mcp
v1.0.3
Published
生产级群发邮件 MCP 服务器(教学向)
Readme
mail-mcp
生产级群发邮件 MCP 服务器(教学驱动开发)。
架构
工具层(send_email / send_batch / validate_list / render_preview / get_status;send_email 支持抄送/密送,send_batch 支持公共抄送/密送) └─ 收件人管线(parse → validate → dedupe → batch) └─ 发送引擎(SendQueue:并发 + 分钟/日限速 + 分类重试) └─ Provider 适配器(SmtpEmailProvider,可替换) 资源层(mail://templates、mail://campaigns/{id})+ 提示词(compose_campaign)
快速开始
pnpm install
cp src/config/.env.example .env # 填写 SMTP 与 DKIM(模板在 src/config/ 下)
pnpm build
node dist/index.js # MCP stdio 服务器;用 MCP Inspector 调试MCP 客户端配置
mail-mcp 是 stdio 服务器:客户端负责拉起进程、喂 stdin、读 stdout。不需要手动开服务、不需要端口。
DeepSeek Harness(DSH)
DSH 通过 profile patch 挂 MCP 插件(@deepseek-ai/dsh-mcp-client,随安装版自带,无需装包)。
文件:~/.dsh/profiles/web/cordis.patch.yml(Windows 为 C:\Users\<你>\.dsh\profiles\web\cordis.patch.yml),末尾追加:
# mail-mcp:本地 stdio 邮件服务器。凭证写进 env 块(用户绑定,随配置走,不进项目仓库)。
- insert:
- id: mcp-mail-mcp
name: '@deepseek-ai/dsh-mcp-client'
config:
serverName: mail-mcp
transport: stdio
command: node
args: ['/绝对路径/mail-mcp/dist/index.js']
env:
MAIL_SMTP_HOST: 'smtp.163.com'
MAIL_SMTP_PORT: '465'
MAIL_SMTP_SECURE: 'true'
MAIL_SMTP_USER: '你的账号'
MAIL_SMTP_PASS: '你的密码'
MAIL_FROM: '你的账号'
cwd: '/绝对路径/mail-mcp' # 可选:指到项目根,.env 探测链自动生效
failOnStartupError: true
toolCallTimeoutMs: 60000保存即生效(当前会话工具就可用,无需重启 GUI)。
关键点:
工具名前缀:模型看到的是
mcp__mail-mcp__send_email、mcp__mail-mcp__send_batch等——serverName决定前缀,全局唯一,改前缀会让历史会话的权限规则作废。DSH 没有 MCP 配置目录:MCP 接入只有 profile patch 一条路(无目录扫描/自动发现)。项目里的
.mcp.json是 Cline/Cursor 的约定,DSH 不读取。技能(skill)才有专用扫描目录:项目级.dsh/skills、.agents/skills,用户级~/.dsh/skills、~/.agents/skills(<根>/<名字>/SKILL.md,只扫一层)。patch 语义:新增插件必须包在
- insert:列表里(裸写- id: xxx是覆盖已有插件行);patch 对config是整块替换不做深合并——以后改配置要重写全部键。env 块 vs .env:DSH 会清洗子进程环境(变量名匹配
/KEY|PASSWORD|SECRET|TOKEN/i的丢弃),再把env块合并上去。SMTP 凭证写进 env 块最稳(随用户配置走,多项目共享,不进仓库);cwd指到项目根后.env探测链也会自动补缺——优先级上 env 块永远压过 .env 文件。配置优先级(从高到低,逐级短路):
1. 已在 process.env 的变量(客户端 env 块 / shell 导出) ← dotenv 永不覆盖 2. MAIL_ENV_FILE 指定的 .env 文件 ← 有值即用,不检查存在 3. <工作目录>/.env ← 存在才用 4. <dist 上级>/.env(打包后即项目根,import.meta.url 锚定) ← 存在才用 5. 代码默认值 defaultConfig ← 最后兜底resolveEnvFile()逐级短路:上一级"有"就用,不再看下一级;.env只补缺、不覆盖已存在的键——所以cwd里若恰好有个.env(比如家目录),它会压过项目根那份。无 SMTP 也能启动:get_status 会显示
smtpConfigured: false并优雅降级,发信工具返回清晰错误——这正是设计成"配置缺失不是崩溃"的原因。
验证(三步):
# 1) 配置预检:组合树里应出现 mcp-mail-mcp
dsh --profile web --dump-config注意该命令会重写 profile 的 cordis.yml,
workspace-write沙箱策略下可能报 EPERM,需放开写入权限。
- 直接对服务器做 MCP 握手(
initialize+tools/list,应列出 5 个工具):
printf '%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"s","version":"0"}}}' \
'{"jsonrpc":"2.0","method":"notifications/initialized"}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
| node dist/index.js- 真跑一次
send_email(或先get_status确认smtpConfigured: true)。
其他客户端(同构配置,各客户端自己的配置文件里):
| 客户端 | 配置点 | 说明 |
|---|---|---|
| MCP Inspector | 命令配置 | node /绝对路径/dist/index.js,环境变量填 MAIL_*(开发调试首选) |
| Claude Desktop / 通用 | mcpServers 配置 | {"command":"node","args":["/绝对路径/dist/index.js"],"env":{MAIL_*...}};支持 cwd 的客户端可指项目根 |
| 全局安装 | npm i -g 后 command: mail-mcp | 免绝对路径;包内无 .env,凭证仍走 env 块 |
参考:在 DSH 里接入企业微信:MCP 与 CLI+Skill 两条路(streamable-http 示例;mail-mcp 是 stdio,配置键为 command/args/env/cwd)。
环境变量配置(.env)
启动时按 env 块/外部环境 → MAIL_ENV_FILE → cwd/.env → 项目根/.env → 默认值 逐级短路读取(详见上文"配置优先级");.env 只补缺、不覆盖已存在的变量。相对路径(MAIL_AUDIT_DIR 等)按服务器进程的 cwd 解析。
完整示例见 src/config/.env.example;cp 到 .env 后按需修改即可。
