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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (182) hide show
  1. package/README.md +250 -17
  2. package/dist/auth.js +6 -0
  3. package/dist/index.js +1 -1
  4. package/dist/lib/calendar-helpers.js +197 -0
  5. package/dist/lib/document-id-resolver.js +76 -0
  6. package/dist/lib/drive-helpers.js +263 -0
  7. package/dist/lib/gmail-helpers.js +204 -0
  8. package/dist/lib/response-formatter.js +82 -0
  9. package/dist/lib/validation.js +112 -0
  10. package/dist/tools/calendar/acl/calendar_acl_insert.js +80 -0
  11. package/dist/tools/calendar/acl/calendar_acl_list.js +82 -0
  12. package/dist/tools/calendar/basic/calendar_create_event.js +113 -0
  13. package/dist/tools/calendar/basic/calendar_delete_event.js +52 -0
  14. package/dist/tools/calendar/basic/calendar_get_event.js +52 -0
  15. package/dist/tools/calendar/basic/calendar_list_events.js +86 -0
  16. package/dist/tools/calendar/basic/calendar_update_event.js +116 -0
  17. package/dist/tools/calendar/calendarlist/calendar_calendarlist_get.js +73 -0
  18. package/dist/tools/calendar/calendarlist/calendar_calendarlist_list.js +87 -0
  19. package/dist/tools/calendar/calendars/calendar_calendars_get.js +52 -0
  20. package/dist/tools/calendar/calendars/calendar_calendars_insert.js +66 -0
  21. package/dist/tools/calendar/calendars/calendar_calendars_update.js +85 -0
  22. package/dist/tools/calendar/colors/calendar_colors_get.js +46 -0
  23. package/dist/tools/calendar/events_advanced/calendar_events_instances.js +81 -0
  24. package/dist/tools/calendar/events_advanced/calendar_events_move.js +63 -0
  25. package/dist/tools/calendar/events_advanced/calendar_events_quickadd.js +52 -0
  26. package/dist/tools/calendar/freebusy/calendar_freebusy_query.js +69 -0
  27. package/dist/tools/calendar/settings/calendar_settings_list.js +81 -0
  28. package/dist/tools/docs/basic/gdocs_create.js +37 -0
  29. package/dist/tools/docs/basic/gdocs_get_metadata.js +45 -0
  30. package/dist/tools/docs/basic/gdocs_list_documents.js +59 -0
  31. package/dist/tools/docs/basic/gdocs_read.js +62 -0
  32. package/dist/tools/docs/content/gdocs_append_text.js +57 -0
  33. package/dist/tools/docs/content/gdocs_apply_style.js +86 -0
  34. package/dist/tools/docs/content/gdocs_create_heading.js +89 -0
  35. package/dist/tools/docs/content/gdocs_create_list.js +86 -0
  36. package/dist/tools/docs/content/gdocs_delete_text.js +64 -0
  37. package/dist/tools/docs/content/gdocs_format_text.js +137 -0
  38. package/dist/tools/docs/content/gdocs_insert_text.js +62 -0
  39. package/dist/tools/docs/content/gdocs_replace_text.js +64 -0
  40. package/dist/tools/docs/content/gdocs_set_alignment.js +76 -0
  41. package/dist/tools/docs/content/gdocs_update_text.js +78 -0
  42. package/dist/tools/docs/elements/gdocs_batch_update.js +108 -0
  43. package/dist/tools/docs/elements/gdocs_create_table.js +73 -0
  44. package/dist/tools/docs/elements/gdocs_export.js +62 -0
  45. package/dist/tools/docs/elements/gdocs_insert_image.js +96 -0
  46. package/dist/tools/docs/elements/gdocs_insert_link.js +77 -0
  47. package/dist/tools/docs/elements/gdocs_insert_page_break.js +55 -0
  48. package/dist/tools/docs/elements/gdocs_insert_toc.js +71 -0
  49. package/dist/tools/docs/elements/gdocs_merge_documents.js +104 -0
  50. package/dist/tools/docs/elements/gdocs_suggest_mode.js +41 -0
  51. package/dist/tools/drive/advanced/drive_empty_trash.js +56 -0
  52. package/dist/tools/drive/advanced/drive_export_file.js +158 -0
  53. package/dist/tools/drive/advanced/drive_list_revisions.js +80 -0
  54. package/dist/tools/drive/basic/drive_get_metadata.js +49 -0
  55. package/dist/tools/drive/basic/drive_list_files.js +76 -0
  56. package/dist/tools/drive/file/drive_copy_file.js +79 -0
  57. package/dist/tools/drive/file/drive_create_file.js +72 -0
  58. package/dist/tools/drive/file/drive_delete_file.js +48 -0
  59. package/dist/tools/drive/file/drive_move_file.js +79 -0
  60. package/dist/tools/drive/file/drive_rename_file.js +58 -0
  61. package/dist/tools/drive/file/drive_update_file.js +106 -0
  62. package/dist/tools/drive/file/drive_upload_file.js +80 -0
  63. package/dist/tools/drive/folder/drive_create_folder.js +67 -0
  64. package/dist/tools/drive/folder/drive_list_folder_contents.js +68 -0
  65. package/dist/tools/drive/folder/drive_move_to_folder.js +59 -0
  66. package/dist/tools/drive/permissions/drive_list_permissions.js +115 -0
  67. package/dist/tools/drive/permissions/drive_remove_permission.js +71 -0
  68. package/dist/tools/drive/permissions/drive_share_file.js +116 -0
  69. package/dist/tools/drive/permissions/drive_update_permission.js +79 -0
  70. package/dist/tools/gmail/basic/gmail_get_message.js +95 -0
  71. package/dist/tools/gmail/basic/gmail_get_thread.js +46 -0
  72. package/dist/tools/gmail/basic/gmail_list_labels.js +54 -0
  73. package/dist/tools/gmail/basic/gmail_search_messages.js +59 -0
  74. package/dist/tools/gmail/batch/gmail_batch_modify_labels.js +74 -0
  75. package/dist/tools/gmail/batch/gmail_get_messages_batch.js +120 -0
  76. package/dist/tools/gmail/batch/gmail_get_threads_batch.js +102 -0
  77. package/dist/tools/gmail/labels/gmail_manage_label.js +131 -0
  78. package/dist/tools/gmail/labels/gmail_modify_labels.js +65 -0
  79. package/dist/tools/gmail/send/gmail_draft_message.js +117 -0
  80. package/dist/tools/gmail/send/gmail_send_message.js +109 -0
  81. package/dist/tools/index.js +386 -3
  82. package/package.json +8 -3
  83. package/dist/tools/basic/gsheets_add_sheet.js +0 -65
  84. package/dist/tools/basic/gsheets_copy_sheet.js +0 -56
  85. package/dist/tools/basic/gsheets_copy_to.js +0 -113
  86. package/dist/tools/basic/gsheets_create_spreadsheet.js +0 -88
  87. package/dist/tools/basic/gsheets_delete_columns.js +0 -69
  88. package/dist/tools/basic/gsheets_delete_rows.js +0 -69
  89. package/dist/tools/basic/gsheets_delete_sheet.js +0 -56
  90. package/dist/tools/basic/gsheets_duplicate_sheet.js +0 -72
  91. package/dist/tools/basic/gsheets_insert_columns.js +0 -69
  92. package/dist/tools/basic/gsheets_insert_rows.js +0 -69
  93. package/dist/tools/basic/gsheets_list_sheets.js +0 -53
  94. package/dist/tools/basic/gsheets_read.js +0 -120
  95. package/dist/tools/basic/gsheets_rename_sheet.js +0 -64
  96. package/dist/tools/charts/gsheets_add_bubble.js +0 -176
  97. package/dist/tools/charts/gsheets_add_candlestick.js +0 -192
  98. package/dist/tools/charts/gsheets_add_chart.js +0 -162
  99. package/dist/tools/charts/gsheets_add_combo.js +0 -169
  100. package/dist/tools/charts/gsheets_add_histogram.js +0 -143
  101. package/dist/tools/charts/gsheets_add_org_chart.js +0 -160
  102. package/dist/tools/charts/gsheets_add_treemap.js +0 -177
  103. package/dist/tools/charts/gsheets_add_waterfall.js +0 -155
  104. package/dist/tools/charts/gsheets_delete_chart.js +0 -56
  105. package/dist/tools/charts/gsheets_update_chart.js +0 -118
  106. package/dist/tools/data/gsheets_append_data.js +0 -68
  107. package/dist/tools/data/gsheets_batch_clear.js +0 -53
  108. package/dist/tools/data/gsheets_batch_update.js +0 -81
  109. package/dist/tools/data/gsheets_clear_data.js +0 -53
  110. package/dist/tools/data/gsheets_create_filter.js +0 -81
  111. package/dist/tools/data/gsheets_find_replace.js +0 -124
  112. package/dist/tools/data/gsheets_set_data_validation.js +0 -153
  113. package/dist/tools/data/gsheets_sort_range.js +0 -102
  114. package/dist/tools/data/gsheets_update_cell.js +0 -44
  115. package/dist/tools/formatting/gsheets_auto_resize.js +0 -75
  116. package/dist/tools/formatting/gsheets_format_cells.js +0 -161
  117. package/dist/tools/formatting/gsheets_freeze_columns.js +0 -67
  118. package/dist/tools/formatting/gsheets_freeze_rows.js +0 -67
  119. package/dist/tools/formatting/gsheets_merge_cells.js +0 -85
  120. package/dist/tools/formatting/gsheets_set_number_format.js +0 -116
  121. package/dist/tools/formatting/gsheets_unmerge_cells.js +0 -79
  122. package/dist/tools/formatting/gsheets_update_borders.js +0 -212
  123. package/dist/tools/gdrive/gdrive_read_file.js +0 -77
  124. package/dist/tools/gdrive/gdrive_search.js +0 -71
  125. package/dist/tools/gdrive_read_file.js +0 -77
  126. package/dist/tools/gdrive_search.js +0 -71
  127. package/dist/tools/gsheets_add_bubble.js +0 -176
  128. package/dist/tools/gsheets_add_candlestick.js +0 -192
  129. package/dist/tools/gsheets_add_chart.js +0 -162
  130. package/dist/tools/gsheets_add_combo.js +0 -169
  131. package/dist/tools/gsheets_add_conditional_format.js +0 -175
  132. package/dist/tools/gsheets_add_histogram.js +0 -143
  133. package/dist/tools/gsheets_add_named_range.js +0 -87
  134. package/dist/tools/gsheets_add_org_chart.js +0 -160
  135. package/dist/tools/gsheets_add_protected_range.js +0 -127
  136. package/dist/tools/gsheets_add_sheet.js +0 -65
  137. package/dist/tools/gsheets_add_treemap.js +0 -177
  138. package/dist/tools/gsheets_add_waterfall.js +0 -155
  139. package/dist/tools/gsheets_append_data.js +0 -68
  140. package/dist/tools/gsheets_auto_resize.js +0 -75
  141. package/dist/tools/gsheets_batch_clear.js +0 -53
  142. package/dist/tools/gsheets_batch_update.js +0 -81
  143. package/dist/tools/gsheets_clear_data.js +0 -53
  144. package/dist/tools/gsheets_copy_sheet.js +0 -56
  145. package/dist/tools/gsheets_copy_to.js +0 -113
  146. package/dist/tools/gsheets_create_filter.js +0 -81
  147. package/dist/tools/gsheets_create_spreadsheet.js +0 -88
  148. package/dist/tools/gsheets_delete_chart.js +0 -56
  149. package/dist/tools/gsheets_delete_columns.js +0 -69
  150. package/dist/tools/gsheets_delete_named_range.js +0 -56
  151. package/dist/tools/gsheets_delete_protected_range.js +0 -56
  152. package/dist/tools/gsheets_delete_rows.js +0 -69
  153. package/dist/tools/gsheets_delete_sheet.js +0 -56
  154. package/dist/tools/gsheets_duplicate_sheet.js +0 -72
  155. package/dist/tools/gsheets_find_replace.js +0 -124
  156. package/dist/tools/gsheets_format_cells.js +0 -161
  157. package/dist/tools/gsheets_freeze_columns.js +0 -67
  158. package/dist/tools/gsheets_freeze_rows.js +0 -67
  159. package/dist/tools/gsheets_insert_columns.js +0 -69
  160. package/dist/tools/gsheets_insert_rows.js +0 -69
  161. package/dist/tools/gsheets_list_sheets.js +0 -53
  162. package/dist/tools/gsheets_merge_cells.js +0 -85
  163. package/dist/tools/gsheets_read.js +0 -120
  164. package/dist/tools/gsheets_rename_sheet.js +0 -64
  165. package/dist/tools/gsheets_set_data_validation.js +0 -153
  166. package/dist/tools/gsheets_set_number_format.js +0 -116
  167. package/dist/tools/gsheets_sort_range.js +0 -102
  168. package/dist/tools/gsheets_unmerge_cells.js +0 -79
  169. package/dist/tools/gsheets_update_borders.js +0 -212
  170. package/dist/tools/gsheets_update_cell.js +0 -44
  171. package/dist/tools/gsheets_update_chart.js +0 -118
  172. package/dist/tools/gsheets_update_named_range.js +0 -112
  173. package/dist/tools/gsheets_update_protected_range.js +0 -110
  174. package/dist/tools/protection/gsheets_add_conditional_format.js +0 -175
  175. package/dist/tools/protection/gsheets_add_named_range.js +0 -87
  176. package/dist/tools/protection/gsheets_add_protected_range.js +0 -127
  177. package/dist/tools/protection/gsheets_delete_named_range.js +0 -56
  178. package/dist/tools/protection/gsheets_delete_protected_range.js +0 -56
  179. package/dist/tools/protection/gsheets_update_named_range.js +0 -112
  180. package/dist/tools/protection/gsheets_update_protected_range.js +0 -110
  181. /package/dist/tools/drive/{drive_read_file.js → basic/drive_read_file.js} +0 -0
  182. /package/dist/tools/drive/{drive_search.js → basic/drive_search.js} +0 -0
@@ -0,0 +1,80 @@
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_upload_file",
6
+ description: "Upload a file to Google Drive with content. Creates a new file with the specified name, MIME type, and content. Optionally specify parent folder(s) and description.",
7
+ inputSchema: {
8
+ type: "object",
9
+ properties: {
10
+ name: {
11
+ type: "string",
12
+ description: "File name",
13
+ },
14
+ mimeType: {
15
+ type: "string",
16
+ description: "MIME type of the file (e.g., 'text/plain', 'application/json', 'image/png')",
17
+ },
18
+ content: {
19
+ type: "string",
20
+ description: "File content (base64 encoded for binary files, plain text for text files)",
21
+ },
22
+ parents: {
23
+ type: "array",
24
+ items: {
25
+ type: "string",
26
+ },
27
+ description: "Parent folder ID(s) where the file should be created",
28
+ optional: true,
29
+ },
30
+ description: {
31
+ type: "string",
32
+ description: "File description",
33
+ optional: true,
34
+ },
35
+ },
36
+ required: ["name", "mimeType", "content"],
37
+ },
38
+ };
39
+ export async function uploadFile(args) {
40
+ try {
41
+ const drive = google.drive("v3");
42
+ const { name, mimeType, content, parents, description } = args;
43
+ // Create file metadata
44
+ const fileMetadata = {
45
+ name,
46
+ mimeType,
47
+ };
48
+ if (parents && parents.length > 0) {
49
+ fileMetadata.parents = parents;
50
+ }
51
+ if (description) {
52
+ fileMetadata.description = description;
53
+ }
54
+ // Upload file with content
55
+ const response = await drive.files.create({
56
+ requestBody: fileMetadata,
57
+ media: {
58
+ mimeType,
59
+ body: content,
60
+ },
61
+ fields: "id, name, mimeType, size, modifiedTime, webViewLink, parents",
62
+ });
63
+ const file = response.data;
64
+ const fileId = file.id || "";
65
+ let output = `āœ… File uploaded successfully!\n\n`;
66
+ output += formatFileMetadata(file, true);
67
+ output += `\nšŸ“Ž Web URL: ${generateDriveWebUrl(fileId)}\n`;
68
+ return ResponseFormatter.success({
69
+ fileId,
70
+ name: file.name,
71
+ mimeType: file.mimeType,
72
+ size: file.size,
73
+ webViewLink: file.webViewLink,
74
+ parents: file.parents,
75
+ }, output);
76
+ }
77
+ catch (error) {
78
+ return ResponseFormatter.error(error);
79
+ }
80
+ }
@@ -0,0 +1,67 @@
1
+ import { google } from "googleapis";
2
+ import { ResponseFormatter } from "../../../lib/response-formatter.js";
3
+ import { formatFileMetadata, generateFolderWebUrl, FOLDER_MIME_TYPE, } from "../../../lib/drive-helpers.js";
4
+ export const schema = {
5
+ name: "drive_create_folder",
6
+ description: "Create a new folder in Google Drive. Can optionally place the folder inside a parent folder. Returns folder ID and metadata.",
7
+ inputSchema: {
8
+ type: "object",
9
+ properties: {
10
+ name: {
11
+ type: "string",
12
+ description: "Folder name",
13
+ },
14
+ parents: {
15
+ type: "array",
16
+ items: {
17
+ type: "string",
18
+ },
19
+ description: "Parent folder ID(s) where the folder should be created (optional)",
20
+ optional: true,
21
+ },
22
+ description: {
23
+ type: "string",
24
+ description: "Folder description",
25
+ optional: true,
26
+ },
27
+ },
28
+ required: ["name"],
29
+ },
30
+ };
31
+ export async function createFolder(args) {
32
+ try {
33
+ const drive = google.drive("v3");
34
+ const { name, parents, description } = args;
35
+ // Create folder metadata
36
+ const folderMetadata = {
37
+ name,
38
+ mimeType: FOLDER_MIME_TYPE,
39
+ };
40
+ if (parents && parents.length > 0) {
41
+ folderMetadata.parents = parents;
42
+ }
43
+ if (description) {
44
+ folderMetadata.description = description;
45
+ }
46
+ // Create folder
47
+ const response = await drive.files.create({
48
+ requestBody: folderMetadata,
49
+ fields: "id, name, mimeType, modifiedTime, webViewLink, parents",
50
+ });
51
+ const folder = response.data;
52
+ const folderId = folder.id || "";
53
+ let output = `āœ… Folder created successfully!\n\n`;
54
+ output += formatFileMetadata(folder, true);
55
+ output += `\nšŸ“‚ Web URL: ${generateFolderWebUrl(folderId)}\n`;
56
+ return ResponseFormatter.success({
57
+ folderId,
58
+ name: folder.name,
59
+ mimeType: folder.mimeType,
60
+ webViewLink: folder.webViewLink,
61
+ parents: folder.parents,
62
+ }, output);
63
+ }
64
+ catch (error) {
65
+ return ResponseFormatter.error(error);
66
+ }
67
+ }
@@ -0,0 +1,68 @@
1
+ import { google } from "googleapis";
2
+ import { ResponseFormatter } from "../../../lib/response-formatter.js";
3
+ import { formatFileList } from "../../../lib/drive-helpers.js";
4
+ export const schema = {
5
+ name: "drive_list_folder_contents",
6
+ description: "List all files and folders inside a specific folder in Google Drive. Supports pagination and sorting. Returns file IDs, names, types, and sizes.",
7
+ inputSchema: {
8
+ type: "object",
9
+ properties: {
10
+ folderId: {
11
+ type: "string",
12
+ description: "ID of the folder to list contents from",
13
+ },
14
+ pageSize: {
15
+ type: "number",
16
+ description: "Number of items to return per page (max 1000, default 100)",
17
+ optional: true,
18
+ },
19
+ pageToken: {
20
+ type: "string",
21
+ description: "Page token for pagination",
22
+ optional: true,
23
+ },
24
+ orderBy: {
25
+ type: "string",
26
+ description: "Sort order (e.g., 'name', 'modifiedTime desc', 'createdTime')",
27
+ optional: true,
28
+ },
29
+ },
30
+ required: ["folderId"],
31
+ },
32
+ };
33
+ export async function listFolderContents(args) {
34
+ try {
35
+ const drive = google.drive("v3");
36
+ const { folderId, pageSize = 100, pageToken, orderBy } = args;
37
+ // Build query for folder contents
38
+ const q = `'${folderId}' in parents and trashed = false`;
39
+ const response = await drive.files.list({
40
+ q,
41
+ pageSize: Math.min(pageSize, 1000),
42
+ pageToken,
43
+ orderBy: orderBy || "modifiedTime desc",
44
+ fields: "nextPageToken, files(id, name, mimeType, size, modifiedTime, createdTime, webViewLink, parents, iconLink)",
45
+ });
46
+ const files = response.data.files || [];
47
+ const nextPageToken = response.data.nextPageToken;
48
+ let output = `šŸ“ Folder Contents\n`;
49
+ output += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`;
50
+ output += `Folder ID: ${folderId}\n\n`;
51
+ output += formatFileList(files, nextPageToken);
52
+ return ResponseFormatter.success({
53
+ folderId,
54
+ count: files.length,
55
+ files: files.map((f) => ({
56
+ id: f.id,
57
+ name: f.name,
58
+ mimeType: f.mimeType,
59
+ size: f.size,
60
+ modifiedTime: f.modifiedTime,
61
+ })),
62
+ nextPageToken,
63
+ }, output);
64
+ }
65
+ catch (error) {
66
+ return ResponseFormatter.error(error);
67
+ }
68
+ }
@@ -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
+ }