Skip to main content
Glama

getEmailDetail

Retrieve detailed content from a specific email message by providing its unique identifier and folder location, enabling AI assistants to access and process email information for various operations.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
uidYes
folderNoINBOX
contentRangeNo

Implementation Reference

  • Registration of the getEmailDetail MCP tool, including input schema and handler function that delegates to MailService.getMailDetail and formats the response for the user.
    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)}` } ] }; } } );
  • Zod schema for getEmailDetail tool inputs: uid (required number), folder (string default 'INBOX'), optional contentRange for partial content fetching.
    { uid: z.number(), folder: z.string().default('INBOX'), contentRange: z.object({ start: z.number().default(0), end: z.number().default(2000) }).optional() },
  • The executing handler logic for getEmailDetail: fetches email via MailService, handles fallback search, formats details and body content with optional range, returns formatted text.
    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)}` } ] }; } }
  • Core helper method MailService.getMailDetail that performs IMAP fetch and parsing to retrieve full email details including body and attachments.
    async getMailDetail(uid: number | string, folder: string = 'INBOX'): Promise<MailItem | null> { await this.connectImap(); // 确保 uid 为数字类型 const numericUid = typeof uid === 'string' ? parseInt(uid, 10) : uid; return new Promise((resolve, reject) => { this.imapClient.openBox(folder, false, (err) => { if (err) { reject(err); return; } const fetch = this.imapClient.fetch([numericUid], { bodies: '', struct: true, markSeen: false, }); let mailItem: MailItem | null = null; let attributes: any = null; let bodyParsed = false; let endReceived = false; // 检查是否所有处理都已完成并可以返回结果 const checkAndResolve = () => { if (bodyParsed && endReceived) { // 如果有属性数据但mailItem还没设置上,则现在设置 if (attributes && mailItem) { mailItem.flags = attributes.flags; mailItem.isRead = attributes.flags.includes('\\Seen'); mailItem.size = attributes.size || 0; } resolve(mailItem); } }; fetch.on('message', (msg) => { msg.on('body', (stream) => { // 创建一个可读流缓冲区 let buffer = ''; stream.on('data', (chunk) => { buffer += chunk.toString('utf8'); }); stream.once('end', () => { // 使用simpleParser解析邮件内容 const readable = new Readable(); readable.push(buffer); readable.push(null); simpleParser(readable).then((parsed: ParsedMail) => { // 处理发件人信息 const from: EmailAddress[] = []; if (parsed.from && 'value' in parsed.from) { from.push(...(parsed.from.value.map(addr => ({ name: addr.name || undefined, address: addr.address || '', })))); } // 处理收件人信息 const to: EmailAddress[] = []; if (parsed.to && 'value' in parsed.to) { to.push(...(parsed.to.value.map(addr => ({ name: addr.name || undefined, address: addr.address || '', })))); } // 处理抄送人信息 const cc: EmailAddress[] = []; if (parsed.cc && 'value' in parsed.cc) { cc.push(...(parsed.cc.value.map(addr => ({ name: addr.name || undefined, address: addr.address || '', })))); } mailItem = { id: numericUid.toString(), uid: numericUid, subject: parsed.subject || '', from, to, cc: cc.length > 0 ? cc : undefined, date: parsed.date || new Date(), isRead: false, // 将通过attributes更新 hasAttachments: parsed.attachments.length > 0, attachments: parsed.attachments.map(att => ({ filename: att.filename || 'unknown', contentType: att.contentType, size: att.size, })), textBody: parsed.text || undefined, htmlBody: parsed.html || undefined, size: 0, // 将通过attributes更新 folder, }; // 如果已经接收到属性,现在应用它们 if (attributes) { mailItem.flags = attributes.flags; mailItem.isRead = attributes.flags.includes('\\Seen'); mailItem.size = attributes.size || 0; } bodyParsed = true; checkAndResolve(); }).catch(err => { console.error('解析邮件详情错误:', err); reject(err); }); }); }); msg.once('attributes', (attrs) => { attributes = attrs; if (mailItem) { mailItem.flags = attrs.flags; mailItem.isRead = attrs.flags.includes('\\Seen'); mailItem.size = attrs.size || 0; } }); }); fetch.once('error', (err) => { reject(err); }); fetch.once('end', () => { endReceived = true; // 如果邮件没有内容,或者处理过程中出现问题,尝试确保至少返回空结果 if (!bodyParsed && !mailItem) { console.log(`没有找到UID为${numericUid}的邮件或邮件内容为空`); } checkAndResolve(); }); }); }); }
  • TypeScript interface MailItem defining the structure of email data returned by getMailDetail.
    export interface MailItem { id: string; uid: number; subject: string; from: { name?: string; address: string }[]; to: { name?: string; address: string }[]; cc?: { name?: string; address: string }[]; date: Date; isRead: boolean; hasAttachments: boolean; attachments?: { filename: string; contentType: string; size: number }[]; textBody?: string; htmlBody?: string; flags?: string[]; size: number; folder: string; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/shuakami/mcp-mail'

If you have feedback or need assistance with the MCP directory API, please join our Discord server