@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.
- package/README.md +146 -17
- package/dist/auth.js +5 -0
- package/dist/lib/calendar-helpers.js +197 -0
- package/dist/lib/drive-helpers.js +263 -0
- package/dist/lib/gmail-helpers.js +204 -0
- package/dist/tools/calendar/acl/calendar_acl_insert.js +80 -0
- package/dist/tools/calendar/acl/calendar_acl_list.js +82 -0
- package/dist/tools/calendar/basic/calendar_create_event.js +113 -0
- package/dist/tools/calendar/basic/calendar_delete_event.js +52 -0
- package/dist/tools/calendar/basic/calendar_get_event.js +52 -0
- package/dist/tools/calendar/basic/calendar_list_events.js +86 -0
- package/dist/tools/calendar/basic/calendar_update_event.js +116 -0
- package/dist/tools/calendar/calendarlist/calendar_calendarlist_get.js +73 -0
- package/dist/tools/calendar/calendarlist/calendar_calendarlist_list.js +87 -0
- package/dist/tools/calendar/calendars/calendar_calendars_get.js +52 -0
- package/dist/tools/calendar/calendars/calendar_calendars_insert.js +66 -0
- package/dist/tools/calendar/calendars/calendar_calendars_update.js +85 -0
- package/dist/tools/calendar/colors/calendar_colors_get.js +46 -0
- package/dist/tools/calendar/events_advanced/calendar_events_instances.js +81 -0
- package/dist/tools/calendar/events_advanced/calendar_events_move.js +63 -0
- package/dist/tools/calendar/events_advanced/calendar_events_quickadd.js +52 -0
- package/dist/tools/calendar/freebusy/calendar_freebusy_query.js +69 -0
- package/dist/tools/calendar/settings/calendar_settings_list.js +81 -0
- package/dist/tools/drive/advanced/drive_empty_trash.js +56 -0
- package/dist/tools/drive/advanced/drive_export_file.js +158 -0
- package/dist/tools/drive/advanced/drive_list_revisions.js +80 -0
- package/dist/tools/drive/basic/drive_get_metadata.js +49 -0
- package/dist/tools/drive/basic/drive_list_files.js +76 -0
- package/dist/tools/drive/file/drive_copy_file.js +79 -0
- package/dist/tools/drive/file/drive_create_file.js +72 -0
- package/dist/tools/drive/file/drive_delete_file.js +48 -0
- package/dist/tools/drive/file/drive_move_file.js +79 -0
- package/dist/tools/drive/file/drive_rename_file.js +58 -0
- package/dist/tools/drive/file/drive_update_file.js +106 -0
- package/dist/tools/drive/file/drive_upload_file.js +80 -0
- package/dist/tools/drive/folder/drive_create_folder.js +67 -0
- package/dist/tools/drive/folder/drive_list_folder_contents.js +68 -0
- package/dist/tools/drive/folder/drive_move_to_folder.js +59 -0
- package/dist/tools/drive/permissions/drive_list_permissions.js +115 -0
- package/dist/tools/drive/permissions/drive_remove_permission.js +71 -0
- package/dist/tools/drive/permissions/drive_share_file.js +116 -0
- package/dist/tools/drive/permissions/drive_update_permission.js +79 -0
- package/dist/tools/gmail/basic/gmail_get_message.js +95 -0
- package/dist/tools/gmail/basic/gmail_get_thread.js +46 -0
- package/dist/tools/gmail/basic/gmail_list_labels.js +54 -0
- package/dist/tools/gmail/basic/gmail_search_messages.js +59 -0
- package/dist/tools/gmail/batch/gmail_batch_modify_labels.js +74 -0
- package/dist/tools/gmail/batch/gmail_get_messages_batch.js +120 -0
- package/dist/tools/gmail/batch/gmail_get_threads_batch.js +102 -0
- package/dist/tools/gmail/labels/gmail_manage_label.js +131 -0
- package/dist/tools/gmail/labels/gmail_modify_labels.js +65 -0
- package/dist/tools/gmail/send/gmail_draft_message.js +117 -0
- package/dist/tools/gmail/send/gmail_send_message.js +109 -0
- package/dist/tools/index.js +267 -3
- package/package.json +8 -3
- package/dist/tools/basic/gsheets_add_sheet.js +0 -65
- package/dist/tools/basic/gsheets_copy_sheet.js +0 -56
- package/dist/tools/basic/gsheets_copy_to.js +0 -113
- package/dist/tools/basic/gsheets_create_spreadsheet.js +0 -88
- package/dist/tools/basic/gsheets_delete_columns.js +0 -69
- package/dist/tools/basic/gsheets_delete_rows.js +0 -69
- package/dist/tools/basic/gsheets_delete_sheet.js +0 -56
- package/dist/tools/basic/gsheets_duplicate_sheet.js +0 -72
- package/dist/tools/basic/gsheets_insert_columns.js +0 -69
- package/dist/tools/basic/gsheets_insert_rows.js +0 -69
- package/dist/tools/basic/gsheets_list_sheets.js +0 -53
- package/dist/tools/basic/gsheets_read.js +0 -120
- package/dist/tools/basic/gsheets_rename_sheet.js +0 -64
- package/dist/tools/charts/gsheets_add_bubble.js +0 -176
- package/dist/tools/charts/gsheets_add_candlestick.js +0 -192
- package/dist/tools/charts/gsheets_add_chart.js +0 -162
- package/dist/tools/charts/gsheets_add_combo.js +0 -169
- package/dist/tools/charts/gsheets_add_histogram.js +0 -143
- package/dist/tools/charts/gsheets_add_org_chart.js +0 -160
- package/dist/tools/charts/gsheets_add_treemap.js +0 -177
- package/dist/tools/charts/gsheets_add_waterfall.js +0 -155
- package/dist/tools/charts/gsheets_delete_chart.js +0 -56
- package/dist/tools/charts/gsheets_update_chart.js +0 -118
- package/dist/tools/data/gsheets_append_data.js +0 -68
- package/dist/tools/data/gsheets_batch_clear.js +0 -53
- package/dist/tools/data/gsheets_batch_update.js +0 -81
- package/dist/tools/data/gsheets_clear_data.js +0 -53
- package/dist/tools/data/gsheets_create_filter.js +0 -81
- package/dist/tools/data/gsheets_find_replace.js +0 -124
- package/dist/tools/data/gsheets_set_data_validation.js +0 -153
- package/dist/tools/data/gsheets_sort_range.js +0 -102
- package/dist/tools/data/gsheets_update_cell.js +0 -44
- package/dist/tools/formatting/gsheets_auto_resize.js +0 -75
- package/dist/tools/formatting/gsheets_format_cells.js +0 -161
- package/dist/tools/formatting/gsheets_freeze_columns.js +0 -67
- package/dist/tools/formatting/gsheets_freeze_rows.js +0 -67
- package/dist/tools/formatting/gsheets_merge_cells.js +0 -85
- package/dist/tools/formatting/gsheets_set_number_format.js +0 -116
- package/dist/tools/formatting/gsheets_unmerge_cells.js +0 -79
- package/dist/tools/formatting/gsheets_update_borders.js +0 -212
- package/dist/tools/gdrive/gdrive_read_file.js +0 -77
- package/dist/tools/gdrive/gdrive_search.js +0 -71
- package/dist/tools/gdrive_read_file.js +0 -77
- package/dist/tools/gdrive_search.js +0 -71
- package/dist/tools/gsheets_add_bubble.js +0 -176
- package/dist/tools/gsheets_add_candlestick.js +0 -192
- package/dist/tools/gsheets_add_chart.js +0 -162
- package/dist/tools/gsheets_add_combo.js +0 -169
- package/dist/tools/gsheets_add_conditional_format.js +0 -175
- package/dist/tools/gsheets_add_histogram.js +0 -143
- package/dist/tools/gsheets_add_named_range.js +0 -87
- package/dist/tools/gsheets_add_org_chart.js +0 -160
- package/dist/tools/gsheets_add_protected_range.js +0 -127
- package/dist/tools/gsheets_add_sheet.js +0 -65
- package/dist/tools/gsheets_add_treemap.js +0 -177
- package/dist/tools/gsheets_add_waterfall.js +0 -155
- package/dist/tools/gsheets_append_data.js +0 -68
- package/dist/tools/gsheets_auto_resize.js +0 -75
- package/dist/tools/gsheets_batch_clear.js +0 -53
- package/dist/tools/gsheets_batch_update.js +0 -81
- package/dist/tools/gsheets_clear_data.js +0 -53
- package/dist/tools/gsheets_copy_sheet.js +0 -56
- package/dist/tools/gsheets_copy_to.js +0 -113
- package/dist/tools/gsheets_create_filter.js +0 -81
- package/dist/tools/gsheets_create_spreadsheet.js +0 -88
- package/dist/tools/gsheets_delete_chart.js +0 -56
- package/dist/tools/gsheets_delete_columns.js +0 -69
- package/dist/tools/gsheets_delete_named_range.js +0 -56
- package/dist/tools/gsheets_delete_protected_range.js +0 -56
- package/dist/tools/gsheets_delete_rows.js +0 -69
- package/dist/tools/gsheets_delete_sheet.js +0 -56
- package/dist/tools/gsheets_duplicate_sheet.js +0 -72
- package/dist/tools/gsheets_find_replace.js +0 -124
- package/dist/tools/gsheets_format_cells.js +0 -161
- package/dist/tools/gsheets_freeze_columns.js +0 -67
- package/dist/tools/gsheets_freeze_rows.js +0 -67
- package/dist/tools/gsheets_insert_columns.js +0 -69
- package/dist/tools/gsheets_insert_rows.js +0 -69
- package/dist/tools/gsheets_list_sheets.js +0 -53
- package/dist/tools/gsheets_merge_cells.js +0 -85
- package/dist/tools/gsheets_read.js +0 -120
- package/dist/tools/gsheets_rename_sheet.js +0 -64
- package/dist/tools/gsheets_set_data_validation.js +0 -153
- package/dist/tools/gsheets_set_number_format.js +0 -116
- package/dist/tools/gsheets_sort_range.js +0 -102
- package/dist/tools/gsheets_unmerge_cells.js +0 -79
- package/dist/tools/gsheets_update_borders.js +0 -212
- package/dist/tools/gsheets_update_cell.js +0 -44
- package/dist/tools/gsheets_update_chart.js +0 -118
- package/dist/tools/gsheets_update_named_range.js +0 -112
- package/dist/tools/gsheets_update_protected_range.js +0 -110
- package/dist/tools/protection/gsheets_add_conditional_format.js +0 -175
- package/dist/tools/protection/gsheets_add_named_range.js +0 -87
- package/dist/tools/protection/gsheets_add_protected_range.js +0 -127
- package/dist/tools/protection/gsheets_delete_named_range.js +0 -56
- package/dist/tools/protection/gsheets_delete_protected_range.js +0 -56
- package/dist/tools/protection/gsheets_update_named_range.js +0 -112
- package/dist/tools/protection/gsheets_update_protected_range.js +0 -110
- /package/dist/tools/drive/{drive_read_file.js → basic/drive_read_file.js} +0 -0
- /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
|
+
}
|