gogcli-mcp-gmail 2.0.12 → 2.3.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/CHANGELOG.md +19 -0
- package/dist/index.js +327 -32
- package/manifest.json +57 -1
- package/package.json +1 -1
- package/src/tools/gmail-extra.ts +345 -16
- package/tests/tools/gmail-extra.test.ts +449 -0
package/src/tools/gmail-extra.ts
CHANGED
|
@@ -1,6 +1,62 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import { accountParam, runOrDiagnose } from '../../../gogcli-mcp/src/lib.js';
|
|
3
|
+
import { accountParam, runOrDiagnose, toText, type ToolResult } from '../../../gogcli-mcp/src/lib.js';
|
|
4
|
+
|
|
5
|
+
type GmailHeader = { name?: string; value?: string };
|
|
6
|
+
type GmailMessage = {
|
|
7
|
+
id?: string;
|
|
8
|
+
threadId?: string;
|
|
9
|
+
internalDate?: string;
|
|
10
|
+
labelIds?: string[];
|
|
11
|
+
snippet?: string;
|
|
12
|
+
payload?: { headers?: GmailHeader[] };
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// Headers worth keeping in a snippets-only thread view.
|
|
16
|
+
const SNIPPET_HEADERS = ['From', 'To', 'Cc', 'Subject', 'Date'];
|
|
17
|
+
|
|
18
|
+
// Reduce a full Gmail message to a lightweight overview: id/labels/snippet plus
|
|
19
|
+
// the key envelope headers, dropping the raw MIME payload that dominates the
|
|
20
|
+
// size of a thread fetch.
|
|
21
|
+
function summarizeMessage(m: GmailMessage): Record<string, unknown> {
|
|
22
|
+
const rawHeaders = m.payload?.headers;
|
|
23
|
+
const headers: Record<string, string | undefined> = {};
|
|
24
|
+
if (Array.isArray(rawHeaders)) {
|
|
25
|
+
for (const h of rawHeaders) {
|
|
26
|
+
if (h.name && SNIPPET_HEADERS.includes(h.name)) headers[h.name] = h.value;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
id: m.id,
|
|
31
|
+
threadId: m.threadId,
|
|
32
|
+
internalDate: m.internalDate,
|
|
33
|
+
labelIds: m.labelIds,
|
|
34
|
+
snippet: m.snippet,
|
|
35
|
+
headers,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Wrapper-side trim of a `gog gmail thread get` JSON result: keep only the last
|
|
40
|
+
// `latestN` messages and/or reduce each to a snippet view. gog has no native
|
|
41
|
+
// message-limit flag, so this is done by post-processing its output. Any
|
|
42
|
+
// non-JSON output (an error, an unexpected shape) is passed through untouched.
|
|
43
|
+
function trimThread(
|
|
44
|
+
result: ToolResult,
|
|
45
|
+
latestN: number | undefined,
|
|
46
|
+
snippetsOnly: boolean | undefined,
|
|
47
|
+
): ToolResult {
|
|
48
|
+
try {
|
|
49
|
+
const parsed = JSON.parse(result.content[0].text) as { thread?: { messages?: unknown[] } };
|
|
50
|
+
const messages = parsed.thread?.messages;
|
|
51
|
+
if (!Array.isArray(messages)) return result;
|
|
52
|
+
let trimmed: unknown[] = messages;
|
|
53
|
+
if (latestN !== undefined) trimmed = trimmed.slice(-latestN);
|
|
54
|
+
if (snippetsOnly) trimmed = trimmed.map((m) => summarizeMessage(m as GmailMessage));
|
|
55
|
+
return toText(JSON.stringify({ ...parsed, thread: { ...parsed.thread, messages: trimmed } }));
|
|
56
|
+
} catch {
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
4
60
|
|
|
5
61
|
export function registerExtraGmailTools(server: McpServer): void {
|
|
6
62
|
server.registerTool('gog_gmail_raw', {
|
|
@@ -152,23 +208,27 @@ export function registerExtraGmailTools(server: McpServer): void {
|
|
|
152
208
|
});
|
|
153
209
|
|
|
154
210
|
server.registerTool('gog_gmail_thread_get', {
|
|
155
|
-
description: 'Get a Gmail thread with all messages,
|
|
211
|
+
description: 'Get a Gmail thread with all messages. For long threads that overflow context, use latestN to fetch only the most recent messages and/or snippetsOnly for a lightweight per-message headers+snippet view; sanitizeContent strips raw payloads/HTML and is the biggest size reducer when you do need bodies.',
|
|
156
212
|
annotations: { readOnlyHint: true },
|
|
157
213
|
inputSchema: {
|
|
158
214
|
threadId: z.string().describe('Gmail thread ID'),
|
|
159
215
|
download: z.boolean().optional().describe('Download all attachments'),
|
|
160
216
|
full: z.boolean().optional().describe('Show full message bodies'),
|
|
161
|
-
sanitizeContent: z.boolean().optional().describe('Strip HTML, remove URLs, omit raw payloads from JSON'),
|
|
217
|
+
sanitizeContent: z.boolean().optional().describe('Strip HTML, remove URLs, omit raw payloads from JSON (largest payload-size reduction)'),
|
|
218
|
+
latestN: z.number().int().positive().optional().describe('Return only the most recent N messages in the thread (wrapper-side trim; avoids overflowing context on long threads)'),
|
|
219
|
+
snippetsOnly: z.boolean().optional().describe('Reduce each message to its id, labels, snippet, and key headers (From/To/Cc/Subject/Date), dropping full bodies'),
|
|
162
220
|
outDir: z.string().optional().describe('Directory to write attachments to (default: current directory)'),
|
|
163
221
|
account: accountParam,
|
|
164
222
|
},
|
|
165
|
-
}, async ({ threadId, download, full, sanitizeContent, outDir, account }) => {
|
|
223
|
+
}, async ({ threadId, download, full, sanitizeContent, latestN, snippetsOnly, outDir, account }) => {
|
|
166
224
|
const args = ['gmail', 'thread', 'get', threadId];
|
|
167
225
|
if (download) args.push('--download');
|
|
168
226
|
if (full) args.push('--full');
|
|
169
227
|
if (sanitizeContent) args.push('--sanitize-content');
|
|
170
228
|
if (outDir) args.push(`--out-dir=${outDir}`);
|
|
171
|
-
|
|
229
|
+
const result = await runOrDiagnose(args, { account });
|
|
230
|
+
if (latestN === undefined && !snippetsOnly) return result;
|
|
231
|
+
return trimThread(result, latestN, snippetsOnly);
|
|
172
232
|
});
|
|
173
233
|
|
|
174
234
|
server.registerTool('gog_gmail_thread_modify', {
|
|
@@ -316,6 +376,8 @@ export function registerExtraGmailTools(server: McpServer): void {
|
|
|
316
376
|
quote: z.boolean().optional().describe('Include quoted original message in reply (requires replyToMessageId)'),
|
|
317
377
|
attach: z.array(z.string()).optional().describe('Attachment file paths (repeatable)'),
|
|
318
378
|
from: z.string().optional().describe('Send from this email address (must be a verified send-as alias)'),
|
|
379
|
+
omitRecipients: z.boolean().optional().describe('Create the draft with no recipients even if to/cc/bcc are supplied — an accidental-send guard. Populate recipients in a later update before sending.'),
|
|
380
|
+
returnFull: z.boolean().optional().describe('After writing, re-fetch and return the full stored draft (subject, body, recipients) instead of just the write acknowledgement. Costs one extra read.'),
|
|
319
381
|
account: accountParam,
|
|
320
382
|
};
|
|
321
383
|
|
|
@@ -331,12 +393,15 @@ export function registerExtraGmailTools(server: McpServer): void {
|
|
|
331
393
|
quote?: boolean;
|
|
332
394
|
attach?: string[];
|
|
333
395
|
from?: string;
|
|
396
|
+
omitRecipients?: boolean;
|
|
334
397
|
};
|
|
335
398
|
|
|
336
399
|
function appendDraftFlags(args: string[], f: DraftFlags): void {
|
|
337
|
-
if (f.
|
|
338
|
-
|
|
339
|
-
|
|
400
|
+
if (!f.omitRecipients) {
|
|
401
|
+
if (f.to) args.push(`--to=${f.to}`);
|
|
402
|
+
if (f.cc) args.push(`--cc=${f.cc}`);
|
|
403
|
+
if (f.bcc) args.push(`--bcc=${f.bcc}`);
|
|
404
|
+
}
|
|
340
405
|
args.push(`--subject=${f.subject}`);
|
|
341
406
|
args.push(`--body=${f.body}`);
|
|
342
407
|
if (f.bodyHtml) args.push(`--body-html=${f.bodyHtml}`);
|
|
@@ -347,13 +412,41 @@ export function registerExtraGmailTools(server: McpServer): void {
|
|
|
347
412
|
if (f.from) args.push(`--from=${f.from}`);
|
|
348
413
|
}
|
|
349
414
|
|
|
415
|
+
// Run a draft write, then — when returnFull is set — re-fetch the stored
|
|
416
|
+
// draft so the caller can verify subject/body/recipients persisted without a
|
|
417
|
+
// separate gog_gmail_drafts_get round trip. For updates the id is known up
|
|
418
|
+
// front; for creates it's read from the write response's draftId. Degrades to
|
|
419
|
+
// the raw write result if the id can't be determined.
|
|
420
|
+
async function writeDraft(
|
|
421
|
+
args: string[],
|
|
422
|
+
account: string | undefined,
|
|
423
|
+
returnFull: boolean | undefined,
|
|
424
|
+
knownDraftId?: string,
|
|
425
|
+
): Promise<ToolResult> {
|
|
426
|
+
const result = await runOrDiagnose(args, { account });
|
|
427
|
+
if (!returnFull) return result;
|
|
428
|
+
// The write must have returned a JSON acknowledgement before we re-fetch.
|
|
429
|
+
// A failed write (an error ToolResult, not JSON) is surfaced as-is rather
|
|
430
|
+
// than masked by re-fetching the unchanged draft — this matters for the
|
|
431
|
+
// update path, where a known draftId would otherwise re-fetch a stale draft.
|
|
432
|
+
let parsed: { draftId?: string };
|
|
433
|
+
try {
|
|
434
|
+
parsed = JSON.parse(result.content[0].text) as { draftId?: string };
|
|
435
|
+
} catch {
|
|
436
|
+
return result;
|
|
437
|
+
}
|
|
438
|
+
const draftId = knownDraftId ?? parsed.draftId;
|
|
439
|
+
if (!draftId) return result;
|
|
440
|
+
return runOrDiagnose(['gmail', 'drafts', 'get', draftId], { account });
|
|
441
|
+
}
|
|
442
|
+
|
|
350
443
|
server.registerTool('gog_gmail_drafts_create', {
|
|
351
|
-
description: 'Create a new Gmail draft.',
|
|
444
|
+
description: 'Create a new Gmail draft. Recipients (to/cc/bcc) are optional; omit them (or set omitRecipients) to create a recipient-less draft as an accidental-send guard.',
|
|
352
445
|
inputSchema: draftWriteSchema,
|
|
353
|
-
}, async ({ account, ...flags }) => {
|
|
446
|
+
}, async ({ account, returnFull, ...flags }) => {
|
|
354
447
|
const args = ['gmail', 'drafts', 'create'];
|
|
355
448
|
appendDraftFlags(args, flags);
|
|
356
|
-
return
|
|
449
|
+
return writeDraft(args, account, returnFull);
|
|
357
450
|
});
|
|
358
451
|
|
|
359
452
|
server.registerTool('gog_gmail_drafts_update', {
|
|
@@ -363,21 +456,24 @@ export function registerExtraGmailTools(server: McpServer): void {
|
|
|
363
456
|
draftId: z.string().describe('Draft ID'),
|
|
364
457
|
...draftWriteSchema,
|
|
365
458
|
},
|
|
366
|
-
}, async ({ draftId, account, ...flags }) => {
|
|
459
|
+
}, async ({ draftId, account, returnFull, ...flags }) => {
|
|
367
460
|
const args = ['gmail', 'drafts', 'update', draftId];
|
|
368
461
|
appendDraftFlags(args, flags);
|
|
369
|
-
return
|
|
462
|
+
return writeDraft(args, account, returnFull, draftId);
|
|
370
463
|
});
|
|
371
464
|
|
|
372
465
|
server.registerTool('gog_gmail_drafts_delete', {
|
|
373
|
-
description: '
|
|
466
|
+
description: 'Permanently delete a Gmail draft (not reversible — drafts do not go to Trash). Requires force:true to delete non-interactively.',
|
|
374
467
|
annotations: { destructiveHint: true },
|
|
375
468
|
inputSchema: {
|
|
376
469
|
draftId: z.string().describe('Draft ID'),
|
|
470
|
+
force: z.boolean().optional().describe('Required to delete in this non-interactive context — without it the delete is refused as a safety guard.'),
|
|
377
471
|
account: accountParam,
|
|
378
472
|
},
|
|
379
|
-
}, async ({ draftId, account }) => {
|
|
380
|
-
|
|
473
|
+
}, async ({ draftId, account, force }) => {
|
|
474
|
+
const args = ['gmail', 'drafts', 'delete', draftId];
|
|
475
|
+
if (force) args.push('--force');
|
|
476
|
+
return runOrDiagnose(args, { account });
|
|
381
477
|
});
|
|
382
478
|
|
|
383
479
|
server.registerTool('gog_gmail_drafts_send', {
|
|
@@ -447,4 +543,237 @@ export function registerExtraGmailTools(server: McpServer): void {
|
|
|
447
543
|
if (allowSelf) args.push('--allow-self');
|
|
448
544
|
return runOrDiagnose(args, { account });
|
|
449
545
|
});
|
|
546
|
+
|
|
547
|
+
server.registerTool('gog_gmail_messages_search', {
|
|
548
|
+
description: 'Search individual messages (not threads) using Gmail query syntax. Returns one result per matching message.',
|
|
549
|
+
annotations: { readOnlyHint: true },
|
|
550
|
+
inputSchema: {
|
|
551
|
+
query: z.string().describe('Gmail search query (e.g. "from:alice is:unread has:attachment")'),
|
|
552
|
+
max: z.number().optional().describe('Max results'),
|
|
553
|
+
page: z.string().optional().describe('Page token'),
|
|
554
|
+
all: z.boolean().optional().describe('Fetch all pages'),
|
|
555
|
+
includeBody: z.boolean().optional().describe('Include the decoded message body in each result'),
|
|
556
|
+
full: z.boolean().optional().describe('Show full message bodies without truncation (implies includeBody)'),
|
|
557
|
+
bodyFormat: z.enum(['text', 'html']).optional().describe('Body format preference when includeBody is set'),
|
|
558
|
+
account: accountParam,
|
|
559
|
+
},
|
|
560
|
+
}, async ({ query, max, page, all, includeBody, full, bodyFormat, account }) => {
|
|
561
|
+
const args = ['gmail', 'messages', 'search', query];
|
|
562
|
+
if (max !== undefined) args.push(`--max=${max}`);
|
|
563
|
+
if (page) args.push(`--page=${page}`);
|
|
564
|
+
if (all) args.push('--all');
|
|
565
|
+
if (includeBody) args.push('--include-body');
|
|
566
|
+
if (full) args.push('--full');
|
|
567
|
+
if (bodyFormat) args.push(`--body-format=${bodyFormat}`);
|
|
568
|
+
return runOrDiagnose(args, { account });
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
server.registerTool('gog_gmail_labels_style', {
|
|
572
|
+
description: "Change a user label's color or visibility (background/text color from Gmail's palette, label-list and message-list visibility).",
|
|
573
|
+
annotations: { destructiveHint: true },
|
|
574
|
+
inputSchema: {
|
|
575
|
+
labelIdOrName: z.string().describe('Label ID or name to restyle'),
|
|
576
|
+
backgroundColor: z.string().optional().describe("Background color from Gmail's label palette as #RRGGBB"),
|
|
577
|
+
textColor: z.string().optional().describe("Text color from Gmail's label palette as #RRGGBB"),
|
|
578
|
+
labelListVisibility: z.enum(['labelShow', 'labelShowIfUnread', 'labelHide']).optional().describe('Label-list visibility'),
|
|
579
|
+
messageListVisibility: z.enum(['show', 'hide']).optional().describe('Message-list visibility'),
|
|
580
|
+
account: accountParam,
|
|
581
|
+
},
|
|
582
|
+
}, async ({ labelIdOrName, backgroundColor, textColor, labelListVisibility, messageListVisibility, account }) => {
|
|
583
|
+
const args = ['gmail', 'labels', 'style', labelIdOrName];
|
|
584
|
+
if (backgroundColor) args.push(`--background-color=${backgroundColor}`);
|
|
585
|
+
if (textColor) args.push(`--text-color=${textColor}`);
|
|
586
|
+
if (labelListVisibility) args.push(`--label-list-visibility=${labelListVisibility}`);
|
|
587
|
+
if (messageListVisibility) args.push(`--message-list-visibility=${messageListVisibility}`);
|
|
588
|
+
return runOrDiagnose(args, { account });
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
server.registerTool('gog_gmail_vacation_get', {
|
|
592
|
+
description: 'Get the current vacation responder (auto-reply) settings.',
|
|
593
|
+
annotations: { readOnlyHint: true },
|
|
594
|
+
inputSchema: {
|
|
595
|
+
account: accountParam,
|
|
596
|
+
},
|
|
597
|
+
}, async ({ account }) => {
|
|
598
|
+
return runOrDiagnose(['gmail', 'settings', 'vacation', 'get'], { account });
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
server.registerTool('gog_gmail_vacation_update', {
|
|
602
|
+
description: 'Update the vacation responder. Pass enable (with subject/body) to turn it on, or disable to turn it off; optional start/end RFC3339 times and contactsOnly/domainOnly scoping.',
|
|
603
|
+
inputSchema: {
|
|
604
|
+
enable: z.boolean().optional().describe('Enable the vacation responder'),
|
|
605
|
+
disable: z.boolean().optional().describe('Disable the vacation responder'),
|
|
606
|
+
subject: z.string().optional().describe('Subject line for the auto-reply'),
|
|
607
|
+
body: z.string().optional().describe('HTML body of the auto-reply message'),
|
|
608
|
+
start: z.string().optional().describe('Start time in RFC3339 format (e.g. 2024-12-20T00:00:00Z)'),
|
|
609
|
+
end: z.string().optional().describe('End time in RFC3339 format (e.g. 2024-12-31T23:59:59Z)'),
|
|
610
|
+
contactsOnly: z.boolean().optional().describe('Only respond to contacts'),
|
|
611
|
+
domainOnly: z.boolean().optional().describe('Only respond to senders in the same domain'),
|
|
612
|
+
account: accountParam,
|
|
613
|
+
},
|
|
614
|
+
}, async ({ enable, disable, subject, body, start, end, contactsOnly, domainOnly, account }) => {
|
|
615
|
+
const args = ['gmail', 'settings', 'vacation', 'update'];
|
|
616
|
+
if (enable) args.push('--enable');
|
|
617
|
+
if (disable) args.push('--disable');
|
|
618
|
+
if (subject) args.push(`--subject=${subject}`);
|
|
619
|
+
if (body) args.push(`--body=${body}`);
|
|
620
|
+
if (start) args.push(`--start=${start}`);
|
|
621
|
+
if (end) args.push(`--end=${end}`);
|
|
622
|
+
if (contactsOnly) args.push('--contacts-only');
|
|
623
|
+
if (domainOnly) args.push('--domain-only');
|
|
624
|
+
return runOrDiagnose(args, { account });
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
server.registerTool('gog_gmail_filters_list', {
|
|
628
|
+
description: 'List all Gmail filters for the account.',
|
|
629
|
+
annotations: { readOnlyHint: true },
|
|
630
|
+
inputSchema: {
|
|
631
|
+
account: accountParam,
|
|
632
|
+
},
|
|
633
|
+
}, async ({ account }) => {
|
|
634
|
+
return runOrDiagnose(['gmail', 'settings', 'filters', 'list'], { account });
|
|
635
|
+
});
|
|
636
|
+
|
|
637
|
+
server.registerTool('gog_gmail_filters_get', {
|
|
638
|
+
description: 'Get the criteria and actions of a single Gmail filter by ID.',
|
|
639
|
+
annotations: { readOnlyHint: true },
|
|
640
|
+
inputSchema: {
|
|
641
|
+
filterId: z.string().describe('Filter ID'),
|
|
642
|
+
account: accountParam,
|
|
643
|
+
},
|
|
644
|
+
}, async ({ filterId, account }) => {
|
|
645
|
+
return runOrDiagnose(['gmail', 'settings', 'filters', 'get', filterId], { account });
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
server.registerTool('gog_gmail_filters_create', {
|
|
649
|
+
description: 'Create a Gmail filter. Specify match criteria (from/to/subject/query/hasAttachment) and one or more actions (label, archive, mark-read, star, important, trash, forward, never-spam).',
|
|
650
|
+
inputSchema: {
|
|
651
|
+
from: z.string().optional().describe('Match messages from this sender'),
|
|
652
|
+
to: z.string().optional().describe('Match messages to this recipient'),
|
|
653
|
+
subject: z.string().optional().describe('Match messages with this subject'),
|
|
654
|
+
query: z.string().optional().describe('Advanced Gmail search query for matching'),
|
|
655
|
+
hasAttachment: z.boolean().optional().describe('Match messages with attachments'),
|
|
656
|
+
addLabel: z.string().optional().describe('Label(s) to add to matching messages (comma-separated, name or ID)'),
|
|
657
|
+
removeLabel: z.string().optional().describe('Label(s) to remove from matching messages (comma-separated, name or ID)'),
|
|
658
|
+
archive: z.boolean().optional().describe('Archive matching messages (skip inbox)'),
|
|
659
|
+
markRead: z.boolean().optional().describe('Mark matching messages as read'),
|
|
660
|
+
star: z.boolean().optional().describe('Star matching messages'),
|
|
661
|
+
important: z.boolean().optional().describe('Mark as important'),
|
|
662
|
+
trash: z.boolean().optional().describe('Move matching messages to trash'),
|
|
663
|
+
neverSpam: z.boolean().optional().describe('Never mark as spam'),
|
|
664
|
+
forward: z.string().optional().describe('Forward to this email address (must be a verified forwarding address)'),
|
|
665
|
+
account: accountParam,
|
|
666
|
+
},
|
|
667
|
+
}, async ({ from, to, subject, query, hasAttachment, addLabel, removeLabel, archive, markRead, star, important, trash, neverSpam, forward, account }) => {
|
|
668
|
+
const args = ['gmail', 'settings', 'filters', 'create'];
|
|
669
|
+
if (from) args.push(`--from=${from}`);
|
|
670
|
+
if (to) args.push(`--to=${to}`);
|
|
671
|
+
if (subject) args.push(`--subject=${subject}`);
|
|
672
|
+
if (query) args.push(`--query=${query}`);
|
|
673
|
+
if (hasAttachment) args.push('--has-attachment');
|
|
674
|
+
if (addLabel) args.push(`--add-label=${addLabel}`);
|
|
675
|
+
if (removeLabel) args.push(`--remove-label=${removeLabel}`);
|
|
676
|
+
if (archive) args.push('--archive');
|
|
677
|
+
if (markRead) args.push('--mark-read');
|
|
678
|
+
if (star) args.push('--star');
|
|
679
|
+
if (important) args.push('--important');
|
|
680
|
+
if (trash) args.push('--trash');
|
|
681
|
+
if (neverSpam) args.push('--never-spam');
|
|
682
|
+
if (forward) args.push(`--forward=${forward}`);
|
|
683
|
+
return runOrDiagnose(args, { account });
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
server.registerTool('gog_gmail_filters_delete', {
|
|
687
|
+
description: 'Delete a Gmail filter by ID.',
|
|
688
|
+
annotations: { destructiveHint: true },
|
|
689
|
+
inputSchema: {
|
|
690
|
+
filterId: z.string().describe('Filter ID to delete'),
|
|
691
|
+
account: accountParam,
|
|
692
|
+
},
|
|
693
|
+
}, async ({ filterId, account }) => {
|
|
694
|
+
return runOrDiagnose(['gmail', 'settings', 'filters', 'delete', filterId], { account });
|
|
695
|
+
});
|
|
696
|
+
|
|
697
|
+
server.registerTool('gog_gmail_sendas_list', {
|
|
698
|
+
description: 'List all send-as aliases configured for the account.',
|
|
699
|
+
annotations: { readOnlyHint: true },
|
|
700
|
+
inputSchema: {
|
|
701
|
+
account: accountParam,
|
|
702
|
+
},
|
|
703
|
+
}, async ({ account }) => {
|
|
704
|
+
return runOrDiagnose(['gmail', 'settings', 'sendas', 'list'], { account });
|
|
705
|
+
});
|
|
706
|
+
|
|
707
|
+
server.registerTool('gog_gmail_sendas_get', {
|
|
708
|
+
description: 'Get details of a single send-as alias by its email address.',
|
|
709
|
+
annotations: { readOnlyHint: true },
|
|
710
|
+
inputSchema: {
|
|
711
|
+
email: z.string().describe('Send-as alias email address'),
|
|
712
|
+
account: accountParam,
|
|
713
|
+
},
|
|
714
|
+
}, async ({ email, account }) => {
|
|
715
|
+
return runOrDiagnose(['gmail', 'settings', 'sendas', 'get', email], { account });
|
|
716
|
+
});
|
|
717
|
+
|
|
718
|
+
server.registerTool('gog_gmail_sendas_create', {
|
|
719
|
+
description: 'Create a send-as alias. Newly added aliases generally require email verification before they can be used (see gog_gmail_sendas_verify).',
|
|
720
|
+
inputSchema: {
|
|
721
|
+
email: z.string().describe('Email address of the new send-as alias'),
|
|
722
|
+
displayName: z.string().optional().describe('Name that appears in the From field'),
|
|
723
|
+
replyTo: z.string().optional().describe('Reply-to address'),
|
|
724
|
+
signature: z.string().optional().describe('HTML signature for emails sent from this alias'),
|
|
725
|
+
treatAsAlias: z.boolean().optional().describe('Treat as alias (replies sent from Gmail web)'),
|
|
726
|
+
account: accountParam,
|
|
727
|
+
},
|
|
728
|
+
}, async ({ email, displayName, replyTo, signature, treatAsAlias, account }) => {
|
|
729
|
+
const args = ['gmail', 'settings', 'sendas', 'create', email];
|
|
730
|
+
if (displayName) args.push(`--display-name=${displayName}`);
|
|
731
|
+
if (replyTo) args.push(`--reply-to=${replyTo}`);
|
|
732
|
+
if (signature) args.push(`--signature=${signature}`);
|
|
733
|
+
if (treatAsAlias) args.push('--treat-as-alias');
|
|
734
|
+
return runOrDiagnose(args, { account });
|
|
735
|
+
});
|
|
736
|
+
|
|
737
|
+
server.registerTool('gog_gmail_sendas_update', {
|
|
738
|
+
description: 'Update a send-as alias (display name, reply-to, signature, alias handling, or make it the default).',
|
|
739
|
+
annotations: { destructiveHint: true },
|
|
740
|
+
inputSchema: {
|
|
741
|
+
email: z.string().describe('Send-as alias email address to update'),
|
|
742
|
+
displayName: z.string().optional().describe('Name that appears in the From field'),
|
|
743
|
+
replyTo: z.string().optional().describe('Reply-to address'),
|
|
744
|
+
signature: z.string().optional().describe('HTML signature'),
|
|
745
|
+
treatAsAlias: z.boolean().optional().describe('Treat as alias'),
|
|
746
|
+
makeDefault: z.boolean().optional().describe('Make this the default send-as address'),
|
|
747
|
+
account: accountParam,
|
|
748
|
+
},
|
|
749
|
+
}, async ({ email, displayName, replyTo, signature, treatAsAlias, makeDefault, account }) => {
|
|
750
|
+
const args = ['gmail', 'settings', 'sendas', 'update', email];
|
|
751
|
+
if (displayName) args.push(`--display-name=${displayName}`);
|
|
752
|
+
if (replyTo) args.push(`--reply-to=${replyTo}`);
|
|
753
|
+
if (signature) args.push(`--signature=${signature}`);
|
|
754
|
+
if (treatAsAlias) args.push('--treat-as-alias');
|
|
755
|
+
if (makeDefault) args.push('--make-default');
|
|
756
|
+
return runOrDiagnose(args, { account });
|
|
757
|
+
});
|
|
758
|
+
|
|
759
|
+
server.registerTool('gog_gmail_sendas_delete', {
|
|
760
|
+
description: 'Delete a send-as alias by its email address.',
|
|
761
|
+
annotations: { destructiveHint: true },
|
|
762
|
+
inputSchema: {
|
|
763
|
+
email: z.string().describe('Send-as alias email address to delete'),
|
|
764
|
+
account: accountParam,
|
|
765
|
+
},
|
|
766
|
+
}, async ({ email, account }) => {
|
|
767
|
+
return runOrDiagnose(['gmail', 'settings', 'sendas', 'delete', email], { account });
|
|
768
|
+
});
|
|
769
|
+
|
|
770
|
+
server.registerTool('gog_gmail_sendas_verify', {
|
|
771
|
+
description: 'Resend the verification email for a send-as alias that is pending verification.',
|
|
772
|
+
inputSchema: {
|
|
773
|
+
email: z.string().describe('Send-as alias email address to verify'),
|
|
774
|
+
account: accountParam,
|
|
775
|
+
},
|
|
776
|
+
}, async ({ email, account }) => {
|
|
777
|
+
return runOrDiagnose(['gmail', 'settings', 'sendas', 'verify', email], { account });
|
|
778
|
+
});
|
|
450
779
|
}
|