Mail MCP Tool

by shuakami
Verified
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { z } from 'zod'; import { MailService, MailConfig, MailInfo, MailSearchOptions, MailItem } from './mail-service.js'; import path from 'path'; import fs from 'fs'; export class MailMCP { private server: McpServer; private mailService: MailService; constructor() { // 验证环境变量 this.validateEnvironmentVariables(); // 从环境变量加载配置 const config: MailConfig = { smtp: { host: process.env.SMTP_HOST!, port: parseInt(process.env.SMTP_PORT || '587'), secure: process.env.SMTP_SECURE === 'true', auth: { user: process.env.SMTP_USER!, pass: process.env.SMTP_PASS!, } }, imap: { host: process.env.IMAP_HOST!, port: parseInt(process.env.IMAP_PORT || '993'), secure: process.env.IMAP_SECURE === 'true', auth: { user: process.env.IMAP_USER!, pass: process.env.IMAP_PASS!, } }, defaults: { fromName: process.env.DEFAULT_FROM_NAME || process.env.SMTP_USER?.split('@')[0] || '', fromEmail: process.env.DEFAULT_FROM_EMAIL || process.env.SMTP_USER || '', } }; // 初始化邮件服务 this.mailService = new MailService(config); // 初始化MCP服务器 this.server = new McpServer({ name: "mail-mcp", version: "1.0.0" }); // 注册工具 this.registerTools(); // 连接到标准输入/输出 const transport = new StdioServerTransport(); this.server.connect(transport).catch(err => { console.error('连接MCP传输错误:', err); }); } /** * 验证必要的环境变量是否已设置 */ private validateEnvironmentVariables(): void { const requiredVars = [ 'SMTP_HOST', 'SMTP_USER', 'SMTP_PASS', 'IMAP_HOST', 'IMAP_USER', 'IMAP_PASS' ]; const missingVars = requiredVars.filter(varName => !process.env[varName]); if (missingVars.length > 0) { const errorMessage = ` Missing required environment variables: ${missingVars.join('\n')} Please set these variables in your .env file: SMTP_HOST=your.smtp.server SMTP_PORT=587 (or your server port) SMTP_SECURE=true/false SMTP_USER=your.email@domain.com SMTP_PASS=your_password IMAP_HOST=your.imap.server IMAP_PORT=993 (or your server port) IMAP_SECURE=true/false IMAP_USER=your.email@domain.com IMAP_PASS=your_password Optional variables: DEFAULT_FROM_NAME=Your Name DEFAULT_FROM_EMAIL=your.email@domain.com `; console.error(errorMessage); throw new Error('Missing required environment variables'); } // 验证端口号 const smtpPort = parseInt(process.env.SMTP_PORT || '587'); const imapPort = parseInt(process.env.IMAP_PORT || '993'); if (isNaN(smtpPort) || smtpPort <= 0 || smtpPort > 65535) { throw new Error('Invalid SMTP_PORT. Must be a number between 1 and 65535'); } if (isNaN(imapPort) || imapPort <= 0 || imapPort > 65535) { throw new Error('Invalid IMAP_PORT. Must be a number between 1 and 65535'); } } /** * 注册所有MCP工具 */ private registerTools(): void { // 邮件发送相关工具 this.registerSendingTools(); // 邮件接收和查询相关工具 this.registerReceivingTools(); // 邮件文件夹管理工具 this.registerFolderTools(); // 邮件标记工具 this.registerFlagTools(); } /** * 注册邮件发送相关工具 */ private registerSendingTools(): void { // 群发邮件工具 this.server.tool( "sendBulkMail", { to: z.array(z.string()), cc: z.array(z.string()).optional(), bcc: z.array(z.string()).optional(), subject: z.string(), text: z.string().optional(), html: z.string().optional(), attachments: z.array( z.object({ filename: z.string(), content: z.union([z.string(), z.instanceof(Buffer)]), contentType: z.string().optional() }) ).optional() }, async (params) => { try { if (!params.text && !params.html) { return { content: [ { type: "text", text: `邮件内容不能为空,请提供text或html参数。` } ] }; } console.log(`开始群发邮件,收件人数量: ${params.to.length}`); const results = []; let successCount = 0; let failureCount = 0; // 分批发送,每批最多10个收件人 const batchSize = 10; for (let i = 0; i < params.to.length; i += batchSize) { const batch = params.to.slice(i, i + batchSize); try { const result = await this.mailService.sendMail({ to: batch, cc: params.cc, bcc: params.bcc, subject: params.subject, text: params.text, html: params.html, attachments: params.attachments }); results.push(result); if (result.success) { successCount += batch.length; } else { failureCount += batch.length; } // 添加延迟,避免邮件服务器限制 if (i + batchSize < params.to.length) { await new Promise(resolve => setTimeout(resolve, 1000)); } } catch (error) { console.error(`发送批次 ${i / batchSize + 1} 时出错:`, error); failureCount += batch.length; } } return { content: [ { type: "text", text: `群发邮件完成。\n成功: ${successCount}个收件人\n失败: ${failureCount}个收件人\n\n${ failureCount > 0 ? '部分邮件发送失败,可能是由于邮件服务器限制或收件人地址无效。' : '' }` } ] }; } catch (error) { return { content: [ { type: "text", text: `群发邮件时发生错误: ${error instanceof Error ? error.message : String(error)}` } ] }; } } ); this.server.tool( "sendMail", { to: z.string().or(z.array(z.string())), cc: z.string().or(z.array(z.string())).optional(), bcc: z.string().or(z.array(z.string())).optional(), subject: z.string(), text: z.string().optional(), html: z.string().optional(), useHtml: z.boolean().default(false), attachments: z.array( z.object({ filename: z.string(), content: z.union([z.string(), z.instanceof(Buffer)]), contentType: z.string().optional() }) ).optional() }, async (params) => { try { // 检查内容是否提供 if (!params.text && !params.html) { return { content: [ { type: "text", text: `邮件内容不能为空,请提供text或html参数。` } ] }; } // 如果指定使用HTML但没有提供HTML内容,自动转换 if (params.useHtml && !params.html && params.text) { // 简单转换文本为HTML params.html = params.text .split('\n') .map(line => `<p>${line}</p>`) .join(''); } // 处理收件人信息,确保to字段一定存在 const to = typeof params.to === 'string' ? params.to : params.to; const mailInfo: MailInfo = { to: to, subject: params.subject, attachments: params.attachments }; // 处理抄送和密送信息 if (params.cc) { mailInfo.cc = typeof params.cc === 'string' ? params.cc : params.cc; } if (params.bcc) { mailInfo.bcc = typeof params.bcc === 'string' ? params.bcc : params.bcc; } // 设置邮件内容 if (params.html || (params.useHtml && params.text)) { mailInfo.html = params.html || params.text?.split('\n').map(line => `<p>${line}</p>`).join(''); } else { mailInfo.text = params.text; } const result = await this.mailService.sendMail(mailInfo); if (result.success) { return { content: [ { type: "text", text: `邮件发送成功,消息ID: ${result.messageId}\n\n提示:如果需要等待对方回复,可以使用 waitForReply 工具。` } ] }; } else { return { content: [ { type: "text", text: `邮件发送失败: ${result.error}` } ] }; } } catch (error) { return { content: [ { type: "text", text: `发送邮件时发生错误: ${error instanceof Error ? error.message : String(error)}` } ] }; } } ); // 发送简单邮件工具(保留原有实现) this.server.tool( "sendSimpleMail", { to: z.string(), subject: z.string(), body: z.string() }, async ({ to, subject, body }) => { try { const result = await this.mailService.sendMail({ to, subject, text: body }); if (result.success) { return { content: [ { type: "text", text: `简单邮件发送成功,消息ID: ${result.messageId}\n\n提示:如果需要等待对方回复,可以使用 waitForReply 工具。` } ] }; } else { return { content: [ { type: "text", text: `简单邮件发送失败: ${result.error}` } ] }; } } catch (error) { return { content: [ { type: "text", text: `发送简单邮件时发生错误: ${error instanceof Error ? error.message : String(error)}` } ] }; } } ); // 添加专门的HTML邮件发送工具 this.server.tool( "sendHtmlMail", { to: z.string(), cc: z.string().optional(), bcc: z.string().optional(), subject: z.string(), html: z.string(), attachments: z.array( z.object({ filename: z.string(), content: z.union([z.string(), z.instanceof(Buffer)]), contentType: z.string().optional() }) ).optional() }, async (params) => { try { const mailInfo: MailInfo = { to: params.to, subject: params.subject, html: params.html }; if (params.cc) { mailInfo.cc = params.cc; } if (params.bcc) { mailInfo.bcc = params.bcc; } if (params.attachments) { mailInfo.attachments = params.attachments; } const result = await this.mailService.sendMail(mailInfo); if (result.success) { return { content: [ { type: "text", text: `HTML邮件发送成功,消息ID: ${result.messageId}\n\n提示:如果需要等待对方回复,可以使用 waitForReply 工具。` } ] }; } else { return { content: [ { type: "text", text: `HTML邮件发送失败: ${result.error}` } ] }; } } catch (error) { return { content: [ { type: "text", text: `发送HTML邮件时发生错误: ${error instanceof Error ? error.message : String(error)}` } ] }; } } ); } /** * 注册邮件接收和查询相关工具 */ private registerReceivingTools(): void { // 等待新邮件回复 // 此工具用于等待用户的邮件回复。可以多次调用此工具,建议在调用前先检查现有邮件列表。 this.server.tool( "waitForReply", { folder: z.string().default('INBOX'), timeout: z.number().default(3 * 60 * 60 * 1000) }, async ({ folder, timeout }) => { try { const result = await this.mailService.waitForNewReply(folder, timeout); // 如果是未读邮件警告 if (result && typeof result === 'object' && 'type' in result && result.type === 'unread_warning') { let warningText = `⚠️ 检测到${result.mails.length}封最近5分钟内的未读邮件。\n`; warningText += `请先处理(阅读或回复)这些邮件,再继续等待新回复:\n\n`; result.mails.forEach((mail, index) => { const fromStr = mail.from.map(f => f.name ? `${f.name} <${f.address}>` : f.address).join(', '); warningText += `${index + 1}. 主题: ${mail.subject}\n`; warningText += ` 发件人: ${fromStr}\n`; warningText += ` 时间: ${mail.date.toLocaleString()}\n`; warningText += ` UID: ${mail.uid}\n\n`; }); warningText += `提示:\n`; warningText += `1. 使用 markAsRead 工具将邮件标记为已读\n`; warningText += `2. 使用 getEmailDetail 工具查看邮件详情\n`; warningText += `3. 处理完这些邮件后,再次调用 waitForReply 工具等待新回复\n`; return { content: [ { type: "text", text: warningText } ] }; } // 如果超时 if (!result) { return { content: [ { type: "text", text: `等待邮件回复超时(${timeout / 1000}秒)` } ] }; } // 收到新邮件 const email = result as MailItem; // 添加类型断言 const fromStr = email.from.map(f => f.name ? `${f.name} <${f.address}>` : f.address).join(', '); const date = email.date.toLocaleString(); const status = email.isRead ? '已读' : '未读'; const attachmentInfo = email.hasAttachments ? '📎' : ''; let resultText = `收到新邮件!\n\n`; resultText += `[${status}] ${attachmentInfo} 来自: ${fromStr}\n`; resultText += `主题: ${email.subject}\n`; resultText += `时间: ${date}\n`; resultText += `UID: ${email.uid}\n\n`; if (email.textBody) { resultText += `内容:\n${email.textBody}\n\n`; } return { content: [ { type: "text", text: resultText } ] }; } catch (error) { return { content: [ { type: "text", text: `等待邮件回复时发生错误: ${error instanceof Error ? error.message : String(error)}` } ] }; } } ); // 高级邮件搜索 - 支持多文件夹和复杂条件 this.server.tool( "searchEmails", { keywords: z.string().optional(), folders: z.array(z.string()).optional(), startDate: z.date().optional(), endDate: z.date().optional(), from: z.string().optional(), to: z.string().optional(), subject: z.string().optional(), hasAttachment: z.boolean().optional(), maxResults: z.number().default(50), includeBody: z.boolean().default(false) }, async (params) => { try { console.log(`开始执行高级邮件搜索,关键词: ${params.keywords || '无'}`); const emails = await this.mailService.advancedSearchMails({ folders: params.folders, keywords: params.keywords, startDate: params.startDate, endDate: params.endDate, from: params.from, to: params.to, subject: params.subject, hasAttachment: params.hasAttachment, maxResults: params.maxResults, includeBody: params.includeBody }); // 转换为人类可读格式 if (emails.length === 0) { return { content: [ { type: "text", text: `没有找到符合条件的邮件。` } ] }; } const searchTerms = []; if (params.keywords) searchTerms.push(`关键词"${params.keywords}"`); if (params.from) searchTerms.push(`发件人包含"${params.from}"`); if (params.to) searchTerms.push(`收件人包含"${params.to}"`); if (params.subject) searchTerms.push(`主题包含"${params.subject}"`); if (params.startDate) searchTerms.push(`开始日期${params.startDate.toLocaleDateString()}`); if (params.endDate) searchTerms.push(`结束日期${params.endDate.toLocaleDateString()}`); if (params.hasAttachment) searchTerms.push(`包含附件`); const searchDescription = searchTerms.length > 0 ? `搜索条件: ${searchTerms.join(', ')}` : '所有邮件'; let resultText = `🔍 邮件搜索结果 (${emails.length}封邮件)\n${searchDescription}\n\n`; emails.forEach((email, index) => { const fromStr = email.from.map(f => f.name ? `${f.name} <${f.address}>` : f.address).join(', '); const date = email.date.toLocaleString(); const status = email.isRead ? '已读' : '未读'; const attachmentInfo = email.hasAttachments ? '有' : ''; const folder = email.folder; resultText += `${index + 1}. [${status}] ${attachmentInfo} 来自: ${fromStr}\n`; resultText += ` 主题: ${email.subject}\n`; resultText += ` 时间: ${date}\n`; resultText += ` 文件夹: ${folder}\n`; resultText += ` UID: ${email.uid}\n\n`; }); resultText += `使用 getEmailDetail 工具并提供 UID 和 folder 可以查看邮件详情。`; return { content: [ { type: "text", text: resultText } ] }; } catch (error) { return { content: [ { type: "text", text: `搜索邮件时发生错误: ${error instanceof Error ? error.message : String(error)}` } ] }; } } ); // 获取收件箱邮件列表 this.server.tool( "listEmails", { folder: z.string().default('INBOX'), limit: z.number().default(20), readStatus: z.enum(['read', 'unread', 'all']).default('all'), from: z.string().optional(), to: z.string().optional(), subject: z.string().optional(), fromDate: z.date().optional(), toDate: z.date().optional(), hasAttachments: z.boolean().optional() }, async (params) => { try { const options: MailSearchOptions = { folder: params.folder, limit: params.limit, readStatus: params.readStatus, from: params.from, to: params.to, subject: params.subject, fromDate: params.fromDate, toDate: params.toDate, hasAttachments: params.hasAttachments }; const emails = await this.mailService.searchMails(options); // 转换为人类可读格式 if (emails.length === 0) { return { content: [ { type: "text", text: `在${params.folder}文件夹中没有找到符合条件的邮件。` } ] }; } let resultText = `在${params.folder}文件夹中找到了${emails.length}封邮件:\n\n`; emails.forEach((email, index) => { const fromStr = email.from.map(f => f.name ? `${f.name} <${f.address}>` : f.address).join(', '); const date = email.date.toLocaleString(); const status = email.isRead ? '已读' : '未读'; const attachmentInfo = email.hasAttachments ? '📎' : ''; resultText += `${index + 1}. [${status}] ${attachmentInfo} 来自: ${fromStr}\n`; resultText += ` 主题: ${email.subject}\n`; resultText += ` 时间: ${date}\n`; resultText += ` UID: ${email.uid}\n\n`; }); resultText += `使用 getEmailDetail 工具并提供 UID 可以查看邮件详情。`; return { content: [ { type: "text", text: resultText } ] }; } catch (error) { return { content: [ { type: "text", text: `获取邮件列表时发生错误: ${error instanceof Error ? error.message : String(error)}` } ] }; } } ); // 获取通讯录 this.server.tool( "getContacts", { maxResults: z.number().default(50), searchTerm: z.string().optional() }, async (params) => { try { const result = await this.mailService.getContacts({ maxResults: params.maxResults, searchTerm: params.searchTerm }); const contacts = result.contacts; // 转换为人类可读格式 if (contacts.length === 0) { const message = params.searchTerm ? `没有找到包含"${params.searchTerm}"的联系人。` : `没有找到任何联系人。`; return { content: [ { type: "text", text: message } ] }; } const header = params.searchTerm ? `📋 搜索结果: 包含"${params.searchTerm}"的联系人 (${contacts.length}个):\n\n` : `📋 联系人列表 (${contacts.length}个):\n\n`; let resultText = header; contacts.forEach((contact, index) => { const name = contact.name || '(无名称)'; const frequency = contact.frequency; const lastContact = contact.lastContact ? contact.lastContact.toLocaleDateString() : '未知'; resultText += `${index + 1}. ${name} <${contact.email}>\n`; resultText += ` 邮件频率: ${frequency}次\n`; resultText += ` 最后联系: ${lastContact}\n\n`; }); return { content: [ { type: "text", text: resultText } ] }; } catch (error) { return { content: [ { type: "text", text: `获取联系人时发生错误: ${error instanceof Error ? error.message : String(error)}` } ] }; } } ); // 获取邮件详情 this.server.tool( "getEmailDetail", { uid: z.number(), folder: z.string().default('INBOX'), contentRange: z.object({ start: z.number().default(0), end: z.number().default(2000) }).optional() }, async ({ uid, folder, contentRange }) => { try { // 对于QQ邮箱的特殊处理,先尝试获取邮件详情 const numericUid = Number(uid); let email = await this.mailService.getMailDetail(numericUid, folder); // 如果正常获取失败,尝试通过搜索来获取指定UID的邮件 if (!email) { console.log(`通过常规方法获取邮件详情失败,尝试使用搜索方法获取UID为${numericUid}的邮件`); const searchResults = await this.mailService.searchMails({ folder: folder, limit: 50 // 搜索更多邮件以提高找到目标的可能性 }); // 从搜索结果中找到指定UID的邮件 const foundEmail = searchResults.find(e => e.uid === numericUid); if (foundEmail) { console.log(`在搜索结果中找到了UID为${numericUid}的邮件`); email = foundEmail; // 尝试获取邮件正文(如果没有) if (!email.textBody && !email.htmlBody) { console.log(`邮件没有正文内容,尝试单独获取正文`); try { // 这里可以添加额外的尝试获取正文的逻辑 // ... } catch (e) { console.error('获取邮件正文时出错:', e); } } } } if (!email) { return { content: [ { type: "text", text: `未找到UID为${numericUid}的邮件` } ] }; } // 转换为人类可读格式 const fromStr = email.from.map(f => f.name ? `${f.name} <${f.address}>` : f.address).join(', '); const toStr = email.to.map(t => t.name ? `${t.name} <${t.address}>` : t.address).join(', '); const ccStr = email.cc ? email.cc.map(c => c.name ? `${c.name} <${c.address}>` : c.address).join(', ') : ''; const date = email.date.toLocaleString(); const status = email.isRead ? '已读' : '未读'; let resultText = `📧 邮件详情 (UID: ${email.uid})\n\n`; resultText += `主题: ${email.subject}\n`; resultText += `发件人: ${fromStr}\n`; resultText += `收件人: ${toStr}\n`; if (ccStr) resultText += `抄送: ${ccStr}\n`; resultText += `日期: ${date}\n`; resultText += `状态: ${status}\n`; resultText += `文件夹: ${email.folder}\n`; if (email.hasAttachments && email.attachments && email.attachments.length > 0) { resultText += `\n📎 附件 (${email.attachments.length}个):\n`; email.attachments.forEach((att, index) => { const sizeInKB = Math.round(att.size / 1024); resultText += `${index + 1}. ${att.filename} (${sizeInKB} KB, ${att.contentType})\n`; }); } // 获取邮件内容 let content = ''; if (email.textBody) { content = email.textBody; } else if (email.htmlBody) { // 简单的HTML转文本处理 content = '(HTML内容,显示纯文本版本)\n\n' + email.htmlBody .replace(/<br\s*\/?>/gi, '\n') .replace(/<\/p>/gi, '\n\n') .replace(/<[^>]*>/g, ''); } else { content = '(邮件没有文本内容或内容无法获取)\n\n' + '可能原因:\n' + '1. QQ邮箱IMAP访问限制\n' + '2. 邮件内容格式特殊\n' + '建议直接在QQ邮箱网页或客户端查看完整内容'; } // 计算内容总长度 const totalLength = content.length; // 设置默认范围 const start = contentRange?.start || 0; const end = Math.min(contentRange?.end || 2000, totalLength); // 根据范围截取内容 const selectedContent = content.substring(start, end); resultText += `\n📄 内容 (${start+1}-${end}/${totalLength}字符):\n\n`; resultText += selectedContent; // 如果有更多内容,添加提示 if (end < totalLength) { resultText += `\n\n[...]\n\n(内容过长,仅显示前${end}个字符。使用contentRange参数可查看更多内容,例如查看${end+1}-${Math.min(end+2000, totalLength)}范围:contentRange.start=${end}, contentRange.end=${Math.min(end+2000, totalLength)})`; } return { content: [ { type: "text", text: resultText } ] }; } catch (error) { return { content: [ { type: "text", text: `获取邮件详情时发生错误: ${error instanceof Error ? error.message : String(error)}` } ] }; } } ); // 删除邮件 this.server.tool( "deleteEmail", { uid: z.number(), folder: z.string().default('INBOX') }, async ({ uid, folder }) => { try { const numericUid = Number(uid); const success = await this.mailService.deleteMail(numericUid, folder); if (success) { return { content: [ { type: "text", text: `邮件(UID: ${numericUid})已从${folder}文件夹中删除` } ] }; } else { return { content: [ { type: "text", text: `删除邮件(UID: ${numericUid})失败` } ] }; } } catch (error) { return { content: [ { type: "text", text: `删除邮件时发生错误: ${error instanceof Error ? error.message : String(error)}` } ] }; } } ); // 移动邮件到其他文件夹 this.server.tool( "moveEmail", { uid: z.number(), sourceFolder: z.string(), targetFolder: z.string() }, async ({ uid, sourceFolder, targetFolder }) => { try { const numericUid = Number(uid); const success = await this.mailService.moveMail(numericUid, sourceFolder, targetFolder); if (success) { return { content: [ { type: "text", text: `邮件(UID: ${numericUid})已成功从"${sourceFolder}"移动到"${targetFolder}"文件夹` } ] }; } else { return { content: [ { type: "text", text: `移动邮件(UID: ${numericUid})失败` } ] }; } } catch (error) { return { content: [ { type: "text", text: `移动邮件时发生错误: ${error instanceof Error ? error.message : String(error)}` } ] }; } } ); // 添加获取附件工具 this.server.tool( "getAttachment", { uid: z.number(), folder: z.string().default('INBOX'), attachmentIndex: z.number(), saveToFile: z.boolean().default(true) }, async (params) => { try { const attachment = await this.mailService.getAttachment( params.uid, params.folder, params.attachmentIndex ); if (!attachment) { return { content: [ { type: "text", text: `未找到UID为${params.uid}的邮件的第${params.attachmentIndex}个附件` } ] }; } // 根据是否保存到文件处理附件 if (params.saveToFile) { // 创建附件保存目录 const downloadDir = path.join(process.cwd(), 'downloads'); if (!fs.existsSync(downloadDir)) { fs.mkdirSync(downloadDir, { recursive: true }); } // 生成安全的文件名(去除非法字符) const safeFilename = attachment.filename.replace(/[/\\?%*:|"<>]/g, '-'); const filePath = path.join(downloadDir, safeFilename); // 写入文件 fs.writeFileSync(filePath, attachment.content); return { content: [ { type: "text", text: `附件 "${attachment.filename}" 已下载保存至 ${filePath}\n类型: ${attachment.contentType}\n大小: ${Math.round(attachment.content.length / 1024)} KB` } ] }; } else { // 根据内容类型处理内容 if (attachment.contentType.startsWith('text/') || attachment.contentType === 'application/json') { // 文本文件显示内容 const textContent = attachment.content.toString('utf-8'); return { content: [ { type: "text", text: `📎 附件 "${attachment.filename}" (${attachment.contentType})\n\n${textContent.substring(0, 10000)}${textContent.length > 10000 ? '\n\n[内容过长,已截断]' : ''}` } ] }; } else if (attachment.contentType.startsWith('image/')) { // 图片文件提供Base64编码 const base64Content = attachment.content.toString('base64'); return { content: [ { type: "text", text: `📎 图片附件 "${attachment.filename}" (${attachment.contentType})\n大小: ${Math.round(attachment.content.length / 1024)} KB\n\n[图片内容已转为Base64编码,可用于在线预览]` } ] }; } else { // 其他二进制文件 return { content: [ { type: "text", text: `📎 二进制附件 "${attachment.filename}" (${attachment.contentType})\n大小: ${Math.round(attachment.content.length / 1024)} KB\n\n[二进制内容无法直接显示]` } ] }; } } } catch (error) { return { content: [ { type: "text", text: `获取附件时发生错误: ${error instanceof Error ? error.message : String(error)}` } ] }; } } ); } /** * 注册文件夹管理工具 */ private registerFolderTools(): void { // 获取所有邮件文件夹 this.server.tool( "listFolders", { random_string: z.string().optional() }, async () => { try { const folders = await this.mailService.getFolders(); if (folders.length === 0) { return { content: [ { type: "text", text: "没有找到邮件文件夹。" } ] }; } let resultText = `📁 邮件文件夹列表 (${folders.length}个):\n\n`; folders.forEach((folder, index) => { resultText += `${index + 1}. ${folder}\n`; }); return { content: [ { type: "text", text: resultText } ] }; } catch (error) { return { content: [ { type: "text", text: `获取邮件文件夹列表时发生错误: ${error instanceof Error ? error.message : String(error)}` } ] }; } } ); } /** * 注册邮件标记工具 */ private registerFlagTools(): void { // 批量将邮件标记为已读 this.server.tool( "markMultipleAsRead", { uids: z.array(z.number()), folder: z.string().default('INBOX') }, async ({ uids, folder }) => { try { const numericUids = uids.map(uid => Number(uid)); const success = await this.mailService.markMultipleAsRead(numericUids, folder); if (success) { return { content: [ { type: "text", text: `已将 ${uids.length} 封邮件标记为已读` } ] }; } else { return { content: [ { type: "text", text: `批量标记邮件为已读失败` } ] }; } } catch (error) { return { content: [ { type: "text", text: `批量标记邮件为已读时发生错误: ${error instanceof Error ? error.message : String(error)}` } ] }; } } ); // 批量将邮件标记为未读 this.server.tool( "markMultipleAsUnread", { uids: z.array(z.number()), folder: z.string().default('INBOX') }, async ({ uids, folder }) => { try { const numericUids = uids.map(uid => Number(uid)); const success = await this.mailService.markMultipleAsUnread(numericUids, folder); if (success) { return { content: [ { type: "text", text: `已将 ${uids.length} 封邮件标记为未读` } ] }; } else { return { content: [ { type: "text", text: `批量标记邮件为未读失败` } ] }; } } catch (error) { return { content: [ { type: "text", text: `批量标记邮件为未读时发生错误: ${error instanceof Error ? error.message : String(error)}` } ] }; } } ); // 将邮件标记为已读 this.server.tool( "markAsRead", { uid: z.number(), folder: z.string().default('INBOX') }, async ({ uid, folder }) => { try { const numericUid = Number(uid); const success = await this.mailService.markAsRead(numericUid, folder); if (success) { return { content: [ { type: "text", text: `邮件(UID: ${uid})已标记为已读` } ] }; } else { return { content: [ { type: "text", text: `标记邮件(UID: ${uid})为已读失败` } ] }; } } catch (error) { return { content: [ { type: "text", text: `标记邮件为已读时发生错误: ${error instanceof Error ? error.message : String(error)}` } ] }; } } ); // 将邮件标记为未读 this.server.tool( "markAsUnread", { uid: z.number(), folder: z.string().default('INBOX') }, async ({ uid, folder }) => { try { const numericUid = Number(uid); const success = await this.mailService.markAsUnread(numericUid, folder); if (success) { return { content: [ { type: "text", text: `邮件(UID: ${uid})已标记为未读` } ] }; } else { return { content: [ { type: "text", text: `标记邮件(UID: ${uid})为未读失败` } ] }; } } catch (error) { return { content: [ { type: "text", text: `标记邮件为未读时发生错误: ${error instanceof Error ? error.message : String(error)}` } ] }; } } ); } /** * 关闭所有连接 */ async close(): Promise<void> { await this.mailService.close(); } }