gogcli-mcp-gmail 2.2.0 → 2.4.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
@@ -31241,11 +31241,13 @@ function registerGmailTools(server2) {
31241
31241
  inputSchema: {
31242
31242
  query: external_exports.string().describe("Gmail search query"),
31243
31243
  max: external_exports.number().int().optional().describe("Max results to return (default: 10)"),
31244
+ fromContact: external_exports.string().optional().describe("Resolve a Google Contact (name or email) to its addresses and AND a from:(addr OR addr) clause onto the query \u2014 saves looking the contact up first when you only know who, not which address."),
31244
31245
  account: accountParam
31245
31246
  }
31246
- }, async ({ query, max, account }) => {
31247
+ }, async ({ query, max, fromContact, account }) => {
31247
31248
  const args = ["gmail", "search", query];
31248
31249
  if (max !== void 0) args.push(`--max=${max}`);
31250
+ if (fromContact) args.push(`--from-contact=${fromContact}`);
31249
31251
  return runOrDiagnose(args, { account });
31250
31252
  });
31251
31253
  server2.registerTool("gog_gmail_get", {
@@ -31295,7 +31297,7 @@ var failIfNotEmptyParam = external_exports.boolean().optional().describe(
31295
31297
  );
31296
31298
 
31297
31299
  // ../gogcli-mcp/src/server.ts
31298
- var VERSION = true ? "2.2.0" : "0.0.0";
31300
+ var VERSION = true ? "2.4.0" : "0.0.0";
31299
31301
  function createServer(options) {
31300
31302
  return new McpServer({
31301
31303
  name: options?.name ?? "gogcli",
@@ -31763,6 +31765,225 @@ function registerExtraGmailTools(server2) {
31763
31765
  if (allowSelf) args.push("--allow-self");
31764
31766
  return runOrDiagnose(args, { account });
31765
31767
  });
31768
+ server2.registerTool("gog_gmail_messages_search", {
31769
+ description: "Search individual messages (not threads) using Gmail query syntax. Returns one result per matching message.",
31770
+ annotations: { readOnlyHint: true },
31771
+ inputSchema: {
31772
+ query: external_exports.string().describe('Gmail search query (e.g. "from:alice is:unread has:attachment")'),
31773
+ max: external_exports.number().optional().describe("Max results"),
31774
+ page: external_exports.string().optional().describe("Page token"),
31775
+ all: external_exports.boolean().optional().describe("Fetch all pages"),
31776
+ includeBody: external_exports.boolean().optional().describe("Include the decoded message body in each result"),
31777
+ full: external_exports.boolean().optional().describe("Show full message bodies without truncation (implies includeBody)"),
31778
+ bodyFormat: external_exports.enum(["text", "html"]).optional().describe("Body format preference when includeBody is set"),
31779
+ account: accountParam
31780
+ }
31781
+ }, async ({ query, max, page, all, includeBody, full, bodyFormat, account }) => {
31782
+ const args = ["gmail", "messages", "search", query];
31783
+ if (max !== void 0) args.push(`--max=${max}`);
31784
+ if (page) args.push(`--page=${page}`);
31785
+ if (all) args.push("--all");
31786
+ if (includeBody) args.push("--include-body");
31787
+ if (full) args.push("--full");
31788
+ if (bodyFormat) args.push(`--body-format=${bodyFormat}`);
31789
+ return runOrDiagnose(args, { account });
31790
+ });
31791
+ server2.registerTool("gog_gmail_labels_style", {
31792
+ description: "Change a user label's color or visibility (background/text color from Gmail's palette, label-list and message-list visibility).",
31793
+ annotations: { destructiveHint: true },
31794
+ inputSchema: {
31795
+ labelIdOrName: external_exports.string().describe("Label ID or name to restyle"),
31796
+ backgroundColor: external_exports.string().optional().describe("Background color from Gmail's label palette as #RRGGBB"),
31797
+ textColor: external_exports.string().optional().describe("Text color from Gmail's label palette as #RRGGBB"),
31798
+ labelListVisibility: external_exports.enum(["labelShow", "labelShowIfUnread", "labelHide"]).optional().describe("Label-list visibility"),
31799
+ messageListVisibility: external_exports.enum(["show", "hide"]).optional().describe("Message-list visibility"),
31800
+ account: accountParam
31801
+ }
31802
+ }, async ({ labelIdOrName, backgroundColor, textColor, labelListVisibility, messageListVisibility, account }) => {
31803
+ const args = ["gmail", "labels", "style", labelIdOrName];
31804
+ if (backgroundColor) args.push(`--background-color=${backgroundColor}`);
31805
+ if (textColor) args.push(`--text-color=${textColor}`);
31806
+ if (labelListVisibility) args.push(`--label-list-visibility=${labelListVisibility}`);
31807
+ if (messageListVisibility) args.push(`--message-list-visibility=${messageListVisibility}`);
31808
+ return runOrDiagnose(args, { account });
31809
+ });
31810
+ server2.registerTool("gog_gmail_vacation_get", {
31811
+ description: "Get the current vacation responder (auto-reply) settings.",
31812
+ annotations: { readOnlyHint: true },
31813
+ inputSchema: {
31814
+ account: accountParam
31815
+ }
31816
+ }, async ({ account }) => {
31817
+ return runOrDiagnose(["gmail", "settings", "vacation", "get"], { account });
31818
+ });
31819
+ server2.registerTool("gog_gmail_vacation_update", {
31820
+ 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.",
31821
+ inputSchema: {
31822
+ enable: external_exports.boolean().optional().describe("Enable the vacation responder"),
31823
+ disable: external_exports.boolean().optional().describe("Disable the vacation responder"),
31824
+ subject: external_exports.string().optional().describe("Subject line for the auto-reply"),
31825
+ body: external_exports.string().optional().describe("HTML body of the auto-reply message"),
31826
+ start: external_exports.string().optional().describe("Start time in RFC3339 format (e.g. 2024-12-20T00:00:00Z)"),
31827
+ end: external_exports.string().optional().describe("End time in RFC3339 format (e.g. 2024-12-31T23:59:59Z)"),
31828
+ contactsOnly: external_exports.boolean().optional().describe("Only respond to contacts"),
31829
+ domainOnly: external_exports.boolean().optional().describe("Only respond to senders in the same domain"),
31830
+ account: accountParam
31831
+ }
31832
+ }, async ({ enable, disable, subject, body, start, end, contactsOnly, domainOnly, account }) => {
31833
+ const args = ["gmail", "settings", "vacation", "update"];
31834
+ if (enable) args.push("--enable");
31835
+ if (disable) args.push("--disable");
31836
+ if (subject) args.push(`--subject=${subject}`);
31837
+ if (body) args.push(`--body=${body}`);
31838
+ if (start) args.push(`--start=${start}`);
31839
+ if (end) args.push(`--end=${end}`);
31840
+ if (contactsOnly) args.push("--contacts-only");
31841
+ if (domainOnly) args.push("--domain-only");
31842
+ return runOrDiagnose(args, { account });
31843
+ });
31844
+ server2.registerTool("gog_gmail_filters_list", {
31845
+ description: "List all Gmail filters for the account.",
31846
+ annotations: { readOnlyHint: true },
31847
+ inputSchema: {
31848
+ account: accountParam
31849
+ }
31850
+ }, async ({ account }) => {
31851
+ return runOrDiagnose(["gmail", "settings", "filters", "list"], { account });
31852
+ });
31853
+ server2.registerTool("gog_gmail_filters_get", {
31854
+ description: "Get the criteria and actions of a single Gmail filter by ID.",
31855
+ annotations: { readOnlyHint: true },
31856
+ inputSchema: {
31857
+ filterId: external_exports.string().describe("Filter ID"),
31858
+ account: accountParam
31859
+ }
31860
+ }, async ({ filterId, account }) => {
31861
+ return runOrDiagnose(["gmail", "settings", "filters", "get", filterId], { account });
31862
+ });
31863
+ server2.registerTool("gog_gmail_filters_create", {
31864
+ 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).",
31865
+ inputSchema: {
31866
+ from: external_exports.string().optional().describe("Match messages from this sender"),
31867
+ to: external_exports.string().optional().describe("Match messages to this recipient"),
31868
+ subject: external_exports.string().optional().describe("Match messages with this subject"),
31869
+ query: external_exports.string().optional().describe("Advanced Gmail search query for matching"),
31870
+ hasAttachment: external_exports.boolean().optional().describe("Match messages with attachments"),
31871
+ addLabel: external_exports.string().optional().describe("Label(s) to add to matching messages (comma-separated, name or ID)"),
31872
+ removeLabel: external_exports.string().optional().describe("Label(s) to remove from matching messages (comma-separated, name or ID)"),
31873
+ archive: external_exports.boolean().optional().describe("Archive matching messages (skip inbox)"),
31874
+ markRead: external_exports.boolean().optional().describe("Mark matching messages as read"),
31875
+ star: external_exports.boolean().optional().describe("Star matching messages"),
31876
+ important: external_exports.boolean().optional().describe("Mark as important"),
31877
+ trash: external_exports.boolean().optional().describe("Move matching messages to trash"),
31878
+ neverSpam: external_exports.boolean().optional().describe("Never mark as spam"),
31879
+ forward: external_exports.string().optional().describe("Forward to this email address (must be a verified forwarding address)"),
31880
+ account: accountParam
31881
+ }
31882
+ }, async ({ from, to, subject, query, hasAttachment, addLabel, removeLabel, archive, markRead, star, important, trash, neverSpam, forward, account }) => {
31883
+ const args = ["gmail", "settings", "filters", "create"];
31884
+ if (from) args.push(`--from=${from}`);
31885
+ if (to) args.push(`--to=${to}`);
31886
+ if (subject) args.push(`--subject=${subject}`);
31887
+ if (query) args.push(`--query=${query}`);
31888
+ if (hasAttachment) args.push("--has-attachment");
31889
+ if (addLabel) args.push(`--add-label=${addLabel}`);
31890
+ if (removeLabel) args.push(`--remove-label=${removeLabel}`);
31891
+ if (archive) args.push("--archive");
31892
+ if (markRead) args.push("--mark-read");
31893
+ if (star) args.push("--star");
31894
+ if (important) args.push("--important");
31895
+ if (trash) args.push("--trash");
31896
+ if (neverSpam) args.push("--never-spam");
31897
+ if (forward) args.push(`--forward=${forward}`);
31898
+ return runOrDiagnose(args, { account });
31899
+ });
31900
+ server2.registerTool("gog_gmail_filters_delete", {
31901
+ description: "Delete a Gmail filter by ID.",
31902
+ annotations: { destructiveHint: true },
31903
+ inputSchema: {
31904
+ filterId: external_exports.string().describe("Filter ID to delete"),
31905
+ account: accountParam
31906
+ }
31907
+ }, async ({ filterId, account }) => {
31908
+ return runOrDiagnose(["gmail", "settings", "filters", "delete", filterId], { account });
31909
+ });
31910
+ server2.registerTool("gog_gmail_sendas_list", {
31911
+ description: "List all send-as aliases configured for the account.",
31912
+ annotations: { readOnlyHint: true },
31913
+ inputSchema: {
31914
+ account: accountParam
31915
+ }
31916
+ }, async ({ account }) => {
31917
+ return runOrDiagnose(["gmail", "settings", "sendas", "list"], { account });
31918
+ });
31919
+ server2.registerTool("gog_gmail_sendas_get", {
31920
+ description: "Get details of a single send-as alias by its email address.",
31921
+ annotations: { readOnlyHint: true },
31922
+ inputSchema: {
31923
+ email: external_exports.string().describe("Send-as alias email address"),
31924
+ account: accountParam
31925
+ }
31926
+ }, async ({ email: email3, account }) => {
31927
+ return runOrDiagnose(["gmail", "settings", "sendas", "get", email3], { account });
31928
+ });
31929
+ server2.registerTool("gog_gmail_sendas_create", {
31930
+ description: "Create a send-as alias. Newly added aliases generally require email verification before they can be used (see gog_gmail_sendas_verify).",
31931
+ inputSchema: {
31932
+ email: external_exports.string().describe("Email address of the new send-as alias"),
31933
+ displayName: external_exports.string().optional().describe("Name that appears in the From field"),
31934
+ replyTo: external_exports.string().optional().describe("Reply-to address"),
31935
+ signature: external_exports.string().optional().describe("HTML signature for emails sent from this alias"),
31936
+ treatAsAlias: external_exports.boolean().optional().describe("Treat as alias (replies sent from Gmail web)"),
31937
+ account: accountParam
31938
+ }
31939
+ }, async ({ email: email3, displayName, replyTo, signature, treatAsAlias, account }) => {
31940
+ const args = ["gmail", "settings", "sendas", "create", email3];
31941
+ if (displayName) args.push(`--display-name=${displayName}`);
31942
+ if (replyTo) args.push(`--reply-to=${replyTo}`);
31943
+ if (signature) args.push(`--signature=${signature}`);
31944
+ if (treatAsAlias) args.push("--treat-as-alias");
31945
+ return runOrDiagnose(args, { account });
31946
+ });
31947
+ server2.registerTool("gog_gmail_sendas_update", {
31948
+ description: "Update a send-as alias (display name, reply-to, signature, alias handling, or make it the default).",
31949
+ annotations: { destructiveHint: true },
31950
+ inputSchema: {
31951
+ email: external_exports.string().describe("Send-as alias email address to update"),
31952
+ displayName: external_exports.string().optional().describe("Name that appears in the From field"),
31953
+ replyTo: external_exports.string().optional().describe("Reply-to address"),
31954
+ signature: external_exports.string().optional().describe("HTML signature"),
31955
+ treatAsAlias: external_exports.boolean().optional().describe("Treat as alias"),
31956
+ makeDefault: external_exports.boolean().optional().describe("Make this the default send-as address"),
31957
+ account: accountParam
31958
+ }
31959
+ }, async ({ email: email3, displayName, replyTo, signature, treatAsAlias, makeDefault, account }) => {
31960
+ const args = ["gmail", "settings", "sendas", "update", email3];
31961
+ if (displayName) args.push(`--display-name=${displayName}`);
31962
+ if (replyTo) args.push(`--reply-to=${replyTo}`);
31963
+ if (signature) args.push(`--signature=${signature}`);
31964
+ if (treatAsAlias) args.push("--treat-as-alias");
31965
+ if (makeDefault) args.push("--make-default");
31966
+ return runOrDiagnose(args, { account });
31967
+ });
31968
+ server2.registerTool("gog_gmail_sendas_delete", {
31969
+ description: "Delete a send-as alias by its email address.",
31970
+ annotations: { destructiveHint: true },
31971
+ inputSchema: {
31972
+ email: external_exports.string().describe("Send-as alias email address to delete"),
31973
+ account: accountParam
31974
+ }
31975
+ }, async ({ email: email3, account }) => {
31976
+ return runOrDiagnose(["gmail", "settings", "sendas", "delete", email3], { account });
31977
+ });
31978
+ server2.registerTool("gog_gmail_sendas_verify", {
31979
+ description: "Resend the verification email for a send-as alias that is pending verification.",
31980
+ inputSchema: {
31981
+ email: external_exports.string().describe("Send-as alias email address to verify"),
31982
+ account: accountParam
31983
+ }
31984
+ }, async ({ email: email3, account }) => {
31985
+ return runOrDiagnose(["gmail", "settings", "sendas", "verify", email3], { account });
31986
+ });
31766
31987
  }
31767
31988
 
31768
31989
  // 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.4.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.4.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
+ });