Skip to main content
Glama
shuakami

Mail MCP Tool

by shuakami

searchEmails

Find emails in your inbox using keywords, date ranges, sender/recipient filters, and attachment criteria to locate specific messages quickly.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
keywordsNo
foldersNo
startDateNo
endDateNo
fromNo
toNo
subjectNo
hasAttachmentNo
maxResultsNo
includeBodyNo

Implementation Reference

  • Registration of the 'searchEmails' MCP tool, including Zod input schema and the handler function that performs advanced email search by calling mailService.advancedSearchMails and formats results.
    this.server.tool(
      "searchEmails",
      {
        keywords: z.string().optional(),
        folders: z.array(z.string()).optional(),
        startDate: z.union([z.date(), z.string().datetime({ message: "startDate 必须是有效的 ISO 8601 日期时间字符串或 Date 对象" })]).optional(),
        endDate: z.union([z.date(), z.string().datetime({ message: "endDate 必须是有效的 ISO 8601 日期时间字符串或 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 startDate = typeof params.startDate === 'string' ? new Date(params.startDate) : params.startDate;
          const endDate = typeof params.endDate === 'string' ? new Date(params.endDate) : params.endDate;
    
          const emails = await this.mailService.advancedSearchMails({
            folders: params.folders,
            keywords: params.keywords,
            startDate: startDate,
            endDate: 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 (startDate) searchTerms.push(`开始日期${startDate.toLocaleDateString()}`);
          if (endDate) searchTerms.push(`结束日期${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)}` }
            ]
          };
        }
      }
    );
  • The handler function for the searchEmails tool, which invokes the mail service's advanced search and returns human-readable results.
      async (params) => {
        try {
          console.log(`开始执行高级邮件搜索,关键词: ${params.keywords || '无'}`);
          
          // 处理日期字符串
          const startDate = typeof params.startDate === 'string' ? new Date(params.startDate) : params.startDate;
          const endDate = typeof params.endDate === 'string' ? new Date(params.endDate) : params.endDate;
    
          const emails = await this.mailService.advancedSearchMails({
            folders: params.folders,
            keywords: params.keywords,
            startDate: startDate,
            endDate: 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 (startDate) searchTerms.push(`开始日期${startDate.toLocaleDateString()}`);
          if (endDate) searchTerms.push(`结束日期${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)}` }
            ]
          };
        }
      }
    );
  • Zod schema defining input parameters for the searchEmails tool (keywords, folders, dates, from/to/subject filters, etc.).
      keywords: z.string().optional(),
      folders: z.array(z.string()).optional(),
      startDate: z.union([z.date(), z.string().datetime({ message: "startDate 必须是有效的 ISO 8601 日期时间字符串或 Date 对象" })]).optional(),
      endDate: z.union([z.date(), z.string().datetime({ message: "endDate 必须是有效的 ISO 8601 日期时间字符串或 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)
    },
  • Helper method advancedSearchMails in MailService class that implements the core IMAP search logic across multiple folders with advanced filtering and keyword matching.
    async advancedSearchMails(options: {
      folders?: string[];        // 要搜索的文件夹列表,默认为INBOX
      keywords?: string;         // 全文搜索关键词
      startDate?: Date;          // 开始日期
      endDate?: Date;            // 结束日期
      from?: string;             // 发件人
      to?: string;               // 收件人
      subject?: string;          // 主题
      hasAttachment?: boolean;   // 是否有附件
      maxResults?: number;       // 最大结果数
      includeBody?: boolean;     // 是否包含邮件正文
    }): Promise<MailItem[]> {
      const allResults: MailItem[] = [];
      const folders = options.folders || ['INBOX'];
      const maxResults = options.maxResults || 100;
      
      console.log(`执行高级搜索,文件夹: ${folders.join(', ')}, 关键词: ${options.keywords || '无'}`);
      
      // 对每个文件夹执行搜索
      for (const folder of folders) {
        if (allResults.length >= maxResults) break;
        
        try {
          const folderResults = await this.searchMails({
            folder,
            readStatus: 'all',
            fromDate: options.startDate,
            toDate: options.endDate,
            from: options.from,
            to: options.to,
            subject: options.subject,
            hasAttachments: options.hasAttachment,
            limit: maxResults - allResults.length
          });
          
          // 如果包含关键词,执行全文匹配
          if (options.keywords && options.keywords.trim() !== '') {
            const keywordLower = options.keywords.toLowerCase();
            const filteredResults = folderResults.filter(mail => {
              // 在主题、发件人、收件人中搜索
              const subjectMatch = mail.subject.toLowerCase().includes(keywordLower);
              const fromMatch = mail.from.some(f => 
                (f.name?.toLowerCase() || '').includes(keywordLower) || 
                f.address.toLowerCase().includes(keywordLower)
              );
              const toMatch = mail.to.some(t => 
                (t.name?.toLowerCase() || '').includes(keywordLower) || 
                t.address.toLowerCase().includes(keywordLower)
              );
              
              // 如果需要在正文中搜索,可能需要额外获取邮件详情
              let bodyMatch = false;
              if (options.includeBody) {
                bodyMatch = (mail.textBody?.toLowerCase() || '').includes(keywordLower) ||
                           (mail.htmlBody?.toLowerCase() || '').includes(keywordLower);
              }
              
              return subjectMatch || fromMatch || toMatch || bodyMatch;
            });
            
            allResults.push(...filteredResults);
          } else {
            allResults.push(...folderResults);
          }
        } catch (error) {
          console.error(`搜索文件夹 ${folder} 时出错:`, error);
          // 继续搜索其他文件夹
        }
      }
      
      // 按日期降序排序(最新的邮件优先)
      allResults.sort((a, b) => b.date.getTime() - a.date.getTime());
      
      // 限制结果数量
      return allResults.slice(0, maxResults);
    }
Behavior1/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Tool has no description.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness1/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Tool has no description.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness1/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Tool has no description.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Tool has no description.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose1/5

Does the description clearly state what the tool does and how it differs from similar tools?

Tool has no description.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines1/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Tool has no description.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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