Skip to main content
Glama

reply_email

Reply to email threads with markdown formatting, automatically handling threading headers and supporting optional CC recipients.

Instructions

Reply to an email in its existing thread. Threading headers (In-Reply-To, References) are set automatically. The body is written in markdown. If the mailbox is in read_only mode, this returns a 403 error with upgrade instructions. If the mailbox uses gated oversight, the response status will be 'pending_approval' — the reply is queued for human review. Do not retry or resend when you see pending_approval.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
email_idYesThe email ID to reply to
markdownYesReply body in markdown format
ccNoCC email addresses
mailbox_idNoMailbox ID (uses MULTIMAIL_MAILBOX_ID env var if not provided)

Implementation Reference

  • The main handler function that executes the reply_email tool logic. It gets the mailbox ID, prepares the request body with markdown content and optional CC recipients, calls the API endpoint to reply to an email, and returns the response as JSON.
    async ({ email_id, markdown, cc, mailbox_id }) => {
      const id = getMailboxId(mailbox_id);
      const body: Record<string, unknown> = { markdown };
      if (cc?.length) body.cc = cc;
      const data = await apiCall("POST", `/v1/mailboxes/${encodeURIComponent(id)}/reply/${encodeURIComponent(email_id)}`, body);
      return { content: [{ type: "text" as const, text: JSON.stringify(data, null, 2) }] };
    }
  • Zod schema defining the input validation for the reply_email tool. Specifies required parameters (email_id, markdown) and optional parameters (cc, mailbox_id) with their types and descriptions.
    {
      email_id: z.string().describe("The email ID to reply to"),
      markdown: z.string().describe("Reply body in markdown format"),
      cc: z.array(z.string().email()).optional().describe("CC email addresses"),
      mailbox_id: z.string().optional().describe("Mailbox ID (uses MULTIMAIL_MAILBOX_ID env var if not provided)"),
    },
  • src/index.ts:151-168 (registration)
    Complete registration of the reply_email tool using server.tool(). Includes the tool name, description, schema, and handler function.
    // Tool 5: reply_email
    server.tool(
      "reply_email",
      "Reply to an email in its existing thread. Threading headers (In-Reply-To, References) are set automatically. The body is written in markdown. If the mailbox is in read_only mode, this returns a 403 error with upgrade instructions. If the mailbox uses gated oversight, the response status will be 'pending_approval' — the reply is queued for human review. Do not retry or resend when you see pending_approval.",
      {
        email_id: z.string().describe("The email ID to reply to"),
        markdown: z.string().describe("Reply body in markdown format"),
        cc: z.array(z.string().email()).optional().describe("CC email addresses"),
        mailbox_id: z.string().optional().describe("Mailbox ID (uses MULTIMAIL_MAILBOX_ID env var if not provided)"),
      },
      async ({ email_id, markdown, cc, mailbox_id }) => {
        const id = getMailboxId(mailbox_id);
        const body: Record<string, unknown> = { markdown };
        if (cc?.length) body.cc = cc;
        const data = await apiCall("POST", `/v1/mailboxes/${encodeURIComponent(id)}/reply/${encodeURIComponent(email_id)}`, body);
        return { content: [{ type: "text" as const, text: JSON.stringify(data, null, 2) }] };
      }
    );
  • Helper function apiCall used by the reply_email handler to make HTTP requests to the MultiMail API with proper authentication and error handling.
    async function apiCall(method: string, path: string, body?: unknown): Promise<unknown> {
      const url = `${BASE_URL}${path}`;
      const headers: Record<string, string> = {
        "Authorization": `Bearer ${API_KEY}`,
        "Content-Type": "application/json",
      };
    
      const res = await fetch(url, {
        method,
        headers,
        body: body ? JSON.stringify(body) : undefined,
      });
    
      const data = await parseResponse(res);
    
      if (!res.ok) {
        if (res.status === 401) {
          throw new Error("Invalid API key. Check MULTIMAIL_API_KEY environment variable.");
        }
        if (res.status === 403) {
          throw new Error(`API key lacks required scope for this operation. ${data.error || ""}`);
        }
        if (res.status === 429) {
          const retryAfter = res.headers.get("retry-after") || "unknown";
          throw new Error(`Rate limit exceeded. Retry after ${retryAfter} seconds.`);
        }
        throw new Error(`API error ${res.status}: ${data.error || JSON.stringify(data)}`);
      }
    
      return data;
    }
  • Helper function getMailboxId used by the reply_email handler to resolve the mailbox ID from either the arguments or the MULTIMAIL_MAILBOX_ID environment variable.
    function getMailboxId(argsMailboxId?: string): string {
      const id = argsMailboxId || DEFAULT_MAILBOX_ID;
      if (!id) {
        throw new Error(
          "No mailbox_id provided and MULTIMAIL_MAILBOX_ID is not set. " +
          "Either pass mailbox_id or set the MULTIMAIL_MAILBOX_ID environment variable. " +
          "Use list_mailboxes to discover available mailboxes."
        );
      }
      return id;
    }

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/multimail-dev/multi-mail'

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