@alanse/mcp-server-google-workspace 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +146 -17
- package/dist/auth.js +5 -0
- package/dist/lib/calendar-helpers.js +197 -0
- package/dist/lib/drive-helpers.js +263 -0
- package/dist/lib/gmail-helpers.js +204 -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/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 +267 -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,52 @@
|
|
|
1
|
+
import { google } from "googleapis";
|
|
2
|
+
import { ResponseFormatter } from "../../../lib/response-formatter.js";
|
|
3
|
+
import { formatEvent } from "../../../lib/calendar-helpers.js";
|
|
4
|
+
export const schema = {
|
|
5
|
+
name: "calendar_events_quickadd",
|
|
6
|
+
description: "Create a calendar event from a simple text description using natural language processing. Examples: 'Meeting with John tomorrow at 2pm', 'Lunch at noon on Friday', 'Conference call next Monday 10-11am'. The system automatically parses the text to extract event details.",
|
|
7
|
+
inputSchema: {
|
|
8
|
+
type: "object",
|
|
9
|
+
properties: {
|
|
10
|
+
calendarId: {
|
|
11
|
+
type: "string",
|
|
12
|
+
description: "Calendar identifier (default: 'primary' for user's main calendar)",
|
|
13
|
+
},
|
|
14
|
+
text: {
|
|
15
|
+
type: "string",
|
|
16
|
+
description: "Natural language text describing the event (e.g., 'Dinner with Sarah tomorrow at 7pm')",
|
|
17
|
+
},
|
|
18
|
+
sendUpdates: {
|
|
19
|
+
type: "string",
|
|
20
|
+
description: "Whether to send event notifications: 'all', 'externalOnly', 'none'. Default: 'none'",
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
required: ["text"],
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
export async function quickAddEvent(args) {
|
|
27
|
+
try {
|
|
28
|
+
const calendar = google.calendar("v3");
|
|
29
|
+
const { calendarId = "primary", text, sendUpdates = "none", } = args;
|
|
30
|
+
const response = await calendar.events.quickAdd({
|
|
31
|
+
calendarId,
|
|
32
|
+
text,
|
|
33
|
+
sendUpdates,
|
|
34
|
+
});
|
|
35
|
+
const event = response.data;
|
|
36
|
+
let message = `✅ Event created via QuickAdd\n\n`;
|
|
37
|
+
message += `Input Text: "${text}"\n\n`;
|
|
38
|
+
message += formatEvent(event, calendarId);
|
|
39
|
+
return ResponseFormatter.success({
|
|
40
|
+
eventId: event.id,
|
|
41
|
+
calendarId,
|
|
42
|
+
summary: event.summary,
|
|
43
|
+
start: event.start,
|
|
44
|
+
end: event.end,
|
|
45
|
+
htmlLink: event.htmlLink,
|
|
46
|
+
inputText: text,
|
|
47
|
+
}, message);
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
return ResponseFormatter.error(error);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { google } from "googleapis";
|
|
2
|
+
import { ResponseFormatter } from "../../../lib/response-formatter.js";
|
|
3
|
+
import { formatFreeBusyResponse } from "../../../lib/calendar-helpers.js";
|
|
4
|
+
export const schema = {
|
|
5
|
+
name: "calendar_freebusy_query",
|
|
6
|
+
description: "Query free/busy information for one or more calendars within a specified time range. Returns busy time periods to help schedule meetings or check availability. Useful for finding meeting slots that work for multiple attendees.",
|
|
7
|
+
inputSchema: {
|
|
8
|
+
type: "object",
|
|
9
|
+
properties: {
|
|
10
|
+
timeMin: {
|
|
11
|
+
type: "string",
|
|
12
|
+
description: "Start of the time range to query (ISO 8601 format, e.g., '2024-01-01T00:00:00Z')",
|
|
13
|
+
},
|
|
14
|
+
timeMax: {
|
|
15
|
+
type: "string",
|
|
16
|
+
description: "End of the time range to query (ISO 8601 format)",
|
|
17
|
+
},
|
|
18
|
+
calendars: {
|
|
19
|
+
type: "array",
|
|
20
|
+
items: {
|
|
21
|
+
type: "string",
|
|
22
|
+
},
|
|
23
|
+
description: "List of calendar IDs to query (default: ['primary'])",
|
|
24
|
+
},
|
|
25
|
+
timeZone: {
|
|
26
|
+
type: "string",
|
|
27
|
+
description: "Time zone for the query (e.g., 'America/New_York', 'UTC'). Default: UTC",
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
required: ["timeMin", "timeMax"],
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
export async function queryFreeBusy(args) {
|
|
34
|
+
try {
|
|
35
|
+
const calendar = google.calendar("v3");
|
|
36
|
+
const { timeMin, timeMax, calendars = ["primary"], timeZone = "UTC", } = args;
|
|
37
|
+
const response = await calendar.freebusy.query({
|
|
38
|
+
requestBody: {
|
|
39
|
+
timeMin,
|
|
40
|
+
timeMax,
|
|
41
|
+
timeZone,
|
|
42
|
+
items: calendars.map((id) => ({ id })),
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
const freeBusyData = response.data;
|
|
46
|
+
if (!freeBusyData) {
|
|
47
|
+
return ResponseFormatter.error(new Error("Failed to query free/busy information"));
|
|
48
|
+
}
|
|
49
|
+
const formattedOutput = formatFreeBusyResponse(freeBusyData);
|
|
50
|
+
// Calculate total busy periods across all calendars
|
|
51
|
+
let totalBusyPeriods = 0;
|
|
52
|
+
if (freeBusyData.calendars) {
|
|
53
|
+
Object.values(freeBusyData.calendars).forEach((cal) => {
|
|
54
|
+
if (cal.busy) {
|
|
55
|
+
totalBusyPeriods += cal.busy.length;
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
return ResponseFormatter.success({
|
|
60
|
+
timeMin: freeBusyData.timeMin,
|
|
61
|
+
timeMax: freeBusyData.timeMax,
|
|
62
|
+
calendars: freeBusyData.calendars,
|
|
63
|
+
totalBusyPeriods,
|
|
64
|
+
}, `Free/Busy query results:\n\n${formattedOutput}`);
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
return ResponseFormatter.error(error);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -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,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
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { google } from "googleapis";
|
|
2
|
+
import { ResponseFormatter } from "../../../lib/response-formatter.js";
|
|
3
|
+
import { formatRevisionList, } from "../../../lib/drive-helpers.js";
|
|
4
|
+
export const schema = {
|
|
5
|
+
name: "drive_list_revisions",
|
|
6
|
+
description: "List all revisions of a file in Google Drive. Shows revision history with timestamps and modification details. Supports pagination.",
|
|
7
|
+
inputSchema: {
|
|
8
|
+
type: "object",
|
|
9
|
+
properties: {
|
|
10
|
+
fileId: {
|
|
11
|
+
type: "string",
|
|
12
|
+
description: "ID of the file to list revisions for",
|
|
13
|
+
},
|
|
14
|
+
pageSize: {
|
|
15
|
+
type: "number",
|
|
16
|
+
description: "Maximum number of revisions to return per page (default: 10, max: 1000)",
|
|
17
|
+
optional: true,
|
|
18
|
+
},
|
|
19
|
+
pageToken: {
|
|
20
|
+
type: "string",
|
|
21
|
+
description: "Token for pagination to get the next page of results",
|
|
22
|
+
optional: true,
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
required: ["fileId"],
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
export async function listRevisions(args) {
|
|
29
|
+
try {
|
|
30
|
+
const drive = google.drive("v3");
|
|
31
|
+
const { fileId, pageSize = 10, pageToken } = args;
|
|
32
|
+
// Get file metadata
|
|
33
|
+
const fileResponse = await drive.files.get({
|
|
34
|
+
fileId,
|
|
35
|
+
fields: "id, name, mimeType, size, modifiedTime, createdTime",
|
|
36
|
+
});
|
|
37
|
+
const file = fileResponse.data;
|
|
38
|
+
// List revisions
|
|
39
|
+
const revisionsResponse = await drive.revisions.list({
|
|
40
|
+
fileId,
|
|
41
|
+
pageSize: Math.min(pageSize, 1000), // Cap at 1000
|
|
42
|
+
pageToken: pageToken || undefined,
|
|
43
|
+
fields: "revisions(id, modifiedTime, lastModifyingUser, size, keepForever, originalFilename, mimeType), nextPageToken",
|
|
44
|
+
});
|
|
45
|
+
const revisions = revisionsResponse.data.revisions || [];
|
|
46
|
+
const nextPageToken = revisionsResponse.data.nextPageToken;
|
|
47
|
+
// Format output
|
|
48
|
+
let output = `📄 Revision History for "${file.name}"\n`;
|
|
49
|
+
output += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`;
|
|
50
|
+
output += `Total Revisions: ${revisions.length}\n`;
|
|
51
|
+
if (nextPageToken) {
|
|
52
|
+
output += `(More revisions available - use nextPageToken for pagination)\n`;
|
|
53
|
+
}
|
|
54
|
+
output += `\n${formatRevisionList(revisions)}\n`;
|
|
55
|
+
// Format detailed output for each revision
|
|
56
|
+
let detailedRevisions = revisions.map((revision, index) => ({
|
|
57
|
+
index: index + 1,
|
|
58
|
+
revisionId: revision.id || "",
|
|
59
|
+
modifiedTime: revision.modifiedTime || "Unknown",
|
|
60
|
+
modifiedBy: revision.lastModifyingUser?.displayName || "Unknown",
|
|
61
|
+
modifiedByEmail: revision.lastModifyingUser?.emailAddress || "Unknown",
|
|
62
|
+
size: revision.size ? parseInt(revision.size) : 0,
|
|
63
|
+
mimeType: revision.mimeType || "Unknown",
|
|
64
|
+
keepForever: revision.keepForever || false,
|
|
65
|
+
originalFilename: revision.originalFilename || file.name,
|
|
66
|
+
}));
|
|
67
|
+
return ResponseFormatter.success({
|
|
68
|
+
fileId,
|
|
69
|
+
fileName: file.name,
|
|
70
|
+
totalRevisions: revisions.length,
|
|
71
|
+
revisions: detailedRevisions,
|
|
72
|
+
nextPageToken: nextPageToken || null,
|
|
73
|
+
hasMoreResults: !!nextPageToken,
|
|
74
|
+
pageSize,
|
|
75
|
+
}, output);
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
return ResponseFormatter.error(error);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { google } from "googleapis";
|
|
2
|
+
import { ResponseFormatter } from "../../../lib/response-formatter.js";
|
|
3
|
+
import { formatFileMetadata } from "../../../lib/drive-helpers.js";
|
|
4
|
+
export const schema = {
|
|
5
|
+
name: "drive_get_metadata",
|
|
6
|
+
description: "Get detailed metadata for a specific file in Google Drive. Returns file ID, name, MIME type, size, created/modified times, sharing settings, and more.",
|
|
7
|
+
inputSchema: {
|
|
8
|
+
type: "object",
|
|
9
|
+
properties: {
|
|
10
|
+
fileId: {
|
|
11
|
+
type: "string",
|
|
12
|
+
description: "ID of the file to get metadata for",
|
|
13
|
+
},
|
|
14
|
+
fields: {
|
|
15
|
+
type: "string",
|
|
16
|
+
description: "Comma-separated list of fields to include (default: all common fields)",
|
|
17
|
+
optional: true,
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
required: ["fileId"],
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
export async function getMetadata(args) {
|
|
24
|
+
try {
|
|
25
|
+
const drive = google.drive("v3");
|
|
26
|
+
const { fileId, fields } = args;
|
|
27
|
+
const defaultFields = "id, name, mimeType, size, createdTime, modifiedTime, modifiedByMe, owners, lastModifyingUser, shared, sharingUser, permissions, parents, webViewLink, webContentLink, iconLink, thumbnailLink, description, starred, trashed, explicitlyTrashed, properties, appProperties, spaces, version, teamDriveId, driveId, hasAugmentedPermissions, hasVersions, headRevisionId, copyRequiresWriterPermission, writersCanShare, folderColorRgb, originalFilename, fullFileExtension, fileExtension, md5Checksum, sha1Checksum, sha256Checksum";
|
|
28
|
+
const response = await drive.files.get({
|
|
29
|
+
fileId,
|
|
30
|
+
fields: fields || defaultFields,
|
|
31
|
+
supportsAllDrives: true,
|
|
32
|
+
});
|
|
33
|
+
const file = response.data;
|
|
34
|
+
const output = formatFileMetadata(file, true);
|
|
35
|
+
return ResponseFormatter.success({
|
|
36
|
+
fileId: file.id,
|
|
37
|
+
name: file.name,
|
|
38
|
+
mimeType: file.mimeType,
|
|
39
|
+
size: file.size,
|
|
40
|
+
createdTime: file.createdTime,
|
|
41
|
+
modifiedTime: file.modifiedTime,
|
|
42
|
+
webViewLink: file.webViewLink,
|
|
43
|
+
metadata: file,
|
|
44
|
+
}, output);
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
return ResponseFormatter.error(error);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { google } from "googleapis";
|
|
2
|
+
import { ResponseFormatter } from "../../../lib/response-formatter.js";
|
|
3
|
+
import { formatFileList } from "../../../lib/drive-helpers.js";
|
|
4
|
+
export const schema = {
|
|
5
|
+
name: "drive_list_files",
|
|
6
|
+
description: "List files in Google Drive. Supports filtering by query, folder, pagination, and sorting. Returns file IDs, names, types, and sizes.",
|
|
7
|
+
inputSchema: {
|
|
8
|
+
type: "object",
|
|
9
|
+
properties: {
|
|
10
|
+
query: {
|
|
11
|
+
type: "string",
|
|
12
|
+
description: "Search query using Google Drive query syntax (e.g., \"name contains 'report'\")",
|
|
13
|
+
optional: true,
|
|
14
|
+
},
|
|
15
|
+
folderId: {
|
|
16
|
+
type: "string",
|
|
17
|
+
description: "Folder ID to list files from (limits search to specific folder)",
|
|
18
|
+
optional: true,
|
|
19
|
+
},
|
|
20
|
+
pageSize: {
|
|
21
|
+
type: "number",
|
|
22
|
+
description: "Number of files to return per page (max 1000, default 100)",
|
|
23
|
+
optional: true,
|
|
24
|
+
},
|
|
25
|
+
pageToken: {
|
|
26
|
+
type: "string",
|
|
27
|
+
description: "Page token for pagination",
|
|
28
|
+
optional: true,
|
|
29
|
+
},
|
|
30
|
+
orderBy: {
|
|
31
|
+
type: "string",
|
|
32
|
+
description: "Sort order (e.g., 'name', 'modifiedTime desc', 'createdTime')",
|
|
33
|
+
optional: true,
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
required: [],
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
export async function listFiles(args) {
|
|
40
|
+
try {
|
|
41
|
+
const drive = google.drive("v3");
|
|
42
|
+
const { query, folderId, pageSize = 100, pageToken, orderBy } = args;
|
|
43
|
+
// Build query
|
|
44
|
+
let q = "trashed = false";
|
|
45
|
+
if (query) {
|
|
46
|
+
q += ` and (${query})`;
|
|
47
|
+
}
|
|
48
|
+
if (folderId) {
|
|
49
|
+
q += ` and '${folderId}' in parents`;
|
|
50
|
+
}
|
|
51
|
+
const response = await drive.files.list({
|
|
52
|
+
q,
|
|
53
|
+
pageSize: Math.min(pageSize, 1000),
|
|
54
|
+
pageToken,
|
|
55
|
+
orderBy: orderBy || "modifiedTime desc",
|
|
56
|
+
fields: "nextPageToken, files(id, name, mimeType, size, modifiedTime, createdTime, webViewLink, parents, iconLink)",
|
|
57
|
+
});
|
|
58
|
+
const files = response.data.files || [];
|
|
59
|
+
const nextPageToken = response.data.nextPageToken;
|
|
60
|
+
const output = formatFileList(files, nextPageToken);
|
|
61
|
+
return ResponseFormatter.success({
|
|
62
|
+
count: files.length,
|
|
63
|
+
files: files.map((f) => ({
|
|
64
|
+
id: f.id,
|
|
65
|
+
name: f.name,
|
|
66
|
+
mimeType: f.mimeType,
|
|
67
|
+
size: f.size,
|
|
68
|
+
modifiedTime: f.modifiedTime,
|
|
69
|
+
})),
|
|
70
|
+
nextPageToken,
|
|
71
|
+
}, output);
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
return ResponseFormatter.error(error);
|
|
75
|
+
}
|
|
76
|
+
}
|