@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,96 @@
1
+ import { google } from "googleapis";
2
+ import { ResponseFormatter } from "../../../lib/response-formatter.js";
3
+ import { DocumentIdResolver } from "../../../lib/document-id-resolver.js";
4
+ import { Validator } from "../../../lib/validation.js";
5
+ export const schema = {
6
+ name: "gdocs_insert_image",
7
+ description: "Insert an image from a URL into a Google Document at a specific position.",
8
+ inputSchema: {
9
+ type: "object",
10
+ properties: {
11
+ documentId: {
12
+ type: "string",
13
+ description: "Document ID or full Google Docs URL",
14
+ },
15
+ imageUrl: {
16
+ type: "string",
17
+ description: "URL of the image to insert (must be publicly accessible)",
18
+ },
19
+ index: {
20
+ type: "number",
21
+ description: "Position to insert image (1-based)",
22
+ },
23
+ width: {
24
+ type: "number",
25
+ description: "Image width in points (optional)",
26
+ },
27
+ height: {
28
+ type: "number",
29
+ description: "Image height in points (optional)",
30
+ },
31
+ },
32
+ required: ["documentId", "imageUrl", "index"],
33
+ },
34
+ };
35
+ export async function insertImage(args) {
36
+ try {
37
+ // Validate inputs
38
+ if (!Validator.validateIndex(args.index)) {
39
+ throw new Error("Index must be a positive integer");
40
+ }
41
+ if (!Validator.validateUrl(args.imageUrl)) {
42
+ throw new Error("Invalid image URL");
43
+ }
44
+ if (args.width && !Validator.validatePositiveNumber(args.width)) {
45
+ throw new Error("Width must be a positive number");
46
+ }
47
+ if (args.height && !Validator.validatePositiveNumber(args.height)) {
48
+ throw new Error("Height must be a positive number");
49
+ }
50
+ // Resolve document ID from URL if needed
51
+ const docRef = DocumentIdResolver.resolve(args.documentId);
52
+ const documentId = docRef.id;
53
+ const docs = google.docs("v1");
54
+ // Build image properties
55
+ const imageProperties = {
56
+ contentUri: args.imageUrl,
57
+ };
58
+ if (args.width || args.height) {
59
+ imageProperties.sourceUri = args.imageUrl;
60
+ }
61
+ // Build object size if dimensions specified
62
+ let objectSize;
63
+ if (args.width || args.height) {
64
+ objectSize = {
65
+ width: args.width ? { magnitude: args.width, unit: "PT" } : undefined,
66
+ height: args.height ? { magnitude: args.height, unit: "PT" } : undefined,
67
+ };
68
+ }
69
+ const response = await docs.documents.batchUpdate({
70
+ documentId,
71
+ requestBody: {
72
+ requests: [
73
+ {
74
+ insertInlineImage: {
75
+ uri: args.imageUrl,
76
+ location: {
77
+ index: args.index,
78
+ },
79
+ objectSize,
80
+ },
81
+ },
82
+ ],
83
+ },
84
+ });
85
+ return ResponseFormatter.success({
86
+ documentId,
87
+ imageUrl: args.imageUrl,
88
+ insertedAt: args.index,
89
+ width: args.width,
90
+ height: args.height,
91
+ }, "Image inserted successfully");
92
+ }
93
+ catch (error) {
94
+ return ResponseFormatter.error(error);
95
+ }
96
+ }
@@ -0,0 +1,77 @@
1
+ import { google } from "googleapis";
2
+ import { ResponseFormatter } from "../../../lib/response-formatter.js";
3
+ import { DocumentIdResolver } from "../../../lib/document-id-resolver.js";
4
+ import { Validator } from "../../../lib/validation.js";
5
+ export const schema = {
6
+ name: "gdocs_insert_link",
7
+ description: "Insert a hyperlink on a text range in a Google Document.",
8
+ inputSchema: {
9
+ type: "object",
10
+ properties: {
11
+ documentId: {
12
+ type: "string",
13
+ description: "Document ID or full Google Docs URL",
14
+ },
15
+ startIndex: {
16
+ type: "number",
17
+ description: "Start position of text to link (1-based, inclusive)",
18
+ },
19
+ endIndex: {
20
+ type: "number",
21
+ description: "End position of text to link (1-based, exclusive)",
22
+ },
23
+ url: {
24
+ type: "string",
25
+ description: "URL to link to (must start with http:// or https://)",
26
+ },
27
+ },
28
+ required: ["documentId", "startIndex", "endIndex", "url"],
29
+ },
30
+ };
31
+ export async function insertLink(args) {
32
+ try {
33
+ // Validate inputs
34
+ if (!Validator.validateRange(args.startIndex, args.endIndex)) {
35
+ throw new Error("Invalid range: startIndex must be less than endIndex and both must be positive");
36
+ }
37
+ if (!Validator.validateUrl(args.url)) {
38
+ throw new Error("Invalid URL: must start with http:// or https://");
39
+ }
40
+ // Resolve document ID from URL if needed
41
+ const docRef = DocumentIdResolver.resolve(args.documentId);
42
+ const documentId = docRef.id;
43
+ const docs = google.docs("v1");
44
+ const response = await docs.documents.batchUpdate({
45
+ documentId,
46
+ requestBody: {
47
+ requests: [
48
+ {
49
+ updateTextStyle: {
50
+ textStyle: {
51
+ link: {
52
+ url: args.url,
53
+ },
54
+ },
55
+ fields: "link",
56
+ range: {
57
+ startIndex: args.startIndex,
58
+ endIndex: args.endIndex,
59
+ },
60
+ },
61
+ },
62
+ ],
63
+ },
64
+ });
65
+ return ResponseFormatter.success({
66
+ documentId,
67
+ url: args.url,
68
+ range: {
69
+ startIndex: args.startIndex,
70
+ endIndex: args.endIndex,
71
+ },
72
+ }, "Hyperlink inserted successfully");
73
+ }
74
+ catch (error) {
75
+ return ResponseFormatter.error(error);
76
+ }
77
+ }
@@ -0,0 +1,55 @@
1
+ import { google } from "googleapis";
2
+ import { ResponseFormatter } from "../../../lib/response-formatter.js";
3
+ import { DocumentIdResolver } from "../../../lib/document-id-resolver.js";
4
+ import { Validator } from "../../../lib/validation.js";
5
+ export const schema = {
6
+ name: "gdocs_insert_page_break",
7
+ description: "Insert a page break at a specific position in a Google Document.",
8
+ inputSchema: {
9
+ type: "object",
10
+ properties: {
11
+ documentId: {
12
+ type: "string",
13
+ description: "Document ID or full Google Docs URL",
14
+ },
15
+ index: {
16
+ type: "number",
17
+ description: "Position to insert page break (1-based)",
18
+ },
19
+ },
20
+ required: ["documentId", "index"],
21
+ },
22
+ };
23
+ export async function insertPageBreak(args) {
24
+ try {
25
+ // Validate inputs
26
+ if (!Validator.validateIndex(args.index)) {
27
+ throw new Error("Index must be a positive integer");
28
+ }
29
+ // Resolve document ID from URL if needed
30
+ const docRef = DocumentIdResolver.resolve(args.documentId);
31
+ const documentId = docRef.id;
32
+ const docs = google.docs("v1");
33
+ const response = await docs.documents.batchUpdate({
34
+ documentId,
35
+ requestBody: {
36
+ requests: [
37
+ {
38
+ insertPageBreak: {
39
+ location: {
40
+ index: args.index,
41
+ },
42
+ },
43
+ },
44
+ ],
45
+ },
46
+ });
47
+ return ResponseFormatter.success({
48
+ documentId,
49
+ insertedAt: args.index,
50
+ }, "Page break inserted successfully");
51
+ }
52
+ catch (error) {
53
+ return ResponseFormatter.error(error);
54
+ }
55
+ }
@@ -0,0 +1,71 @@
1
+ import { google } from "googleapis";
2
+ import { ResponseFormatter } from "../../../lib/response-formatter.js";
3
+ import { DocumentIdResolver } from "../../../lib/document-id-resolver.js";
4
+ import { Validator } from "../../../lib/validation.js";
5
+ export const schema = {
6
+ name: "gdocs_insert_toc",
7
+ description: "Insert a 'Table of Contents' heading at a specific position. NOTE: The Google Docs API doesn't support auto-generated TOC insertion. To insert an actual auto-updating TOC, use Insert > Table of contents in the Google Docs UI.",
8
+ inputSchema: {
9
+ type: "object",
10
+ properties: {
11
+ documentId: {
12
+ type: "string",
13
+ description: "Document ID or full Google Docs URL",
14
+ },
15
+ index: {
16
+ type: "number",
17
+ description: "Position to insert TOC heading (1-based)",
18
+ },
19
+ },
20
+ required: ["documentId", "index"],
21
+ },
22
+ };
23
+ export async function insertToc(args) {
24
+ try {
25
+ // Validate inputs
26
+ if (!Validator.validateIndex(args.index)) {
27
+ throw new Error("Index must be a positive integer");
28
+ }
29
+ // Resolve document ID from URL if needed
30
+ const docRef = DocumentIdResolver.resolve(args.documentId);
31
+ const documentId = docRef.id;
32
+ const docs = google.docs("v1");
33
+ // NOTE: Google Docs API doesn't support programmatic TOC insertion
34
+ // This is a workaround that inserts a styled "Table of Contents" heading
35
+ const response = await docs.documents.batchUpdate({
36
+ documentId,
37
+ requestBody: {
38
+ requests: [
39
+ {
40
+ insertText: {
41
+ location: {
42
+ index: args.index,
43
+ },
44
+ text: "Table of Contents\n",
45
+ },
46
+ },
47
+ {
48
+ updateParagraphStyle: {
49
+ range: {
50
+ startIndex: args.index,
51
+ endIndex: args.index + 19, // "Table of Contents\n".length
52
+ },
53
+ paragraphStyle: {
54
+ namedStyleType: "HEADING_1",
55
+ },
56
+ fields: "namedStyleType",
57
+ },
58
+ },
59
+ ],
60
+ },
61
+ });
62
+ return ResponseFormatter.success({
63
+ documentId,
64
+ insertedAt: args.index,
65
+ note: "Inserted 'Table of Contents' heading. To insert an auto-updating TOC, use Insert > Table of contents in the Google Docs UI. The API doesn't support programmatic TOC generation.",
66
+ }, "Table of Contents heading inserted successfully");
67
+ }
68
+ catch (error) {
69
+ return ResponseFormatter.error(error);
70
+ }
71
+ }
@@ -0,0 +1,104 @@
1
+ import { google } from "googleapis";
2
+ import { ResponseFormatter } from "../../../lib/response-formatter.js";
3
+ import { DocumentIdResolver } from "../../../lib/document-id-resolver.js";
4
+ export const schema = {
5
+ name: "gdocs_merge_documents",
6
+ description: "Merge multiple Google Documents into one. Creates a new document or appends to an existing target document.",
7
+ inputSchema: {
8
+ type: "object",
9
+ properties: {
10
+ sourceDocumentIds: {
11
+ type: "array",
12
+ description: "Array of source document IDs or URLs to merge (in order)",
13
+ items: {
14
+ type: "string",
15
+ },
16
+ },
17
+ targetDocumentId: {
18
+ type: "string",
19
+ description: "Target document ID or URL to merge into. If not provided, creates a new document.",
20
+ },
21
+ title: {
22
+ type: "string",
23
+ description: "Title for the new merged document (only used if targetDocumentId is not provided)",
24
+ },
25
+ },
26
+ required: ["sourceDocumentIds"],
27
+ },
28
+ };
29
+ export async function mergeDocuments(args) {
30
+ try {
31
+ if (args.sourceDocumentIds.length === 0) {
32
+ throw new Error("At least one source document is required");
33
+ }
34
+ const docs = google.docs("v1");
35
+ // Resolve source document IDs
36
+ const sourceIds = args.sourceDocumentIds.map((id) => {
37
+ const ref = DocumentIdResolver.resolve(id);
38
+ return ref.id;
39
+ });
40
+ // Determine target document
41
+ let targetDocumentId;
42
+ if (args.targetDocumentId) {
43
+ const targetRef = DocumentIdResolver.resolve(args.targetDocumentId);
44
+ targetDocumentId = targetRef.id;
45
+ }
46
+ else {
47
+ // Create a new document
48
+ const createResponse = await docs.documents.create({
49
+ requestBody: {
50
+ title: args.title || "Merged Document",
51
+ },
52
+ });
53
+ targetDocumentId = createResponse.data.documentId;
54
+ }
55
+ // Read each source document and append to target
56
+ for (const sourceId of sourceIds) {
57
+ const sourceDoc = await docs.documents.get({ documentId: sourceId });
58
+ // Extract text content from source
59
+ let textContent = "";
60
+ if (sourceDoc.data.body?.content) {
61
+ for (const element of sourceDoc.data.body.content) {
62
+ if (element.paragraph?.elements) {
63
+ for (const textElement of element.paragraph.elements) {
64
+ if (textElement.textRun?.content) {
65
+ textContent += textElement.textRun.content;
66
+ }
67
+ }
68
+ }
69
+ }
70
+ }
71
+ // Get target document end index
72
+ const targetDoc = await docs.documents.get({ documentId: targetDocumentId });
73
+ const endIndex = targetDoc.data.body?.content?.[targetDoc.data.body.content.length - 1]?.endIndex || 1;
74
+ // Append content to target
75
+ if (textContent) {
76
+ await docs.documents.batchUpdate({
77
+ documentId: targetDocumentId,
78
+ requestBody: {
79
+ requests: [
80
+ {
81
+ insertText: {
82
+ text: "\n" + textContent,
83
+ location: {
84
+ index: endIndex - 1,
85
+ },
86
+ },
87
+ },
88
+ ],
89
+ },
90
+ });
91
+ }
92
+ }
93
+ const targetUrl = `https://docs.google.com/document/d/${targetDocumentId}/edit`;
94
+ return ResponseFormatter.success({
95
+ targetDocumentId,
96
+ targetDocumentUrl: targetUrl,
97
+ mergedDocuments: sourceIds.length,
98
+ sourceDocumentIds: sourceIds,
99
+ }, `Successfully merged ${sourceIds.length} document(s)`);
100
+ }
101
+ catch (error) {
102
+ return ResponseFormatter.error(error);
103
+ }
104
+ }
@@ -0,0 +1,41 @@
1
+ import { google } from "googleapis";
2
+ import { ResponseFormatter } from "../../../lib/response-formatter.js";
3
+ import { DocumentIdResolver } from "../../../lib/document-id-resolver.js";
4
+ export const schema = {
5
+ name: "gdocs_suggest_mode",
6
+ description: "NOTE: This tool cannot programmatically control suggestion mode. The Google Docs API doesn't support enabling/disabling suggestion mode. To use suggestion mode, open the document in Google Docs UI and use the 'Editing/Suggesting/Viewing' dropdown in the top-right corner.",
7
+ inputSchema: {
8
+ type: "object",
9
+ properties: {
10
+ documentId: {
11
+ type: "string",
12
+ description: "Document ID or full Google Docs URL",
13
+ },
14
+ enabled: {
15
+ type: "boolean",
16
+ description: "true to enable suggestion mode, false to disable",
17
+ },
18
+ },
19
+ required: ["documentId", "enabled"],
20
+ },
21
+ };
22
+ export async function suggestMode(args) {
23
+ try {
24
+ // Resolve document ID from URL if needed
25
+ const docRef = DocumentIdResolver.resolve(args.documentId);
26
+ const documentId = docRef.id;
27
+ const docs = google.docs("v1");
28
+ // Verify document exists
29
+ await docs.documents.get({ documentId });
30
+ // Return informative error about API limitation
31
+ return ResponseFormatter.error(new Error(`Suggestion mode cannot be controlled programmatically via the Google Docs API. ` +
32
+ `To ${args.enabled ? "enable" : "disable"} suggestion mode, please:\n\n` +
33
+ `1. Open the document at: https://docs.google.com/document/d/${documentId}/edit\n` +
34
+ `2. Click the 'Editing' dropdown in the top-right corner\n` +
35
+ `3. Select '${args.enabled ? "Suggesting" : "Editing"}'\n\n` +
36
+ `This is a limitation of the Google Docs API, not this tool.`));
37
+ }
38
+ catch (error) {
39
+ return ResponseFormatter.error(error);
40
+ }
41
+ }
@@ -0,0 +1,56 @@
1
+ import { google } from "googleapis";
2
+ import { ResponseFormatter } from "../../../lib/response-formatter.js";
3
+ export const schema = {
4
+ name: "drive_empty_trash",
5
+ description: "Permanently delete all files in the trash for the authenticated user. This operation cannot be undone.",
6
+ inputSchema: {
7
+ type: "object",
8
+ properties: {},
9
+ required: [],
10
+ },
11
+ };
12
+ export async function emptyTrash(args) {
13
+ try {
14
+ const drive = google.drive("v3");
15
+ // First, count items in trash for confirmation message
16
+ let trashedItemsCount = 0;
17
+ let nextPageToken;
18
+ try {
19
+ // Get count of items in trash
20
+ const countResponse = await drive.files.list({
21
+ q: "trashed=true",
22
+ pageSize: 1,
23
+ fields: "nextPageToken",
24
+ });
25
+ // Try to get actual count by iterating through all pages
26
+ let hasMore = true;
27
+ nextPageToken = countResponse.data.nextPageToken || undefined;
28
+ // Estimate count from first response
29
+ if (countResponse.data.files && countResponse.data.files.length > 0) {
30
+ trashedItemsCount = countResponse.data.files.length;
31
+ }
32
+ }
33
+ catch {
34
+ // If count fails, proceed with emptying trash anyway
35
+ trashedItemsCount = 0;
36
+ }
37
+ // Empty the trash
38
+ await drive.files.emptyTrash({});
39
+ let output = `🗑️ Trash Emptied Successfully!\n`;
40
+ output += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`;
41
+ output += `✅ All files in trash have been permanently deleted.\n`;
42
+ output += `⚠️ This action cannot be undone.\n`;
43
+ if (trashedItemsCount > 0) {
44
+ output += `📊 Estimated items deleted: ${trashedItemsCount}+\n`;
45
+ }
46
+ return ResponseFormatter.success({
47
+ status: "success",
48
+ message: "Trash emptied successfully",
49
+ timestamp: new Date().toISOString(),
50
+ warning: "This action cannot be undone. Deleted files are permanently removed.",
51
+ }, output);
52
+ }
53
+ catch (error) {
54
+ return ResponseFormatter.error(error);
55
+ }
56
+ }
@@ -0,0 +1,158 @@
1
+ import { google } from "googleapis";
2
+ import { ResponseFormatter } from "../../../lib/response-formatter.js";
3
+ import { convertMimeType, isGoogleWorkspaceDocument, } from "../../../lib/drive-helpers.js";
4
+ export const schema = {
5
+ name: "drive_export_file",
6
+ description: "Export a Google Workspace document (Docs, Sheets, Slides, Drawing) to a different format. Only works with Google Workspace files.",
7
+ inputSchema: {
8
+ type: "object",
9
+ properties: {
10
+ fileId: {
11
+ type: "string",
12
+ description: "ID of the file to export",
13
+ },
14
+ mimeType: {
15
+ type: "string",
16
+ description: "Target MIME type for export. Optional - uses default if not specified. Examples: application/pdf, text/csv, text/plain, etc.",
17
+ optional: true,
18
+ },
19
+ format: {
20
+ type: "string",
21
+ description: "Export format name (shorthand). For Docs: pdf, docx, html, txt, odt, rtf, epub, markdown. For Sheets: csv, xlsx, ods, tsv, html. For Slides: pdf, pptx, odp, txt. For Drawing: png, jpeg, svg, pdf.",
22
+ optional: true,
23
+ },
24
+ },
25
+ required: ["fileId"],
26
+ },
27
+ };
28
+ export async function exportFile(args) {
29
+ try {
30
+ const drive = google.drive("v3");
31
+ const { fileId, mimeType: customMimeType, format } = args;
32
+ // Get file metadata to check type
33
+ const fileResponse = await drive.files.get({
34
+ fileId,
35
+ fields: "id, name, mimeType, size",
36
+ });
37
+ const file = fileResponse.data;
38
+ const sourceMimeType = file.mimeType || "";
39
+ // Check if it's a Google Workspace document
40
+ if (!isGoogleWorkspaceDocument(sourceMimeType)) {
41
+ return ResponseFormatter.error(`File is not a Google Workspace document. MIME type: ${sourceMimeType}. Only Google Docs, Sheets, Slides, and Drawings can be exported.`);
42
+ }
43
+ // Determine target MIME type
44
+ let targetMimeType;
45
+ if (customMimeType) {
46
+ targetMimeType = customMimeType;
47
+ }
48
+ else if (format) {
49
+ try {
50
+ targetMimeType = convertMimeType(sourceMimeType, format);
51
+ }
52
+ catch (error) {
53
+ return ResponseFormatter.error(error.message);
54
+ }
55
+ }
56
+ else {
57
+ // Use default format
58
+ try {
59
+ targetMimeType = convertMimeType(sourceMimeType);
60
+ }
61
+ catch (error) {
62
+ return ResponseFormatter.error(error.message);
63
+ }
64
+ }
65
+ // Get export MIME type label
66
+ const formatLabel = getFormatLabel(targetMimeType);
67
+ // Export the file
68
+ const exportResponse = await drive.files.export({
69
+ fileId,
70
+ mimeType: targetMimeType,
71
+ }, { responseType: "arraybuffer" });
72
+ const fileBuffer = Buffer.from(exportResponse.data);
73
+ const base64Content = fileBuffer.toString("base64");
74
+ const fileName = `${file.name || "export"}.${getFileExtension(targetMimeType)}`;
75
+ let output = `✅ File exported successfully!\n\n`;
76
+ output += `📄 File Name: ${file.name}\n`;
77
+ output += `📊 Source Format: ${getDocumentTypeLabel(sourceMimeType)}\n`;
78
+ output += `📥 Exported Format: ${formatLabel}\n`;
79
+ output += `💾 File Size: ${(fileBuffer.length / 1024).toFixed(2)} KB\n`;
80
+ output += `📁 Export File Name: ${fileName}\n`;
81
+ return ResponseFormatter.success({
82
+ fileId,
83
+ fileName: file.name,
84
+ exportFileName: fileName,
85
+ sourceFormat: sourceMimeType,
86
+ targetFormat: targetMimeType,
87
+ fileSizeBytes: fileBuffer.length,
88
+ fileSizeKB: (fileBuffer.length / 1024).toFixed(2),
89
+ content: base64Content,
90
+ contentEncoding: "base64",
91
+ }, output);
92
+ }
93
+ catch (error) {
94
+ return ResponseFormatter.error(error);
95
+ }
96
+ }
97
+ /**
98
+ * Get file extension based on MIME type
99
+ */
100
+ function getFileExtension(mimeType) {
101
+ const extensions = {
102
+ "application/pdf": "pdf",
103
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document": "docx",
104
+ "text/html": "html",
105
+ "text/plain": "txt",
106
+ "application/vnd.oasis.opendocument.text": "odt",
107
+ "application/rtf": "rtf",
108
+ "application/epub+zip": "epub",
109
+ "text/markdown": "md",
110
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "xlsx",
111
+ "text/csv": "csv",
112
+ "application/vnd.oasis.opendocument.spreadsheet": "ods",
113
+ "text/tab-separated-values": "tsv",
114
+ "application/vnd.openxmlformats-officedocument.presentationml.presentation": "pptx",
115
+ "application/vnd.oasis.opendocument.presentation": "odp",
116
+ "image/png": "png",
117
+ "image/jpeg": "jpg",
118
+ "image/svg+xml": "svg",
119
+ };
120
+ return extensions[mimeType] || "bin";
121
+ }
122
+ /**
123
+ * Get format label for display
124
+ */
125
+ function getFormatLabel(mimeType) {
126
+ const labels = {
127
+ "application/pdf": "PDF Document",
128
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document": "Microsoft Word",
129
+ "text/html": "HTML",
130
+ "text/plain": "Plain Text",
131
+ "application/vnd.oasis.opendocument.text": "OpenDocument Text",
132
+ "application/rtf": "Rich Text Format",
133
+ "application/epub+zip": "EPUB eBook",
134
+ "text/markdown": "Markdown",
135
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "Microsoft Excel",
136
+ "text/csv": "CSV (Comma Separated Values)",
137
+ "application/vnd.oasis.opendocument.spreadsheet": "OpenDocument Spreadsheet",
138
+ "text/tab-separated-values": "TSV (Tab Separated Values)",
139
+ "application/vnd.openxmlformats-officedocument.presentationml.presentation": "Microsoft PowerPoint",
140
+ "application/vnd.oasis.opendocument.presentation": "OpenDocument Presentation",
141
+ "image/png": "PNG Image",
142
+ "image/jpeg": "JPEG Image",
143
+ "image/svg+xml": "SVG Image",
144
+ };
145
+ return labels[mimeType] || mimeType;
146
+ }
147
+ /**
148
+ * Get document type label
149
+ */
150
+ function getDocumentTypeLabel(mimeType) {
151
+ const labels = {
152
+ "application/vnd.google-apps.document": "Google Docs",
153
+ "application/vnd.google-apps.spreadsheet": "Google Sheets",
154
+ "application/vnd.google-apps.presentation": "Google Slides",
155
+ "application/vnd.google-apps.drawing": "Google Drawing",
156
+ };
157
+ return labels[mimeType] || "Google Workspace Document";
158
+ }