@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 { formatFileMetadata, generateDriveWebUrl, } from "../../../lib/drive-helpers.js";
|
|
4
|
+
export const schema = {
|
|
5
|
+
name: "drive_move_to_folder",
|
|
6
|
+
description: "Move a file to a specified folder in Google Drive. Removes the file from its current parent folder and adds it to the new folder. Requires file ID and folder ID.",
|
|
7
|
+
inputSchema: {
|
|
8
|
+
type: "object",
|
|
9
|
+
properties: {
|
|
10
|
+
fileId: {
|
|
11
|
+
type: "string",
|
|
12
|
+
description: "ID of the file to move",
|
|
13
|
+
},
|
|
14
|
+
folderId: {
|
|
15
|
+
type: "string",
|
|
16
|
+
description: "ID of the destination folder",
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
required: ["fileId", "folderId"],
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
export async function moveToFolder(args) {
|
|
23
|
+
try {
|
|
24
|
+
const drive = google.drive("v3");
|
|
25
|
+
const { fileId, folderId } = args;
|
|
26
|
+
// Get current file metadata
|
|
27
|
+
const fileResponse = await drive.files.get({
|
|
28
|
+
fileId,
|
|
29
|
+
fields: "id, name, parents",
|
|
30
|
+
});
|
|
31
|
+
const fileName = fileResponse.data.name || "Unknown";
|
|
32
|
+
const currentParents = fileResponse.data.parents || [];
|
|
33
|
+
// Move the file to the new folder
|
|
34
|
+
const response = await drive.files.update({
|
|
35
|
+
fileId,
|
|
36
|
+
addParents: folderId,
|
|
37
|
+
removeParents: currentParents.join(","),
|
|
38
|
+
fields: "id, name, mimeType, size, modifiedTime, webViewLink, parents",
|
|
39
|
+
});
|
|
40
|
+
const movedFile = response.data;
|
|
41
|
+
let output = `✅ File moved successfully!\n\n`;
|
|
42
|
+
output += `📄 File: ${fileName}\n`;
|
|
43
|
+
output += ` From: ${currentParents.join(", ") || "Root"}\n`;
|
|
44
|
+
output += ` To: ${folderId}\n\n`;
|
|
45
|
+
output += formatFileMetadata(movedFile, true);
|
|
46
|
+
output += `\n📎 Web URL: ${generateDriveWebUrl(fileId)}\n`;
|
|
47
|
+
return ResponseFormatter.success({
|
|
48
|
+
fileId,
|
|
49
|
+
name: movedFile.name,
|
|
50
|
+
previousParents: currentParents,
|
|
51
|
+
newParents: movedFile.parents,
|
|
52
|
+
mimeType: movedFile.mimeType,
|
|
53
|
+
webViewLink: movedFile.webViewLink,
|
|
54
|
+
}, output);
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
return ResponseFormatter.error(error);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { google } from "googleapis";
|
|
2
|
+
import { ResponseFormatter } from "../../../lib/response-formatter.js";
|
|
3
|
+
import { getRoleLabel, getTypeLabel, } from "../../../lib/drive-helpers.js";
|
|
4
|
+
export const schema = {
|
|
5
|
+
name: "drive_list_permissions",
|
|
6
|
+
description: "List all permissions for a Google Drive file. Shows who has access and what level of access they have.",
|
|
7
|
+
inputSchema: {
|
|
8
|
+
type: "object",
|
|
9
|
+
properties: {
|
|
10
|
+
fileId: {
|
|
11
|
+
type: "string",
|
|
12
|
+
description: "ID of the file to get permissions for",
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
required: ["fileId"],
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
export async function listPermissions(args) {
|
|
19
|
+
try {
|
|
20
|
+
const drive = google.drive("v3");
|
|
21
|
+
const { fileId } = args;
|
|
22
|
+
// Get file metadata
|
|
23
|
+
const fileResponse = await drive.files.get({
|
|
24
|
+
fileId,
|
|
25
|
+
fields: "id, name, mimeType, webViewLink",
|
|
26
|
+
});
|
|
27
|
+
const fileName = fileResponse.data.name || "Unknown";
|
|
28
|
+
const webViewLink = fileResponse.data.webViewLink || "";
|
|
29
|
+
// List permissions
|
|
30
|
+
const permissionsResponse = await drive.permissions.list({
|
|
31
|
+
fileId,
|
|
32
|
+
fields: "permissions(id, type, role, emailAddress, displayName, domain, permissionDetails)",
|
|
33
|
+
pageSize: 100,
|
|
34
|
+
});
|
|
35
|
+
const permissions = permissionsResponse.data.permissions || [];
|
|
36
|
+
// Group permissions by type
|
|
37
|
+
const groupedPermissions = {
|
|
38
|
+
users: permissions.filter((p) => p.type === "user"),
|
|
39
|
+
groups: permissions.filter((p) => p.type === "group"),
|
|
40
|
+
domains: permissions.filter((p) => p.type === "domain"),
|
|
41
|
+
anyone: permissions.filter((p) => p.type === "anyone"),
|
|
42
|
+
};
|
|
43
|
+
let output = `📋 File Permissions\n`;
|
|
44
|
+
output += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n`;
|
|
45
|
+
output += `📄 File: ${fileName}\n`;
|
|
46
|
+
output += `ID: ${fileId}\n`;
|
|
47
|
+
if (webViewLink) {
|
|
48
|
+
output += `Link: ${webViewLink}\n`;
|
|
49
|
+
}
|
|
50
|
+
output += `\n`;
|
|
51
|
+
// Display permissions by type
|
|
52
|
+
if (groupedPermissions.users.length > 0) {
|
|
53
|
+
output += `👤 Users (${groupedPermissions.users.length}):\n`;
|
|
54
|
+
groupedPermissions.users.forEach((p, i) => {
|
|
55
|
+
output += ` ${i + 1}. ${p.emailAddress || "Unknown"}\n`;
|
|
56
|
+
output += ` Role: ${getRoleLabel(p.role || "")}\n`;
|
|
57
|
+
output += ` Permission ID: ${p.id}\n\n`;
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
if (groupedPermissions.groups.length > 0) {
|
|
61
|
+
output += `👥 Groups (${groupedPermissions.groups.length}):\n`;
|
|
62
|
+
groupedPermissions.groups.forEach((p, i) => {
|
|
63
|
+
output += ` ${i + 1}. ${p.emailAddress || "Unknown"}\n`;
|
|
64
|
+
output += ` Role: ${getRoleLabel(p.role || "")}\n`;
|
|
65
|
+
output += ` Permission ID: ${p.id}\n\n`;
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
if (groupedPermissions.domains.length > 0) {
|
|
69
|
+
output += `🌐 Domains (${groupedPermissions.domains.length}):\n`;
|
|
70
|
+
groupedPermissions.domains.forEach((p, i) => {
|
|
71
|
+
output += ` ${i + 1}. ${p.domain || "Unknown"}\n`;
|
|
72
|
+
output += ` Role: ${getRoleLabel(p.role || "")}\n`;
|
|
73
|
+
output += ` Permission ID: ${p.id}\n\n`;
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
if (groupedPermissions.anyone.length > 0) {
|
|
77
|
+
output += `🌍 Anyone with Link (${groupedPermissions.anyone.length}):\n`;
|
|
78
|
+
groupedPermissions.anyone.forEach((p) => {
|
|
79
|
+
output += ` Role: ${getRoleLabel(p.role || "")}\n`;
|
|
80
|
+
output += ` Permission ID: ${p.id}\n\n`;
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
if (permissions.length === 0) {
|
|
84
|
+
output += `No permissions found.`;
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
output += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`;
|
|
88
|
+
output += `Total: ${permissions.length} permission(s)`;
|
|
89
|
+
}
|
|
90
|
+
return ResponseFormatter.success({
|
|
91
|
+
fileId,
|
|
92
|
+
fileName,
|
|
93
|
+
totalPermissions: permissions.length,
|
|
94
|
+
permissions: permissions.map((p) => ({
|
|
95
|
+
id: p.id,
|
|
96
|
+
type: p.type,
|
|
97
|
+
role: p.role,
|
|
98
|
+
typeLabel: getTypeLabel(p.type || ""),
|
|
99
|
+
roleLabel: getRoleLabel(p.role || ""),
|
|
100
|
+
emailAddress: p.emailAddress,
|
|
101
|
+
displayName: p.displayName,
|
|
102
|
+
domain: p.domain,
|
|
103
|
+
})),
|
|
104
|
+
summary: {
|
|
105
|
+
users: groupedPermissions.users.length,
|
|
106
|
+
groups: groupedPermissions.groups.length,
|
|
107
|
+
domains: groupedPermissions.domains.length,
|
|
108
|
+
anyone: groupedPermissions.anyone.length,
|
|
109
|
+
},
|
|
110
|
+
}, output);
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
return ResponseFormatter.error(error);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { google } from "googleapis";
|
|
2
|
+
import { ResponseFormatter } from "../../../lib/response-formatter.js";
|
|
3
|
+
import { getTypeLabel } from "../../../lib/drive-helpers.js";
|
|
4
|
+
export const schema = {
|
|
5
|
+
name: "drive_remove_permission",
|
|
6
|
+
description: "Remove a permission from a Google Drive file, revoking access for a user, group, domain, or link. Must specify the permission ID returned from list_permissions.",
|
|
7
|
+
inputSchema: {
|
|
8
|
+
type: "object",
|
|
9
|
+
properties: {
|
|
10
|
+
fileId: {
|
|
11
|
+
type: "string",
|
|
12
|
+
description: "ID of the file to remove permission from",
|
|
13
|
+
},
|
|
14
|
+
permissionId: {
|
|
15
|
+
type: "string",
|
|
16
|
+
description: "ID of the permission to remove (get from drive_list_permissions)",
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
required: ["fileId", "permissionId"],
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
export async function removePermission(args) {
|
|
23
|
+
try {
|
|
24
|
+
const drive = google.drive("v3");
|
|
25
|
+
const { fileId, permissionId } = args;
|
|
26
|
+
// Get file metadata
|
|
27
|
+
const fileResponse = await drive.files.get({
|
|
28
|
+
fileId,
|
|
29
|
+
fields: "id, name, mimeType",
|
|
30
|
+
});
|
|
31
|
+
const fileName = fileResponse.data.name || "Unknown";
|
|
32
|
+
// Get permission details before removing (for display purposes)
|
|
33
|
+
const permissionResponse = await drive.permissions.get({
|
|
34
|
+
fileId,
|
|
35
|
+
permissionId,
|
|
36
|
+
fields: "id, type, role, emailAddress, displayName, domain",
|
|
37
|
+
});
|
|
38
|
+
const permission = permissionResponse.data;
|
|
39
|
+
const permissionType = permission.type || "unknown";
|
|
40
|
+
const recipientInfo = permission.emailAddress ||
|
|
41
|
+
permission.domain ||
|
|
42
|
+
permission.displayName ||
|
|
43
|
+
"Unknown";
|
|
44
|
+
// Delete permission
|
|
45
|
+
await drive.permissions.delete({
|
|
46
|
+
fileId,
|
|
47
|
+
permissionId,
|
|
48
|
+
});
|
|
49
|
+
let output = `✅ Permission removed successfully!\n\n`;
|
|
50
|
+
output += `📄 File Details:\n`;
|
|
51
|
+
output += ` Name: ${fileName}\n`;
|
|
52
|
+
output += ` ID: ${fileId}\n\n`;
|
|
53
|
+
output += `🔓 Removed Permission:\n`;
|
|
54
|
+
output += ` Permission ID: ${permissionId}\n`;
|
|
55
|
+
output += ` Type: ${getTypeLabel(permissionType)}\n`;
|
|
56
|
+
output += ` Recipient: ${recipientInfo}\n`;
|
|
57
|
+
output += ` Role: ${permission.role || "unknown"}\n`;
|
|
58
|
+
output += `\nAccess has been revoked.`;
|
|
59
|
+
return ResponseFormatter.success({
|
|
60
|
+
fileId,
|
|
61
|
+
fileName,
|
|
62
|
+
permissionId,
|
|
63
|
+
removedPermissionType: permissionType,
|
|
64
|
+
recipient: recipientInfo,
|
|
65
|
+
role: permission.role,
|
|
66
|
+
}, output);
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
return ResponseFormatter.error(error);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { google } from "googleapis";
|
|
2
|
+
import { ResponseFormatter } from "../../../lib/response-formatter.js";
|
|
3
|
+
import { formatPermission } from "../../../lib/drive-helpers.js";
|
|
4
|
+
export const schema = {
|
|
5
|
+
name: "drive_share_file",
|
|
6
|
+
description: "Share a file on Google Drive by adding permissions. Supports sharing with users, groups, domains, or anyone with the link. Can optionally send notification emails.",
|
|
7
|
+
inputSchema: {
|
|
8
|
+
type: "object",
|
|
9
|
+
properties: {
|
|
10
|
+
fileId: {
|
|
11
|
+
type: "string",
|
|
12
|
+
description: "ID of the file to share",
|
|
13
|
+
},
|
|
14
|
+
role: {
|
|
15
|
+
type: "string",
|
|
16
|
+
enum: ["owner", "organizer", "fileOrganizer", "writer", "commenter", "reader"],
|
|
17
|
+
description: "Permission role: owner (full control), writer (editor), commenter (comment only), reader (viewer)",
|
|
18
|
+
},
|
|
19
|
+
type: {
|
|
20
|
+
type: "string",
|
|
21
|
+
enum: ["user", "group", "domain", "anyone"],
|
|
22
|
+
description: "Type of recipient: user (email), group (group email), domain (entire domain), anyone (anyone with link)",
|
|
23
|
+
},
|
|
24
|
+
emailAddress: {
|
|
25
|
+
type: "string",
|
|
26
|
+
description: "Email address (required for type=user or type=group)",
|
|
27
|
+
optional: true,
|
|
28
|
+
},
|
|
29
|
+
domain: {
|
|
30
|
+
type: "string",
|
|
31
|
+
description: "Domain name (required for type=domain, e.g., 'example.com')",
|
|
32
|
+
optional: true,
|
|
33
|
+
},
|
|
34
|
+
sendNotificationEmail: {
|
|
35
|
+
type: "boolean",
|
|
36
|
+
description: "Send notification email to the recipient(s). Default: true",
|
|
37
|
+
optional: true,
|
|
38
|
+
},
|
|
39
|
+
emailMessage: {
|
|
40
|
+
type: "string",
|
|
41
|
+
description: "Custom message to include in notification email",
|
|
42
|
+
optional: true,
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
required: ["fileId", "role", "type"],
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
export async function shareFile(args) {
|
|
49
|
+
try {
|
|
50
|
+
const drive = google.drive("v3");
|
|
51
|
+
const { fileId, role, type, emailAddress, domain, sendNotificationEmail = true, emailMessage, } = args;
|
|
52
|
+
// Validate required fields based on type
|
|
53
|
+
if (type === "user" || type === "group") {
|
|
54
|
+
if (!emailAddress) {
|
|
55
|
+
return ResponseFormatter.error(`emailAddress is required for type='${type}'`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (type === "domain") {
|
|
59
|
+
if (!domain) {
|
|
60
|
+
return ResponseFormatter.error("domain is required for type='domain'");
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// Get file metadata
|
|
64
|
+
const fileResponse = await drive.files.get({
|
|
65
|
+
fileId,
|
|
66
|
+
fields: "id, name, mimeType",
|
|
67
|
+
});
|
|
68
|
+
const fileName = fileResponse.data.name || "Unknown";
|
|
69
|
+
// Create permission object
|
|
70
|
+
const permissionBody = {
|
|
71
|
+
role,
|
|
72
|
+
type,
|
|
73
|
+
};
|
|
74
|
+
if (emailAddress) {
|
|
75
|
+
permissionBody.emailAddress = emailAddress;
|
|
76
|
+
}
|
|
77
|
+
if (domain) {
|
|
78
|
+
permissionBody.domain = domain;
|
|
79
|
+
}
|
|
80
|
+
// Add permission
|
|
81
|
+
const permissionResponse = await drive.permissions.create({
|
|
82
|
+
fileId,
|
|
83
|
+
requestBody: permissionBody,
|
|
84
|
+
fields: "id, role, type, emailAddress, displayName, domain, permissionDetails",
|
|
85
|
+
sendNotificationEmail,
|
|
86
|
+
emailMessage: emailMessage || undefined,
|
|
87
|
+
});
|
|
88
|
+
const permission = permissionResponse.data;
|
|
89
|
+
const permissionId = permission.id || "";
|
|
90
|
+
let output = `✅ File shared successfully!\n\n`;
|
|
91
|
+
output += `📄 File Details:\n`;
|
|
92
|
+
output += ` Name: ${fileName}\n`;
|
|
93
|
+
output += ` ID: ${fileId}\n\n`;
|
|
94
|
+
output += `🔗 Permission Details:\n`;
|
|
95
|
+
output += formatPermission(permission);
|
|
96
|
+
if (sendNotificationEmail) {
|
|
97
|
+
output += `\n📧 Notification email sent`;
|
|
98
|
+
if (emailMessage) {
|
|
99
|
+
output += ` with custom message`;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return ResponseFormatter.success({
|
|
103
|
+
fileId,
|
|
104
|
+
fileName,
|
|
105
|
+
permissionId,
|
|
106
|
+
role,
|
|
107
|
+
type,
|
|
108
|
+
emailAddress: emailAddress || undefined,
|
|
109
|
+
domain: domain || undefined,
|
|
110
|
+
notificationSent: sendNotificationEmail,
|
|
111
|
+
}, output);
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
return ResponseFormatter.error(error);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { google } from "googleapis";
|
|
2
|
+
import { ResponseFormatter } from "../../../lib/response-formatter.js";
|
|
3
|
+
import { formatPermission, getRoleLabel } from "../../../lib/drive-helpers.js";
|
|
4
|
+
export const schema = {
|
|
5
|
+
name: "drive_update_permission",
|
|
6
|
+
description: "Update an existing permission on a Google Drive file. Allows changing the role (access level) for a user, group, domain, or link. Must specify the permission ID returned from list_permissions.",
|
|
7
|
+
inputSchema: {
|
|
8
|
+
type: "object",
|
|
9
|
+
properties: {
|
|
10
|
+
fileId: {
|
|
11
|
+
type: "string",
|
|
12
|
+
description: "ID of the file whose permission to update",
|
|
13
|
+
},
|
|
14
|
+
permissionId: {
|
|
15
|
+
type: "string",
|
|
16
|
+
description: "ID of the permission to update (get from drive_list_permissions)",
|
|
17
|
+
},
|
|
18
|
+
role: {
|
|
19
|
+
type: "string",
|
|
20
|
+
enum: ["owner", "organizer", "fileOrganizer", "writer", "commenter", "reader"],
|
|
21
|
+
description: "New permission role: owner (full control), writer (editor), commenter (comment only), reader (viewer)",
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
required: ["fileId", "permissionId", "role"],
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
export async function updatePermission(args) {
|
|
28
|
+
try {
|
|
29
|
+
const drive = google.drive("v3");
|
|
30
|
+
const { fileId, permissionId, role } = args;
|
|
31
|
+
// Get file metadata
|
|
32
|
+
const fileResponse = await drive.files.get({
|
|
33
|
+
fileId,
|
|
34
|
+
fields: "id, name, mimeType",
|
|
35
|
+
});
|
|
36
|
+
const fileName = fileResponse.data.name || "Unknown";
|
|
37
|
+
// Get current permission to show what changed
|
|
38
|
+
const currentPermissionResponse = await drive.permissions.get({
|
|
39
|
+
fileId,
|
|
40
|
+
permissionId,
|
|
41
|
+
fields: "id, type, role, emailAddress, displayName, domain",
|
|
42
|
+
});
|
|
43
|
+
const currentPermission = currentPermissionResponse.data;
|
|
44
|
+
const oldRole = currentPermission.role || "";
|
|
45
|
+
// Update permission
|
|
46
|
+
const updateResponse = await drive.permissions.update({
|
|
47
|
+
fileId,
|
|
48
|
+
permissionId,
|
|
49
|
+
requestBody: {
|
|
50
|
+
role,
|
|
51
|
+
},
|
|
52
|
+
fields: "id, role, type, emailAddress, displayName, domain, permissionDetails",
|
|
53
|
+
});
|
|
54
|
+
const updatedPermission = updateResponse.data;
|
|
55
|
+
let output = `✅ Permission updated successfully!\n\n`;
|
|
56
|
+
output += `📄 File Details:\n`;
|
|
57
|
+
output += ` Name: ${fileName}\n`;
|
|
58
|
+
output += ` ID: ${fileId}\n\n`;
|
|
59
|
+
output += `🔄 Changes Made:\n`;
|
|
60
|
+
output += ` Permission ID: ${permissionId}\n`;
|
|
61
|
+
output += ` Previous Role: ${getRoleLabel(oldRole)}\n`;
|
|
62
|
+
output += ` New Role: ${getRoleLabel(role)}\n\n`;
|
|
63
|
+
output += `📋 Updated Permission Details:\n`;
|
|
64
|
+
output += formatPermission(updatedPermission);
|
|
65
|
+
return ResponseFormatter.success({
|
|
66
|
+
fileId,
|
|
67
|
+
fileName,
|
|
68
|
+
permissionId,
|
|
69
|
+
previousRole: oldRole,
|
|
70
|
+
newRole: role,
|
|
71
|
+
recipientType: currentPermission.type,
|
|
72
|
+
recipientEmail: currentPermission.emailAddress,
|
|
73
|
+
recipientDomain: currentPermission.domain,
|
|
74
|
+
}, output);
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
return ResponseFormatter.error(error);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -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
|
+
}
|