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
|
@@ -1,528 +1,358 @@
|
|
|
1
|
-
// Gmail Settings tools
|
|
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
|
-
const {
|
|
56
|
-
return
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
}),
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
server.addTool({
|
|
181
|
-
name: '
|
|
182
|
-
description:
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
parameters: z.object({
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
},
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
parameters: z.object({}),
|
|
360
|
-
execute: async () => {
|
|
361
|
-
const gmail = await getGmailClient();
|
|
362
|
-
const { data } = await gmail.users.settings.sendAs.list({ userId: 'me' });
|
|
363
|
-
return JSON.stringify(data);
|
|
364
|
-
},
|
|
365
|
-
});
|
|
366
|
-
|
|
367
|
-
server.addTool({
|
|
368
|
-
name: 'patch_send_as',
|
|
369
|
-
description: 'Patches the specified send-as alias',
|
|
370
|
-
parameters: z.object({
|
|
371
|
-
sendAsEmail: z.string().describe("The send-as alias to update"),
|
|
372
|
-
displayName: z.string().optional().describe("Name for the 'From:' header"),
|
|
373
|
-
replyToAddress: z.string().optional().describe("Email for 'Reply-To:' header"),
|
|
374
|
-
signature: z.string().optional().describe("Optional HTML signature"),
|
|
375
|
-
isPrimary: z.boolean().optional().describe("Whether this is the primary address"),
|
|
376
|
-
treatAsAlias: z.boolean().optional().describe("Whether Gmail treats this as an alias"),
|
|
377
|
-
}),
|
|
378
|
-
execute: async (params) => {
|
|
379
|
-
const { sendAsEmail, ...patchData } = params;
|
|
380
|
-
const gmail = await getGmailClient();
|
|
381
|
-
const { data } = await gmail.users.settings.sendAs.patch({ userId: 'me', sendAsEmail, requestBody: patchData });
|
|
382
|
-
return JSON.stringify(data);
|
|
383
|
-
},
|
|
384
|
-
});
|
|
385
|
-
|
|
386
|
-
server.addTool({
|
|
387
|
-
name: 'update_send_as',
|
|
388
|
-
description: 'Updates a send-as alias',
|
|
389
|
-
parameters: z.object({
|
|
390
|
-
sendAsEmail: z.string().describe("The send-as alias to update"),
|
|
391
|
-
displayName: z.string().optional().describe("Name for the 'From:' header"),
|
|
392
|
-
replyToAddress: z.string().optional().describe("Email for 'Reply-To:' header"),
|
|
393
|
-
signature: z.string().optional().describe("Optional HTML signature"),
|
|
394
|
-
isPrimary: z.boolean().optional().describe("Whether this is the primary address"),
|
|
395
|
-
treatAsAlias: z.boolean().optional().describe("Whether Gmail treats this as an alias"),
|
|
396
|
-
}),
|
|
397
|
-
execute: async (params) => {
|
|
398
|
-
const { sendAsEmail, ...updateData } = params;
|
|
399
|
-
const gmail = await getGmailClient();
|
|
400
|
-
const { data } = await gmail.users.settings.sendAs.update({ userId: 'me', sendAsEmail, requestBody: updateData });
|
|
401
|
-
return JSON.stringify(data);
|
|
402
|
-
},
|
|
403
|
-
});
|
|
404
|
-
|
|
405
|
-
server.addTool({
|
|
406
|
-
name: 'verify_send_as',
|
|
407
|
-
description: 'Sends a verification email to the specified send-as alias',
|
|
408
|
-
parameters: z.object({
|
|
409
|
-
sendAsEmail: z.string().describe("The send-as alias to verify"),
|
|
410
|
-
}),
|
|
411
|
-
execute: async (params) => {
|
|
412
|
-
const gmail = await getGmailClient();
|
|
413
|
-
const { data } = await gmail.users.settings.sendAs.verify({ userId: 'me', sendAsEmail: params.sendAsEmail });
|
|
414
|
-
return JSON.stringify(data || { success: true });
|
|
415
|
-
},
|
|
416
|
-
});
|
|
417
|
-
|
|
418
|
-
// --- S/MIME ---
|
|
419
|
-
|
|
420
|
-
server.addTool({
|
|
421
|
-
name: 'delete_smime_info',
|
|
422
|
-
description: 'Deletes the specified S/MIME config for a send-as alias',
|
|
423
|
-
parameters: z.object({
|
|
424
|
-
sendAsEmail: z.string().describe("The email address in the 'From:' header"),
|
|
425
|
-
id: z.string().describe("The S/MIME config ID"),
|
|
426
|
-
}),
|
|
427
|
-
execute: async (params) => {
|
|
428
|
-
const gmail = await getGmailClient();
|
|
429
|
-
const { data } = await gmail.users.settings.sendAs.smimeInfo.delete({ userId: 'me', sendAsEmail: params.sendAsEmail, id: params.id });
|
|
430
|
-
return JSON.stringify(data || { success: true });
|
|
431
|
-
},
|
|
432
|
-
});
|
|
433
|
-
|
|
434
|
-
server.addTool({
|
|
435
|
-
name: 'get_smime_info',
|
|
436
|
-
description: 'Gets the specified S/MIME config for a send-as alias',
|
|
437
|
-
parameters: z.object({
|
|
438
|
-
sendAsEmail: z.string().describe("The email address in the 'From:' header"),
|
|
439
|
-
id: z.string().describe("The S/MIME config ID"),
|
|
440
|
-
}),
|
|
441
|
-
execute: async (params) => {
|
|
442
|
-
const gmail = await getGmailClient();
|
|
443
|
-
const { data } = await gmail.users.settings.sendAs.smimeInfo.get({ userId: 'me', sendAsEmail: params.sendAsEmail, id: params.id });
|
|
444
|
-
return JSON.stringify(data);
|
|
445
|
-
},
|
|
446
|
-
});
|
|
447
|
-
|
|
448
|
-
server.addTool({
|
|
449
|
-
name: 'insert_smime_info',
|
|
450
|
-
description: 'Insert (upload) S/MIME config for a send-as alias',
|
|
451
|
-
parameters: z.object({
|
|
452
|
-
sendAsEmail: z.string().describe("The email address in the 'From:' header"),
|
|
453
|
-
encryptedKeyPassword: z.string().describe("Encrypted key password"),
|
|
454
|
-
pkcs12: z.string().describe("PKCS#12 format key pair and certificate chain"),
|
|
455
|
-
}),
|
|
456
|
-
execute: async (params) => {
|
|
457
|
-
const gmail = await getGmailClient();
|
|
458
|
-
const { data } = await gmail.users.settings.sendAs.smimeInfo.insert({ userId: 'me', sendAsEmail: params.sendAsEmail, requestBody: params });
|
|
459
|
-
return JSON.stringify(data);
|
|
460
|
-
},
|
|
461
|
-
});
|
|
462
|
-
|
|
463
|
-
server.addTool({
|
|
464
|
-
name: 'list_smime_info',
|
|
465
|
-
description: 'Lists S/MIME configs for a send-as alias',
|
|
466
|
-
parameters: z.object({
|
|
467
|
-
sendAsEmail: z.string().describe("The email address in the 'From:' header"),
|
|
468
|
-
}),
|
|
469
|
-
execute: async (params) => {
|
|
470
|
-
const gmail = await getGmailClient();
|
|
471
|
-
const { data } = await gmail.users.settings.sendAs.smimeInfo.list({ userId: 'me', sendAsEmail: params.sendAsEmail });
|
|
472
|
-
return JSON.stringify(data);
|
|
473
|
-
},
|
|
474
|
-
});
|
|
475
|
-
|
|
476
|
-
server.addTool({
|
|
477
|
-
name: 'set_default_smime_info',
|
|
478
|
-
description: 'Sets the default S/MIME config for a send-as alias',
|
|
479
|
-
parameters: z.object({
|
|
480
|
-
sendAsEmail: z.string().describe("The email address in the 'From:' header"),
|
|
481
|
-
id: z.string().describe("The S/MIME config ID"),
|
|
482
|
-
}),
|
|
483
|
-
execute: async (params) => {
|
|
484
|
-
const gmail = await getGmailClient();
|
|
485
|
-
const { data } = await gmail.users.settings.sendAs.smimeInfo.setDefault({ userId: 'me', sendAsEmail: params.sendAsEmail, id: params.id });
|
|
486
|
-
return JSON.stringify(data || { success: true });
|
|
487
|
-
},
|
|
488
|
-
});
|
|
489
|
-
|
|
490
|
-
// --- Profile & Watch ---
|
|
491
|
-
|
|
492
|
-
server.addTool({
|
|
493
|
-
name: 'get_profile',
|
|
494
|
-
description: 'Get the current user\'s Gmail profile',
|
|
495
|
-
parameters: z.object({}),
|
|
496
|
-
execute: async () => {
|
|
497
|
-
const gmail = await getGmailClient();
|
|
498
|
-
const { data } = await gmail.users.getProfile({ userId: 'me' });
|
|
499
|
-
return JSON.stringify(data);
|
|
500
|
-
},
|
|
501
|
-
});
|
|
502
|
-
|
|
503
|
-
server.addTool({
|
|
504
|
-
name: 'watch_mailbox',
|
|
505
|
-
description: 'Watch for changes to the user\'s mailbox via Cloud Pub/Sub',
|
|
506
|
-
parameters: z.object({
|
|
507
|
-
topicName: z.string().describe("Cloud Pub/Sub topic to publish notifications to"),
|
|
508
|
-
labelIds: z.array(z.string()).optional().describe("Label IDs to restrict notifications to"),
|
|
509
|
-
labelFilterAction: z.enum(['include', 'exclude']).optional().describe("Whether to include or exclude specified labels"),
|
|
510
|
-
}),
|
|
511
|
-
execute: async (params) => {
|
|
512
|
-
const gmail = await getGmailClient();
|
|
513
|
-
const { data } = await gmail.users.watch({ userId: 'me', requestBody: params });
|
|
514
|
-
return JSON.stringify(data);
|
|
515
|
-
},
|
|
516
|
-
});
|
|
517
|
-
|
|
518
|
-
server.addTool({
|
|
519
|
-
name: 'stop_mail_watch',
|
|
520
|
-
description: 'Stop receiving push notifications for the user\'s mailbox',
|
|
521
|
-
parameters: z.object({}),
|
|
522
|
-
execute: async () => {
|
|
523
|
-
const gmail = await getGmailClient();
|
|
524
|
-
const { data } = await gmail.users.stop({ userId: 'me' });
|
|
525
|
-
return JSON.stringify(data || { success: true });
|
|
526
|
-
},
|
|
527
|
-
});
|
|
528
|
-
}
|
|
1
|
+
// Gmail Settings tools — consolidated dispatch tools (issue #31/#32/#33).
|
|
2
|
+
// manageGmailSettings/manageSmime/manageFilter replace the former ~34 granular
|
|
3
|
+
// account-config tools. Profile/watch stay granular (renamed to camelCase).
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { UserError } from 'fastmcp';
|
|
6
|
+
import { getGmailClient } from '../../clients.js';
|
|
7
|
+
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// manageGmailSettings — resource/action dispatch over the Gmail settings API.
|
|
10
|
+
// Each handler reproduces the exact underlying gmail.users.settings.* call the
|
|
11
|
+
// former granular tool made, so this is a surface reshape, not a behavior change.
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
const SETTINGS_OPS = {
|
|
14
|
+
imap: {
|
|
15
|
+
get: (gmail) => gmail.users.settings.getImap({ userId: 'me' }),
|
|
16
|
+
update: (gmail, payload) => gmail.users.settings.updateImap({ userId: 'me', requestBody: payload }),
|
|
17
|
+
},
|
|
18
|
+
pop: {
|
|
19
|
+
get: (gmail) => gmail.users.settings.getPop({ userId: 'me' }),
|
|
20
|
+
update: (gmail, payload) => gmail.users.settings.updatePop({ userId: 'me', requestBody: payload }),
|
|
21
|
+
},
|
|
22
|
+
vacation: {
|
|
23
|
+
get: (gmail) => gmail.users.settings.getVacation({ userId: 'me' }),
|
|
24
|
+
update: (gmail, payload) => gmail.users.settings.updateVacation({ userId: 'me', requestBody: payload }),
|
|
25
|
+
},
|
|
26
|
+
language: {
|
|
27
|
+
get: (gmail) => gmail.users.settings.getLanguage({ userId: 'me' }),
|
|
28
|
+
update: (gmail, payload) => gmail.users.settings.updateLanguage({ userId: 'me', requestBody: payload }),
|
|
29
|
+
},
|
|
30
|
+
autoForwarding: {
|
|
31
|
+
get: (gmail) => gmail.users.settings.getAutoForwarding({ userId: 'me' }),
|
|
32
|
+
update: (gmail, payload) => gmail.users.settings.updateAutoForwarding({ userId: 'me', requestBody: payload }),
|
|
33
|
+
},
|
|
34
|
+
forwardingAddress: {
|
|
35
|
+
get: (gmail, payload) => gmail.users.settings.forwardingAddresses.get({ userId: 'me', forwardingEmail: payload.forwardingEmail }),
|
|
36
|
+
list: (gmail) => gmail.users.settings.forwardingAddresses.list({ userId: 'me' }),
|
|
37
|
+
create: (gmail, payload) => gmail.users.settings.forwardingAddresses.create({ userId: 'me', requestBody: payload }),
|
|
38
|
+
delete: (gmail, payload) => gmail.users.settings.forwardingAddresses.delete({ userId: 'me', forwardingEmail: payload.forwardingEmail }),
|
|
39
|
+
},
|
|
40
|
+
delegate: {
|
|
41
|
+
get: (gmail, payload) => gmail.users.settings.delegates.get({ userId: 'me', delegateEmail: payload.delegateEmail }),
|
|
42
|
+
list: (gmail) => gmail.users.settings.delegates.list({ userId: 'me' }),
|
|
43
|
+
create: (gmail, payload) => gmail.users.settings.delegates.create({ userId: 'me', requestBody: { delegateEmail: payload.delegateEmail } }),
|
|
44
|
+
delete: (gmail, payload) => gmail.users.settings.delegates.delete({ userId: 'me', delegateEmail: payload.delegateEmail }),
|
|
45
|
+
},
|
|
46
|
+
sendAs: {
|
|
47
|
+
get: (gmail, payload) => gmail.users.settings.sendAs.get({ userId: 'me', sendAsEmail: payload.sendAsEmail }),
|
|
48
|
+
list: (gmail) => gmail.users.settings.sendAs.list({ userId: 'me' }),
|
|
49
|
+
create: (gmail, payload) => gmail.users.settings.sendAs.create({ userId: 'me', requestBody: payload }),
|
|
50
|
+
patch: (gmail, payload) => {
|
|
51
|
+
const { sendAsEmail, ...body } = payload;
|
|
52
|
+
return gmail.users.settings.sendAs.patch({ userId: 'me', sendAsEmail, requestBody: body });
|
|
53
|
+
},
|
|
54
|
+
update: (gmail, payload) => {
|
|
55
|
+
const { sendAsEmail, ...body } = payload;
|
|
56
|
+
return gmail.users.settings.sendAs.update({ userId: 'me', sendAsEmail, requestBody: body });
|
|
57
|
+
},
|
|
58
|
+
delete: (gmail, payload) => gmail.users.settings.sendAs.delete({ userId: 'me', sendAsEmail: payload.sendAsEmail }),
|
|
59
|
+
verify: (gmail, payload) => gmail.users.settings.sendAs.verify({ userId: 'me', sendAsEmail: payload.sendAsEmail }),
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
function validCombosText() {
|
|
64
|
+
return Object.entries(SETTINGS_OPS)
|
|
65
|
+
.map(([resource, actions]) => ` - ${resource}: ${Object.keys(actions).join(', ')}`)
|
|
66
|
+
.join('\n');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Per-operation Zod payload schemas — mirror the exact fields (names, types,
|
|
70
|
+
// enums, and required-ness) of the former granular tools' request schemas, so
|
|
71
|
+
// a malformed payload (wrong type, invalid enum, missing required field) is
|
|
72
|
+
// rejected before it ever reaches the Gmail API instead of only being checked
|
|
73
|
+
// for key *presence*. Actions with no entry here (every "get"/"list" that
|
|
74
|
+
// takes no identifier) take no payload.
|
|
75
|
+
const sendAsProfileFields = {
|
|
76
|
+
displayName: z.string().optional().describe("Name for the 'From:' header"),
|
|
77
|
+
replyToAddress: z.string().optional().describe("Email for 'Reply-To:' header"),
|
|
78
|
+
signature: z.string().optional().describe('Optional HTML signature'),
|
|
79
|
+
isPrimary: z.boolean().optional().describe('Whether this is the primary address'),
|
|
80
|
+
treatAsAlias: z.boolean().optional().describe('Whether Gmail treats this as an alias'),
|
|
81
|
+
};
|
|
82
|
+
const PAYLOAD_SCHEMAS = {
|
|
83
|
+
imap: {
|
|
84
|
+
update: z.object({
|
|
85
|
+
enabled: z.boolean(),
|
|
86
|
+
expungeBehavior: z.enum(['archive', 'trash', 'deleteForever']).optional(),
|
|
87
|
+
maxFolderSize: z.number().optional(),
|
|
88
|
+
}),
|
|
89
|
+
},
|
|
90
|
+
pop: {
|
|
91
|
+
update: z.object({
|
|
92
|
+
accessWindow: z.enum(['disabled', 'allMail', 'fromNowOn']),
|
|
93
|
+
disposition: z.enum(['archive', 'trash', 'leaveInInbox']),
|
|
94
|
+
}),
|
|
95
|
+
},
|
|
96
|
+
vacation: {
|
|
97
|
+
update: z.object({
|
|
98
|
+
enableAutoReply: z.boolean(),
|
|
99
|
+
responseSubject: z.string().optional(),
|
|
100
|
+
responseBodyPlainText: z.string(),
|
|
101
|
+
restrictToContacts: z.boolean().optional(),
|
|
102
|
+
restrictToDomain: z.boolean().optional(),
|
|
103
|
+
startTime: z.string().optional(),
|
|
104
|
+
endTime: z.string().optional(),
|
|
105
|
+
}),
|
|
106
|
+
},
|
|
107
|
+
language: {
|
|
108
|
+
update: z.object({ displayLanguage: z.string() }),
|
|
109
|
+
},
|
|
110
|
+
autoForwarding: {
|
|
111
|
+
update: z.object({
|
|
112
|
+
enabled: z.boolean(),
|
|
113
|
+
emailAddress: z.string(),
|
|
114
|
+
disposition: z.enum(['leaveInInbox', 'archive', 'trash', 'markRead']),
|
|
115
|
+
}),
|
|
116
|
+
},
|
|
117
|
+
forwardingAddress: {
|
|
118
|
+
get: z.object({ forwardingEmail: z.string() }),
|
|
119
|
+
create: z.object({ forwardingEmail: z.string() }),
|
|
120
|
+
delete: z.object({ forwardingEmail: z.string() }),
|
|
121
|
+
},
|
|
122
|
+
delegate: {
|
|
123
|
+
get: z.object({ delegateEmail: z.string() }),
|
|
124
|
+
create: z.object({ delegateEmail: z.string() }),
|
|
125
|
+
delete: z.object({ delegateEmail: z.string() }),
|
|
126
|
+
},
|
|
127
|
+
sendAs: {
|
|
128
|
+
get: z.object({ sendAsEmail: z.string() }),
|
|
129
|
+
delete: z.object({ sendAsEmail: z.string() }),
|
|
130
|
+
verify: z.object({ sendAsEmail: z.string() }),
|
|
131
|
+
create: z.object({ sendAsEmail: z.string(), ...sendAsProfileFields }),
|
|
132
|
+
patch: z.object({ sendAsEmail: z.string(), ...sendAsProfileFields }),
|
|
133
|
+
update: z.object({ sendAsEmail: z.string(), ...sendAsProfileFields }),
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
function formatZodIssues(issues) {
|
|
138
|
+
return issues.map((issue) => `${issue.path.join('.') || '(root)'}: ${issue.message}`).join('; ');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Validates+parses payload against the per-resource/action schema above.
|
|
142
|
+
// Returns the typed payload on success; throws a UserError naming every
|
|
143
|
+
// failing field on failure. Actions with no schema entry take no payload.
|
|
144
|
+
function parsePayload(resource, action, payload) {
|
|
145
|
+
const schema = PAYLOAD_SCHEMAS[resource]?.[action];
|
|
146
|
+
if (!schema) return payload ?? {};
|
|
147
|
+
const result = schema.safeParse(payload ?? {});
|
|
148
|
+
if (!result.success) {
|
|
149
|
+
throw new UserError(
|
|
150
|
+
`resource="${resource}" action="${action}" payload validation failed: ${formatZodIssues(result.error.issues)}`
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
return result.data;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Mirrors parsePayload's checks as Zod issues (rather than a thrown UserError)
|
|
157
|
+
// so they surface directly from parameters.parse()/safeParse(), not only from
|
|
158
|
+
// execute(). This is what restores the enum/type guidance a caller building
|
|
159
|
+
// the raw MCP request sees, on top of the runtime check in execute() below.
|
|
160
|
+
function addPayloadIssues(resource, action, payload, ctx) {
|
|
161
|
+
const resourceOps = SETTINGS_OPS[resource];
|
|
162
|
+
if (!resourceOps || !resourceOps[action]) {
|
|
163
|
+
ctx.addIssue({
|
|
164
|
+
code: z.ZodIssueCode.custom,
|
|
165
|
+
message: `Invalid resource/action combination: resource="${resource}" action="${action}".\nValid combinations:\n${validCombosText()}`,
|
|
166
|
+
});
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const schema = PAYLOAD_SCHEMAS[resource]?.[action];
|
|
170
|
+
if (!schema) return;
|
|
171
|
+
const result = schema.safeParse(payload ?? {});
|
|
172
|
+
if (!result.success) {
|
|
173
|
+
for (const issue of result.error.issues) {
|
|
174
|
+
ctx.addIssue({ ...issue, path: ['payload', ...issue.path] });
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function register(server) {
|
|
180
|
+
server.addTool({
|
|
181
|
+
name: 'manageGmailSettings',
|
|
182
|
+
description:
|
|
183
|
+
'Manage Gmail account settings via a resource/action dispatch. ' +
|
|
184
|
+
'Provide `resource`, `action`, and (where the action needs a body or identifier) a `payload`.\n\n' +
|
|
185
|
+
'Valid resource → actions:\n' +
|
|
186
|
+
' - imap: get, update (payload: { enabled, expungeBehavior?, maxFolderSize? })\n' +
|
|
187
|
+
' - pop: get, update (payload: { accessWindow, disposition })\n' +
|
|
188
|
+
' - vacation: get, update (payload: { enableAutoReply, responseSubject?, responseBodyPlainText, restrictToContacts?, restrictToDomain?, startTime?, endTime? })\n' +
|
|
189
|
+
' - language: get, update (payload: { displayLanguage })\n' +
|
|
190
|
+
' - autoForwarding: get, update (payload: { enabled, emailAddress, disposition })\n' +
|
|
191
|
+
' - forwardingAddress: list, get/delete (payload: { forwardingEmail }), create (payload: { forwardingEmail })\n' +
|
|
192
|
+
' - delegate: list, get/create/delete (payload: { delegateEmail })\n' +
|
|
193
|
+
' - sendAs: list, get/delete/verify (payload: { sendAsEmail }), create/patch/update (payload: { sendAsEmail, displayName?, replyToAddress?, signature?, isPrimary?, treatAsAlias? })',
|
|
194
|
+
parameters: z.object({
|
|
195
|
+
resource: z.enum(['imap', 'pop', 'vacation', 'language', 'autoForwarding', 'forwardingAddress', 'delegate', 'sendAs'])
|
|
196
|
+
.describe('The settings resource to operate on'),
|
|
197
|
+
action: z.enum(['get', 'update', 'list', 'create', 'delete', 'patch', 'verify'])
|
|
198
|
+
.describe('The operation to perform (must be valid for the chosen resource)'),
|
|
199
|
+
payload: z.record(z.string(), z.unknown()).optional()
|
|
200
|
+
.describe('Resource/action-specific fields (request body or identifier). See the description for valid keys per resource.'),
|
|
201
|
+
}).superRefine((data, ctx) => addPayloadIssues(data.resource, data.action, data.payload, ctx)),
|
|
202
|
+
execute: async ({ resource, action, payload = {} }) => {
|
|
203
|
+
const resourceOps = SETTINGS_OPS[resource];
|
|
204
|
+
const handler = resourceOps && resourceOps[action];
|
|
205
|
+
if (!handler) {
|
|
206
|
+
throw new UserError(
|
|
207
|
+
`Invalid resource/action combination: resource="${resource}", action="${action}".\n` +
|
|
208
|
+
`Valid combinations:\n${validCombosText()}`
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
const parsedPayload = parsePayload(resource, action, payload);
|
|
212
|
+
const gmail = await getGmailClient();
|
|
213
|
+
const { data } = await handler(gmail, parsedPayload);
|
|
214
|
+
return JSON.stringify(data || { success: true });
|
|
215
|
+
},
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
// --- S/MIME ---
|
|
219
|
+
server.addTool({
|
|
220
|
+
name: 'manageSmime',
|
|
221
|
+
description:
|
|
222
|
+
'Manage S/MIME configurations for a send-as alias. action: list | get | insert | delete | setDefault. ' +
|
|
223
|
+
'All actions require sendAsEmail. get/delete/setDefault require id. insert requires encryptedKeyPassword and pkcs12.',
|
|
224
|
+
parameters: z.object({
|
|
225
|
+
action: z.enum(['list', 'get', 'insert', 'delete', 'setDefault']).describe('The S/MIME operation to perform'),
|
|
226
|
+
sendAsEmail: z.string().describe("The email address in the 'From:' header"),
|
|
227
|
+
id: z.string().optional().describe('The S/MIME config ID (required for get, delete, setDefault)'),
|
|
228
|
+
encryptedKeyPassword: z.string().optional().describe('Encrypted key password (required for insert)'),
|
|
229
|
+
pkcs12: z.string().optional().describe('PKCS#12 format key pair and certificate chain (required for insert)'),
|
|
230
|
+
}),
|
|
231
|
+
execute: async (params) => {
|
|
232
|
+
if (['get', 'delete', 'setDefault'].includes(params.action) && !params.id) {
|
|
233
|
+
throw new UserError(`manageSmime action="${params.action}" requires id.`);
|
|
234
|
+
}
|
|
235
|
+
if (params.action === 'insert' && (!params.encryptedKeyPassword || !params.pkcs12)) {
|
|
236
|
+
throw new UserError('manageSmime action="insert" requires encryptedKeyPassword and pkcs12.');
|
|
237
|
+
}
|
|
238
|
+
const gmail = await getGmailClient();
|
|
239
|
+
const smime = gmail.users.settings.sendAs.smimeInfo;
|
|
240
|
+
const { action, sendAsEmail, id } = params;
|
|
241
|
+
let data;
|
|
242
|
+
switch (action) {
|
|
243
|
+
case 'list':
|
|
244
|
+
({ data } = await smime.list({ userId: 'me', sendAsEmail }));
|
|
245
|
+
break;
|
|
246
|
+
case 'get':
|
|
247
|
+
({ data } = await smime.get({ userId: 'me', sendAsEmail, id }));
|
|
248
|
+
break;
|
|
249
|
+
case 'insert':
|
|
250
|
+
({ data } = await smime.insert({
|
|
251
|
+
userId: 'me',
|
|
252
|
+
sendAsEmail,
|
|
253
|
+
requestBody: { sendAsEmail, encryptedKeyPassword: params.encryptedKeyPassword, pkcs12: params.pkcs12 },
|
|
254
|
+
}));
|
|
255
|
+
break;
|
|
256
|
+
case 'delete':
|
|
257
|
+
({ data } = await smime.delete({ userId: 'me', sendAsEmail, id }));
|
|
258
|
+
break;
|
|
259
|
+
case 'setDefault':
|
|
260
|
+
({ data } = await smime.setDefault({ userId: 'me', sendAsEmail, id }));
|
|
261
|
+
break;
|
|
262
|
+
}
|
|
263
|
+
return JSON.stringify(data || { success: true });
|
|
264
|
+
},
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
// --- Filters ---
|
|
268
|
+
server.addTool({
|
|
269
|
+
name: 'manageFilter',
|
|
270
|
+
description:
|
|
271
|
+
'Manage Gmail message filters. action: create | delete | get | list. ' +
|
|
272
|
+
'create requires criteria and filterAction. get/delete require id.',
|
|
273
|
+
parameters: z.object({
|
|
274
|
+
action: z.enum(['create', 'delete', 'get', 'list']).describe('The filter operation to perform'),
|
|
275
|
+
id: z.string().optional().describe('The filter ID (required for get and delete)'),
|
|
276
|
+
criteria: z.object({
|
|
277
|
+
from: z.string().optional().describe("Sender's display name or email"),
|
|
278
|
+
to: z.string().optional().describe("Recipient's display name or email"),
|
|
279
|
+
subject: z.string().optional().describe('Case-insensitive phrase in subject'),
|
|
280
|
+
query: z.string().optional().describe('Gmail search query for filter criteria'),
|
|
281
|
+
negatedQuery: z.string().optional().describe('Query for criteria the message must NOT match'),
|
|
282
|
+
hasAttachment: z.boolean().optional().describe('Whether the message has any attachment'),
|
|
283
|
+
excludeChats: z.boolean().optional().describe('Exclude chats from results'),
|
|
284
|
+
size: z.number().optional().describe('Size of RFC822 message in bytes'),
|
|
285
|
+
sizeComparison: z.enum(['smaller', 'larger']).optional().describe('Size comparison operator'),
|
|
286
|
+
}).optional().describe('Filter criteria (required for create)'),
|
|
287
|
+
filterAction: z.object({
|
|
288
|
+
addLabelIds: z.array(z.string()).optional().describe('Labels to add'),
|
|
289
|
+
removeLabelIds: z.array(z.string()).optional().describe('Labels to remove'),
|
|
290
|
+
forward: z.string().optional().describe('Email to forward to'),
|
|
291
|
+
}).optional().describe('Actions on matching messages (required for create)'),
|
|
292
|
+
}),
|
|
293
|
+
execute: async (params) => {
|
|
294
|
+
if (params.action === 'create' && (!params.criteria || !params.filterAction)) {
|
|
295
|
+
throw new UserError('manageFilter action="create" requires criteria and filterAction.');
|
|
296
|
+
}
|
|
297
|
+
if (['get', 'delete'].includes(params.action) && !params.id) {
|
|
298
|
+
throw new UserError(`manageFilter action="${params.action}" requires id.`);
|
|
299
|
+
}
|
|
300
|
+
const gmail = await getGmailClient();
|
|
301
|
+
const filters = gmail.users.settings.filters;
|
|
302
|
+
let data;
|
|
303
|
+
switch (params.action) {
|
|
304
|
+
case 'create':
|
|
305
|
+
({ data } = await filters.create({ userId: 'me', requestBody: { criteria: params.criteria, action: params.filterAction } }));
|
|
306
|
+
break;
|
|
307
|
+
case 'delete':
|
|
308
|
+
({ data } = await filters.delete({ userId: 'me', id: params.id }));
|
|
309
|
+
break;
|
|
310
|
+
case 'get':
|
|
311
|
+
({ data } = await filters.get({ userId: 'me', id: params.id }));
|
|
312
|
+
break;
|
|
313
|
+
case 'list':
|
|
314
|
+
({ data } = await filters.list({ userId: 'me' }));
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
317
|
+
return JSON.stringify(data || { success: true });
|
|
318
|
+
},
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
// --- Profile & Watch (kept granular, renamed to camelCase) ---
|
|
322
|
+
server.addTool({
|
|
323
|
+
name: 'getProfile',
|
|
324
|
+
description: 'Get the current user\'s Gmail profile',
|
|
325
|
+
parameters: z.object({}),
|
|
326
|
+
execute: async () => {
|
|
327
|
+
const gmail = await getGmailClient();
|
|
328
|
+
const { data } = await gmail.users.getProfile({ userId: 'me' });
|
|
329
|
+
return JSON.stringify(data);
|
|
330
|
+
},
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
server.addTool({
|
|
334
|
+
name: 'watchMailbox',
|
|
335
|
+
description: 'Watch for changes to the user\'s mailbox via Cloud Pub/Sub',
|
|
336
|
+
parameters: z.object({
|
|
337
|
+
topicName: z.string().describe("Cloud Pub/Sub topic to publish notifications to"),
|
|
338
|
+
labelIds: z.array(z.string()).optional().describe("Label IDs to restrict notifications to"),
|
|
339
|
+
labelFilterAction: z.enum(['include', 'exclude']).optional().describe("Whether to include or exclude specified labels"),
|
|
340
|
+
}),
|
|
341
|
+
execute: async (params) => {
|
|
342
|
+
const gmail = await getGmailClient();
|
|
343
|
+
const { data } = await gmail.users.watch({ userId: 'me', requestBody: params });
|
|
344
|
+
return JSON.stringify(data);
|
|
345
|
+
},
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
server.addTool({
|
|
349
|
+
name: 'stopMailWatch',
|
|
350
|
+
description: 'Stop receiving push notifications for the user\'s mailbox',
|
|
351
|
+
parameters: z.object({}),
|
|
352
|
+
execute: async () => {
|
|
353
|
+
const gmail = await getGmailClient();
|
|
354
|
+
const { data } = await gmail.users.stop({ userId: 'me' });
|
|
355
|
+
return JSON.stringify(data || { success: true });
|
|
356
|
+
},
|
|
357
|
+
});
|
|
358
|
+
}
|