Skip to main content
Glama
owen-nash

Fastmail MCP Server

by owen-nash

reply_email

Reply to an email with proper threading headers. Automatically fetches the original message to build the reply chain. Optionally save as draft or send immediately.

Instructions

Reply to an existing email with proper threading headers (In-Reply-To, References). Automatically fetches the original email to build the reply chain. By default sends immediately; set send=false to save as a draft instead.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
originalEmailIdYesID of the email to reply to
toNoRecipient email addresses (optional, defaults to the original sender)
ccNoCC email addresses (optional)
bccNoBCC email addresses (optional)
fromNoSender email address (optional, defaults to account primary email)
textBodyNoPlain text body (optional)
htmlBodyNoHTML body (optional)
sendNoWhether to send the reply immediately (default: true). Set to false to save as draft instead.
replyToNoReply-To email addresses (replies go here instead of to the sender)

Implementation Reference

  • src/index.ts:243-292 (registration)
    Tool registration for 'reply_email' in ListToolsRequestSchema handler: defines name, description, and inputSchema (with properties: originalEmailId, to, cc, bcc, from, textBody, htmlBody, send, replyTo)
    {
      name: 'reply_email',
      description: 'Reply to an existing email with proper threading headers (In-Reply-To, References). Automatically fetches the original email to build the reply chain. By default sends immediately; set send=false to save as a draft instead.',
      inputSchema: {
        type: 'object',
        properties: {
          originalEmailId: {
            type: 'string',
            description: 'ID of the email to reply to',
          },
          to: {
            type: 'array',
            items: { type: 'string' },
            description: 'Recipient email addresses (optional, defaults to the original sender)',
          },
          cc: {
            type: 'array',
            items: { type: 'string' },
            description: 'CC email addresses (optional)',
          },
          bcc: {
            type: 'array',
            items: { type: 'string' },
            description: 'BCC email addresses (optional)',
          },
          from: {
            type: 'string',
            description: 'Sender email address (optional, defaults to account primary email)',
          },
          textBody: {
            type: 'string',
            description: 'Plain text body (optional)',
          },
          htmlBody: {
            type: 'string',
            description: 'HTML body (optional)',
          },
          send: {
            type: ['boolean', 'string'],
            description: 'Whether to send the reply immediately (default: true). Set to false to save as draft instead.',
          },
          replyTo: {
            type: 'array',
            items: { type: 'string' },
            description: 'Reply-To email addresses (replies go here instead of to the sender)',
          },
        },
        required: ['originalEmailId'],
      },
    },
  • Handler for 'reply_email' in CallToolRequestSchema switch statement: fetches original email, builds threading headers (In-Reply-To, References), prepends 'Re:' to subject, defaults recipients to original sender, then either creates a draft (if send=false) or sends the reply email via JmapClient.
    case 'reply_email': {
      const { originalEmailId, to, cc, bcc, from, textBody, htmlBody, send, replyTo } = args as any;
      const shouldSend = coerceBool(send) ?? true;
      if (!originalEmailId) {
        throw new McpError(ErrorCode.InvalidParams, 'originalEmailId is required');
      }
      if (shouldSend && !textBody && !htmlBody) {
        throw new McpError(ErrorCode.InvalidParams, 'Either textBody or htmlBody is required');
      }
    
      // Fetch the original email to get threading headers
      const originalEmail = await client.getEmailById(originalEmailId);
    
      // Build threading headers
      const originalMessageId = originalEmail.messageId?.[0];
      if (!originalMessageId) {
        throw new McpError(ErrorCode.InternalError, 'Original email does not have a Message-ID; cannot thread reply');
      }
    
      const inReplyToHeader = [originalMessageId];
      const referencesHeader = [
        ...(originalEmail.references || []),
        originalMessageId,
      ];
    
      // Build subject with Re: prefix
      let replySubject = originalEmail.subject || '';
      if (!/^Re:/i.test(replySubject)) {
        replySubject = `Re: ${replySubject}`;
      }
    
      // Default recipients to the original sender
      const toArray = coerceStringArray(to);
      const replyRecipients = (toArray && toArray.length > 0)
        ? toArray
        : (Array.isArray(originalEmail.from) ? originalEmail.from.map((addr: any) => addr.email).filter(Boolean) : []);
    
      if (replyRecipients.length === 0) {
        throw new McpError(ErrorCode.InvalidParams, 'Could not determine reply recipient. Please provide "to" explicitly.');
      }
    
      const replyParams = {
        to: replyRecipients,
        cc,
        bcc,
        from,
        subject: replySubject,
        textBody,
        htmlBody,
        inReplyTo: inReplyToHeader,
        references: referencesHeader,
        replyTo,
      };
    
      if (!shouldSend) {
        const emailId = await client.createDraft(replyParams);
        return {
          content: [
            {
              type: 'text',
              text: `Reply draft saved successfully (Email ID: ${emailId}). Subject: ${replySubject}`,
            },
          ],
        };
      }
    
      const submissionId = await client.sendEmail(replyParams);
    
      return {
        content: [
          {
            type: 'text',
            text: `Reply sent successfully. Submission ID: ${submissionId}`,
          },
        ],
      };
    }
  • Input schema for reply_email: defines originalEmailId (required), to, cc, bcc, from, textBody, htmlBody, send (default true), and replyTo fields.
      properties: {
        originalEmailId: {
          type: 'string',
          description: 'ID of the email to reply to',
        },
        to: {
          type: 'array',
          items: { type: 'string' },
          description: 'Recipient email addresses (optional, defaults to the original sender)',
        },
        cc: {
          type: 'array',
          items: { type: 'string' },
          description: 'CC email addresses (optional)',
        },
        bcc: {
          type: 'array',
          items: { type: 'string' },
          description: 'BCC email addresses (optional)',
        },
        from: {
          type: 'string',
          description: 'Sender email address (optional, defaults to account primary email)',
        },
        textBody: {
          type: 'string',
          description: 'Plain text body (optional)',
        },
        htmlBody: {
          type: 'string',
          description: 'HTML body (optional)',
        },
        send: {
          type: ['boolean', 'string'],
          description: 'Whether to send the reply immediately (default: true). Set to false to save as draft instead.',
        },
        replyTo: {
          type: 'array',
          items: { type: 'string' },
          description: 'Reply-To email addresses (replies go here instead of to the sender)',
        },
      },
      required: ['originalEmailId'],
    },
Behavior4/5

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

Discloses automatic fetching of original email and threading header construction. No annotations exist, so description carries full burden; adequately covers key behaviors for a reply action.

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?

Two concise sentences with no extraneous information; every part adds value.

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

Completeness4/5

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

Covers core functionality and key parameter (send) well. No output schema exists, but description doesn't describe return value; acceptable for a straightforward reply tool.

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

Parameters4/5

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

Schema coverage is 100%, but description adds value by stating defaults (e.g., to defaults to original sender) and explaining send parameter behavior beyond schema type descriptions.

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

Purpose5/5

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

The description clearly states the tool's purpose: 'Reply to an existing email with proper threading headers', distinguishing it from siblings like send_email or create_draft.

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

Usage Guidelines4/5

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

Provides clear guidance on the send parameter: 'By default sends immediately; set send=false to save as a draft instead.' Implicitly differentiates from send_email and create_draft, but lacks explicit when-not-to-use instructions.

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/owen-nash/fastmail-mcp'

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