@alanse/mcp-server-google-workspace 1.0.0 → 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 (155) hide show
  1. package/README.md +146 -17
  2. package/dist/auth.js +5 -0
  3. package/dist/lib/calendar-helpers.js +197 -0
  4. package/dist/lib/drive-helpers.js +263 -0
  5. package/dist/lib/gmail-helpers.js +204 -0
  6. package/dist/tools/calendar/acl/calendar_acl_insert.js +80 -0
  7. package/dist/tools/calendar/acl/calendar_acl_list.js +82 -0
  8. package/dist/tools/calendar/basic/calendar_create_event.js +113 -0
  9. package/dist/tools/calendar/basic/calendar_delete_event.js +52 -0
  10. package/dist/tools/calendar/basic/calendar_get_event.js +52 -0
  11. package/dist/tools/calendar/basic/calendar_list_events.js +86 -0
  12. package/dist/tools/calendar/basic/calendar_update_event.js +116 -0
  13. package/dist/tools/calendar/calendarlist/calendar_calendarlist_get.js +73 -0
  14. package/dist/tools/calendar/calendarlist/calendar_calendarlist_list.js +87 -0
  15. package/dist/tools/calendar/calendars/calendar_calendars_get.js +52 -0
  16. package/dist/tools/calendar/calendars/calendar_calendars_insert.js +66 -0
  17. package/dist/tools/calendar/calendars/calendar_calendars_update.js +85 -0
  18. package/dist/tools/calendar/colors/calendar_colors_get.js +46 -0
  19. package/dist/tools/calendar/events_advanced/calendar_events_instances.js +81 -0
  20. package/dist/tools/calendar/events_advanced/calendar_events_move.js +63 -0
  21. package/dist/tools/calendar/events_advanced/calendar_events_quickadd.js +52 -0
  22. package/dist/tools/calendar/freebusy/calendar_freebusy_query.js +69 -0
  23. package/dist/tools/calendar/settings/calendar_settings_list.js +81 -0
  24. package/dist/tools/drive/advanced/drive_empty_trash.js +56 -0
  25. package/dist/tools/drive/advanced/drive_export_file.js +158 -0
  26. package/dist/tools/drive/advanced/drive_list_revisions.js +80 -0
  27. package/dist/tools/drive/basic/drive_get_metadata.js +49 -0
  28. package/dist/tools/drive/basic/drive_list_files.js +76 -0
  29. package/dist/tools/drive/file/drive_copy_file.js +79 -0
  30. package/dist/tools/drive/file/drive_create_file.js +72 -0
  31. package/dist/tools/drive/file/drive_delete_file.js +48 -0
  32. package/dist/tools/drive/file/drive_move_file.js +79 -0
  33. package/dist/tools/drive/file/drive_rename_file.js +58 -0
  34. package/dist/tools/drive/file/drive_update_file.js +106 -0
  35. package/dist/tools/drive/file/drive_upload_file.js +80 -0
  36. package/dist/tools/drive/folder/drive_create_folder.js +67 -0
  37. package/dist/tools/drive/folder/drive_list_folder_contents.js +68 -0
  38. package/dist/tools/drive/folder/drive_move_to_folder.js +59 -0
  39. package/dist/tools/drive/permissions/drive_list_permissions.js +115 -0
  40. package/dist/tools/drive/permissions/drive_remove_permission.js +71 -0
  41. package/dist/tools/drive/permissions/drive_share_file.js +116 -0
  42. package/dist/tools/drive/permissions/drive_update_permission.js +79 -0
  43. package/dist/tools/gmail/basic/gmail_get_message.js +95 -0
  44. package/dist/tools/gmail/basic/gmail_get_thread.js +46 -0
  45. package/dist/tools/gmail/basic/gmail_list_labels.js +54 -0
  46. package/dist/tools/gmail/basic/gmail_search_messages.js +59 -0
  47. package/dist/tools/gmail/batch/gmail_batch_modify_labels.js +74 -0
  48. package/dist/tools/gmail/batch/gmail_get_messages_batch.js +120 -0
  49. package/dist/tools/gmail/batch/gmail_get_threads_batch.js +102 -0
  50. package/dist/tools/gmail/labels/gmail_manage_label.js +131 -0
  51. package/dist/tools/gmail/labels/gmail_modify_labels.js +65 -0
  52. package/dist/tools/gmail/send/gmail_draft_message.js +117 -0
  53. package/dist/tools/gmail/send/gmail_send_message.js +109 -0
  54. package/dist/tools/index.js +267 -3
  55. package/package.json +8 -3
  56. package/dist/tools/basic/gsheets_add_sheet.js +0 -65
  57. package/dist/tools/basic/gsheets_copy_sheet.js +0 -56
  58. package/dist/tools/basic/gsheets_copy_to.js +0 -113
  59. package/dist/tools/basic/gsheets_create_spreadsheet.js +0 -88
  60. package/dist/tools/basic/gsheets_delete_columns.js +0 -69
  61. package/dist/tools/basic/gsheets_delete_rows.js +0 -69
  62. package/dist/tools/basic/gsheets_delete_sheet.js +0 -56
  63. package/dist/tools/basic/gsheets_duplicate_sheet.js +0 -72
  64. package/dist/tools/basic/gsheets_insert_columns.js +0 -69
  65. package/dist/tools/basic/gsheets_insert_rows.js +0 -69
  66. package/dist/tools/basic/gsheets_list_sheets.js +0 -53
  67. package/dist/tools/basic/gsheets_read.js +0 -120
  68. package/dist/tools/basic/gsheets_rename_sheet.js +0 -64
  69. package/dist/tools/charts/gsheets_add_bubble.js +0 -176
  70. package/dist/tools/charts/gsheets_add_candlestick.js +0 -192
  71. package/dist/tools/charts/gsheets_add_chart.js +0 -162
  72. package/dist/tools/charts/gsheets_add_combo.js +0 -169
  73. package/dist/tools/charts/gsheets_add_histogram.js +0 -143
  74. package/dist/tools/charts/gsheets_add_org_chart.js +0 -160
  75. package/dist/tools/charts/gsheets_add_treemap.js +0 -177
  76. package/dist/tools/charts/gsheets_add_waterfall.js +0 -155
  77. package/dist/tools/charts/gsheets_delete_chart.js +0 -56
  78. package/dist/tools/charts/gsheets_update_chart.js +0 -118
  79. package/dist/tools/data/gsheets_append_data.js +0 -68
  80. package/dist/tools/data/gsheets_batch_clear.js +0 -53
  81. package/dist/tools/data/gsheets_batch_update.js +0 -81
  82. package/dist/tools/data/gsheets_clear_data.js +0 -53
  83. package/dist/tools/data/gsheets_create_filter.js +0 -81
  84. package/dist/tools/data/gsheets_find_replace.js +0 -124
  85. package/dist/tools/data/gsheets_set_data_validation.js +0 -153
  86. package/dist/tools/data/gsheets_sort_range.js +0 -102
  87. package/dist/tools/data/gsheets_update_cell.js +0 -44
  88. package/dist/tools/formatting/gsheets_auto_resize.js +0 -75
  89. package/dist/tools/formatting/gsheets_format_cells.js +0 -161
  90. package/dist/tools/formatting/gsheets_freeze_columns.js +0 -67
  91. package/dist/tools/formatting/gsheets_freeze_rows.js +0 -67
  92. package/dist/tools/formatting/gsheets_merge_cells.js +0 -85
  93. package/dist/tools/formatting/gsheets_set_number_format.js +0 -116
  94. package/dist/tools/formatting/gsheets_unmerge_cells.js +0 -79
  95. package/dist/tools/formatting/gsheets_update_borders.js +0 -212
  96. package/dist/tools/gdrive/gdrive_read_file.js +0 -77
  97. package/dist/tools/gdrive/gdrive_search.js +0 -71
  98. package/dist/tools/gdrive_read_file.js +0 -77
  99. package/dist/tools/gdrive_search.js +0 -71
  100. package/dist/tools/gsheets_add_bubble.js +0 -176
  101. package/dist/tools/gsheets_add_candlestick.js +0 -192
  102. package/dist/tools/gsheets_add_chart.js +0 -162
  103. package/dist/tools/gsheets_add_combo.js +0 -169
  104. package/dist/tools/gsheets_add_conditional_format.js +0 -175
  105. package/dist/tools/gsheets_add_histogram.js +0 -143
  106. package/dist/tools/gsheets_add_named_range.js +0 -87
  107. package/dist/tools/gsheets_add_org_chart.js +0 -160
  108. package/dist/tools/gsheets_add_protected_range.js +0 -127
  109. package/dist/tools/gsheets_add_sheet.js +0 -65
  110. package/dist/tools/gsheets_add_treemap.js +0 -177
  111. package/dist/tools/gsheets_add_waterfall.js +0 -155
  112. package/dist/tools/gsheets_append_data.js +0 -68
  113. package/dist/tools/gsheets_auto_resize.js +0 -75
  114. package/dist/tools/gsheets_batch_clear.js +0 -53
  115. package/dist/tools/gsheets_batch_update.js +0 -81
  116. package/dist/tools/gsheets_clear_data.js +0 -53
  117. package/dist/tools/gsheets_copy_sheet.js +0 -56
  118. package/dist/tools/gsheets_copy_to.js +0 -113
  119. package/dist/tools/gsheets_create_filter.js +0 -81
  120. package/dist/tools/gsheets_create_spreadsheet.js +0 -88
  121. package/dist/tools/gsheets_delete_chart.js +0 -56
  122. package/dist/tools/gsheets_delete_columns.js +0 -69
  123. package/dist/tools/gsheets_delete_named_range.js +0 -56
  124. package/dist/tools/gsheets_delete_protected_range.js +0 -56
  125. package/dist/tools/gsheets_delete_rows.js +0 -69
  126. package/dist/tools/gsheets_delete_sheet.js +0 -56
  127. package/dist/tools/gsheets_duplicate_sheet.js +0 -72
  128. package/dist/tools/gsheets_find_replace.js +0 -124
  129. package/dist/tools/gsheets_format_cells.js +0 -161
  130. package/dist/tools/gsheets_freeze_columns.js +0 -67
  131. package/dist/tools/gsheets_freeze_rows.js +0 -67
  132. package/dist/tools/gsheets_insert_columns.js +0 -69
  133. package/dist/tools/gsheets_insert_rows.js +0 -69
  134. package/dist/tools/gsheets_list_sheets.js +0 -53
  135. package/dist/tools/gsheets_merge_cells.js +0 -85
  136. package/dist/tools/gsheets_read.js +0 -120
  137. package/dist/tools/gsheets_rename_sheet.js +0 -64
  138. package/dist/tools/gsheets_set_data_validation.js +0 -153
  139. package/dist/tools/gsheets_set_number_format.js +0 -116
  140. package/dist/tools/gsheets_sort_range.js +0 -102
  141. package/dist/tools/gsheets_unmerge_cells.js +0 -79
  142. package/dist/tools/gsheets_update_borders.js +0 -212
  143. package/dist/tools/gsheets_update_cell.js +0 -44
  144. package/dist/tools/gsheets_update_chart.js +0 -118
  145. package/dist/tools/gsheets_update_named_range.js +0 -112
  146. package/dist/tools/gsheets_update_protected_range.js +0 -110
  147. package/dist/tools/protection/gsheets_add_conditional_format.js +0 -175
  148. package/dist/tools/protection/gsheets_add_named_range.js +0 -87
  149. package/dist/tools/protection/gsheets_add_protected_range.js +0 -127
  150. package/dist/tools/protection/gsheets_delete_named_range.js +0 -56
  151. package/dist/tools/protection/gsheets_delete_protected_range.js +0 -56
  152. package/dist/tools/protection/gsheets_update_named_range.js +0 -112
  153. package/dist/tools/protection/gsheets_update_protected_range.js +0 -110
  154. /package/dist/tools/drive/{drive_read_file.js → basic/drive_read_file.js} +0 -0
  155. /package/dist/tools/drive/{drive_search.js → basic/drive_search.js} +0 -0
@@ -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
+ }
@@ -0,0 +1,65 @@
1
+ import { google } from "googleapis";
2
+ import { ResponseFormatter } from "../../../lib/response-formatter.js";
3
+ export const schema = {
4
+ name: "gmail_modify_labels",
5
+ description: "Add or remove labels from a Gmail message. Can add and remove multiple labels in a single operation. Use gmail_list_labels to get available label IDs.",
6
+ inputSchema: {
7
+ type: "object",
8
+ properties: {
9
+ messageId: {
10
+ type: "string",
11
+ description: "The Gmail message ID to modify",
12
+ },
13
+ addLabelIds: {
14
+ type: "array",
15
+ items: { type: "string" },
16
+ description: "Array of label IDs to add (e.g., ['INBOX', 'STARRED', 'Label_123'])",
17
+ optional: true,
18
+ },
19
+ removeLabelIds: {
20
+ type: "array",
21
+ items: { type: "string" },
22
+ description: "Array of label IDs to remove (e.g., ['UNREAD', 'SPAM'])",
23
+ optional: true,
24
+ },
25
+ },
26
+ required: ["messageId"],
27
+ },
28
+ };
29
+ export async function modifyLabels(args) {
30
+ try {
31
+ const gmail = google.gmail("v1");
32
+ const { messageId, addLabelIds = [], removeLabelIds = [] } = args;
33
+ // Validate at least one operation
34
+ if (addLabelIds.length === 0 && removeLabelIds.length === 0) {
35
+ return ResponseFormatter.error(new Error("At least one of addLabelIds or removeLabelIds must be provided"));
36
+ }
37
+ // Modify labels
38
+ const response = await gmail.users.messages.modify({
39
+ userId: "me",
40
+ id: messageId,
41
+ requestBody: {
42
+ addLabelIds: addLabelIds.length > 0 ? addLabelIds : undefined,
43
+ removeLabelIds: removeLabelIds.length > 0 ? removeLabelIds : undefined,
44
+ },
45
+ });
46
+ const updatedLabels = response.data.labelIds || [];
47
+ let output = `✅ Successfully modified labels for message: ${messageId}\n\n`;
48
+ if (addLabelIds.length > 0) {
49
+ output += `➕ Added labels: ${addLabelIds.join(", ")}\n`;
50
+ }
51
+ if (removeLabelIds.length > 0) {
52
+ output += `➖ Removed labels: ${removeLabelIds.join(", ")}\n`;
53
+ }
54
+ output += `\n📋 Current labels: ${updatedLabels.join(", ") || "(none)"}`;
55
+ return ResponseFormatter.success({
56
+ messageId,
57
+ addedLabels: addLabelIds,
58
+ removedLabels: removeLabelIds,
59
+ currentLabels: updatedLabels,
60
+ }, output);
61
+ }
62
+ catch (error) {
63
+ return ResponseFormatter.error(error);
64
+ }
65
+ }
@@ -0,0 +1,117 @@
1
+ import { google } from "googleapis";
2
+ import { ResponseFormatter } from "../../../lib/response-formatter.js";
3
+ import { prepareGmailMessage, generateGmailWebUrl, } from "../../../lib/gmail-helpers.js";
4
+ export const schema = {
5
+ name: "gmail_draft_message",
6
+ description: "Create a draft email message in Gmail. The draft is saved but not sent, allowing for later editing or sending. Supports plain text or HTML body, CC/BCC recipients, and threading.",
7
+ inputSchema: {
8
+ type: "object",
9
+ properties: {
10
+ subject: {
11
+ type: "string",
12
+ description: "Email subject line",
13
+ },
14
+ body: {
15
+ type: "string",
16
+ description: "Email body content (plain text or HTML)",
17
+ },
18
+ bodyFormat: {
19
+ type: "string",
20
+ enum: ["plain", "html"],
21
+ description: "Body format type (default: plain)",
22
+ optional: true,
23
+ },
24
+ to: {
25
+ type: "string",
26
+ description: "Recipient email address(es). Multiple addresses can be comma-separated.",
27
+ optional: true,
28
+ },
29
+ cc: {
30
+ type: "string",
31
+ description: "CC recipient(s). Multiple addresses can be comma-separated.",
32
+ optional: true,
33
+ },
34
+ bcc: {
35
+ type: "string",
36
+ description: "BCC recipient(s). Multiple addresses can be comma-separated.",
37
+ optional: true,
38
+ },
39
+ threadId: {
40
+ type: "string",
41
+ description: "Thread ID to reply to (makes this a reply draft in an existing conversation)",
42
+ optional: true,
43
+ },
44
+ inReplyTo: {
45
+ type: "string",
46
+ description: "Message-ID header of the message being replied to (for threading)",
47
+ optional: true,
48
+ },
49
+ references: {
50
+ type: "string",
51
+ description: "References header for email threading (space-separated Message-IDs)",
52
+ optional: true,
53
+ },
54
+ },
55
+ required: ["subject", "body"],
56
+ },
57
+ };
58
+ export async function draftMessage(args) {
59
+ try {
60
+ const gmail = google.gmail("v1");
61
+ const { subject, body, bodyFormat = "plain", to, cc, bcc, threadId, inReplyTo, references, } = args;
62
+ // Prepare base64url encoded MIME message
63
+ const encodedMessage = prepareGmailMessage({
64
+ to: to || "",
65
+ subject,
66
+ body,
67
+ bodyFormat,
68
+ cc,
69
+ bcc,
70
+ inReplyTo,
71
+ references,
72
+ });
73
+ // Create the draft
74
+ const response = await gmail.users.drafts.create({
75
+ userId: "me",
76
+ requestBody: {
77
+ message: {
78
+ raw: encodedMessage,
79
+ threadId: threadId || undefined,
80
+ },
81
+ },
82
+ });
83
+ const draft = response.data;
84
+ const draftId = draft.id || "";
85
+ const messageId = draft.message?.id || "";
86
+ const draftThreadId = draft.message?.threadId || "";
87
+ let output = `✅ Draft created successfully!\n\n`;
88
+ output += `Draft ID: ${draftId}\n`;
89
+ output += `Message ID: ${messageId}\n`;
90
+ if (draftThreadId)
91
+ output += `Thread ID: ${draftThreadId}\n`;
92
+ output += `Web URL: ${generateGmailWebUrl(messageId)}\n\n`;
93
+ output += `📧 Details:\n`;
94
+ if (to)
95
+ output += ` To: ${to}\n`;
96
+ if (cc)
97
+ output += ` CC: ${cc}\n`;
98
+ if (bcc)
99
+ output += ` BCC: ${bcc}\n`;
100
+ output += ` Subject: ${subject}\n`;
101
+ output += ` Format: ${bodyFormat}\n`;
102
+ if (threadId)
103
+ output += ` Reply to thread: ${threadId}\n`;
104
+ output += `\n💡 The draft is saved in your Gmail Drafts folder and can be edited or sent later.`;
105
+ return ResponseFormatter.success({
106
+ draftId,
107
+ messageId,
108
+ threadId: draftThreadId,
109
+ subject,
110
+ bodyFormat,
111
+ to: to || null,
112
+ }, output);
113
+ }
114
+ catch (error) {
115
+ return ResponseFormatter.error(error);
116
+ }
117
+ }