File size: 2,888 Bytes
7dc28be
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
import type { FastMCP } from 'fastmcp';
import { UserError } from 'fastmcp';
import { z } from 'zod';
import { getGmailClient } from '../../clients.js';
import { prepareMimeRequest } from './helpers.js';

export function register(server: FastMCP) {
  server.addTool({
    name: 'sendEmail',
    description:
      'Sends a plain-text email from the authenticated Gmail account. Supports cc/bcc and optional threading by passing replyToMessageId (which copies threadId and sets In-Reply-To/References so the reply lands in the same thread).',
    parameters: z.strictObject({
      to: z
        .union([z.string(), z.array(z.string()).min(1)])
        .describe('Recipient email address, or an array of recipient email addresses.'),
      subject: z.string().describe('Email subject line.'),
      body: z.string().describe('Plain-text body of the email.'),
      cc: z.array(z.string()).optional().describe('Optional list of Cc recipients.'),
      bcc: z.array(z.string()).optional().describe('Optional list of Bcc recipients.'),
      replyToMessageId: z
        .string()
        .optional()
        .describe(
          'Optional Gmail message ID to reply to. When set, the new email is threaded with the original and uses In-Reply-To/References headers.'
        ),
    }),
    execute: async (args, { log }) => {
      const gmail = await getGmailClient();

      try {
        const { raw, threadId, toList } = await prepareMimeRequest(gmail, args);
        log.info(
          `Sending Gmail message to ${toList.join(', ')}${
            args.replyToMessageId ? ` (reply to ${args.replyToMessageId})` : ''
          }`
        );

        const send = await gmail.users.messages.send({
          userId: 'me',
          requestBody: {
            raw,
            ...(threadId ? { threadId } : {}),
          },
        });

        return JSON.stringify(
          {
            success: true,
            id: send.data.id,
            threadId: send.data.threadId,
            labelIds: send.data.labelIds ?? [],
            to: toList,
            subject: args.subject,
            message: `Email sent to ${toList.join(', ')}.`,
          },
          null,
          2
        );
      } catch (error: any) {
        log.error(`Error sending Gmail message: ${error.message || error}`);
        if (error.code === 401)
          throw new UserError(
            'Gmail authorization failed. Re-authorize to grant the gmail.modify scope.'
          );
        if (error.code === 403)
          throw new UserError(
            'Permission denied. The account does not have permission to send mail via this OAuth client.'
          );
        if (error.code === 400)
          throw new UserError(`Gmail rejected the message: ${error.message || 'Bad request'}`);
        throw new UserError(`Failed to send email: ${error.message || 'Unknown error'}`);
      }
    },
  });
}