@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,81 @@
1
+ import { google } from "googleapis";
2
+ import { ResponseFormatter } from "../../../lib/response-formatter.js";
3
+ export const schema = {
4
+ name: "calendar_settings_list",
5
+ description: "List all user settings for the authenticated user's Google Calendar. Returns configuration settings such as timezone, date format, country, language, and other Calendar preferences. Useful for understanding user's Calendar configuration.",
6
+ inputSchema: {
7
+ type: "object",
8
+ properties: {
9
+ maxResults: {
10
+ type: "number",
11
+ description: "Maximum number of settings to return per page (default: 100, max: 250)",
12
+ },
13
+ pageToken: {
14
+ type: "string",
15
+ description: "Token for accessing subsequent result pages",
16
+ },
17
+ },
18
+ required: [],
19
+ },
20
+ };
21
+ export async function listSettings(args = {}) {
22
+ try {
23
+ const calendar = google.calendar("v3");
24
+ const { maxResults, pageToken, } = args;
25
+ const response = await calendar.settings.list({
26
+ maxResults,
27
+ pageToken,
28
+ });
29
+ const settings = response.data.items || [];
30
+ const totalSettings = settings.length;
31
+ if (totalSettings === 0) {
32
+ return ResponseFormatter.success({ settings: [], count: 0 }, "No settings found.");
33
+ }
34
+ let message = `⚙️ Calendar Settings (${totalSettings} setting${totalSettings > 1 ? "s" : ""})\n\n`;
35
+ // Group settings by category for better readability
36
+ const categorizedSettings = {
37
+ "General": [],
38
+ "Time & Date": [],
39
+ "Locale": [],
40
+ "Other": [],
41
+ };
42
+ settings.forEach((setting) => {
43
+ const id = setting.id || "";
44
+ if (id.includes("timezone") || id.includes("time") || id.includes("date") || id.includes("hour")) {
45
+ categorizedSettings["Time & Date"].push(setting);
46
+ }
47
+ else if (id.includes("locale") || id.includes("language") || id.includes("country")) {
48
+ categorizedSettings["Locale"].push(setting);
49
+ }
50
+ else if (id.includes("format") || id.includes("weekStart") || id.includes("show")) {
51
+ categorizedSettings["General"].push(setting);
52
+ }
53
+ else {
54
+ categorizedSettings["Other"].push(setting);
55
+ }
56
+ });
57
+ // Display settings by category
58
+ Object.entries(categorizedSettings).forEach(([category, items]) => {
59
+ if (items.length > 0) {
60
+ message += `\n${category}:\n`;
61
+ items.forEach((setting) => {
62
+ message += ` ${setting.id}: ${setting.value}\n`;
63
+ });
64
+ }
65
+ });
66
+ if (response.data.nextPageToken) {
67
+ message += `\n\n📄 Next Page Token: ${response.data.nextPageToken}`;
68
+ }
69
+ return ResponseFormatter.success({
70
+ settings: settings.map((s) => ({
71
+ id: s.id,
72
+ value: s.value,
73
+ })),
74
+ count: totalSettings,
75
+ nextPageToken: response.data.nextPageToken || null,
76
+ }, message);
77
+ }
78
+ catch (error) {
79
+ return ResponseFormatter.error(error);
80
+ }
81
+ }
@@ -0,0 +1,37 @@
1
+ import { google } from "googleapis";
2
+ import { ResponseFormatter } from "../../../lib/response-formatter.js";
3
+ export const schema = {
4
+ name: "gdocs_create",
5
+ description: "Create a new Google Document",
6
+ inputSchema: {
7
+ type: "object",
8
+ properties: {
9
+ title: {
10
+ type: "string",
11
+ description: "The title of the new document",
12
+ },
13
+ },
14
+ required: ["title"],
15
+ },
16
+ };
17
+ export async function createDocument(args) {
18
+ try {
19
+ const docs = google.docs("v1");
20
+ const response = await docs.documents.create({
21
+ requestBody: {
22
+ title: args.title,
23
+ },
24
+ });
25
+ const documentId = response.data.documentId;
26
+ const documentUrl = `https://docs.google.com/document/d/${documentId}/edit`;
27
+ return ResponseFormatter.success({
28
+ documentId,
29
+ documentUrl,
30
+ title: response.data.title,
31
+ revisionId: response.data.revisionId,
32
+ }, "Document created successfully");
33
+ }
34
+ catch (error) {
35
+ return ResponseFormatter.error(error);
36
+ }
37
+ }
@@ -0,0 +1,45 @@
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_get_metadata",
6
+ description: "Get metadata for a Google Document including title, revision ID, and document style. Accepts either a document ID or a full Google Docs URL.",
7
+ inputSchema: {
8
+ type: "object",
9
+ properties: {
10
+ documentId: {
11
+ type: "string",
12
+ description: "Document ID or full Google Docs URL (e.g., https://docs.google.com/document/d/DOCUMENT_ID/edit)",
13
+ },
14
+ },
15
+ required: ["documentId"],
16
+ },
17
+ };
18
+ export async function getMetadata(args) {
19
+ try {
20
+ // Resolve document ID from URL if needed
21
+ const docRef = DocumentIdResolver.resolve(args.documentId);
22
+ const documentId = docRef.id;
23
+ const docs = google.docs("v1");
24
+ const response = await docs.documents.get({
25
+ documentId,
26
+ });
27
+ const doc = response.data;
28
+ const metadata = {
29
+ documentId: doc.documentId,
30
+ title: doc.title,
31
+ revisionId: doc.revisionId,
32
+ suggestionsViewMode: doc.suggestionsViewMode,
33
+ documentStyle: doc.documentStyle,
34
+ namedStyles: doc.namedStyles,
35
+ headers: Object.keys(doc.headers || {}).length,
36
+ footers: Object.keys(doc.footers || {}).length,
37
+ inlineObjects: Object.keys(doc.inlineObjects || {}).length,
38
+ lists: Object.keys(doc.lists || {}).length,
39
+ };
40
+ return ResponseFormatter.success(metadata, "Document metadata retrieved successfully");
41
+ }
42
+ catch (error) {
43
+ return ResponseFormatter.error(error);
44
+ }
45
+ }
@@ -0,0 +1,59 @@
1
+ import { google } from "googleapis";
2
+ import { ResponseFormatter } from "../../../lib/response-formatter.js";
3
+ export const schema = {
4
+ name: "gdocs_list_documents",
5
+ description: "List Google Documents from Google Drive. Use query parameter to filter by name, owner, etc.",
6
+ inputSchema: {
7
+ type: "object",
8
+ properties: {
9
+ query: {
10
+ type: "string",
11
+ description: "Search query to filter documents (e.g., 'name contains \"report\"'). Default: list all documents",
12
+ },
13
+ pageToken: {
14
+ type: "string",
15
+ description: "Token for pagination to get the next page of results",
16
+ },
17
+ pageSize: {
18
+ type: "number",
19
+ description: "Maximum number of documents to return (default: 10, max: 100)",
20
+ },
21
+ },
22
+ required: [],
23
+ },
24
+ };
25
+ export async function listDocuments(args) {
26
+ try {
27
+ const drive = google.drive("v3");
28
+ // Build query to find Google Docs files
29
+ let query = "mimeType='application/vnd.google-apps.document'";
30
+ if (args.query) {
31
+ query += ` and ${args.query}`;
32
+ }
33
+ const response = await drive.files.list({
34
+ q: query,
35
+ pageSize: args.pageSize || 10,
36
+ pageToken: args.pageToken,
37
+ fields: "nextPageToken, files(id, name, createdTime, modifiedTime, owners, webViewLink)",
38
+ orderBy: "modifiedTime desc",
39
+ });
40
+ const files = response.data.files || [];
41
+ const documents = files.map((file) => ({
42
+ documentId: file.id,
43
+ name: file.name,
44
+ createdTime: file.createdTime,
45
+ modifiedTime: file.modifiedTime,
46
+ owners: file.owners?.map((owner) => owner.emailAddress),
47
+ webViewLink: file.webViewLink,
48
+ }));
49
+ const result = {
50
+ documents,
51
+ count: documents.length,
52
+ nextPageToken: response.data.nextPageToken,
53
+ };
54
+ return ResponseFormatter.success(result, `Found ${documents.length} document(s)`);
55
+ }
56
+ catch (error) {
57
+ return ResponseFormatter.error(error);
58
+ }
59
+ }
@@ -0,0 +1,62 @@
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_read",
6
+ description: "Read content from a Google Document. Accepts either a document ID or a full Google Docs URL.",
7
+ inputSchema: {
8
+ type: "object",
9
+ properties: {
10
+ documentId: {
11
+ type: "string",
12
+ description: "Document ID or full Google Docs URL (e.g., https://docs.google.com/document/d/DOCUMENT_ID/edit)",
13
+ },
14
+ includeFormatting: {
15
+ type: "boolean",
16
+ description: "Whether to include text formatting information (default: false)",
17
+ },
18
+ },
19
+ required: ["documentId"],
20
+ },
21
+ };
22
+ export async function readDocument(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
+ const response = await docs.documents.get({
29
+ documentId,
30
+ });
31
+ const doc = response.data;
32
+ // Extract plain text content
33
+ let textContent = "";
34
+ if (doc.body?.content) {
35
+ for (const element of doc.body.content) {
36
+ if (element.paragraph?.elements) {
37
+ for (const textElement of element.paragraph.elements) {
38
+ if (textElement.textRun?.content) {
39
+ textContent += textElement.textRun.content;
40
+ }
41
+ }
42
+ }
43
+ }
44
+ }
45
+ const result = {
46
+ documentId: doc.documentId,
47
+ title: doc.title,
48
+ revisionId: doc.revisionId,
49
+ textContent,
50
+ };
51
+ // Include formatting if requested
52
+ if (args.includeFormatting) {
53
+ result.body = doc.body;
54
+ result.documentStyle = doc.documentStyle;
55
+ result.namedStyles = doc.namedStyles;
56
+ }
57
+ return ResponseFormatter.success(result, "Document read successfully");
58
+ }
59
+ catch (error) {
60
+ return ResponseFormatter.error(error);
61
+ }
62
+ }
@@ -0,0 +1,57 @@
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_append_text",
6
+ description: "Append text to the end of a Google Document.",
7
+ inputSchema: {
8
+ type: "object",
9
+ properties: {
10
+ documentId: {
11
+ type: "string",
12
+ description: "Document ID or full Google Docs URL",
13
+ },
14
+ text: {
15
+ type: "string",
16
+ description: "Text to append to the end of the document",
17
+ },
18
+ },
19
+ required: ["documentId", "text"],
20
+ },
21
+ };
22
+ export async function appendText(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
+ // First get the document to find the end index
29
+ const doc = await docs.documents.get({ documentId });
30
+ const endIndex = doc.data.body?.content?.[doc.data.body.content.length - 1]?.endIndex || 1;
31
+ // Insert text at the end (endIndex - 1 because endIndex is exclusive)
32
+ const response = await docs.documents.batchUpdate({
33
+ documentId,
34
+ requestBody: {
35
+ requests: [
36
+ {
37
+ insertText: {
38
+ text: args.text,
39
+ location: {
40
+ index: endIndex - 1,
41
+ },
42
+ },
43
+ },
44
+ ],
45
+ },
46
+ });
47
+ return ResponseFormatter.success({
48
+ documentId,
49
+ appendedText: args.text,
50
+ appendedAt: endIndex - 1,
51
+ length: args.text.length,
52
+ }, "Text appended successfully");
53
+ }
54
+ catch (error) {
55
+ return ResponseFormatter.error(error);
56
+ }
57
+ }
@@ -0,0 +1,86 @@
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_apply_style",
7
+ description: "Apply a named style (Normal, Heading 1-6, Title, Subtitle) to a paragraph 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 paragraph to style (1-based, inclusive)",
18
+ },
19
+ endIndex: {
20
+ type: "number",
21
+ description: "End position of paragraph to style (1-based, exclusive)",
22
+ },
23
+ namedStyleType: {
24
+ type: "string",
25
+ description: "Named style to apply",
26
+ enum: [
27
+ "NORMAL_TEXT",
28
+ "HEADING_1",
29
+ "HEADING_2",
30
+ "HEADING_3",
31
+ "HEADING_4",
32
+ "HEADING_5",
33
+ "HEADING_6",
34
+ "TITLE",
35
+ "SUBTITLE",
36
+ ],
37
+ },
38
+ },
39
+ required: ["documentId", "startIndex", "endIndex", "namedStyleType"],
40
+ },
41
+ };
42
+ export async function applyStyle(args) {
43
+ try {
44
+ // Validate inputs
45
+ if (!Validator.validateRange(args.startIndex, args.endIndex)) {
46
+ throw new Error("Invalid range: startIndex must be less than endIndex and both must be positive");
47
+ }
48
+ if (!Validator.validateNamedStyleType(args.namedStyleType)) {
49
+ throw new Error("Invalid namedStyleType. Must be one of: NORMAL_TEXT, HEADING_1-6, TITLE, SUBTITLE");
50
+ }
51
+ // Resolve document ID from URL if needed
52
+ const docRef = DocumentIdResolver.resolve(args.documentId);
53
+ const documentId = docRef.id;
54
+ const docs = google.docs("v1");
55
+ const response = await docs.documents.batchUpdate({
56
+ documentId,
57
+ requestBody: {
58
+ requests: [
59
+ {
60
+ updateParagraphStyle: {
61
+ paragraphStyle: {
62
+ namedStyleType: args.namedStyleType,
63
+ },
64
+ fields: "namedStyleType",
65
+ range: {
66
+ startIndex: args.startIndex,
67
+ endIndex: args.endIndex,
68
+ },
69
+ },
70
+ },
71
+ ],
72
+ },
73
+ });
74
+ return ResponseFormatter.success({
75
+ documentId,
76
+ namedStyleType: args.namedStyleType,
77
+ range: {
78
+ startIndex: args.startIndex,
79
+ endIndex: args.endIndex,
80
+ },
81
+ }, `Style '${args.namedStyleType}' applied successfully`);
82
+ }
83
+ catch (error) {
84
+ return ResponseFormatter.error(error);
85
+ }
86
+ }
@@ -0,0 +1,89 @@
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_create_heading",
6
+ description: "Create a heading (H1-H6) in a Google Document. If index is not provided, appends to end.",
7
+ inputSchema: {
8
+ type: "object",
9
+ properties: {
10
+ documentId: {
11
+ type: "string",
12
+ description: "Document ID or full Google Docs URL",
13
+ },
14
+ text: {
15
+ type: "string",
16
+ description: "Heading text",
17
+ },
18
+ level: {
19
+ type: "number",
20
+ description: "Heading level (1-6, where 1 is H1, 2 is H2, etc.)",
21
+ enum: [1, 2, 3, 4, 5, 6],
22
+ },
23
+ index: {
24
+ type: "number",
25
+ description: "Position to insert heading (1-based). If not provided, appends to end.",
26
+ },
27
+ },
28
+ required: ["documentId", "text", "level"],
29
+ },
30
+ };
31
+ export async function createHeading(args) {
32
+ try {
33
+ // Validate level
34
+ if (![1, 2, 3, 4, 5, 6].includes(args.level)) {
35
+ throw new Error("Level must be between 1 and 6");
36
+ }
37
+ // Resolve document ID from URL if needed
38
+ const docRef = DocumentIdResolver.resolve(args.documentId);
39
+ const documentId = docRef.id;
40
+ const docs = google.docs("v1");
41
+ // Determine insertion index
42
+ let insertIndex = args.index;
43
+ if (!insertIndex) {
44
+ const doc = await docs.documents.get({ documentId });
45
+ const endIndex = doc.data.body?.content?.[doc.data.body.content.length - 1]?.endIndex || 1;
46
+ insertIndex = endIndex - 1;
47
+ }
48
+ // Map level to named style
49
+ const namedStyleType = `HEADING_${args.level}`;
50
+ const headingText = args.text + "\n";
51
+ const response = await docs.documents.batchUpdate({
52
+ documentId,
53
+ requestBody: {
54
+ requests: [
55
+ {
56
+ insertText: {
57
+ text: headingText,
58
+ location: {
59
+ index: insertIndex,
60
+ },
61
+ },
62
+ },
63
+ {
64
+ updateParagraphStyle: {
65
+ paragraphStyle: {
66
+ namedStyleType,
67
+ },
68
+ fields: "namedStyleType",
69
+ range: {
70
+ startIndex: insertIndex,
71
+ endIndex: insertIndex + headingText.length,
72
+ },
73
+ },
74
+ },
75
+ ],
76
+ },
77
+ });
78
+ return ResponseFormatter.success({
79
+ documentId,
80
+ headingText: args.text,
81
+ level: args.level,
82
+ insertedAt: insertIndex,
83
+ namedStyleType,
84
+ }, `Heading ${args.level} created successfully`);
85
+ }
86
+ catch (error) {
87
+ return ResponseFormatter.error(error);
88
+ }
89
+ }
@@ -0,0 +1,86 @@
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_create_list",
7
+ description: "Convert a range of paragraphs into a bulleted or numbered list 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 convert to list (1-based, inclusive)",
18
+ },
19
+ endIndex: {
20
+ type: "number",
21
+ description: "End position of text to convert to list (1-based, exclusive)",
22
+ },
23
+ listType: {
24
+ type: "string",
25
+ description: "Type of list to create",
26
+ enum: ["ORDERED", "UNORDERED"],
27
+ },
28
+ nestingLevel: {
29
+ type: "number",
30
+ description: "Nesting level of the list (0-8, default: 0)",
31
+ },
32
+ },
33
+ required: ["documentId", "startIndex", "endIndex", "listType"],
34
+ },
35
+ };
36
+ export async function createList(args) {
37
+ try {
38
+ // Validate inputs
39
+ if (!Validator.validateRange(args.startIndex, args.endIndex)) {
40
+ throw new Error("Invalid range: startIndex must be less than endIndex and both must be positive");
41
+ }
42
+ if (!["ORDERED", "UNORDERED"].includes(args.listType)) {
43
+ throw new Error("listType must be either 'ORDERED' or 'UNORDERED'");
44
+ }
45
+ const nestingLevel = args.nestingLevel || 0;
46
+ if (nestingLevel < 0 || nestingLevel > 8) {
47
+ throw new Error("nestingLevel must be between 0 and 8");
48
+ }
49
+ // Resolve document ID from URL if needed
50
+ const docRef = DocumentIdResolver.resolve(args.documentId);
51
+ const documentId = docRef.id;
52
+ const docs = google.docs("v1");
53
+ // Map list type to glyph type
54
+ const glyphType = args.listType === "ORDERED" ? undefined : "GLYPH_TYPE_UNSPECIFIED";
55
+ const response = await docs.documents.batchUpdate({
56
+ documentId,
57
+ requestBody: {
58
+ requests: [
59
+ {
60
+ createParagraphBullets: {
61
+ range: {
62
+ startIndex: args.startIndex,
63
+ endIndex: args.endIndex,
64
+ },
65
+ bulletPreset: args.listType === "ORDERED"
66
+ ? "NUMBERED_DECIMAL_ALPHA_ROMAN"
67
+ : "BULLET_DISC_CIRCLE_SQUARE",
68
+ },
69
+ },
70
+ ],
71
+ },
72
+ });
73
+ return ResponseFormatter.success({
74
+ documentId,
75
+ listType: args.listType,
76
+ range: {
77
+ startIndex: args.startIndex,
78
+ endIndex: args.endIndex,
79
+ },
80
+ nestingLevel,
81
+ }, `List created successfully (${args.listType.toLowerCase()})`);
82
+ }
83
+ catch (error) {
84
+ return ResponseFormatter.error(error);
85
+ }
86
+ }
@@ -0,0 +1,64 @@
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_delete_text",
7
+ description: "Delete text in a specific range from a Google Document. Indices are 1-based.",
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 delete (1-based, inclusive)",
18
+ },
19
+ endIndex: {
20
+ type: "number",
21
+ description: "End position of text to delete (1-based, exclusive)",
22
+ },
23
+ },
24
+ required: ["documentId", "startIndex", "endIndex"],
25
+ },
26
+ };
27
+ export async function deleteText(args) {
28
+ try {
29
+ // Validate inputs
30
+ if (!Validator.validateRange(args.startIndex, args.endIndex)) {
31
+ throw new Error("Invalid range: startIndex must be less than endIndex and both must be positive");
32
+ }
33
+ // Resolve document ID from URL if needed
34
+ const docRef = DocumentIdResolver.resolve(args.documentId);
35
+ const documentId = docRef.id;
36
+ const docs = google.docs("v1");
37
+ const response = await docs.documents.batchUpdate({
38
+ documentId,
39
+ requestBody: {
40
+ requests: [
41
+ {
42
+ deleteContentRange: {
43
+ range: {
44
+ startIndex: args.startIndex,
45
+ endIndex: args.endIndex,
46
+ },
47
+ },
48
+ },
49
+ ],
50
+ },
51
+ });
52
+ return ResponseFormatter.success({
53
+ documentId,
54
+ deletedRange: {
55
+ startIndex: args.startIndex,
56
+ endIndex: args.endIndex,
57
+ },
58
+ deletedLength: args.endIndex - args.startIndex,
59
+ }, "Text deleted successfully");
60
+ }
61
+ catch (error) {
62
+ return ResponseFormatter.error(error);
63
+ }
64
+ }