@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.
- package/README.md +250 -17
- package/dist/auth.js +6 -0
- package/dist/index.js +1 -1
- package/dist/lib/calendar-helpers.js +197 -0
- package/dist/lib/document-id-resolver.js +76 -0
- package/dist/lib/drive-helpers.js +263 -0
- package/dist/lib/gmail-helpers.js +204 -0
- package/dist/lib/response-formatter.js +82 -0
- package/dist/lib/validation.js +112 -0
- package/dist/tools/calendar/acl/calendar_acl_insert.js +80 -0
- package/dist/tools/calendar/acl/calendar_acl_list.js +82 -0
- package/dist/tools/calendar/basic/calendar_create_event.js +113 -0
- package/dist/tools/calendar/basic/calendar_delete_event.js +52 -0
- package/dist/tools/calendar/basic/calendar_get_event.js +52 -0
- package/dist/tools/calendar/basic/calendar_list_events.js +86 -0
- package/dist/tools/calendar/basic/calendar_update_event.js +116 -0
- package/dist/tools/calendar/calendarlist/calendar_calendarlist_get.js +73 -0
- package/dist/tools/calendar/calendarlist/calendar_calendarlist_list.js +87 -0
- package/dist/tools/calendar/calendars/calendar_calendars_get.js +52 -0
- package/dist/tools/calendar/calendars/calendar_calendars_insert.js +66 -0
- package/dist/tools/calendar/calendars/calendar_calendars_update.js +85 -0
- package/dist/tools/calendar/colors/calendar_colors_get.js +46 -0
- package/dist/tools/calendar/events_advanced/calendar_events_instances.js +81 -0
- package/dist/tools/calendar/events_advanced/calendar_events_move.js +63 -0
- package/dist/tools/calendar/events_advanced/calendar_events_quickadd.js +52 -0
- package/dist/tools/calendar/freebusy/calendar_freebusy_query.js +69 -0
- package/dist/tools/calendar/settings/calendar_settings_list.js +81 -0
- package/dist/tools/docs/basic/gdocs_create.js +37 -0
- package/dist/tools/docs/basic/gdocs_get_metadata.js +45 -0
- package/dist/tools/docs/basic/gdocs_list_documents.js +59 -0
- package/dist/tools/docs/basic/gdocs_read.js +62 -0
- package/dist/tools/docs/content/gdocs_append_text.js +57 -0
- package/dist/tools/docs/content/gdocs_apply_style.js +86 -0
- package/dist/tools/docs/content/gdocs_create_heading.js +89 -0
- package/dist/tools/docs/content/gdocs_create_list.js +86 -0
- package/dist/tools/docs/content/gdocs_delete_text.js +64 -0
- package/dist/tools/docs/content/gdocs_format_text.js +137 -0
- package/dist/tools/docs/content/gdocs_insert_text.js +62 -0
- package/dist/tools/docs/content/gdocs_replace_text.js +64 -0
- package/dist/tools/docs/content/gdocs_set_alignment.js +76 -0
- package/dist/tools/docs/content/gdocs_update_text.js +78 -0
- package/dist/tools/docs/elements/gdocs_batch_update.js +108 -0
- package/dist/tools/docs/elements/gdocs_create_table.js +73 -0
- package/dist/tools/docs/elements/gdocs_export.js +62 -0
- package/dist/tools/docs/elements/gdocs_insert_image.js +96 -0
- package/dist/tools/docs/elements/gdocs_insert_link.js +77 -0
- package/dist/tools/docs/elements/gdocs_insert_page_break.js +55 -0
- package/dist/tools/docs/elements/gdocs_insert_toc.js +71 -0
- package/dist/tools/docs/elements/gdocs_merge_documents.js +104 -0
- package/dist/tools/docs/elements/gdocs_suggest_mode.js +41 -0
- package/dist/tools/drive/advanced/drive_empty_trash.js +56 -0
- package/dist/tools/drive/advanced/drive_export_file.js +158 -0
- package/dist/tools/drive/advanced/drive_list_revisions.js +80 -0
- package/dist/tools/drive/basic/drive_get_metadata.js +49 -0
- package/dist/tools/drive/basic/drive_list_files.js +76 -0
- package/dist/tools/drive/file/drive_copy_file.js +79 -0
- package/dist/tools/drive/file/drive_create_file.js +72 -0
- package/dist/tools/drive/file/drive_delete_file.js +48 -0
- package/dist/tools/drive/file/drive_move_file.js +79 -0
- package/dist/tools/drive/file/drive_rename_file.js +58 -0
- package/dist/tools/drive/file/drive_update_file.js +106 -0
- package/dist/tools/drive/file/drive_upload_file.js +80 -0
- package/dist/tools/drive/folder/drive_create_folder.js +67 -0
- package/dist/tools/drive/folder/drive_list_folder_contents.js +68 -0
- package/dist/tools/drive/folder/drive_move_to_folder.js +59 -0
- package/dist/tools/drive/permissions/drive_list_permissions.js +115 -0
- package/dist/tools/drive/permissions/drive_remove_permission.js +71 -0
- package/dist/tools/drive/permissions/drive_share_file.js +116 -0
- package/dist/tools/drive/permissions/drive_update_permission.js +79 -0
- package/dist/tools/gmail/basic/gmail_get_message.js +95 -0
- package/dist/tools/gmail/basic/gmail_get_thread.js +46 -0
- package/dist/tools/gmail/basic/gmail_list_labels.js +54 -0
- package/dist/tools/gmail/basic/gmail_search_messages.js +59 -0
- package/dist/tools/gmail/batch/gmail_batch_modify_labels.js +74 -0
- package/dist/tools/gmail/batch/gmail_get_messages_batch.js +120 -0
- package/dist/tools/gmail/batch/gmail_get_threads_batch.js +102 -0
- package/dist/tools/gmail/labels/gmail_manage_label.js +131 -0
- package/dist/tools/gmail/labels/gmail_modify_labels.js +65 -0
- package/dist/tools/gmail/send/gmail_draft_message.js +117 -0
- package/dist/tools/gmail/send/gmail_send_message.js +109 -0
- package/dist/tools/index.js +386 -3
- package/package.json +8 -3
- package/dist/tools/basic/gsheets_add_sheet.js +0 -65
- package/dist/tools/basic/gsheets_copy_sheet.js +0 -56
- package/dist/tools/basic/gsheets_copy_to.js +0 -113
- package/dist/tools/basic/gsheets_create_spreadsheet.js +0 -88
- package/dist/tools/basic/gsheets_delete_columns.js +0 -69
- package/dist/tools/basic/gsheets_delete_rows.js +0 -69
- package/dist/tools/basic/gsheets_delete_sheet.js +0 -56
- package/dist/tools/basic/gsheets_duplicate_sheet.js +0 -72
- package/dist/tools/basic/gsheets_insert_columns.js +0 -69
- package/dist/tools/basic/gsheets_insert_rows.js +0 -69
- package/dist/tools/basic/gsheets_list_sheets.js +0 -53
- package/dist/tools/basic/gsheets_read.js +0 -120
- package/dist/tools/basic/gsheets_rename_sheet.js +0 -64
- package/dist/tools/charts/gsheets_add_bubble.js +0 -176
- package/dist/tools/charts/gsheets_add_candlestick.js +0 -192
- package/dist/tools/charts/gsheets_add_chart.js +0 -162
- package/dist/tools/charts/gsheets_add_combo.js +0 -169
- package/dist/tools/charts/gsheets_add_histogram.js +0 -143
- package/dist/tools/charts/gsheets_add_org_chart.js +0 -160
- package/dist/tools/charts/gsheets_add_treemap.js +0 -177
- package/dist/tools/charts/gsheets_add_waterfall.js +0 -155
- package/dist/tools/charts/gsheets_delete_chart.js +0 -56
- package/dist/tools/charts/gsheets_update_chart.js +0 -118
- package/dist/tools/data/gsheets_append_data.js +0 -68
- package/dist/tools/data/gsheets_batch_clear.js +0 -53
- package/dist/tools/data/gsheets_batch_update.js +0 -81
- package/dist/tools/data/gsheets_clear_data.js +0 -53
- package/dist/tools/data/gsheets_create_filter.js +0 -81
- package/dist/tools/data/gsheets_find_replace.js +0 -124
- package/dist/tools/data/gsheets_set_data_validation.js +0 -153
- package/dist/tools/data/gsheets_sort_range.js +0 -102
- package/dist/tools/data/gsheets_update_cell.js +0 -44
- package/dist/tools/formatting/gsheets_auto_resize.js +0 -75
- package/dist/tools/formatting/gsheets_format_cells.js +0 -161
- package/dist/tools/formatting/gsheets_freeze_columns.js +0 -67
- package/dist/tools/formatting/gsheets_freeze_rows.js +0 -67
- package/dist/tools/formatting/gsheets_merge_cells.js +0 -85
- package/dist/tools/formatting/gsheets_set_number_format.js +0 -116
- package/dist/tools/formatting/gsheets_unmerge_cells.js +0 -79
- package/dist/tools/formatting/gsheets_update_borders.js +0 -212
- package/dist/tools/gdrive/gdrive_read_file.js +0 -77
- package/dist/tools/gdrive/gdrive_search.js +0 -71
- package/dist/tools/gdrive_read_file.js +0 -77
- package/dist/tools/gdrive_search.js +0 -71
- package/dist/tools/gsheets_add_bubble.js +0 -176
- package/dist/tools/gsheets_add_candlestick.js +0 -192
- package/dist/tools/gsheets_add_chart.js +0 -162
- package/dist/tools/gsheets_add_combo.js +0 -169
- package/dist/tools/gsheets_add_conditional_format.js +0 -175
- package/dist/tools/gsheets_add_histogram.js +0 -143
- package/dist/tools/gsheets_add_named_range.js +0 -87
- package/dist/tools/gsheets_add_org_chart.js +0 -160
- package/dist/tools/gsheets_add_protected_range.js +0 -127
- package/dist/tools/gsheets_add_sheet.js +0 -65
- package/dist/tools/gsheets_add_treemap.js +0 -177
- package/dist/tools/gsheets_add_waterfall.js +0 -155
- package/dist/tools/gsheets_append_data.js +0 -68
- package/dist/tools/gsheets_auto_resize.js +0 -75
- package/dist/tools/gsheets_batch_clear.js +0 -53
- package/dist/tools/gsheets_batch_update.js +0 -81
- package/dist/tools/gsheets_clear_data.js +0 -53
- package/dist/tools/gsheets_copy_sheet.js +0 -56
- package/dist/tools/gsheets_copy_to.js +0 -113
- package/dist/tools/gsheets_create_filter.js +0 -81
- package/dist/tools/gsheets_create_spreadsheet.js +0 -88
- package/dist/tools/gsheets_delete_chart.js +0 -56
- package/dist/tools/gsheets_delete_columns.js +0 -69
- package/dist/tools/gsheets_delete_named_range.js +0 -56
- package/dist/tools/gsheets_delete_protected_range.js +0 -56
- package/dist/tools/gsheets_delete_rows.js +0 -69
- package/dist/tools/gsheets_delete_sheet.js +0 -56
- package/dist/tools/gsheets_duplicate_sheet.js +0 -72
- package/dist/tools/gsheets_find_replace.js +0 -124
- package/dist/tools/gsheets_format_cells.js +0 -161
- package/dist/tools/gsheets_freeze_columns.js +0 -67
- package/dist/tools/gsheets_freeze_rows.js +0 -67
- package/dist/tools/gsheets_insert_columns.js +0 -69
- package/dist/tools/gsheets_insert_rows.js +0 -69
- package/dist/tools/gsheets_list_sheets.js +0 -53
- package/dist/tools/gsheets_merge_cells.js +0 -85
- package/dist/tools/gsheets_read.js +0 -120
- package/dist/tools/gsheets_rename_sheet.js +0 -64
- package/dist/tools/gsheets_set_data_validation.js +0 -153
- package/dist/tools/gsheets_set_number_format.js +0 -116
- package/dist/tools/gsheets_sort_range.js +0 -102
- package/dist/tools/gsheets_unmerge_cells.js +0 -79
- package/dist/tools/gsheets_update_borders.js +0 -212
- package/dist/tools/gsheets_update_cell.js +0 -44
- package/dist/tools/gsheets_update_chart.js +0 -118
- package/dist/tools/gsheets_update_named_range.js +0 -112
- package/dist/tools/gsheets_update_protected_range.js +0 -110
- package/dist/tools/protection/gsheets_add_conditional_format.js +0 -175
- package/dist/tools/protection/gsheets_add_named_range.js +0 -87
- package/dist/tools/protection/gsheets_add_protected_range.js +0 -127
- package/dist/tools/protection/gsheets_delete_named_range.js +0 -56
- package/dist/tools/protection/gsheets_delete_protected_range.js +0 -56
- package/dist/tools/protection/gsheets_update_named_range.js +0 -112
- package/dist/tools/protection/gsheets_update_protected_range.js +0 -110
- /package/dist/tools/drive/{drive_read_file.js → basic/drive_read_file.js} +0 -0
- /package/dist/tools/drive/{drive_search.js → basic/drive_search.js} +0 -0
|
@@ -0,0 +1,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
|
+
}
|