google-tools-mcp 1.2.12 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +537 -276
- package/dist/cachedToolsList.js +52 -52
- package/dist/googleDocsApiHelpers.js +76 -12
- package/dist/helpers.js +572 -252
- package/dist/httpAuth.js +296 -0
- package/dist/index.js +162 -11
- package/dist/logger.js +87 -87
- package/dist/markdown-transformer/docsToMarkdown.js +128 -0
- package/dist/markdown-transformer/index.js +17 -7
- package/dist/markdown-transformer/markdownToDocs.js +185 -27
- package/dist/readTracker.js +136 -17
- package/dist/sessionContext.js +37 -0
- package/dist/setup.js +187 -9
- package/dist/tools/calendar/getBusy.js +64 -64
- package/dist/tools/calendar/getEvents.js +141 -141
- package/dist/tools/calendar/getFree.js +225 -225
- package/dist/tools/calendar/index.js +19 -19
- package/dist/tools/calendar/listCalendars.js +38 -38
- package/dist/tools/calendar/listRecurringInstances.js +83 -83
- package/dist/tools/calendar/manageCalendar.js +121 -121
- package/dist/tools/calendar/manageEvent.js +258 -258
- package/dist/tools/calendar/moveEvent.js +66 -66
- package/dist/tools/docs/addTab.js +10 -12
- package/dist/tools/docs/appendToGoogleDoc.js +9 -4
- package/dist/tools/docs/deleteRange.js +9 -4
- package/dist/tools/docs/findAndReplace.js +9 -4
- package/dist/tools/docs/formatting/applyParagraphStyle.js +4 -1
- package/dist/tools/docs/formatting/getFormatting.js +171 -171
- package/dist/tools/docs/insertImage.js +15 -2
- package/dist/tools/docs/insertPageBreak.js +4 -1
- package/dist/tools/docs/insertTable.js +4 -1
- package/dist/tools/docs/insertTableWithData.js +4 -1
- package/dist/tools/docs/modifyText.js +14 -4
- package/dist/tools/docs/modifyText.test.js +84 -84
- package/dist/tools/docs/readGoogleDoc.js +71 -11
- package/dist/tools/docs/renameTab.js +12 -14
- package/dist/tools/drafts.js +165 -165
- package/dist/tools/drive/createDocument.js +12 -0
- package/dist/tools/drive/downloadFile.js +268 -268
- package/dist/tools/drive/getFileInfo.js +48 -48
- package/dist/tools/drive/getFilePath.js +50 -50
- package/dist/tools/drive/listDriveFiles.js +112 -112
- package/dist/tools/drive/listSharedDrives.js +52 -52
- package/dist/tools/drive/listSharedWithMe.js +97 -97
- package/dist/tools/drive/uploadFile.js +111 -111
- package/dist/tools/extras/index.js +9 -9
- package/dist/tools/extras/readDriveFile.js +197 -196
- package/dist/tools/extras/readFile.js +84 -84
- package/dist/tools/extras/searchFileContents.js +81 -81
- package/dist/tools/forms/batchUpdateForm.js +81 -81
- package/dist/tools/forms/createForm.js +54 -54
- package/dist/tools/forms/getForm.js +118 -118
- package/dist/tools/forms/getFormResponse.js +45 -45
- package/dist/tools/forms/index.js +15 -15
- package/dist/tools/forms/listFormResponses.js +56 -56
- package/dist/tools/forms/setPublishSettings.js +59 -59
- package/dist/tools/gmail/drafts.js +165 -165
- package/dist/tools/gmail/labels.js +54 -83
- package/dist/tools/gmail/messages.js +444 -441
- package/dist/tools/gmail/settings.js +358 -528
- package/dist/tools/gmail/threads.js +286 -164
- package/dist/tools/index.js +511 -473
- package/dist/tools/labels.js +103 -103
- package/dist/tools/legacyAliases.js +426 -0
- package/dist/tools/maps/directions.js +72 -0
- package/dist/tools/maps/geocode.js +16 -0
- package/dist/tools/maps/index.js +15 -0
- package/dist/tools/maps/mapsClient.js +83 -0
- package/dist/tools/maps/placeDetails.js +16 -0
- package/dist/tools/maps/reverseGeocode.js +19 -0
- package/dist/tools/maps/searchNearby.js +54 -0
- package/dist/tools/maps/searchPlaces.js +24 -0
- package/dist/tools/messages.js +448 -448
- package/dist/tools/settings.js +528 -528
- package/dist/tools/slides/createPresentation.js +104 -104
- package/dist/tools/slides/createShape.js +92 -92
- package/dist/tools/slides/createTextBox.js +82 -82
- package/dist/tools/slides/deleteSlide.js +30 -30
- package/dist/tools/slides/duplicateSlide.js +37 -37
- package/dist/tools/slides/exportThumbnail.js +42 -42
- package/dist/tools/slides/formatParagraph.js +72 -72
- package/dist/tools/slides/formatText.js +84 -84
- package/dist/tools/slides/getPresentation.js +87 -87
- package/dist/tools/slides/index.js +33 -33
- package/dist/tools/slides/reorderSlides.js +41 -41
- package/dist/tools/slides/replaceAllText.js +46 -46
- package/dist/tools/slides/setBackground.js +58 -58
- package/dist/tools/slides/speakerNotes.js +102 -102
- package/dist/tools/slides/styleShape.js +111 -111
- package/dist/tools/slides/updatePresentation.js +128 -128
- package/dist/tools/threads.js +145 -145
- package/dist/tools/utils/appendMarkdownToGoogleDoc.js +30 -6
- package/dist/tools/utils/replaceDocumentWithMarkdown.js +71 -8
- package/dist/updateCheck.js +202 -0
- package/dist/workspace.js +122 -0
- package/package.json +82 -81
package/dist/tools/drafts.js
CHANGED
|
@@ -1,165 +1,165 @@
|
|
|
1
|
-
// Gmail Draft tools
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
import { UserError } from 'fastmcp';
|
|
4
|
-
import { getGmailClient } from '../clients.js';
|
|
5
|
-
import { processMessagePart, constructRawMessage, constructRawMessageWithAttachments } from '../helpers.js';
|
|
6
|
-
|
|
7
|
-
export function register(server) {
|
|
8
|
-
server.addTool({
|
|
9
|
-
name: 'create_draft',
|
|
10
|
-
description: 'Create a draft email in Gmail. Note the mechanics of the raw parameter.',
|
|
11
|
-
parameters: z.object({
|
|
12
|
-
raw: z.string().optional().describe("The entire email message in base64url encoded RFC 2822 format, ignores params.to, cc, bcc, subject, body, includeBodyHtml if provided"),
|
|
13
|
-
threadId: z.string().optional().describe("The thread ID to associate this draft with"),
|
|
14
|
-
to: z.array(z.string()).optional().describe("List of recipient email addresses"),
|
|
15
|
-
cc: z.array(z.string()).optional().describe("List of CC recipient email addresses"),
|
|
16
|
-
bcc: z.array(z.string()).optional().describe("List of BCC recipient email addresses"),
|
|
17
|
-
subject: z.string().optional().describe("The subject of the email"),
|
|
18
|
-
body: z.string().optional().describe("The body of the email. Supports plain text or HTML (auto-detected). Use HTML tags like <p>, <br>, <b> for formatted emails."),
|
|
19
|
-
attachments: z.array(z.object({
|
|
20
|
-
filename: z.string().describe("Attachment file name"),
|
|
21
|
-
mimeType: z.string().describe("MIME type of the attachment"),
|
|
22
|
-
base64Data: z.string().describe("Base64 encoded attachment data"),
|
|
23
|
-
})).optional().describe("File attachments to include"),
|
|
24
|
-
includeBodyHtml: z.boolean().optional().describe("Whether to include the parsed HTML in the return for each body"),
|
|
25
|
-
}),
|
|
26
|
-
execute: async (params, { log }) => {
|
|
27
|
-
const gmail = await getGmailClient();
|
|
28
|
-
let raw = params.raw;
|
|
29
|
-
if (!raw) {
|
|
30
|
-
if (params.attachments?.length) {
|
|
31
|
-
raw = await constructRawMessageWithAttachments(gmail, params);
|
|
32
|
-
} else {
|
|
33
|
-
raw = await constructRawMessage(gmail, params);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
const createParams = { userId: 'me', requestBody: { message: { raw } } };
|
|
37
|
-
if (params.threadId && createParams.requestBody?.message) {
|
|
38
|
-
createParams.requestBody.message.threadId = params.threadId;
|
|
39
|
-
}
|
|
40
|
-
const { data } = await gmail.users.drafts.create(createParams);
|
|
41
|
-
if (data.message?.payload) {
|
|
42
|
-
data.message.payload = processMessagePart(data.message.payload, params.includeBodyHtml);
|
|
43
|
-
}
|
|
44
|
-
return JSON.stringify(data);
|
|
45
|
-
},
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
server.addTool({
|
|
49
|
-
name: 'update_draft',
|
|
50
|
-
description: 'Update an existing draft\'s content. Replaces the draft message with new content.',
|
|
51
|
-
parameters: z.object({
|
|
52
|
-
id: z.string().describe("The ID of the draft to update"),
|
|
53
|
-
raw: z.string().optional().describe("The entire email message in base64url encoded RFC 2822 format, ignores other params if provided"),
|
|
54
|
-
threadId: z.string().optional().describe("The thread ID to associate this draft with"),
|
|
55
|
-
to: z.array(z.string()).optional().describe("List of recipient email addresses"),
|
|
56
|
-
cc: z.array(z.string()).optional().describe("List of CC recipient email addresses"),
|
|
57
|
-
bcc: z.array(z.string()).optional().describe("List of BCC recipient email addresses"),
|
|
58
|
-
subject: z.string().optional().describe("The subject of the email"),
|
|
59
|
-
body: z.string().optional().describe("The body of the email. Supports plain text or HTML (auto-detected). Use HTML tags like <p>, <br>, <b> for formatted emails."),
|
|
60
|
-
attachments: z.array(z.object({
|
|
61
|
-
filename: z.string().describe("Attachment file name"),
|
|
62
|
-
mimeType: z.string().describe("MIME type of the attachment"),
|
|
63
|
-
base64Data: z.string().describe("Base64 encoded attachment data"),
|
|
64
|
-
})).optional().describe("File attachments to include"),
|
|
65
|
-
includeBodyHtml: z.boolean().optional().describe("Whether to include the parsed HTML in the return for each body"),
|
|
66
|
-
}),
|
|
67
|
-
execute: async (params) => {
|
|
68
|
-
const gmail = await getGmailClient();
|
|
69
|
-
let raw = params.raw;
|
|
70
|
-
if (!raw) {
|
|
71
|
-
if (params.attachments?.length) {
|
|
72
|
-
raw = await constructRawMessageWithAttachments(gmail, params);
|
|
73
|
-
} else {
|
|
74
|
-
raw = await constructRawMessage(gmail, params);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
const updateParams = { userId: 'me', id: params.id, requestBody: { message: { raw } } };
|
|
78
|
-
if (params.threadId && updateParams.requestBody?.message) {
|
|
79
|
-
updateParams.requestBody.message.threadId = params.threadId;
|
|
80
|
-
}
|
|
81
|
-
const { data } = await gmail.users.drafts.update(updateParams);
|
|
82
|
-
if (data.message?.payload) {
|
|
83
|
-
data.message.payload = processMessagePart(data.message.payload, params.includeBodyHtml);
|
|
84
|
-
}
|
|
85
|
-
return JSON.stringify(data);
|
|
86
|
-
},
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
server.addTool({
|
|
90
|
-
name: 'delete_draft',
|
|
91
|
-
description: 'Delete a draft',
|
|
92
|
-
parameters: z.object({
|
|
93
|
-
id: z.string().describe("The ID of the draft to delete"),
|
|
94
|
-
}),
|
|
95
|
-
execute: async (params) => {
|
|
96
|
-
const gmail = await getGmailClient();
|
|
97
|
-
const { data } = await gmail.users.drafts.delete({ userId: 'me', id: params.id });
|
|
98
|
-
return JSON.stringify(data || { success: true });
|
|
99
|
-
},
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
server.addTool({
|
|
103
|
-
name: 'get_draft',
|
|
104
|
-
description: 'Get a specific draft by ID',
|
|
105
|
-
parameters: z.object({
|
|
106
|
-
id: z.string().describe("The ID of the draft to retrieve"),
|
|
107
|
-
includeBodyHtml: z.boolean().optional().describe("Whether to include the parsed HTML in the return for each body"),
|
|
108
|
-
}),
|
|
109
|
-
execute: async (params) => {
|
|
110
|
-
const gmail = await getGmailClient();
|
|
111
|
-
const { data } = await gmail.users.drafts.get({ userId: 'me', id: params.id, format: 'full' });
|
|
112
|
-
if (data.message?.payload) {
|
|
113
|
-
data.message.payload = processMessagePart(data.message.payload, params.includeBodyHtml);
|
|
114
|
-
}
|
|
115
|
-
return JSON.stringify(data);
|
|
116
|
-
},
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
server.addTool({
|
|
120
|
-
name: 'list_drafts',
|
|
121
|
-
description: 'List drafts in the user\'s mailbox',
|
|
122
|
-
parameters: z.object({
|
|
123
|
-
maxResults: z.number().optional().describe("Maximum number of drafts to return (1-500)"),
|
|
124
|
-
q: z.string().optional().describe("Only return drafts matching the specified query"),
|
|
125
|
-
includeSpamTrash: z.boolean().optional().describe("Include drafts from SPAM and TRASH"),
|
|
126
|
-
includeBodyHtml: z.boolean().optional().describe("Whether to include the parsed HTML in the return for each body"),
|
|
127
|
-
}),
|
|
128
|
-
execute: async (params) => {
|
|
129
|
-
const gmail = await getGmailClient();
|
|
130
|
-
let drafts = [];
|
|
131
|
-
const { data } = await gmail.users.drafts.list({ userId: 'me', ...params });
|
|
132
|
-
drafts.push(...(data.drafts || []));
|
|
133
|
-
let pageToken = data.nextPageToken;
|
|
134
|
-
while (pageToken) {
|
|
135
|
-
const { data: nextData } = await gmail.users.drafts.list({ userId: 'me', ...params, pageToken });
|
|
136
|
-
drafts.push(...(nextData.drafts || []));
|
|
137
|
-
pageToken = nextData.nextPageToken;
|
|
138
|
-
}
|
|
139
|
-
drafts = drafts.map(draft => {
|
|
140
|
-
if (draft.message?.payload) {
|
|
141
|
-
draft.message.payload = processMessagePart(draft.message.payload, params.includeBodyHtml);
|
|
142
|
-
}
|
|
143
|
-
return draft;
|
|
144
|
-
});
|
|
145
|
-
return JSON.stringify(drafts);
|
|
146
|
-
},
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
server.addTool({
|
|
150
|
-
name: 'send_draft',
|
|
151
|
-
description: 'Send an existing draft',
|
|
152
|
-
parameters: z.object({
|
|
153
|
-
id: z.string().describe("The ID of the draft to send"),
|
|
154
|
-
}),
|
|
155
|
-
execute: async (params) => {
|
|
156
|
-
const gmail = await getGmailClient();
|
|
157
|
-
try {
|
|
158
|
-
const { data } = await gmail.users.drafts.send({ userId: 'me', requestBody: { id: params.id } });
|
|
159
|
-
return JSON.stringify(data);
|
|
160
|
-
} catch (error) {
|
|
161
|
-
throw new UserError('Error sending draft, are you sure you have at least one recipient?');
|
|
162
|
-
}
|
|
163
|
-
},
|
|
164
|
-
});
|
|
165
|
-
}
|
|
1
|
+
// Gmail Draft tools
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { UserError } from 'fastmcp';
|
|
4
|
+
import { getGmailClient } from '../clients.js';
|
|
5
|
+
import { processMessagePart, constructRawMessage, constructRawMessageWithAttachments } from '../helpers.js';
|
|
6
|
+
|
|
7
|
+
export function register(server) {
|
|
8
|
+
server.addTool({
|
|
9
|
+
name: 'create_draft',
|
|
10
|
+
description: 'Create a draft email in Gmail. Note the mechanics of the raw parameter.',
|
|
11
|
+
parameters: z.object({
|
|
12
|
+
raw: z.string().optional().describe("The entire email message in base64url encoded RFC 2822 format, ignores params.to, cc, bcc, subject, body, includeBodyHtml if provided"),
|
|
13
|
+
threadId: z.string().optional().describe("The thread ID to associate this draft with"),
|
|
14
|
+
to: z.array(z.string()).optional().describe("List of recipient email addresses"),
|
|
15
|
+
cc: z.array(z.string()).optional().describe("List of CC recipient email addresses"),
|
|
16
|
+
bcc: z.array(z.string()).optional().describe("List of BCC recipient email addresses"),
|
|
17
|
+
subject: z.string().optional().describe("The subject of the email"),
|
|
18
|
+
body: z.string().optional().describe("The body of the email. Supports plain text or HTML (auto-detected). Use HTML tags like <p>, <br>, <b> for formatted emails."),
|
|
19
|
+
attachments: z.array(z.object({
|
|
20
|
+
filename: z.string().describe("Attachment file name"),
|
|
21
|
+
mimeType: z.string().describe("MIME type of the attachment"),
|
|
22
|
+
base64Data: z.string().describe("Base64 encoded attachment data"),
|
|
23
|
+
})).optional().describe("File attachments to include"),
|
|
24
|
+
includeBodyHtml: z.boolean().optional().describe("Whether to include the parsed HTML in the return for each body"),
|
|
25
|
+
}),
|
|
26
|
+
execute: async (params, { log }) => {
|
|
27
|
+
const gmail = await getGmailClient();
|
|
28
|
+
let raw = params.raw;
|
|
29
|
+
if (!raw) {
|
|
30
|
+
if (params.attachments?.length) {
|
|
31
|
+
raw = await constructRawMessageWithAttachments(gmail, params);
|
|
32
|
+
} else {
|
|
33
|
+
raw = await constructRawMessage(gmail, params);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const createParams = { userId: 'me', requestBody: { message: { raw } } };
|
|
37
|
+
if (params.threadId && createParams.requestBody?.message) {
|
|
38
|
+
createParams.requestBody.message.threadId = params.threadId;
|
|
39
|
+
}
|
|
40
|
+
const { data } = await gmail.users.drafts.create(createParams);
|
|
41
|
+
if (data.message?.payload) {
|
|
42
|
+
data.message.payload = processMessagePart(data.message.payload, params.includeBodyHtml);
|
|
43
|
+
}
|
|
44
|
+
return JSON.stringify(data);
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
server.addTool({
|
|
49
|
+
name: 'update_draft',
|
|
50
|
+
description: 'Update an existing draft\'s content. Replaces the draft message with new content.',
|
|
51
|
+
parameters: z.object({
|
|
52
|
+
id: z.string().describe("The ID of the draft to update"),
|
|
53
|
+
raw: z.string().optional().describe("The entire email message in base64url encoded RFC 2822 format, ignores other params if provided"),
|
|
54
|
+
threadId: z.string().optional().describe("The thread ID to associate this draft with"),
|
|
55
|
+
to: z.array(z.string()).optional().describe("List of recipient email addresses"),
|
|
56
|
+
cc: z.array(z.string()).optional().describe("List of CC recipient email addresses"),
|
|
57
|
+
bcc: z.array(z.string()).optional().describe("List of BCC recipient email addresses"),
|
|
58
|
+
subject: z.string().optional().describe("The subject of the email"),
|
|
59
|
+
body: z.string().optional().describe("The body of the email. Supports plain text or HTML (auto-detected). Use HTML tags like <p>, <br>, <b> for formatted emails."),
|
|
60
|
+
attachments: z.array(z.object({
|
|
61
|
+
filename: z.string().describe("Attachment file name"),
|
|
62
|
+
mimeType: z.string().describe("MIME type of the attachment"),
|
|
63
|
+
base64Data: z.string().describe("Base64 encoded attachment data"),
|
|
64
|
+
})).optional().describe("File attachments to include"),
|
|
65
|
+
includeBodyHtml: z.boolean().optional().describe("Whether to include the parsed HTML in the return for each body"),
|
|
66
|
+
}),
|
|
67
|
+
execute: async (params) => {
|
|
68
|
+
const gmail = await getGmailClient();
|
|
69
|
+
let raw = params.raw;
|
|
70
|
+
if (!raw) {
|
|
71
|
+
if (params.attachments?.length) {
|
|
72
|
+
raw = await constructRawMessageWithAttachments(gmail, params);
|
|
73
|
+
} else {
|
|
74
|
+
raw = await constructRawMessage(gmail, params);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const updateParams = { userId: 'me', id: params.id, requestBody: { message: { raw } } };
|
|
78
|
+
if (params.threadId && updateParams.requestBody?.message) {
|
|
79
|
+
updateParams.requestBody.message.threadId = params.threadId;
|
|
80
|
+
}
|
|
81
|
+
const { data } = await gmail.users.drafts.update(updateParams);
|
|
82
|
+
if (data.message?.payload) {
|
|
83
|
+
data.message.payload = processMessagePart(data.message.payload, params.includeBodyHtml);
|
|
84
|
+
}
|
|
85
|
+
return JSON.stringify(data);
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
server.addTool({
|
|
90
|
+
name: 'delete_draft',
|
|
91
|
+
description: 'Delete a draft',
|
|
92
|
+
parameters: z.object({
|
|
93
|
+
id: z.string().describe("The ID of the draft to delete"),
|
|
94
|
+
}),
|
|
95
|
+
execute: async (params) => {
|
|
96
|
+
const gmail = await getGmailClient();
|
|
97
|
+
const { data } = await gmail.users.drafts.delete({ userId: 'me', id: params.id });
|
|
98
|
+
return JSON.stringify(data || { success: true });
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
server.addTool({
|
|
103
|
+
name: 'get_draft',
|
|
104
|
+
description: 'Get a specific draft by ID',
|
|
105
|
+
parameters: z.object({
|
|
106
|
+
id: z.string().describe("The ID of the draft to retrieve"),
|
|
107
|
+
includeBodyHtml: z.boolean().optional().describe("Whether to include the parsed HTML in the return for each body"),
|
|
108
|
+
}),
|
|
109
|
+
execute: async (params) => {
|
|
110
|
+
const gmail = await getGmailClient();
|
|
111
|
+
const { data } = await gmail.users.drafts.get({ userId: 'me', id: params.id, format: 'full' });
|
|
112
|
+
if (data.message?.payload) {
|
|
113
|
+
data.message.payload = processMessagePart(data.message.payload, params.includeBodyHtml);
|
|
114
|
+
}
|
|
115
|
+
return JSON.stringify(data);
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
server.addTool({
|
|
120
|
+
name: 'list_drafts',
|
|
121
|
+
description: 'List drafts in the user\'s mailbox',
|
|
122
|
+
parameters: z.object({
|
|
123
|
+
maxResults: z.number().optional().describe("Maximum number of drafts to return (1-500)"),
|
|
124
|
+
q: z.string().optional().describe("Only return drafts matching the specified query"),
|
|
125
|
+
includeSpamTrash: z.boolean().optional().describe("Include drafts from SPAM and TRASH"),
|
|
126
|
+
includeBodyHtml: z.boolean().optional().describe("Whether to include the parsed HTML in the return for each body"),
|
|
127
|
+
}),
|
|
128
|
+
execute: async (params) => {
|
|
129
|
+
const gmail = await getGmailClient();
|
|
130
|
+
let drafts = [];
|
|
131
|
+
const { data } = await gmail.users.drafts.list({ userId: 'me', ...params });
|
|
132
|
+
drafts.push(...(data.drafts || []));
|
|
133
|
+
let pageToken = data.nextPageToken;
|
|
134
|
+
while (pageToken) {
|
|
135
|
+
const { data: nextData } = await gmail.users.drafts.list({ userId: 'me', ...params, pageToken });
|
|
136
|
+
drafts.push(...(nextData.drafts || []));
|
|
137
|
+
pageToken = nextData.nextPageToken;
|
|
138
|
+
}
|
|
139
|
+
drafts = drafts.map(draft => {
|
|
140
|
+
if (draft.message?.payload) {
|
|
141
|
+
draft.message.payload = processMessagePart(draft.message.payload, params.includeBodyHtml);
|
|
142
|
+
}
|
|
143
|
+
return draft;
|
|
144
|
+
});
|
|
145
|
+
return JSON.stringify(drafts);
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
server.addTool({
|
|
150
|
+
name: 'send_draft',
|
|
151
|
+
description: 'Send an existing draft',
|
|
152
|
+
parameters: z.object({
|
|
153
|
+
id: z.string().describe("The ID of the draft to send"),
|
|
154
|
+
}),
|
|
155
|
+
execute: async (params) => {
|
|
156
|
+
const gmail = await getGmailClient();
|
|
157
|
+
try {
|
|
158
|
+
const { data } = await gmail.users.drafts.send({ userId: 'me', requestBody: { id: params.id } });
|
|
159
|
+
return JSON.stringify(data);
|
|
160
|
+
} catch (error) {
|
|
161
|
+
throw new UserError('Error sending draft, are you sure you have at least one recipient?');
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
}
|
|
@@ -40,6 +40,7 @@ export function register(server) {
|
|
|
40
40
|
});
|
|
41
41
|
const document = response.data;
|
|
42
42
|
// Add initial content if provided
|
|
43
|
+
let contentWarnings;
|
|
43
44
|
if (args.initialContent) {
|
|
44
45
|
try {
|
|
45
46
|
const docs = await getDocsClient();
|
|
@@ -64,6 +65,13 @@ export function register(server) {
|
|
|
64
65
|
firstHeadingAsTitle: true,
|
|
65
66
|
});
|
|
66
67
|
log.info(formatInsertResult(result));
|
|
68
|
+
// Surface dropped-content warnings in the tool response itself —
|
|
69
|
+
// logging alone leaves the caller believing the initial content
|
|
70
|
+
// rendered faithfully (the same failure mode this warnings
|
|
71
|
+
// feature exists to close for appendMarkdown/replaceDocumentWithMarkdown).
|
|
72
|
+
if (result.warnings?.length) {
|
|
73
|
+
contentWarnings = result.warnings;
|
|
74
|
+
}
|
|
67
75
|
}
|
|
68
76
|
}
|
|
69
77
|
catch (contentError) {
|
|
@@ -74,6 +82,10 @@ export function register(server) {
|
|
|
74
82
|
id: document.id,
|
|
75
83
|
name: document.name,
|
|
76
84
|
url: document.webViewLink,
|
|
85
|
+
...(contentWarnings && {
|
|
86
|
+
warnings: contentWarnings,
|
|
87
|
+
warningNote: `${contentWarnings.length} item${contentWarnings.length === 1 ? '' : 's'} of initialContent could not be converted and ${contentWarnings.length === 1 ? 'was' : 'were'} dropped — see warnings.`,
|
|
88
|
+
}),
|
|
77
89
|
}, null, 2);
|
|
78
90
|
}
|
|
79
91
|
catch (error) {
|