Skip to main content
Glama
adamzaidi

icloud-mcp

by adamzaidi

search_emails

Search iCloud Mail by keywords or specific fields like sender, subject, or date with filters for unread status, attachments, and domain.

Instructions

Search emails by keyword or targeted field queries, with optional filters for date, read status, domain, and more

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
queryNoSearch keyword (matches subject, sender, body — use OR across all fields)
subjectQueryNoMatch only in subject field
bodyQueryNoMatch only in body field
fromQueryNoMatch only in from/sender field
queryModeNoHow to combine subjectQuery/bodyQuery/fromQuery: or (default) or and
mailboxNoMailbox to search (default INBOX)
limitNoMax results (default 10)
includeSnippetNoIf true, include a 200-char body preview snippet for each result (max 10 emails)
senderNoMatch exact sender email address
domainNoMatch any sender from this domain (e.g. substack.com)
subjectNoKeyword to match in subject
beforeNoOnly emails before this date (YYYY-MM-DD)
sinceNoOnly emails since this date (YYYY-MM-DD)
unreadNoTrue for unread only, false for read only
flaggedNoTrue for flagged only, false for unflagged only
largerNoOnly emails larger than this size in KB
smallerNoOnly emails smaller than this size in KB
hasAttachmentNoOnly emails with attachments (client-side BODYSTRUCTURE scan — must be combined with other filters that narrow results to under 500 emails first)
accountNoAccount name to use (e.g. 'icloud', 'gmail'). Defaults to first configured account. Use list_accounts to see available accounts.

Implementation Reference

  • The handler function 'searchEmails' implements searching for emails using IMAP based on queries and filters.
    export async function searchEmails(query, mailbox = 'INBOX', limit = 10, filters = {}, options = {}, creds = null) {
      const { queryMode = 'or', subjectQuery, bodyQuery, fromQuery, includeSnippet = false } = options;
      const client = createRateLimitedClient(creds);
      await client.connect();
      await client.mailboxOpen(mailbox);
    
      // Build text query
      let textQuery;
      const targetedParts = [];
      if (subjectQuery) targetedParts.push({ subject: subjectQuery });
      if (bodyQuery) targetedParts.push({ body: bodyQuery });
      if (fromQuery) targetedParts.push({ from: fromQuery });
    
      if (targetedParts.length > 0) {
        // Targeted field queries
        if (queryMode === 'and') {
          textQuery = Object.assign({}, ...targetedParts); // IMAP AND is implicit
        } else {
          textQuery = targetedParts.length === 1 ? targetedParts[0] : { or: targetedParts };
        }
      } else if (query) {
        // Original OR across subject/from/body
        textQuery = { or: [{ subject: query }, { from: query }, { body: query }] };
      } else {
        textQuery = null;
      }
    
      const extraQuery = buildQuery(filters);
      const hasExtra = Object.keys(extraQuery).length > 0 && !extraQuery.all;
      const finalQuery = textQuery
        ? (hasExtra ? { ...textQuery, ...extraQuery } : textQuery)
        : (hasExtra ? extraQuery : { all: true });
    
      let uids = (await client.search(finalQuery, { uid: true })) ?? [];
      if (!Array.isArray(uids)) uids = [];
    
      if (filters.hasAttachment) {
        if (uids.length > ATTACHMENT_SCAN_LIMIT) {
          await client.logout();
          return { total: null, showing: 0, emails: [], error: `hasAttachment requires narrower filters first — ${uids.length} candidates exceeds scan limit of ${ATTACHMENT_SCAN_LIMIT}. Add from/since/before/subject filters to reduce the set.` };
        }
        uids = await filterUidsByAttachment(client, uids);
      }
    
      const emails = [];
      const recentUids = uids.slice(-limit).reverse();
      for (const uid of recentUids) {
        const msg = await client.fetchOne(uid, { envelope: true, flags: true }, { uid: true });
        if (msg) {
          emails.push({
            uid,
            subject: msg.envelope.subject,
            from: msg.envelope.from?.[0]?.address,
            date: msg.envelope.date,
            flagged: msg.flags.has('\\Flagged'),
            seen: msg.flags.has('\\Seen')
          });
        }
      }
    
      // Fetch body snippets if requested (max 10 emails to avoid timeout)
      if (includeSnippet && emails.length > 0) {
        for (const email of emails.slice(0, 10)) {
          try {
            const meta = await client.fetchOne(email.uid, { bodyStructure: true }, { uid: true });
            if (!meta?.bodyStructure) continue;
            const textPart = findTextPart(meta.bodyStructure);
            if (!textPart) continue;
            const imapKey = textPart.partId ?? 'TEXT';
            const partMsg = await client.fetchOne(email.uid, {
              bodyParts: [{ key: imapKey, start: 0, maxLength: 400 }]
            }, { uid: true });
            const buf = partMsg?.bodyParts?.get(imapKey)
              ?? partMsg?.bodyParts?.get(imapKey.toUpperCase())
              ?? partMsg?.bodyParts?.get(imapKey.toLowerCase());
            if (!buf) continue;
            const decoded = decodeTransferEncoding(buf, textPart.encoding);
            let text = await decodeCharset(decoded, textPart.charset);
            if (textPart.type === 'text/html') text = stripHtml(text);
            email.snippet = text.replace(/\s+/g, ' ').slice(0, 200).trim();
          } catch { /* skip snippet on error */ }
        }
      }
    
      await client.logout();
      return { total: uids.length, showing: emails.length, emails };
    }
Behavior2/5

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

With no annotations provided, the description carries the full burden of behavioral disclosure but fails to mention critical traits: pagination behavior, result ordering, rate limits, or the read-only nature of the operation. It does not describe the return format (absent output schema), and omits the performance constraint mentioned in the hasAttachment parameter description regarding the 500-email limit.

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

Conciseness5/5

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

The description is a single, efficiently structured sentence that front-loads the primary action. Every phrase serves a purpose: defining the search modality (keyword vs. targeted) and exemplifying filter categories. Despite the tool's complexity (19 parameters), the description avoids redundancy and wasted words.

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

Completeness3/5

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

While the schema provides complete parameter documentation, the description remains minimal given the tool's complexity and lack of output schema or annotations. The phrase 'and more' hand-waves over 12 additional filter parameters. For a search tool with nuanced query modes (OR/AND) and performance constraints, the description adequately introduces capabilities but leaves significant behavioral gaps.

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

Parameters3/5

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

Given 100% schema description coverage, the baseline score is 3. The description adds marginal value by categorizing parameters conceptually ('keyword', 'targeted field queries', 'date, read status, domain'), which helps map intent to parameters, but does not elaborate on syntax, format details, or the interaction between query and field-specific parameters beyond what the schema already documents.

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

Purpose4/5

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

The description clearly states the core action (Search) and resource (emails), distinguishing between 'keyword' (general) and 'targeted field queries' (specific fields). However, it does not explicitly differentiate this tool from sibling retrieval tools like get_emails_by_sender or get_emails_by_date_range, which could cause selection ambiguity given the large tool set.

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

Usage Guidelines2/5

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

The description provides no guidance on when to use this tool versus the numerous alternatives (e.g., get_emails_by_sender, read_inbox, get_email). It omits prerequisites, performance considerations, and selection criteria, leaving the agent to infer based on parameter names alone.

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/adamzaidi/icloud-mcp'

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