@alanse/mcp-server-google-workspace 0.2.1 → 1.0.2

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.
Files changed (182) hide show
  1. package/README.md +250 -17
  2. package/dist/auth.js +6 -0
  3. package/dist/index.js +1 -1
  4. package/dist/lib/calendar-helpers.js +197 -0
  5. package/dist/lib/document-id-resolver.js +76 -0
  6. package/dist/lib/drive-helpers.js +263 -0
  7. package/dist/lib/gmail-helpers.js +204 -0
  8. package/dist/lib/response-formatter.js +82 -0
  9. package/dist/lib/validation.js +112 -0
  10. package/dist/tools/calendar/acl/calendar_acl_insert.js +80 -0
  11. package/dist/tools/calendar/acl/calendar_acl_list.js +82 -0
  12. package/dist/tools/calendar/basic/calendar_create_event.js +113 -0
  13. package/dist/tools/calendar/basic/calendar_delete_event.js +52 -0
  14. package/dist/tools/calendar/basic/calendar_get_event.js +52 -0
  15. package/dist/tools/calendar/basic/calendar_list_events.js +86 -0
  16. package/dist/tools/calendar/basic/calendar_update_event.js +116 -0
  17. package/dist/tools/calendar/calendarlist/calendar_calendarlist_get.js +73 -0
  18. package/dist/tools/calendar/calendarlist/calendar_calendarlist_list.js +87 -0
  19. package/dist/tools/calendar/calendars/calendar_calendars_get.js +52 -0
  20. package/dist/tools/calendar/calendars/calendar_calendars_insert.js +66 -0
  21. package/dist/tools/calendar/calendars/calendar_calendars_update.js +85 -0
  22. package/dist/tools/calendar/colors/calendar_colors_get.js +46 -0
  23. package/dist/tools/calendar/events_advanced/calendar_events_instances.js +81 -0
  24. package/dist/tools/calendar/events_advanced/calendar_events_move.js +63 -0
  25. package/dist/tools/calendar/events_advanced/calendar_events_quickadd.js +52 -0
  26. package/dist/tools/calendar/freebusy/calendar_freebusy_query.js +69 -0
  27. package/dist/tools/calendar/settings/calendar_settings_list.js +81 -0
  28. package/dist/tools/docs/basic/gdocs_create.js +37 -0
  29. package/dist/tools/docs/basic/gdocs_get_metadata.js +45 -0
  30. package/dist/tools/docs/basic/gdocs_list_documents.js +59 -0
  31. package/dist/tools/docs/basic/gdocs_read.js +62 -0
  32. package/dist/tools/docs/content/gdocs_append_text.js +57 -0
  33. package/dist/tools/docs/content/gdocs_apply_style.js +86 -0
  34. package/dist/tools/docs/content/gdocs_create_heading.js +89 -0
  35. package/dist/tools/docs/content/gdocs_create_list.js +86 -0
  36. package/dist/tools/docs/content/gdocs_delete_text.js +64 -0
  37. package/dist/tools/docs/content/gdocs_format_text.js +137 -0
  38. package/dist/tools/docs/content/gdocs_insert_text.js +62 -0
  39. package/dist/tools/docs/content/gdocs_replace_text.js +64 -0
  40. package/dist/tools/docs/content/gdocs_set_alignment.js +76 -0
  41. package/dist/tools/docs/content/gdocs_update_text.js +78 -0
  42. package/dist/tools/docs/elements/gdocs_batch_update.js +108 -0
  43. package/dist/tools/docs/elements/gdocs_create_table.js +73 -0
  44. package/dist/tools/docs/elements/gdocs_export.js +62 -0
  45. package/dist/tools/docs/elements/gdocs_insert_image.js +96 -0
  46. package/dist/tools/docs/elements/gdocs_insert_link.js +77 -0
  47. package/dist/tools/docs/elements/gdocs_insert_page_break.js +55 -0
  48. package/dist/tools/docs/elements/gdocs_insert_toc.js +71 -0
  49. package/dist/tools/docs/elements/gdocs_merge_documents.js +104 -0
  50. package/dist/tools/docs/elements/gdocs_suggest_mode.js +41 -0
  51. package/dist/tools/drive/advanced/drive_empty_trash.js +56 -0
  52. package/dist/tools/drive/advanced/drive_export_file.js +158 -0
  53. package/dist/tools/drive/advanced/drive_list_revisions.js +80 -0
  54. package/dist/tools/drive/basic/drive_get_metadata.js +49 -0
  55. package/dist/tools/drive/basic/drive_list_files.js +76 -0
  56. package/dist/tools/drive/file/drive_copy_file.js +79 -0
  57. package/dist/tools/drive/file/drive_create_file.js +72 -0
  58. package/dist/tools/drive/file/drive_delete_file.js +48 -0
  59. package/dist/tools/drive/file/drive_move_file.js +79 -0
  60. package/dist/tools/drive/file/drive_rename_file.js +58 -0
  61. package/dist/tools/drive/file/drive_update_file.js +106 -0
  62. package/dist/tools/drive/file/drive_upload_file.js +80 -0
  63. package/dist/tools/drive/folder/drive_create_folder.js +67 -0
  64. package/dist/tools/drive/folder/drive_list_folder_contents.js +68 -0
  65. package/dist/tools/drive/folder/drive_move_to_folder.js +59 -0
  66. package/dist/tools/drive/permissions/drive_list_permissions.js +115 -0
  67. package/dist/tools/drive/permissions/drive_remove_permission.js +71 -0
  68. package/dist/tools/drive/permissions/drive_share_file.js +116 -0
  69. package/dist/tools/drive/permissions/drive_update_permission.js +79 -0
  70. package/dist/tools/gmail/basic/gmail_get_message.js +95 -0
  71. package/dist/tools/gmail/basic/gmail_get_thread.js +46 -0
  72. package/dist/tools/gmail/basic/gmail_list_labels.js +54 -0
  73. package/dist/tools/gmail/basic/gmail_search_messages.js +59 -0
  74. package/dist/tools/gmail/batch/gmail_batch_modify_labels.js +74 -0
  75. package/dist/tools/gmail/batch/gmail_get_messages_batch.js +120 -0
  76. package/dist/tools/gmail/batch/gmail_get_threads_batch.js +102 -0
  77. package/dist/tools/gmail/labels/gmail_manage_label.js +131 -0
  78. package/dist/tools/gmail/labels/gmail_modify_labels.js +65 -0
  79. package/dist/tools/gmail/send/gmail_draft_message.js +117 -0
  80. package/dist/tools/gmail/send/gmail_send_message.js +109 -0
  81. package/dist/tools/index.js +386 -3
  82. package/package.json +8 -3
  83. package/dist/tools/basic/gsheets_add_sheet.js +0 -65
  84. package/dist/tools/basic/gsheets_copy_sheet.js +0 -56
  85. package/dist/tools/basic/gsheets_copy_to.js +0 -113
  86. package/dist/tools/basic/gsheets_create_spreadsheet.js +0 -88
  87. package/dist/tools/basic/gsheets_delete_columns.js +0 -69
  88. package/dist/tools/basic/gsheets_delete_rows.js +0 -69
  89. package/dist/tools/basic/gsheets_delete_sheet.js +0 -56
  90. package/dist/tools/basic/gsheets_duplicate_sheet.js +0 -72
  91. package/dist/tools/basic/gsheets_insert_columns.js +0 -69
  92. package/dist/tools/basic/gsheets_insert_rows.js +0 -69
  93. package/dist/tools/basic/gsheets_list_sheets.js +0 -53
  94. package/dist/tools/basic/gsheets_read.js +0 -120
  95. package/dist/tools/basic/gsheets_rename_sheet.js +0 -64
  96. package/dist/tools/charts/gsheets_add_bubble.js +0 -176
  97. package/dist/tools/charts/gsheets_add_candlestick.js +0 -192
  98. package/dist/tools/charts/gsheets_add_chart.js +0 -162
  99. package/dist/tools/charts/gsheets_add_combo.js +0 -169
  100. package/dist/tools/charts/gsheets_add_histogram.js +0 -143
  101. package/dist/tools/charts/gsheets_add_org_chart.js +0 -160
  102. package/dist/tools/charts/gsheets_add_treemap.js +0 -177
  103. package/dist/tools/charts/gsheets_add_waterfall.js +0 -155
  104. package/dist/tools/charts/gsheets_delete_chart.js +0 -56
  105. package/dist/tools/charts/gsheets_update_chart.js +0 -118
  106. package/dist/tools/data/gsheets_append_data.js +0 -68
  107. package/dist/tools/data/gsheets_batch_clear.js +0 -53
  108. package/dist/tools/data/gsheets_batch_update.js +0 -81
  109. package/dist/tools/data/gsheets_clear_data.js +0 -53
  110. package/dist/tools/data/gsheets_create_filter.js +0 -81
  111. package/dist/tools/data/gsheets_find_replace.js +0 -124
  112. package/dist/tools/data/gsheets_set_data_validation.js +0 -153
  113. package/dist/tools/data/gsheets_sort_range.js +0 -102
  114. package/dist/tools/data/gsheets_update_cell.js +0 -44
  115. package/dist/tools/formatting/gsheets_auto_resize.js +0 -75
  116. package/dist/tools/formatting/gsheets_format_cells.js +0 -161
  117. package/dist/tools/formatting/gsheets_freeze_columns.js +0 -67
  118. package/dist/tools/formatting/gsheets_freeze_rows.js +0 -67
  119. package/dist/tools/formatting/gsheets_merge_cells.js +0 -85
  120. package/dist/tools/formatting/gsheets_set_number_format.js +0 -116
  121. package/dist/tools/formatting/gsheets_unmerge_cells.js +0 -79
  122. package/dist/tools/formatting/gsheets_update_borders.js +0 -212
  123. package/dist/tools/gdrive/gdrive_read_file.js +0 -77
  124. package/dist/tools/gdrive/gdrive_search.js +0 -71
  125. package/dist/tools/gdrive_read_file.js +0 -77
  126. package/dist/tools/gdrive_search.js +0 -71
  127. package/dist/tools/gsheets_add_bubble.js +0 -176
  128. package/dist/tools/gsheets_add_candlestick.js +0 -192
  129. package/dist/tools/gsheets_add_chart.js +0 -162
  130. package/dist/tools/gsheets_add_combo.js +0 -169
  131. package/dist/tools/gsheets_add_conditional_format.js +0 -175
  132. package/dist/tools/gsheets_add_histogram.js +0 -143
  133. package/dist/tools/gsheets_add_named_range.js +0 -87
  134. package/dist/tools/gsheets_add_org_chart.js +0 -160
  135. package/dist/tools/gsheets_add_protected_range.js +0 -127
  136. package/dist/tools/gsheets_add_sheet.js +0 -65
  137. package/dist/tools/gsheets_add_treemap.js +0 -177
  138. package/dist/tools/gsheets_add_waterfall.js +0 -155
  139. package/dist/tools/gsheets_append_data.js +0 -68
  140. package/dist/tools/gsheets_auto_resize.js +0 -75
  141. package/dist/tools/gsheets_batch_clear.js +0 -53
  142. package/dist/tools/gsheets_batch_update.js +0 -81
  143. package/dist/tools/gsheets_clear_data.js +0 -53
  144. package/dist/tools/gsheets_copy_sheet.js +0 -56
  145. package/dist/tools/gsheets_copy_to.js +0 -113
  146. package/dist/tools/gsheets_create_filter.js +0 -81
  147. package/dist/tools/gsheets_create_spreadsheet.js +0 -88
  148. package/dist/tools/gsheets_delete_chart.js +0 -56
  149. package/dist/tools/gsheets_delete_columns.js +0 -69
  150. package/dist/tools/gsheets_delete_named_range.js +0 -56
  151. package/dist/tools/gsheets_delete_protected_range.js +0 -56
  152. package/dist/tools/gsheets_delete_rows.js +0 -69
  153. package/dist/tools/gsheets_delete_sheet.js +0 -56
  154. package/dist/tools/gsheets_duplicate_sheet.js +0 -72
  155. package/dist/tools/gsheets_find_replace.js +0 -124
  156. package/dist/tools/gsheets_format_cells.js +0 -161
  157. package/dist/tools/gsheets_freeze_columns.js +0 -67
  158. package/dist/tools/gsheets_freeze_rows.js +0 -67
  159. package/dist/tools/gsheets_insert_columns.js +0 -69
  160. package/dist/tools/gsheets_insert_rows.js +0 -69
  161. package/dist/tools/gsheets_list_sheets.js +0 -53
  162. package/dist/tools/gsheets_merge_cells.js +0 -85
  163. package/dist/tools/gsheets_read.js +0 -120
  164. package/dist/tools/gsheets_rename_sheet.js +0 -64
  165. package/dist/tools/gsheets_set_data_validation.js +0 -153
  166. package/dist/tools/gsheets_set_number_format.js +0 -116
  167. package/dist/tools/gsheets_sort_range.js +0 -102
  168. package/dist/tools/gsheets_unmerge_cells.js +0 -79
  169. package/dist/tools/gsheets_update_borders.js +0 -212
  170. package/dist/tools/gsheets_update_cell.js +0 -44
  171. package/dist/tools/gsheets_update_chart.js +0 -118
  172. package/dist/tools/gsheets_update_named_range.js +0 -112
  173. package/dist/tools/gsheets_update_protected_range.js +0 -110
  174. package/dist/tools/protection/gsheets_add_conditional_format.js +0 -175
  175. package/dist/tools/protection/gsheets_add_named_range.js +0 -87
  176. package/dist/tools/protection/gsheets_add_protected_range.js +0 -127
  177. package/dist/tools/protection/gsheets_delete_named_range.js +0 -56
  178. package/dist/tools/protection/gsheets_delete_protected_range.js +0 -56
  179. package/dist/tools/protection/gsheets_update_named_range.js +0 -112
  180. package/dist/tools/protection/gsheets_update_protected_range.js +0 -110
  181. /package/dist/tools/drive/{drive_read_file.js → basic/drive_read_file.js} +0 -0
  182. /package/dist/tools/drive/{drive_search.js → basic/drive_search.js} +0 -0
@@ -0,0 +1,95 @@
1
+ import { google } from "googleapis";
2
+ import { ResponseFormatter } from "../../../lib/response-formatter.js";
3
+ import { extractMessageBodies, formatBodyContent, extractAttachments, getHeader, generateGmailWebUrl, } from "../../../lib/gmail-helpers.js";
4
+ export const schema = {
5
+ name: "gmail_get_message",
6
+ description: "Retrieve full content of a specific Gmail message including subject, sender, recipients, body (text/HTML), and attachments. Returns formatted message with metadata.",
7
+ inputSchema: {
8
+ type: "object",
9
+ properties: {
10
+ messageId: {
11
+ type: "string",
12
+ description: "The Gmail message ID to retrieve",
13
+ },
14
+ },
15
+ required: ["messageId"],
16
+ },
17
+ };
18
+ export async function getMessage(args) {
19
+ try {
20
+ const gmail = google.gmail("v1");
21
+ const { messageId } = args;
22
+ // Fetch message with full format
23
+ const response = await gmail.users.messages.get({
24
+ userId: "me",
25
+ id: messageId,
26
+ format: "full",
27
+ });
28
+ const message = response.data;
29
+ const headers = message.payload?.headers || [];
30
+ // Extract metadata
31
+ const subject = getHeader(headers, "Subject");
32
+ const from = getHeader(headers, "From");
33
+ const to = getHeader(headers, "To");
34
+ const cc = getHeader(headers, "Cc");
35
+ const date = getHeader(headers, "Date");
36
+ const messageIdHeader = getHeader(headers, "Message-ID");
37
+ // Extract body
38
+ const { textBody, htmlBody } = extractMessageBodies(message.payload || {});
39
+ const bodyContent = formatBodyContent(textBody, htmlBody);
40
+ // Extract attachments
41
+ const attachments = extractAttachments(message.payload || {});
42
+ // Build output
43
+ let output = `Message ID: ${messageId}\n`;
44
+ output += `Thread ID: ${message.threadId}\n`;
45
+ output += `Web URL: ${generateGmailWebUrl(messageId)}\n\n`;
46
+ output += `Subject: ${subject}\n`;
47
+ output += `From: ${from}\n`;
48
+ output += `To: ${to}\n`;
49
+ if (cc)
50
+ output += `Cc: ${cc}\n`;
51
+ output += `Date: ${date}\n`;
52
+ if (messageIdHeader)
53
+ output += `Message-ID: ${messageIdHeader}\n`;
54
+ output += `\n${"=".repeat(80)}\n\n`;
55
+ // Truncate very long bodies
56
+ const maxBodyLength = 20000;
57
+ if (bodyContent.length > maxBodyLength) {
58
+ output += bodyContent.substring(0, maxBodyLength);
59
+ output += `\n\n[... truncated ${bodyContent.length - maxBodyLength} characters ...]`;
60
+ }
61
+ else {
62
+ output += bodyContent;
63
+ }
64
+ // Add attachments info
65
+ if (attachments.length > 0) {
66
+ output += `\n\n${"=".repeat(80)}\n\n`;
67
+ output += `📎 Attachments (${attachments.length}):\n`;
68
+ attachments.forEach((att, index) => {
69
+ output += ` ${index + 1}. ${att.filename} (${att.mimeType}, ${Math.round(att.size / 1024)}KB)\n`;
70
+ output += ` Attachment ID: ${att.attachmentId}\n`;
71
+ });
72
+ output += `\nUse 'gmail_get_attachment' tool to download attachments.\n`;
73
+ }
74
+ return ResponseFormatter.success({
75
+ messageId,
76
+ threadId: message.threadId,
77
+ subject,
78
+ from,
79
+ to,
80
+ cc,
81
+ date,
82
+ labels: message.labelIds,
83
+ attachments: attachments.map((a) => ({
84
+ filename: a.filename,
85
+ mimeType: a.mimeType,
86
+ size: a.size,
87
+ attachmentId: a.attachmentId,
88
+ })),
89
+ snippet: message.snippet,
90
+ }, output);
91
+ }
92
+ catch (error) {
93
+ return ResponseFormatter.error(error);
94
+ }
95
+ }
@@ -0,0 +1,46 @@
1
+ import { google } from "googleapis";
2
+ import { ResponseFormatter } from "../../../lib/response-formatter.js";
3
+ import { formatThreadContent } from "../../../lib/gmail-helpers.js";
4
+ export const schema = {
5
+ name: "gmail_get_thread",
6
+ description: "Retrieve a complete Gmail thread (conversation) with all messages. Returns all messages in chronological order with full content, metadata, and attachments.",
7
+ inputSchema: {
8
+ type: "object",
9
+ properties: {
10
+ threadId: {
11
+ type: "string",
12
+ description: "The Gmail thread ID to retrieve",
13
+ },
14
+ },
15
+ required: ["threadId"],
16
+ },
17
+ };
18
+ export async function getThread(args) {
19
+ try {
20
+ const gmail = google.gmail("v1");
21
+ const { threadId } = args;
22
+ // Fetch thread with full format
23
+ const response = await gmail.users.threads.get({
24
+ userId: "me",
25
+ id: threadId,
26
+ format: "full",
27
+ });
28
+ const thread = response.data;
29
+ const messages = thread.messages || [];
30
+ // Format thread content using helper
31
+ const formattedContent = formatThreadContent(thread);
32
+ // Build metadata for structured response
33
+ const messageCount = messages.length;
34
+ const messageIds = messages.map((m) => m.id);
35
+ const snippets = messages.map((m) => m.snippet || "");
36
+ return ResponseFormatter.success({
37
+ threadId,
38
+ messageCount,
39
+ messageIds,
40
+ snippets,
41
+ }, formattedContent);
42
+ }
43
+ catch (error) {
44
+ return ResponseFormatter.error(error);
45
+ }
46
+ }
@@ -0,0 +1,54 @@
1
+ import { google } from "googleapis";
2
+ import { ResponseFormatter } from "../../../lib/response-formatter.js";
3
+ export const schema = {
4
+ name: "gmail_list_labels",
5
+ description: "List all labels in the user's Gmail account. Returns both system labels (INBOX, SENT, TRASH, etc.) and user-created labels with their IDs and names.",
6
+ inputSchema: {
7
+ type: "object",
8
+ properties: {},
9
+ required: [],
10
+ },
11
+ };
12
+ export async function listLabels(args) {
13
+ try {
14
+ const gmail = google.gmail("v1");
15
+ const response = await gmail.users.labels.list({
16
+ userId: "me",
17
+ });
18
+ const labels = response.data.labels || [];
19
+ if (labels.length === 0) {
20
+ return ResponseFormatter.success({ count: 0, labels: [] }, "No labels found in Gmail account");
21
+ }
22
+ // Separate system and user labels
23
+ const systemLabels = labels.filter((l) => l.type === "system");
24
+ const userLabels = labels.filter((l) => l.type === "user");
25
+ let output = `Total labels: ${labels.length}\n\n`;
26
+ if (systemLabels.length > 0) {
27
+ output += `📌 System Labels (${systemLabels.length}):\n`;
28
+ systemLabels.forEach((label) => {
29
+ output += ` - ${label.name} (ID: ${label.id})\n`;
30
+ });
31
+ output += `\n`;
32
+ }
33
+ if (userLabels.length > 0) {
34
+ output += `🏷️ User Labels (${userLabels.length}):\n`;
35
+ userLabels.forEach((label) => {
36
+ output += ` - ${label.name} (ID: ${label.id})\n`;
37
+ });
38
+ }
39
+ return ResponseFormatter.success({
40
+ count: labels.length,
41
+ systemLabels: systemLabels.map((l) => ({
42
+ id: l.id,
43
+ name: l.name,
44
+ })),
45
+ userLabels: userLabels.map((l) => ({
46
+ id: l.id,
47
+ name: l.name,
48
+ })),
49
+ }, output);
50
+ }
51
+ catch (error) {
52
+ return ResponseFormatter.error(error);
53
+ }
54
+ }
@@ -0,0 +1,59 @@
1
+ import { google } from "googleapis";
2
+ import { ResponseFormatter } from "../../../lib/response-formatter.js";
3
+ import { formatMessageList } from "../../../lib/gmail-helpers.js";
4
+ export const schema = {
5
+ name: "gmail_search_messages",
6
+ description: "Search for messages in Gmail using Gmail query syntax. Supports operators like 'from:', 'to:', 'subject:', 'is:unread', 'has:attachment', date ranges, etc. Returns message IDs, thread IDs, and web URLs for each result.",
7
+ inputSchema: {
8
+ type: "object",
9
+ properties: {
10
+ query: {
11
+ type: "string",
12
+ description: "Gmail search query using standard Gmail operators (e.g., 'from:user@example.com subject:invoice is:unread')",
13
+ },
14
+ pageSize: {
15
+ type: "number",
16
+ description: "Maximum number of results to return (default: 10, max: 100)",
17
+ },
18
+ pageToken: {
19
+ type: "string",
20
+ description: "Token for pagination to get the next page of results",
21
+ },
22
+ },
23
+ required: ["query"],
24
+ },
25
+ };
26
+ export async function searchMessages(args) {
27
+ try {
28
+ const gmail = google.gmail("v1");
29
+ const { query, pageSize = 10, pageToken } = args;
30
+ const response = await gmail.users.messages.list({
31
+ userId: "me",
32
+ q: query,
33
+ maxResults: Math.min(pageSize, 100),
34
+ pageToken: pageToken,
35
+ });
36
+ const messages = response.data.messages || [];
37
+ const nextPageToken = response.data.nextPageToken;
38
+ if (messages.length === 0) {
39
+ return ResponseFormatter.success({
40
+ count: 0,
41
+ messages: [],
42
+ query,
43
+ }, `No messages found matching query: "${query}"`);
44
+ }
45
+ const formattedOutput = formatMessageList(messages, nextPageToken);
46
+ return ResponseFormatter.success({
47
+ count: messages.length,
48
+ query,
49
+ messages: messages.map((m) => ({
50
+ id: m.id,
51
+ threadId: m.threadId,
52
+ })),
53
+ nextPageToken,
54
+ }, `Search results for: "${query}"\n\n${formattedOutput}`);
55
+ }
56
+ catch (error) {
57
+ return ResponseFormatter.error(error);
58
+ }
59
+ }
@@ -0,0 +1,74 @@
1
+ import { google } from "googleapis";
2
+ import { ResponseFormatter } from "../../../lib/response-formatter.js";
3
+ export const schema = {
4
+ name: "gmail_batch_modify_labels",
5
+ description: "Modify labels on multiple Gmail messages in a single batch operation. More efficient than calling gmail_modify_labels multiple times. Can add and/or remove labels from many messages at once.",
6
+ inputSchema: {
7
+ type: "object",
8
+ properties: {
9
+ messageIds: {
10
+ type: "array",
11
+ items: { type: "string" },
12
+ description: "Array of Gmail message IDs to modify (max recommended: 1000)",
13
+ },
14
+ addLabelIds: {
15
+ type: "array",
16
+ items: { type: "string" },
17
+ description: "Array of label IDs to add to all specified messages (e.g., ['INBOX', 'STARRED'])",
18
+ optional: true,
19
+ },
20
+ removeLabelIds: {
21
+ type: "array",
22
+ items: { type: "string" },
23
+ description: "Array of label IDs to remove from all specified messages (e.g., ['UNREAD', 'SPAM'])",
24
+ optional: true,
25
+ },
26
+ },
27
+ required: ["messageIds"],
28
+ },
29
+ };
30
+ export async function batchModifyLabels(args) {
31
+ try {
32
+ const gmail = google.gmail("v1");
33
+ const { messageIds, addLabelIds = [], removeLabelIds = [] } = args;
34
+ if (messageIds.length === 0) {
35
+ return ResponseFormatter.error(new Error("messageIds array cannot be empty"));
36
+ }
37
+ if (messageIds.length > 1000) {
38
+ return ResponseFormatter.error(new Error("Too many message IDs. Maximum recommended is 1000 per batch request."));
39
+ }
40
+ // Validate at least one operation
41
+ if (addLabelIds.length === 0 && removeLabelIds.length === 0) {
42
+ return ResponseFormatter.error(new Error("At least one of addLabelIds or removeLabelIds must be provided"));
43
+ }
44
+ // Perform batch modify
45
+ await gmail.users.messages.batchModify({
46
+ userId: "me",
47
+ requestBody: {
48
+ ids: messageIds,
49
+ addLabelIds: addLabelIds.length > 0 ? addLabelIds : undefined,
50
+ removeLabelIds: removeLabelIds.length > 0 ? removeLabelIds : undefined,
51
+ },
52
+ });
53
+ let output = `✅ Successfully modified labels on ${messageIds.length} message(s)\n\n`;
54
+ output += `📊 Operation Summary:\n`;
55
+ output += ` Messages affected: ${messageIds.length}\n`;
56
+ if (addLabelIds.length > 0) {
57
+ output += ` ➕ Added labels: ${addLabelIds.join(", ")}\n`;
58
+ }
59
+ if (removeLabelIds.length > 0) {
60
+ output += ` ➖ Removed labels: ${removeLabelIds.join(", ")}\n`;
61
+ }
62
+ output += `\n📝 Note: Changes applied to all specified messages.\n`;
63
+ output += `Use gmail_get_message to verify individual message labels.`;
64
+ return ResponseFormatter.success({
65
+ messageCount: messageIds.length,
66
+ messageIds,
67
+ addedLabels: addLabelIds,
68
+ removedLabels: removeLabelIds,
69
+ }, output);
70
+ }
71
+ catch (error) {
72
+ return ResponseFormatter.error(error);
73
+ }
74
+ }
@@ -0,0 +1,120 @@
1
+ import { google } from "googleapis";
2
+ import { ResponseFormatter } from "../../../lib/response-formatter.js";
3
+ import { extractMessageBodies, formatBodyContent, getHeader, generateGmailWebUrl, } from "../../../lib/gmail-helpers.js";
4
+ export const schema = {
5
+ name: "gmail_get_messages_batch",
6
+ description: "Retrieve multiple Gmail messages in a single batch request. More efficient than calling gmail_get_message multiple times. Returns message metadata and content for each message ID.",
7
+ inputSchema: {
8
+ type: "object",
9
+ properties: {
10
+ messageIds: {
11
+ type: "array",
12
+ items: { type: "string" },
13
+ description: "Array of Gmail message IDs to retrieve (max recommended: 100)",
14
+ },
15
+ format: {
16
+ type: "string",
17
+ enum: ["full", "metadata"],
18
+ description: "Response format: 'full' includes body content, 'metadata' only headers (default: metadata)",
19
+ optional: true,
20
+ },
21
+ },
22
+ required: ["messageIds"],
23
+ },
24
+ };
25
+ export async function getMessagesBatch(args) {
26
+ try {
27
+ const gmail = google.gmail("v1");
28
+ const { messageIds, format = "metadata" } = args;
29
+ if (messageIds.length === 0) {
30
+ return ResponseFormatter.error(new Error("messageIds array cannot be empty"));
31
+ }
32
+ if (messageIds.length > 100) {
33
+ return ResponseFormatter.error(new Error("Too many message IDs. Maximum recommended is 100 per batch request."));
34
+ }
35
+ // Fetch all messages
36
+ const messagePromises = messageIds.map((messageId) => gmail.users.messages
37
+ .get({
38
+ userId: "me",
39
+ id: messageId,
40
+ format: format,
41
+ })
42
+ .catch((error) => ({
43
+ error: true,
44
+ messageId,
45
+ errorMessage: error.message,
46
+ })));
47
+ const results = await Promise.all(messagePromises);
48
+ // Process results
49
+ const messages = [];
50
+ const errors = [];
51
+ let output = `📬 Batch Message Retrieval Results\n`;
52
+ output += `${"=".repeat(80)}\n\n`;
53
+ output += `Requested: ${messageIds.length} messages\n`;
54
+ results.forEach((result, index) => {
55
+ if (result.error) {
56
+ errors.push({
57
+ messageId: result.messageId,
58
+ error: result.errorMessage,
59
+ });
60
+ }
61
+ else {
62
+ const message = result.data;
63
+ const headers = message.payload?.headers || [];
64
+ const messageData = {
65
+ messageId: message.id,
66
+ threadId: message.threadId,
67
+ subject: getHeader(headers, "Subject"),
68
+ from: getHeader(headers, "From"),
69
+ to: getHeader(headers, "To"),
70
+ date: getHeader(headers, "Date"),
71
+ snippet: message.snippet,
72
+ labelIds: message.labelIds || [],
73
+ };
74
+ // Include body if format is 'full'
75
+ if (format === "full") {
76
+ const { textBody, htmlBody } = extractMessageBodies(message.payload || {});
77
+ messageData.bodyPreview = formatBodyContent(textBody, htmlBody).substring(0, 500);
78
+ }
79
+ messages.push(messageData);
80
+ }
81
+ });
82
+ output += `Successful: ${messages.length}\n`;
83
+ output += `Failed: ${errors.length}\n\n`;
84
+ output += `${"=".repeat(80)}\n\n`;
85
+ // Display successful messages
86
+ if (messages.length > 0) {
87
+ output += `✅ Successfully Retrieved Messages:\n\n`;
88
+ messages.forEach((msg, index) => {
89
+ output += `${index + 1}. ${msg.subject}\n`;
90
+ output += ` From: ${msg.from}\n`;
91
+ output += ` Date: ${msg.date}\n`;
92
+ output += ` Message ID: ${msg.messageId}\n`;
93
+ output += ` URL: ${generateGmailWebUrl(msg.messageId)}\n`;
94
+ if (format === "full" && msg.bodyPreview) {
95
+ output += ` Preview: ${msg.bodyPreview.substring(0, 100)}...\n`;
96
+ }
97
+ output += `\n`;
98
+ });
99
+ }
100
+ // Display errors
101
+ if (errors.length > 0) {
102
+ output += `\n❌ Failed Messages:\n\n`;
103
+ errors.forEach((err, index) => {
104
+ output += `${index + 1}. Message ID: ${err.messageId}\n`;
105
+ output += ` Error: ${err.error}\n\n`;
106
+ });
107
+ }
108
+ return ResponseFormatter.success({
109
+ totalRequested: messageIds.length,
110
+ successCount: messages.length,
111
+ errorCount: errors.length,
112
+ messages,
113
+ errors,
114
+ format,
115
+ }, output);
116
+ }
117
+ catch (error) {
118
+ return ResponseFormatter.error(error);
119
+ }
120
+ }
@@ -0,0 +1,102 @@
1
+ import { google } from "googleapis";
2
+ import { ResponseFormatter } from "../../../lib/response-formatter.js";
3
+ export const schema = {
4
+ name: "gmail_get_threads_batch",
5
+ description: "Retrieve multiple Gmail threads (conversations) in a single batch request. More efficient than calling gmail_get_thread multiple times. Returns all messages in each thread.",
6
+ inputSchema: {
7
+ type: "object",
8
+ properties: {
9
+ threadIds: {
10
+ type: "array",
11
+ items: { type: "string" },
12
+ description: "Array of Gmail thread IDs to retrieve (max recommended: 50)",
13
+ },
14
+ },
15
+ required: ["threadIds"],
16
+ },
17
+ };
18
+ export async function getThreadsBatch(args) {
19
+ try {
20
+ const gmail = google.gmail("v1");
21
+ const { threadIds } = args;
22
+ if (threadIds.length === 0) {
23
+ return ResponseFormatter.error(new Error("threadIds array cannot be empty"));
24
+ }
25
+ if (threadIds.length > 50) {
26
+ return ResponseFormatter.error(new Error("Too many thread IDs. Maximum recommended is 50 per batch request."));
27
+ }
28
+ // Fetch all threads
29
+ const threadPromises = threadIds.map((threadId) => gmail.users.threads
30
+ .get({
31
+ userId: "me",
32
+ id: threadId,
33
+ format: "full",
34
+ })
35
+ .catch((error) => ({
36
+ error: true,
37
+ threadId,
38
+ errorMessage: error.message,
39
+ })));
40
+ const results = await Promise.all(threadPromises);
41
+ // Process results
42
+ const threads = [];
43
+ const errors = [];
44
+ let output = `📬 Batch Thread Retrieval Results\n`;
45
+ output += `${"=".repeat(80)}\n\n`;
46
+ output += `Requested: ${threadIds.length} threads\n`;
47
+ results.forEach((result) => {
48
+ if (result.error) {
49
+ errors.push({
50
+ threadId: result.threadId,
51
+ error: result.errorMessage,
52
+ });
53
+ }
54
+ else {
55
+ const thread = result.data;
56
+ const messages = thread.messages || [];
57
+ threads.push({
58
+ threadId: thread.id,
59
+ messageCount: messages.length,
60
+ snippet: thread.snippet || "",
61
+ messages: messages.map((m) => ({
62
+ messageId: m.id,
63
+ snippet: m.snippet,
64
+ labelIds: m.labelIds || [],
65
+ })),
66
+ });
67
+ }
68
+ });
69
+ output += `Successful: ${threads.length}\n`;
70
+ output += `Failed: ${errors.length}\n\n`;
71
+ output += `${"=".repeat(80)}\n\n`;
72
+ // Display successful threads
73
+ if (threads.length > 0) {
74
+ output += `✅ Successfully Retrieved Threads:\n\n`;
75
+ threads.forEach((thread, index) => {
76
+ output += `${index + 1}. Thread ID: ${thread.threadId}\n`;
77
+ output += ` Messages: ${thread.messageCount}\n`;
78
+ output += ` Snippet: ${thread.snippet}\n`;
79
+ output += ` Message IDs: ${thread.messages.map((m) => m.messageId).join(", ")}\n`;
80
+ output += `\n`;
81
+ });
82
+ }
83
+ // Display errors
84
+ if (errors.length > 0) {
85
+ output += `\n❌ Failed Threads:\n\n`;
86
+ errors.forEach((err, index) => {
87
+ output += `${index + 1}. Thread ID: ${err.threadId}\n`;
88
+ output += ` Error: ${err.error}\n\n`;
89
+ });
90
+ }
91
+ return ResponseFormatter.success({
92
+ totalRequested: threadIds.length,
93
+ successCount: threads.length,
94
+ errorCount: errors.length,
95
+ threads,
96
+ errors,
97
+ }, output);
98
+ }
99
+ catch (error) {
100
+ return ResponseFormatter.error(error);
101
+ }
102
+ }
@@ -0,0 +1,131 @@
1
+ import { google } from "googleapis";
2
+ import { ResponseFormatter } from "../../../lib/response-formatter.js";
3
+ export const schema = {
4
+ name: "gmail_manage_label",
5
+ description: "Create, update, or delete Gmail labels. Supports creating new labels with visibility settings, updating existing label properties, or deleting labels.",
6
+ inputSchema: {
7
+ type: "object",
8
+ properties: {
9
+ action: {
10
+ type: "string",
11
+ enum: ["create", "update", "delete"],
12
+ description: "The action to perform on the label",
13
+ },
14
+ name: {
15
+ type: "string",
16
+ description: "Label name (required for create, optional for update). The display name of the label.",
17
+ optional: true,
18
+ },
19
+ labelId: {
20
+ type: "string",
21
+ description: "Label ID (required for update and delete operations). Get this from gmail_list_labels.",
22
+ optional: true,
23
+ },
24
+ labelListVisibility: {
25
+ type: "string",
26
+ enum: ["labelShow", "labelHide"],
27
+ description: "Whether to show the label in the label list (optional for create/update)",
28
+ optional: true,
29
+ },
30
+ messageListVisibility: {
31
+ type: "string",
32
+ enum: ["show", "hide"],
33
+ description: "Whether to show the label in the message list (optional for create/update)",
34
+ optional: true,
35
+ },
36
+ },
37
+ required: ["action"],
38
+ },
39
+ };
40
+ export async function manageLabel(args) {
41
+ try {
42
+ const gmail = google.gmail("v1");
43
+ const { action, name, labelId, labelListVisibility, messageListVisibility, } = args;
44
+ let output = "";
45
+ let resultData = {};
46
+ switch (action) {
47
+ case "create": {
48
+ if (!name) {
49
+ return ResponseFormatter.error(new Error("Label name is required for create action"));
50
+ }
51
+ const createResponse = await gmail.users.labels.create({
52
+ userId: "me",
53
+ requestBody: {
54
+ name,
55
+ labelListVisibility: labelListVisibility || "labelShow",
56
+ messageListVisibility: messageListVisibility || "show",
57
+ },
58
+ });
59
+ const createdLabel = createResponse.data;
60
+ output = `✅ Successfully created label\n\n`;
61
+ output += `Label ID: ${createdLabel.id}\n`;
62
+ output += `Name: ${createdLabel.name}\n`;
63
+ output += `Label List Visibility: ${createdLabel.labelListVisibility}\n`;
64
+ output += `Message List Visibility: ${createdLabel.messageListVisibility}`;
65
+ resultData = {
66
+ action: "create",
67
+ labelId: createdLabel.id,
68
+ name: createdLabel.name,
69
+ labelListVisibility: createdLabel.labelListVisibility,
70
+ messageListVisibility: createdLabel.messageListVisibility,
71
+ };
72
+ break;
73
+ }
74
+ case "update": {
75
+ if (!labelId) {
76
+ return ResponseFormatter.error(new Error("Label ID is required for update action"));
77
+ }
78
+ const updateBody = {};
79
+ if (name)
80
+ updateBody.name = name;
81
+ if (labelListVisibility)
82
+ updateBody.labelListVisibility = labelListVisibility;
83
+ if (messageListVisibility)
84
+ updateBody.messageListVisibility = messageListVisibility;
85
+ if (Object.keys(updateBody).length === 0) {
86
+ return ResponseFormatter.error(new Error("At least one property (name, labelListVisibility, or messageListVisibility) must be provided for update"));
87
+ }
88
+ const updateResponse = await gmail.users.labels.update({
89
+ userId: "me",
90
+ id: labelId,
91
+ requestBody: updateBody,
92
+ });
93
+ const updatedLabel = updateResponse.data;
94
+ output = `✅ Successfully updated label\n\n`;
95
+ output += `Label ID: ${updatedLabel.id}\n`;
96
+ output += `Name: ${updatedLabel.name}\n`;
97
+ output += `Label List Visibility: ${updatedLabel.labelListVisibility}\n`;
98
+ output += `Message List Visibility: ${updatedLabel.messageListVisibility}`;
99
+ resultData = {
100
+ action: "update",
101
+ labelId: updatedLabel.id,
102
+ name: updatedLabel.name,
103
+ labelListVisibility: updatedLabel.labelListVisibility,
104
+ messageListVisibility: updatedLabel.messageListVisibility,
105
+ };
106
+ break;
107
+ }
108
+ case "delete": {
109
+ if (!labelId) {
110
+ return ResponseFormatter.error(new Error("Label ID is required for delete action"));
111
+ }
112
+ await gmail.users.labels.delete({
113
+ userId: "me",
114
+ id: labelId,
115
+ });
116
+ output = `✅ Successfully deleted label: ${labelId}`;
117
+ resultData = {
118
+ action: "delete",
119
+ labelId,
120
+ };
121
+ break;
122
+ }
123
+ default:
124
+ return ResponseFormatter.error(new Error(`Invalid action: ${action}. Must be create, update, or delete.`));
125
+ }
126
+ return ResponseFormatter.success(resultData, output);
127
+ }
128
+ catch (error) {
129
+ return ResponseFormatter.error(error);
130
+ }
131
+ }