gogcli-mcp-gmail 2.2.0 → 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/dist/index.js CHANGED
@@ -31295,7 +31295,7 @@ var failIfNotEmptyParam = external_exports.boolean().optional().describe(
31295
31295
  );
31296
31296
 
31297
31297
  // ../gogcli-mcp/src/server.ts
31298
- var VERSION = true ? "2.2.0" : "0.0.0";
31298
+ var VERSION = true ? "2.3.0" : "0.0.0";
31299
31299
  function createServer(options) {
31300
31300
  return new McpServer({
31301
31301
  name: options?.name ?? "gogcli",
@@ -31763,6 +31763,225 @@ function registerExtraGmailTools(server2) {
31763
31763
  if (allowSelf) args.push("--allow-self");
31764
31764
  return runOrDiagnose(args, { account });
31765
31765
  });
31766
+ server2.registerTool("gog_gmail_messages_search", {
31767
+ description: "Search individual messages (not threads) using Gmail query syntax. Returns one result per matching message.",
31768
+ annotations: { readOnlyHint: true },
31769
+ inputSchema: {
31770
+ query: external_exports.string().describe('Gmail search query (e.g. "from:alice is:unread has:attachment")'),
31771
+ max: external_exports.number().optional().describe("Max results"),
31772
+ page: external_exports.string().optional().describe("Page token"),
31773
+ all: external_exports.boolean().optional().describe("Fetch all pages"),
31774
+ includeBody: external_exports.boolean().optional().describe("Include the decoded message body in each result"),
31775
+ full: external_exports.boolean().optional().describe("Show full message bodies without truncation (implies includeBody)"),
31776
+ bodyFormat: external_exports.enum(["text", "html"]).optional().describe("Body format preference when includeBody is set"),
31777
+ account: accountParam
31778
+ }
31779
+ }, async ({ query, max, page, all, includeBody, full, bodyFormat, account }) => {
31780
+ const args = ["gmail", "messages", "search", query];
31781
+ if (max !== void 0) args.push(`--max=${max}`);
31782
+ if (page) args.push(`--page=${page}`);
31783
+ if (all) args.push("--all");
31784
+ if (includeBody) args.push("--include-body");
31785
+ if (full) args.push("--full");
31786
+ if (bodyFormat) args.push(`--body-format=${bodyFormat}`);
31787
+ return runOrDiagnose(args, { account });
31788
+ });
31789
+ server2.registerTool("gog_gmail_labels_style", {
31790
+ description: "Change a user label's color or visibility (background/text color from Gmail's palette, label-list and message-list visibility).",
31791
+ annotations: { destructiveHint: true },
31792
+ inputSchema: {
31793
+ labelIdOrName: external_exports.string().describe("Label ID or name to restyle"),
31794
+ backgroundColor: external_exports.string().optional().describe("Background color from Gmail's label palette as #RRGGBB"),
31795
+ textColor: external_exports.string().optional().describe("Text color from Gmail's label palette as #RRGGBB"),
31796
+ labelListVisibility: external_exports.enum(["labelShow", "labelShowIfUnread", "labelHide"]).optional().describe("Label-list visibility"),
31797
+ messageListVisibility: external_exports.enum(["show", "hide"]).optional().describe("Message-list visibility"),
31798
+ account: accountParam
31799
+ }
31800
+ }, async ({ labelIdOrName, backgroundColor, textColor, labelListVisibility, messageListVisibility, account }) => {
31801
+ const args = ["gmail", "labels", "style", labelIdOrName];
31802
+ if (backgroundColor) args.push(`--background-color=${backgroundColor}`);
31803
+ if (textColor) args.push(`--text-color=${textColor}`);
31804
+ if (labelListVisibility) args.push(`--label-list-visibility=${labelListVisibility}`);
31805
+ if (messageListVisibility) args.push(`--message-list-visibility=${messageListVisibility}`);
31806
+ return runOrDiagnose(args, { account });
31807
+ });
31808
+ server2.registerTool("gog_gmail_vacation_get", {
31809
+ description: "Get the current vacation responder (auto-reply) settings.",
31810
+ annotations: { readOnlyHint: true },
31811
+ inputSchema: {
31812
+ account: accountParam
31813
+ }
31814
+ }, async ({ account }) => {
31815
+ return runOrDiagnose(["gmail", "settings", "vacation", "get"], { account });
31816
+ });
31817
+ server2.registerTool("gog_gmail_vacation_update", {
31818
+ 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.",
31819
+ inputSchema: {
31820
+ enable: external_exports.boolean().optional().describe("Enable the vacation responder"),
31821
+ disable: external_exports.boolean().optional().describe("Disable the vacation responder"),
31822
+ subject: external_exports.string().optional().describe("Subject line for the auto-reply"),
31823
+ body: external_exports.string().optional().describe("HTML body of the auto-reply message"),
31824
+ start: external_exports.string().optional().describe("Start time in RFC3339 format (e.g. 2024-12-20T00:00:00Z)"),
31825
+ end: external_exports.string().optional().describe("End time in RFC3339 format (e.g. 2024-12-31T23:59:59Z)"),
31826
+ contactsOnly: external_exports.boolean().optional().describe("Only respond to contacts"),
31827
+ domainOnly: external_exports.boolean().optional().describe("Only respond to senders in the same domain"),
31828
+ account: accountParam
31829
+ }
31830
+ }, async ({ enable, disable, subject, body, start, end, contactsOnly, domainOnly, account }) => {
31831
+ const args = ["gmail", "settings", "vacation", "update"];
31832
+ if (enable) args.push("--enable");
31833
+ if (disable) args.push("--disable");
31834
+ if (subject) args.push(`--subject=${subject}`);
31835
+ if (body) args.push(`--body=${body}`);
31836
+ if (start) args.push(`--start=${start}`);
31837
+ if (end) args.push(`--end=${end}`);
31838
+ if (contactsOnly) args.push("--contacts-only");
31839
+ if (domainOnly) args.push("--domain-only");
31840
+ return runOrDiagnose(args, { account });
31841
+ });
31842
+ server2.registerTool("gog_gmail_filters_list", {
31843
+ description: "List all Gmail filters for the account.",
31844
+ annotations: { readOnlyHint: true },
31845
+ inputSchema: {
31846
+ account: accountParam
31847
+ }
31848
+ }, async ({ account }) => {
31849
+ return runOrDiagnose(["gmail", "settings", "filters", "list"], { account });
31850
+ });
31851
+ server2.registerTool("gog_gmail_filters_get", {
31852
+ description: "Get the criteria and actions of a single Gmail filter by ID.",
31853
+ annotations: { readOnlyHint: true },
31854
+ inputSchema: {
31855
+ filterId: external_exports.string().describe("Filter ID"),
31856
+ account: accountParam
31857
+ }
31858
+ }, async ({ filterId, account }) => {
31859
+ return runOrDiagnose(["gmail", "settings", "filters", "get", filterId], { account });
31860
+ });
31861
+ server2.registerTool("gog_gmail_filters_create", {
31862
+ 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).",
31863
+ inputSchema: {
31864
+ from: external_exports.string().optional().describe("Match messages from this sender"),
31865
+ to: external_exports.string().optional().describe("Match messages to this recipient"),
31866
+ subject: external_exports.string().optional().describe("Match messages with this subject"),
31867
+ query: external_exports.string().optional().describe("Advanced Gmail search query for matching"),
31868
+ hasAttachment: external_exports.boolean().optional().describe("Match messages with attachments"),
31869
+ addLabel: external_exports.string().optional().describe("Label(s) to add to matching messages (comma-separated, name or ID)"),
31870
+ removeLabel: external_exports.string().optional().describe("Label(s) to remove from matching messages (comma-separated, name or ID)"),
31871
+ archive: external_exports.boolean().optional().describe("Archive matching messages (skip inbox)"),
31872
+ markRead: external_exports.boolean().optional().describe("Mark matching messages as read"),
31873
+ star: external_exports.boolean().optional().describe("Star matching messages"),
31874
+ important: external_exports.boolean().optional().describe("Mark as important"),
31875
+ trash: external_exports.boolean().optional().describe("Move matching messages to trash"),
31876
+ neverSpam: external_exports.boolean().optional().describe("Never mark as spam"),
31877
+ forward: external_exports.string().optional().describe("Forward to this email address (must be a verified forwarding address)"),
31878
+ account: accountParam
31879
+ }
31880
+ }, async ({ from, to, subject, query, hasAttachment, addLabel, removeLabel, archive, markRead, star, important, trash, neverSpam, forward, account }) => {
31881
+ const args = ["gmail", "settings", "filters", "create"];
31882
+ if (from) args.push(`--from=${from}`);
31883
+ if (to) args.push(`--to=${to}`);
31884
+ if (subject) args.push(`--subject=${subject}`);
31885
+ if (query) args.push(`--query=${query}`);
31886
+ if (hasAttachment) args.push("--has-attachment");
31887
+ if (addLabel) args.push(`--add-label=${addLabel}`);
31888
+ if (removeLabel) args.push(`--remove-label=${removeLabel}`);
31889
+ if (archive) args.push("--archive");
31890
+ if (markRead) args.push("--mark-read");
31891
+ if (star) args.push("--star");
31892
+ if (important) args.push("--important");
31893
+ if (trash) args.push("--trash");
31894
+ if (neverSpam) args.push("--never-spam");
31895
+ if (forward) args.push(`--forward=${forward}`);
31896
+ return runOrDiagnose(args, { account });
31897
+ });
31898
+ server2.registerTool("gog_gmail_filters_delete", {
31899
+ description: "Delete a Gmail filter by ID.",
31900
+ annotations: { destructiveHint: true },
31901
+ inputSchema: {
31902
+ filterId: external_exports.string().describe("Filter ID to delete"),
31903
+ account: accountParam
31904
+ }
31905
+ }, async ({ filterId, account }) => {
31906
+ return runOrDiagnose(["gmail", "settings", "filters", "delete", filterId], { account });
31907
+ });
31908
+ server2.registerTool("gog_gmail_sendas_list", {
31909
+ description: "List all send-as aliases configured for the account.",
31910
+ annotations: { readOnlyHint: true },
31911
+ inputSchema: {
31912
+ account: accountParam
31913
+ }
31914
+ }, async ({ account }) => {
31915
+ return runOrDiagnose(["gmail", "settings", "sendas", "list"], { account });
31916
+ });
31917
+ server2.registerTool("gog_gmail_sendas_get", {
31918
+ description: "Get details of a single send-as alias by its email address.",
31919
+ annotations: { readOnlyHint: true },
31920
+ inputSchema: {
31921
+ email: external_exports.string().describe("Send-as alias email address"),
31922
+ account: accountParam
31923
+ }
31924
+ }, async ({ email: email3, account }) => {
31925
+ return runOrDiagnose(["gmail", "settings", "sendas", "get", email3], { account });
31926
+ });
31927
+ server2.registerTool("gog_gmail_sendas_create", {
31928
+ description: "Create a send-as alias. Newly added aliases generally require email verification before they can be used (see gog_gmail_sendas_verify).",
31929
+ inputSchema: {
31930
+ email: external_exports.string().describe("Email address of the new send-as alias"),
31931
+ displayName: external_exports.string().optional().describe("Name that appears in the From field"),
31932
+ replyTo: external_exports.string().optional().describe("Reply-to address"),
31933
+ signature: external_exports.string().optional().describe("HTML signature for emails sent from this alias"),
31934
+ treatAsAlias: external_exports.boolean().optional().describe("Treat as alias (replies sent from Gmail web)"),
31935
+ account: accountParam
31936
+ }
31937
+ }, async ({ email: email3, displayName, replyTo, signature, treatAsAlias, account }) => {
31938
+ const args = ["gmail", "settings", "sendas", "create", email3];
31939
+ if (displayName) args.push(`--display-name=${displayName}`);
31940
+ if (replyTo) args.push(`--reply-to=${replyTo}`);
31941
+ if (signature) args.push(`--signature=${signature}`);
31942
+ if (treatAsAlias) args.push("--treat-as-alias");
31943
+ return runOrDiagnose(args, { account });
31944
+ });
31945
+ server2.registerTool("gog_gmail_sendas_update", {
31946
+ description: "Update a send-as alias (display name, reply-to, signature, alias handling, or make it the default).",
31947
+ annotations: { destructiveHint: true },
31948
+ inputSchema: {
31949
+ email: external_exports.string().describe("Send-as alias email address to update"),
31950
+ displayName: external_exports.string().optional().describe("Name that appears in the From field"),
31951
+ replyTo: external_exports.string().optional().describe("Reply-to address"),
31952
+ signature: external_exports.string().optional().describe("HTML signature"),
31953
+ treatAsAlias: external_exports.boolean().optional().describe("Treat as alias"),
31954
+ makeDefault: external_exports.boolean().optional().describe("Make this the default send-as address"),
31955
+ account: accountParam
31956
+ }
31957
+ }, async ({ email: email3, displayName, replyTo, signature, treatAsAlias, makeDefault, account }) => {
31958
+ const args = ["gmail", "settings", "sendas", "update", email3];
31959
+ if (displayName) args.push(`--display-name=${displayName}`);
31960
+ if (replyTo) args.push(`--reply-to=${replyTo}`);
31961
+ if (signature) args.push(`--signature=${signature}`);
31962
+ if (treatAsAlias) args.push("--treat-as-alias");
31963
+ if (makeDefault) args.push("--make-default");
31964
+ return runOrDiagnose(args, { account });
31965
+ });
31966
+ server2.registerTool("gog_gmail_sendas_delete", {
31967
+ description: "Delete a send-as alias by its email address.",
31968
+ annotations: { destructiveHint: true },
31969
+ inputSchema: {
31970
+ email: external_exports.string().describe("Send-as alias email address to delete"),
31971
+ account: accountParam
31972
+ }
31973
+ }, async ({ email: email3, account }) => {
31974
+ return runOrDiagnose(["gmail", "settings", "sendas", "delete", email3], { account });
31975
+ });
31976
+ server2.registerTool("gog_gmail_sendas_verify", {
31977
+ description: "Resend the verification email for a send-as alias that is pending verification.",
31978
+ inputSchema: {
31979
+ email: external_exports.string().describe("Send-as alias email address to verify"),
31980
+ account: accountParam
31981
+ }
31982
+ }, async ({ email: email3, account }) => {
31983
+ return runOrDiagnose(["gmail", "settings", "sendas", "verify", email3], { account });
31984
+ });
31766
31985
  }
31767
31986
 
31768
31987
  // src/index.ts
package/manifest.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "manifest_version": "0.3",
4
4
  "name": "gogcli-mcp-gmail",
5
5
  "display_name": "gogcli (Gmail)",
6
- "version": "2.2.0",
6
+ "version": "2.3.0",
7
7
  "description": "Extended Gmail for Claude via gogcli — auth + full Gmail support (threads, labels, drafts, attachments, forward, autoreply, bulk operations)",
8
8
  "author": {
9
9
  "name": "Chris Hall",
@@ -204,6 +204,62 @@
204
204
  {
205
205
  "name": "gog_gmail_autoreply",
206
206
  "description": "Reply once to all messages matching a Gmail search query"
207
+ },
208
+ {
209
+ "name": "gog_gmail_messages_search",
210
+ "description": "Search individual messages (not threads) using Gmail query syntax"
211
+ },
212
+ {
213
+ "name": "gog_gmail_labels_style",
214
+ "description": "Change a user label's color or visibility"
215
+ },
216
+ {
217
+ "name": "gog_gmail_vacation_get",
218
+ "description": "Get the current vacation responder settings"
219
+ },
220
+ {
221
+ "name": "gog_gmail_vacation_update",
222
+ "description": "Enable, disable, or update the vacation responder"
223
+ },
224
+ {
225
+ "name": "gog_gmail_filters_list",
226
+ "description": "List all Gmail filters"
227
+ },
228
+ {
229
+ "name": "gog_gmail_filters_get",
230
+ "description": "Get a single Gmail filter's criteria and actions"
231
+ },
232
+ {
233
+ "name": "gog_gmail_filters_create",
234
+ "description": "Create a Gmail filter with match criteria and actions"
235
+ },
236
+ {
237
+ "name": "gog_gmail_filters_delete",
238
+ "description": "Delete a Gmail filter by ID"
239
+ },
240
+ {
241
+ "name": "gog_gmail_sendas_list",
242
+ "description": "List all send-as aliases for the account"
243
+ },
244
+ {
245
+ "name": "gog_gmail_sendas_get",
246
+ "description": "Get details of a single send-as alias"
247
+ },
248
+ {
249
+ "name": "gog_gmail_sendas_create",
250
+ "description": "Create a send-as alias"
251
+ },
252
+ {
253
+ "name": "gog_gmail_sendas_update",
254
+ "description": "Update a send-as alias or make it the default"
255
+ },
256
+ {
257
+ "name": "gog_gmail_sendas_delete",
258
+ "description": "Delete a send-as alias"
259
+ },
260
+ {
261
+ "name": "gog_gmail_sendas_verify",
262
+ "description": "Resend the verification email for a send-as alias"
207
263
  }
208
264
  ],
209
265
  "compatibility": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gogcli-mcp-gmail",
3
- "version": "2.2.0",
3
+ "version": "2.3.0",
4
4
  "mcpName": "io.github.chrischall/gogcli-mcp-gmail",
5
5
  "description": "Extended Gmail MCP server via gogcli — auth + full Gmail support (threads, labels, drafts, attachments, forward, autoreply, bulk operations)",
6
6
  "author": "Claude Code (AI) <https://www.anthropic.com/claude>",
@@ -543,4 +543,237 @@ export function registerExtraGmailTools(server: McpServer): void {
543
543
  if (allowSelf) args.push('--allow-self');
544
544
  return runOrDiagnose(args, { account });
545
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
+ });
546
779
  }
@@ -748,3 +748,291 @@ describe('gog_gmail_autoreply', () => {
748
748
  );
749
749
  });
750
750
  });
751
+
752
+ describe('gog_gmail_messages_search', () => {
753
+ it('calls runOrDiagnose with just the query', async () => {
754
+ await handlers.get('gog_gmail_messages_search')!({ query: 'from:alice' });
755
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
756
+ ['gmail', 'messages', 'search', 'from:alice'],
757
+ { account: undefined },
758
+ );
759
+ });
760
+
761
+ it('passes all flags when provided', async () => {
762
+ await handlers.get('gog_gmail_messages_search')!({
763
+ query: 'is:unread',
764
+ max: 10,
765
+ page: 'tok',
766
+ all: true,
767
+ includeBody: true,
768
+ full: true,
769
+ bodyFormat: 'html',
770
+ account: 'me@x.com',
771
+ });
772
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
773
+ ['gmail', 'messages', 'search', 'is:unread', '--max=10', '--page=tok', '--all', '--include-body', '--full', '--body-format=html'],
774
+ { account: 'me@x.com' },
775
+ );
776
+ });
777
+
778
+ it('omits flags when false/absent', async () => {
779
+ await handlers.get('gog_gmail_messages_search')!({ query: 'x', all: false, includeBody: false, full: false });
780
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
781
+ ['gmail', 'messages', 'search', 'x'],
782
+ { account: undefined },
783
+ );
784
+ });
785
+ });
786
+
787
+ describe('gog_gmail_labels_style', () => {
788
+ it('calls runOrDiagnose with just the label', async () => {
789
+ await handlers.get('gog_gmail_labels_style')!({ labelIdOrName: 'Work' });
790
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
791
+ ['gmail', 'labels', 'style', 'Work'],
792
+ { account: undefined },
793
+ );
794
+ });
795
+
796
+ it('passes all style flags when provided', async () => {
797
+ await handlers.get('gog_gmail_labels_style')!({
798
+ labelIdOrName: 'Work',
799
+ backgroundColor: '#000000',
800
+ textColor: '#ffffff',
801
+ labelListVisibility: 'labelHide',
802
+ messageListVisibility: 'hide',
803
+ });
804
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
805
+ ['gmail', 'labels', 'style', 'Work', '--background-color=#000000', '--text-color=#ffffff', '--label-list-visibility=labelHide', '--message-list-visibility=hide'],
806
+ { account: undefined },
807
+ );
808
+ });
809
+ });
810
+
811
+ describe('gog_gmail_vacation_get', () => {
812
+ it('calls runOrDiagnose', async () => {
813
+ await handlers.get('gog_gmail_vacation_get')!({ account: 'me@x.com' });
814
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
815
+ ['gmail', 'settings', 'vacation', 'get'],
816
+ { account: 'me@x.com' },
817
+ );
818
+ });
819
+ });
820
+
821
+ describe('gog_gmail_vacation_update', () => {
822
+ it('calls runOrDiagnose with no flags', async () => {
823
+ await handlers.get('gog_gmail_vacation_update')!({});
824
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
825
+ ['gmail', 'settings', 'vacation', 'update'],
826
+ { account: undefined },
827
+ );
828
+ });
829
+
830
+ it('enables with subject/body/start/end and scoping', async () => {
831
+ await handlers.get('gog_gmail_vacation_update')!({
832
+ enable: true,
833
+ subject: 'Away',
834
+ body: '<p>OOO</p>',
835
+ start: '2024-12-20T00:00:00Z',
836
+ end: '2024-12-31T23:59:59Z',
837
+ contactsOnly: true,
838
+ domainOnly: true,
839
+ });
840
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
841
+ ['gmail', 'settings', 'vacation', 'update', '--enable', '--subject=Away', '--body=<p>OOO</p>', '--start=2024-12-20T00:00:00Z', '--end=2024-12-31T23:59:59Z', '--contacts-only', '--domain-only'],
842
+ { account: undefined },
843
+ );
844
+ });
845
+
846
+ it('disables the responder', async () => {
847
+ await handlers.get('gog_gmail_vacation_update')!({ disable: true, enable: false, contactsOnly: false, domainOnly: false });
848
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
849
+ ['gmail', 'settings', 'vacation', 'update', '--disable'],
850
+ { account: undefined },
851
+ );
852
+ });
853
+ });
854
+
855
+ describe('gog_gmail_filters_list', () => {
856
+ it('calls runOrDiagnose', async () => {
857
+ await handlers.get('gog_gmail_filters_list')!({});
858
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
859
+ ['gmail', 'settings', 'filters', 'list'],
860
+ { account: undefined },
861
+ );
862
+ });
863
+ });
864
+
865
+ describe('gog_gmail_filters_get', () => {
866
+ it('calls runOrDiagnose with the filter ID', async () => {
867
+ await handlers.get('gog_gmail_filters_get')!({ filterId: 'f1' });
868
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
869
+ ['gmail', 'settings', 'filters', 'get', 'f1'],
870
+ { account: undefined },
871
+ );
872
+ });
873
+ });
874
+
875
+ describe('gog_gmail_filters_create', () => {
876
+ it('calls runOrDiagnose with no flags', async () => {
877
+ await handlers.get('gog_gmail_filters_create')!({});
878
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
879
+ ['gmail', 'settings', 'filters', 'create'],
880
+ { account: undefined },
881
+ );
882
+ });
883
+
884
+ it('passes all criteria and actions when provided', async () => {
885
+ await handlers.get('gog_gmail_filters_create')!({
886
+ from: 'alice@x.com',
887
+ to: 'me@x.com',
888
+ subject: 'Report',
889
+ query: 'has:attachment',
890
+ hasAttachment: true,
891
+ addLabel: 'Reports',
892
+ removeLabel: 'INBOX',
893
+ archive: true,
894
+ markRead: true,
895
+ star: true,
896
+ important: true,
897
+ trash: true,
898
+ neverSpam: true,
899
+ forward: 'fwd@x.com',
900
+ });
901
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
902
+ ['gmail', 'settings', 'filters', 'create', '--from=alice@x.com', '--to=me@x.com', '--subject=Report', '--query=has:attachment', '--has-attachment', '--add-label=Reports', '--remove-label=INBOX', '--archive', '--mark-read', '--star', '--important', '--trash', '--never-spam', '--forward=fwd@x.com'],
903
+ { account: undefined },
904
+ );
905
+ });
906
+
907
+ it('omits boolean flags when false', async () => {
908
+ await handlers.get('gog_gmail_filters_create')!({
909
+ from: 'a@x.com',
910
+ hasAttachment: false,
911
+ archive: false,
912
+ markRead: false,
913
+ star: false,
914
+ important: false,
915
+ trash: false,
916
+ neverSpam: false,
917
+ });
918
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
919
+ ['gmail', 'settings', 'filters', 'create', '--from=a@x.com'],
920
+ { account: undefined },
921
+ );
922
+ });
923
+ });
924
+
925
+ describe('gog_gmail_filters_delete', () => {
926
+ it('calls runOrDiagnose with the filter ID', async () => {
927
+ await handlers.get('gog_gmail_filters_delete')!({ filterId: 'f1' });
928
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
929
+ ['gmail', 'settings', 'filters', 'delete', 'f1'],
930
+ { account: undefined },
931
+ );
932
+ });
933
+ });
934
+
935
+ describe('gog_gmail_sendas_list', () => {
936
+ it('calls runOrDiagnose', async () => {
937
+ await handlers.get('gog_gmail_sendas_list')!({});
938
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
939
+ ['gmail', 'settings', 'sendas', 'list'],
940
+ { account: undefined },
941
+ );
942
+ });
943
+ });
944
+
945
+ describe('gog_gmail_sendas_get', () => {
946
+ it('calls runOrDiagnose with the email', async () => {
947
+ await handlers.get('gog_gmail_sendas_get')!({ email: 'alias@x.com' });
948
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
949
+ ['gmail', 'settings', 'sendas', 'get', 'alias@x.com'],
950
+ { account: undefined },
951
+ );
952
+ });
953
+ });
954
+
955
+ describe('gog_gmail_sendas_create', () => {
956
+ it('calls runOrDiagnose with just the email', async () => {
957
+ await handlers.get('gog_gmail_sendas_create')!({ email: 'alias@x.com' });
958
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
959
+ ['gmail', 'settings', 'sendas', 'create', 'alias@x.com'],
960
+ { account: undefined },
961
+ );
962
+ });
963
+
964
+ it('passes all flags when provided', async () => {
965
+ await handlers.get('gog_gmail_sendas_create')!({
966
+ email: 'alias@x.com',
967
+ displayName: 'Alias',
968
+ replyTo: 'reply@x.com',
969
+ signature: '<p>sig</p>',
970
+ treatAsAlias: true,
971
+ });
972
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
973
+ ['gmail', 'settings', 'sendas', 'create', 'alias@x.com', '--display-name=Alias', '--reply-to=reply@x.com', '--signature=<p>sig</p>', '--treat-as-alias'],
974
+ { account: undefined },
975
+ );
976
+ });
977
+
978
+ it('omits treatAsAlias when false', async () => {
979
+ await handlers.get('gog_gmail_sendas_create')!({ email: 'alias@x.com', treatAsAlias: false });
980
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
981
+ ['gmail', 'settings', 'sendas', 'create', 'alias@x.com'],
982
+ { account: undefined },
983
+ );
984
+ });
985
+ });
986
+
987
+ describe('gog_gmail_sendas_update', () => {
988
+ it('calls runOrDiagnose with just the email', async () => {
989
+ await handlers.get('gog_gmail_sendas_update')!({ email: 'alias@x.com' });
990
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
991
+ ['gmail', 'settings', 'sendas', 'update', 'alias@x.com'],
992
+ { account: undefined },
993
+ );
994
+ });
995
+
996
+ it('passes all flags when provided', async () => {
997
+ await handlers.get('gog_gmail_sendas_update')!({
998
+ email: 'alias@x.com',
999
+ displayName: 'Alias',
1000
+ replyTo: 'reply@x.com',
1001
+ signature: '<p>sig</p>',
1002
+ treatAsAlias: true,
1003
+ makeDefault: true,
1004
+ });
1005
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1006
+ ['gmail', 'settings', 'sendas', 'update', 'alias@x.com', '--display-name=Alias', '--reply-to=reply@x.com', '--signature=<p>sig</p>', '--treat-as-alias', '--make-default'],
1007
+ { account: undefined },
1008
+ );
1009
+ });
1010
+
1011
+ it('omits boolean flags when false', async () => {
1012
+ await handlers.get('gog_gmail_sendas_update')!({ email: 'alias@x.com', treatAsAlias: false, makeDefault: false });
1013
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1014
+ ['gmail', 'settings', 'sendas', 'update', 'alias@x.com'],
1015
+ { account: undefined },
1016
+ );
1017
+ });
1018
+ });
1019
+
1020
+ describe('gog_gmail_sendas_delete', () => {
1021
+ it('calls runOrDiagnose with the email', async () => {
1022
+ await handlers.get('gog_gmail_sendas_delete')!({ email: 'alias@x.com' });
1023
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1024
+ ['gmail', 'settings', 'sendas', 'delete', 'alias@x.com'],
1025
+ { account: undefined },
1026
+ );
1027
+ });
1028
+ });
1029
+
1030
+ describe('gog_gmail_sendas_verify', () => {
1031
+ it('calls runOrDiagnose with the email', async () => {
1032
+ await handlers.get('gog_gmail_sendas_verify')!({ email: 'alias@x.com' });
1033
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1034
+ ['gmail', 'settings', 'sendas', 'verify', 'alias@x.com'],
1035
+ { account: undefined },
1036
+ );
1037
+ });
1038
+ });