File size: 3,038 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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import type { FastMCP } from 'fastmcp';
import { UserError } from 'fastmcp';
import { z } from 'zod';
import { getGmailClient } from '../../clients.js';
import { findHeaderValue } from './helpers.js';

export function register(server: FastMCP) {
  server.addTool({
    name: 'listDrafts',
    description:
      'Lists Gmail drafts for the authenticated user. Returns draft IDs along with the recipient, subject, snippet, and date for each. Use sendDraft, updateDraft, or deleteDraft to act on a returned draft.',
    parameters: z.strictObject({
      maxResults: z
        .number()
        .int()
        .min(1)
        .max(100)
        .optional()
        .default(25)
        .describe('Maximum number of drafts to return (1-100). Defaults to 25.'),
      q: z
        .string()
        .optional()
        .describe('Optional Gmail search query to filter drafts (e.g. "subject:proposal").'),
    }),
    execute: async (args, { log }) => {
      const gmail = await getGmailClient();
      log.info(`Listing Gmail drafts (max=${args.maxResults})`);

      try {
        const listResponse = await gmail.users.drafts.list({
          userId: 'me',
          maxResults: args.maxResults,
          q: args.q,
        });

        const draftRefs = listResponse.data.drafts ?? [];
        if (draftRefs.length === 0) {
          return JSON.stringify(
            {
              drafts: [],
              resultSizeEstimate: listResponse.data.resultSizeEstimate ?? 0,
              nextPageToken: listResponse.data.nextPageToken ?? null,
            },
            null,
            2
          );
        }

        const detailed = await Promise.all(
          draftRefs.map((ref) =>
            gmail.users.drafts.get({
              userId: 'me',
              id: ref.id!,
              format: 'metadata',
            })
          )
        );

        const drafts = detailed.map((response) => {
          const draft = response.data;
          const msg = draft.message;
          const headers = msg?.payload?.headers;
          return {
            draftId: draft.id,
            messageId: msg?.id,
            threadId: msg?.threadId,
            snippet: msg?.snippet ?? '',
            to: findHeaderValue(headers, 'To'),
            cc: findHeaderValue(headers, 'Cc'),
            subject: findHeaderValue(headers, 'Subject'),
            date: findHeaderValue(headers, 'Date'),
          };
        });

        return JSON.stringify(
          {
            drafts,
            resultSizeEstimate: listResponse.data.resultSizeEstimate ?? drafts.length,
            nextPageToken: listResponse.data.nextPageToken ?? null,
          },
          null,
          2
        );
      } catch (error: any) {
        log.error(`Error listing drafts: ${error.message || error}`);
        if (error.code === 403)
          throw new UserError('Permission denied. Confirm the gmail.modify scope was granted.');
        throw new UserError(`Failed to list drafts: ${error.message || 'Unknown error'}`);
      }
    },
  });
}