WildDuck MCP插件
模型上下文协议(MCP)服务器插件 WildDuck电子邮件服务器 使人工智能助手能够通过标准化的API与电子邮件数据进行交互。
特性
- MCP资源:访问邮箱、邮件和用户信息
- MCP工具:发送电子邮件、搜索邮件、管理邮箱
- MCP提示:电子邮件摘要和回复起草的预构建提示
- 基于Web的传输:HTTP/REST API实现(不是STDIO)
- 完全集成WildDuck:直接访问WildDuck的处理程序和数据库
安装
- 克隆此存储库:
git clone https://github.com/HamStudy/wildduck-mcp-plugin.git
cd wildduck-mcp-plugin- 安装依赖项:
npm install- 在WildDuck插件目录中创建一个符号链接:
# From your WildDuck installation directory
cd plugins
ln -s /path/to/wildduck-mcp-plugin mcp重要:插件必须符号链接为 mcp (不是 wildduck-mcp-plugin)以确保URL路由正常工作。
- 在WildDuck配置目录中创建一个配置文件:
cp /path/to/wildduck-mcp-plugin/mcp.toml.example config/plugins/mcp.toml- 将插件添加到您的WildDuck插件配置中:
# config/plugins.toml (or wherever you configure plugins)
[[plugins]]
name = "mcp"
enabled = true- 重新启动WildDuck以加载插件
配置
编辑 config/plugins/mcp.toml 配置插件。关键设置:
enabled:启用/禁用插件mcp.readOnly:启用只读模式(禁用所有写入操作)rateLimit:API速率限制cors:web客户端的CORS设置
只读模式
当 mcp.readOnly = true,插件以只读模式运行:
- 只有阅读工具可用(listMailboxes、getMessages、getMessage、searchMessages)
- 写操作已禁用(sendEmail、moveMessage、deleteMessage、markAsRead等)
- 消息永远不会自动标记为已读
- 尝试使用写入工具返回403 Forbidden错误
API终点
安装后,MCP服务器可在以下位置使用:
http://your-wildduck-server:8080/plugin/mcp/MCP端点
POST /plugin/mcp/initialize-初始化MCP会话GET /plugin/mcp/resources-列出可用资源GET /plugin/mcp/resources/:uri-阅读资源GET /plugin/mcp/tools-列出可用工具POST /plugin/mcp/tools/:name-执行工具GET /plugin/mcp/prompts-列出可用提示GET /plugin/mcp/prompts/:name-获得提示
认证
MCP插件使用WildDuck的本地身份验证系统。所有端点都需要使用以下方式之一进行身份验证:
- URL路径参数 (建议MCP客户使用):
/plugin/mcp/{accessToken}/resources- X-Access-Token标头:
X-Access-Token: your-wildduck-access-token- 授权承载头:
Authorization: Bearer your-wildduck-access-token- 查询参数 (不推荐):
?accessToken=your-wildduck-access-token获取访问令牌
要获得MCP插件的访问令牌,请使用WildDuck的API进行身份验证:
curl -X POST http://localhost:8080/authenticate \
-H "Content-Type: application/json" \
-d '{
"username": "user@example.com",
"password": "your-password",
"scope": "master",
"token": true
}'这将返回一个响应 token 字段:
{
"success": true,
"id": "507f1f77bcf86cd799439011",
"username": "user@example.com",
"token": "a1b2c3d4e5f6..."
}支持的令牌类型:
- 访问令牌 (推荐):通过生成
/authenticate端点 - 特定于应用程序的密码:16个字符的应用程序密码
- 主密码:用户的主帐户密码(不建议用于应用程序)
经过身份验证的用户上下文会自动传递给所有MCP操作,您永远不需要指定用户ID。
安全:插件强制执行严格的访问控制:
- 用户只能访问自己的邮箱和邮件
- 所有操作在继续之前都要验证所有权
- 尝试访问其他用户的数据将导致“拒绝访问”错误
- 禁用或暂停的帐户无法访问MCP API
可用资源
静态资源
wildduck://mailbox/list-列出用户的邮箱wildduck://messages/recent-从收件箱或指定邮箱获取最近的邮件wildduck://user/info-获取用户帐户信息
动态资源
wildduck://message/{messageId}-按ID获取包含完整内容的特定消息wildduck://attachment/{messageId}/{attachmentId}-下载附件作为二进制数据
备注:在API响应中返回消息和附件资源URI:
- 列出消息时,每条消息都包含一个
resourceUri领域 - 收到邮件时,每个附件都包含
resourceUri领域
可用工具
电子邮件阅读(始终可用)
listMailboxes-列出所有具有可选邮件计数的邮箱getMessages-从带有分页的邮箱获取邮件(不标记为已读)
- 每条消息包括 hasThread 字段,指示它是否是对话的一部分
getMessage-按ID获取包含完整内容的特定消息(不标记为已读)
- 始终包括 thread 包含相关消息的信息(如果是对话的一部分) - 列出带有元数据(id、文件名、contentType、大小)的附件 - 每个附件包括 publicUrl 用于安全、限时的直接访问
getThread-获取对话线程中的所有消息(查找回复和相关消息)getAttachment-从邮件中下载附件(返回base64编码数据)searchMessages-跨邮箱搜索邮件
电子邮件管理(在只读模式下禁用)
sendEmail-发送电子邮件moveMessage-将邮件移动到另一个邮箱deleteMessage-删除邮件(移动到废纸篓或永久删除)createMailbox-创建新的邮箱文件夹markAsRead-将邮件标记为已读或未读markAsFlag-标记或取消标记信息
可用提示
email_summary-生成最近电子邮件的摘要draft_reply-起草对特定电子邮件的回复
示例用法
使用基于URL的身份验证(建议用于MCP)
初始化MCP会话
curl -X POST http://localhost:8080/plugin/mcp/YOUR_ACCESS_TOKEN/initialize \
-H "Content-Type: application/json" \
-d '{"protocolVersion": "2024-11-05"}'列出邮箱
curl http://localhost:8080/plugin/mcp/YOUR_ACCESS_TOKEN/resources/wildduck://mailbox/list发送电子邮件
curl -X POST http://localhost:8080/plugin/mcp/YOUR_ACCESS_TOKEN/tools/sendEmail \
-H "Content-Type: application/json" \
-d '{
"arguments": {
"to": "recipient@example.com",
"subject": "Hello from MCP",
"text": "This email was sent via MCP!"
}
}'下载附件
# Get attachment info only
curl -X POST http://localhost:8080/plugin/mcp/YOUR_ACCESS_TOKEN/tools/getAttachment \
-H "Content-Type: application/json" \
-d '{
"arguments": {
"messageId": "507f1f77bcf86cd799439011",
"attachmentId": "ATT00001",
"returnType": "info"
}
}'
# Download attachment data (base64 encoded)
curl -X POST http://localhost:8080/plugin/mcp/YOUR_ACCESS_TOKEN/tools/getAttachment \
-H "Content-Type: application/json" \
-d '{
"arguments": {
"messageId": "507f1f77bcf86cd799439011",
"attachmentId": "ATT00001"
}
}'通过资源URI访问消息
# First, get messages which includes resource URIs
curl http://localhost:8080/plugin/mcp/YOUR_ACCESS_TOKEN/tools/getMessages \
-H "Content-Type: application/json" \
-d '{"arguments": {"mailbox": "INBOX"}}'
# Response includes resourceUri for each message:
# {
# "content": [{
# "type": "text",
# "text": "{
# \"messages\": [{
# \"id\": \"507f1f77bcf86cd799439011\",
# \"resourceUri\": \"wildduck://message/507f1f77bcf86cd799439011\"
# ...
# }]
# }"
# }]
# }
# Access the message directly via its resource URI
curl http://localhost:8080/plugin/mcp/YOUR_ACCESS_TOKEN/resources/wildduck://message/507f1f77bcf86cd799439011通过资源URI访问附件
# First, get a message to see attachment resource URIs
curl http://localhost:8080/plugin/mcp/YOUR_ACCESS_TOKEN/resources/wildduck://message/507f1f77bcf86cd799439011
# Response includes resourceUri for each attachment:
# {
# "contents": [{
# "text": "{
# \"attachments\": [{
# \"id\": \"ATT00001\",
# \"filename\": \"document.pdf\",
# \"resourceUri\": \"wildduck://attachment/507f1f77bcf86cd799439011/ATT00001\"
# ...
# }]
# }"
# }]
# }
# Download the attachment directly via its resource URI
curl http://localhost:8080/plugin/mcp/YOUR_ACCESS_TOKEN/resources/wildduck://attachment/507f1f77bcf86cd799439011/ATT00001安全公共附件URL
# Get a message to see the public attachment URLs
curl -X POST http://localhost:8080/plugin/mcp/YOUR_ACCESS_TOKEN/tools/getMessage \
-H "Content-Type: application/json" \
-d '{"arguments": {"messageId": "507f1f77bcf86cd799439011"}}'
# Response includes publicUrl for each attachment:
# {
# "content": [{
# "text": "{
# \"attachments\": [{
# \"id\": \"ATT00001\",
# \"filename\": \"report.pdf\",
# \"publicUrl\": \"http://localhost:8080/plugin/mcp/att/507f1f77bcf86cd799439011/ATT00001/1734567890/AbCdEf123456-_/report.pdf\"
# ...
# }]
# }"
# }]
# }
# The publicUrl can be accessed directly without any authentication:
curl "http://localhost:8080/plugin/mcp/att/507f1f77bcf86cd799439011/ATT00001/1734567890/AbCdEf123456-_/report.pdf"
# These URLs are perfect for:
# - Sharing attachments via email or chat
# - Embedding in web applications
# - Temporary download links
# - Public access without exposing auth tokens使用基于标头的身份验证
curl http://localhost:8080/plugin/mcp/resources/wildduck://mailbox/list \
-H "X-Access-Token: your-access-token"发展
要扩展插件,请执行以下操作:
- 在中添加新资源
lib/mcp-server.jsinitializeCapabilities() - 添加具有适当输入模式的新工具
- 实现处理程序方法
- 如果需要,更新配置架构
安全附件URL
该插件为附件生成安全、有时间限制的URL,可以在不暴露访问令牌的情况下共享:
原理
- 消息响应中的每个附件都包括
publicUrl领域 - URL使用HMAC-SHA1签名,并包含过期时间戳
- 默认过期时间为1小时(可配置)
- 无需身份验证令牌-URL本身包含授权
- 基于干净路径的格式:
/plugin/mcp/att/////filename.ext
示例响应
{
"attachments": [{
"id": "ATT00001",
"filename": "document.pdf",
"contentType": "application/pdf",
"size": 102400,
"resourceUri": "wildduck://attachment/507f1f77bcf86cd799439011/ATT00001",
"publicUrl": "https://mail.example.com/plugin/mcp/att/507f1f77bcf86cd799439011/ATT00001/1734567890/1a2B3c4D5e6F7g8H/document.pdf"
}]
}配置
[mcp.api]
# Public URL is optional - by default URLs are generated from request headers
# Only set this if behind a proxy that doesn't forward proper headers
# publicUrl = "https://mail.example.com"
[mcp.mcp]
# IMPORTANT: Use a secure random string in production!
attachmentSecret = "your-secure-random-string-here"安全功能
- 限时访问(默认1小时后过期)
- 加密签名的URL可防止篡改
- 验证不需要数据库查找
- 可以通过电子邮件、聊天等安全共享。
- 自动文件名清理
安全考虑
访问控制
- 用户隔离:每个用户只能访问自己的数据
- 所有权验证:每个操作在继续之前都会验证资源所有权
- 无跨用户访问:尝试访问其他用户的邮箱或邮件将失败
- 账户状态:已禁用或已挂起的帐户被阻止访问API
最佳实践
- 在生产环境中始终使用HTTPS来保护访问令牌
- 适当配置CORS源以防止未经授权的访问
- 对MCP客户端使用基于URL的身份验证(URL路径中的令牌)
- 实施限速以防止滥用
- 监控访问日志中的可疑活动
- 定期轮换访问令牌
- 从不在用户之间共享访问令牌
令牌安全
- WildDuck访问令牌具有可配置的TTL(生存时间)
- 每次请求时都会验证令牌
- 无效或过期的令牌被拒绝
- 令牌验证包括签名验证
许可证
麻省理工学院
