Skip to main content
Glama
Karopatu

Email Management MCP Server

by Karopatu

Fetch Emails

fetch-emails

Retrieve emails from your inbox using filters for subject, date range, and sender to find specific messages quickly.

Instructions

Get emails from the user's inbox. Can specify the mailbox (INBOX by default), a subject (string), date range (ISO format: YYYY-MM-DDTHH:mm:ss), and sender emails (list of strings) to filter emails.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
mailboxNoMailbox to fetch emails fromINBOX
subjectNoOptional subject to filter emails by subject or sender. Only sent if user provides a subject to search EXPLICITLY!
dateRangeYesDate range is a dictionary with the keys "start" and "end". Must be provided on date time ISO string format.
sendersNoOptional array of email addresses to filter emails by sender

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
errorNo
emailsNo

Implementation Reference

  • Registration of the 'fetch-emails' MCP tool, including schema references and inline handler function.
    server.registerTool(
      "fetch-emails",
      {
        title: "Fetch Emails",
        description:
          "Get emails from the user's inbox. Can specify the mailbox (INBOX by default), a subject (string), date range (ISO format: YYYY-MM-DDTHH:mm:ss), and sender emails (list of strings) to filter emails.",
        inputSchema: FetchEmailsInputSchema.shape,
        outputSchema: FetchEmailsOutputSchema.shape,
      },
      async (params, { requestInfo }) => {
        const authEmail = {
          port: requestInfo?.headers["email-port"],
          email: requestInfo?.headers["email-username"],
          password: requestInfo?.headers["email-password"],
          clientType: requestInfo?.headers["email-client-type"],
        } as AuthEmailType;
    
        const emailClient = new EmailClient(authEmail);
    
        const response = await emailClient.fetchEmails(params);
    
        if (response.error) {
          return {
            isError: true,
            content: [
              {
                type: "text",
                text: response.error,
              },
            ],
          };
        }
    
        const instructionsPromptSource =
          requestInfo?.headers["email-instructions"] ||
          process.env.EMAIL_INSTRUCTIONS ||
          FETCH_EMAILS_PROMPT;
    
        const instructionsPrompt = await getResource(
          instructionsPromptSource as string
        );
    
        const finalResponse = parseResponsePrompt(response, instructionsPrompt);
    
        return {
          content: [
            {
              type: "text",
              text: finalResponse,
            },
          ],
          structuredContent: response,
        };
      }
    );
  • Core handler logic for fetching emails using IMAPFlow, including search criteria, fetching messages, and parsing envelopes.
    async fetchEmails(params: FetchEmailsInputType) {
      const client = this.getClient();
      try {
        await client.connect();
        await client.mailboxOpen(params.mailbox || "INBOX");
    
        const defaultStartDate = DateTime.now().startOf("day");
    
        const defaultEndDate = DateTime.now().endOf("day");
    
        const orConditions: Array<{ from: string }> = [];
    
        if (params.senders) {
          for (const sender of params.senders) {
            orConditions.push({ from: sender });
          }
        }
    
        const localTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
    
        if (params.dateRange.start) {
          params.dateRange.start = params.dateRange.start.replace(
            /Z|GMT-[0-9]+/g,
            ""
          );
          // if no time is provided, set to start of day
          if (!params.dateRange.start.includes("T")) {
            params.dateRange.start += "T00:00:00";
          }
        }
        if (params.dateRange.end) {
          params.dateRange.end = params.dateRange.end.replace(
            /Z|GMT-[0-9]+/g,
            ""
          );
          // if no time is provided, set to end of day
          if (!params.dateRange.end.includes("T")) {
            params.dateRange.end += "T23:59:59";
          }
        }
    
        let sinceDate = params.dateRange.start
          ? DateTime.fromISO(params.dateRange.start)
          : defaultStartDate;
    
        let beforeDate = params.dateRange.end
          ? DateTime.fromISO(params.dateRange.end)
          : defaultEndDate;
    
        if (params.dateRange.start) {
          sinceDate = sinceDate.setZone(localTimezone, { keepLocalTime: true });
        }
        if (params.dateRange.end) {
          beforeDate = beforeDate.setZone(localTimezone, { keepLocalTime: true });
        }
    
        const searchCriteria: SearchObject = {
          subject: params.subject,
          since: sinceDate.toJSDate(),
          before: beforeDate.toJSDate(),
          or: orConditions.length > 0 ? orConditions : undefined,
        };
    
        Logger.debug(
          "Searching emails with criteria:",
          JSON.stringify(searchCriteria, null, 2)
        );
    
        const messagesUIDs = await client.search(searchCriteria, {
          uid: true,
        });
    
        if (!messagesUIDs) {
          return {
            emails: [],
            error: "No emails found matching the criteria.",
          };
        }
    
        Logger.debug(`Found ${messagesUIDs.length} emails matching the criteria`);
    
        const emailsParsed: FetchEmailsOutputType["emails"] = [];
    
        for (const messageUID of messagesUIDs) {
          const message = await client.fetchOne(
            messageUID,
            {
              uid: true,
              envelope: true,
              bodyStructure: true,
            },
            {
              uid: true,
            }
          );
    
          if (!message) {
            continue;
          }
    
          if (message.envelope?.date) {
            const emailDate = DateTime.fromJSDate(message.envelope.date);
    
            const isInRange = emailDate >= sinceDate && emailDate < beforeDate;
            if (!isInRange) {
              continue;
            }
          }
    
          const senderParsed =
            message.envelope?.sender
              ?.map((s) => {
                const { address, name } = s;
                return name ? `${name} <${address}>` : address;
              })
              .join(", ") || "Unknown Sender";
    
          emailsParsed.push({
            id: messageUID,
            subject: message.envelope?.subject || "No Subject",
            sender: senderParsed,
            snippet: "",
            date: message.envelope?.date
              ? DateTime.fromJSDate(message.envelope.date).toISO()
              : undefined,
          });
        }
    
        Logger.info(`Parsed ${emailsParsed.length} emails successfully`);
    
        return {
          emails: emailsParsed,
        };
      } catch (error) {
        Logger.error(
          "Error fetching emails:",
          error instanceof Error ? error.message : "Unknown error"
        );
        return { emails: [], error: "Failed to fetch emails" };
      } finally {
        await client.mailboxClose();
        await client.logout();
      }
    }
  • Zod schema for input parameters of the fetch-emails tool.
    export const FetchEmailsInputSchema = z.object({
      mailbox: z.string().default("INBOX").describe("Mailbox to fetch emails from"),
      subject: z
        .string()
        .optional()
        .describe(
          "Optional subject to filter emails by subject or sender. Only sent if user provides a subject to search EXPLICITLY!"
        ),
      dateRange: z
        .object({
          start: z.string().optional().describe("Start date of the range"),
          end: z.string().optional().describe("End date of the range"),
        })
        .describe(
          'Date range is a dictionary with the keys "start" and "end". Must be provided on date time ISO string format.'
        ),
      senders: z
        .array(z.string())
        .optional()
        .describe("Optional array of email addresses to filter emails by sender"),
    });
  • Zod schema for output of the fetch-emails tool.
    export const FetchEmailsOutputSchema = z.object({
      emails: z
        .array(
          z.object({
            id: z.number(),
            subject: z.string(),
            sender: z.string(),
            snippet: z.string(),
            date: z.string().nullish(),
          })
        )
        .nullish(),
      error: z.string().nullish(),
    });
    export type FetchEmailsOutputType = z.infer<typeof FetchEmailsOutputSchema>;
  • Helper function to parse and format the email response into the prompt template.
    const parseResponsePrompt = (
      response: FetchEmailsOutputType,
      prompt: string
    ) => {
      if (!response.emails || response.emails.length === 0) {
        return prompt.replace("{{emails}}", "[]");
      }
    
      const emailsContent = prompt.replace(
        "{{emails}}",
        JSON.stringify(response.emails)
      );
    
      return emailsContent;
    };
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. It mentions filtering capabilities and default values, but doesn't cover important aspects like pagination behavior, rate limits, authentication requirements, error handling, or what the output contains. For a tool with 4 parameters and an output schema, this leaves significant behavioral gaps.

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

Conciseness4/5

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

The description is appropriately concise - a single sentence that efficiently communicates the core functionality and key parameters. It's front-loaded with the main purpose and doesn't contain unnecessary information. However, it could be slightly more structured by separating the purpose from parameter details.

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?

Given the tool has an output schema (which handles return values) and 100% schema description coverage, the description is reasonably complete for basic understanding. However, for a tool with 4 parameters including a nested object and no annotations, it should provide more behavioral context about how the filtering works, what happens when no emails match criteria, and typical usage patterns.

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?

Schema description coverage is 100%, so the schema already documents all parameters thoroughly. The description adds minimal value beyond the schema - it mentions the default for 'mailbox' and format for 'dateRange', but these are already covered in the schema. The description's mention of filtering logic is somewhat helpful but doesn't significantly enhance understanding beyond what the schema provides.

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 verb ('Get') and resource ('emails from the user's inbox'), making the purpose evident. It distinguishes from the sibling 'send-email' by being a read operation versus a write operation. However, it doesn't explicitly differentiate beyond that basic read/write distinction.

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?

No guidance is provided on when to use this tool versus alternatives. The description mentions filtering capabilities but doesn't specify scenarios where filtering is appropriate or when other tools might be better suited. There's no mention of prerequisites, limitations, or typical use cases.

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/Karopatu/email-management-mcp'

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