@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 { 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
+ }