@alanse/mcp-server-google-workspace 0.2.0 → 1.0.0
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/LICENSE +92 -18
- package/README.md +135 -36
- package/dist/auth.js +3 -2
- package/dist/index.js +6 -6
- package/dist/lib/document-id-resolver.js +76 -0
- package/dist/lib/response-formatter.js +82 -0
- package/dist/lib/validation.js +112 -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/drive_read_file.js +77 -0
- package/dist/tools/drive/drive_search.js +71 -0
- package/dist/tools/index.js +124 -5
- package/package.json +3 -3
|
@@ -0,0 +1,137 @@
|
|
|
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_format_text",
|
|
7
|
+
description: "Apply text formatting (bold, italic, font, color, etc.) to a range in a Google Document.",
|
|
8
|
+
inputSchema: {
|
|
9
|
+
type: "object",
|
|
10
|
+
properties: {
|
|
11
|
+
documentId: {
|
|
12
|
+
type: "string",
|
|
13
|
+
description: "Document ID or full Google Docs URL",
|
|
14
|
+
},
|
|
15
|
+
startIndex: {
|
|
16
|
+
type: "number",
|
|
17
|
+
description: "Start position of text to format (1-based, inclusive)",
|
|
18
|
+
},
|
|
19
|
+
endIndex: {
|
|
20
|
+
type: "number",
|
|
21
|
+
description: "End position of text to format (1-based, exclusive)",
|
|
22
|
+
},
|
|
23
|
+
format: {
|
|
24
|
+
type: "object",
|
|
25
|
+
description: "Formatting options to apply",
|
|
26
|
+
properties: {
|
|
27
|
+
bold: { type: "boolean", description: "Make text bold" },
|
|
28
|
+
italic: { type: "boolean", description: "Make text italic" },
|
|
29
|
+
underline: { type: "boolean", description: "Underline text" },
|
|
30
|
+
fontSize: { type: "number", description: "Font size in points" },
|
|
31
|
+
fontFamily: { type: "string", description: "Font family (e.g., 'Arial', 'Times New Roman')" },
|
|
32
|
+
foregroundColor: {
|
|
33
|
+
type: "object",
|
|
34
|
+
description: "Text color (RGB values 0-1)",
|
|
35
|
+
properties: {
|
|
36
|
+
red: { type: "number" },
|
|
37
|
+
green: { type: "number" },
|
|
38
|
+
blue: { type: "number" },
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
backgroundColor: {
|
|
42
|
+
type: "object",
|
|
43
|
+
description: "Background color (RGB values 0-1)",
|
|
44
|
+
properties: {
|
|
45
|
+
red: { type: "number" },
|
|
46
|
+
green: { type: "number" },
|
|
47
|
+
blue: { type: "number" },
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
required: ["documentId", "startIndex", "endIndex", "format"],
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
export async function formatText(args) {
|
|
57
|
+
try {
|
|
58
|
+
// Validate inputs
|
|
59
|
+
if (!Validator.validateRange(args.startIndex, args.endIndex)) {
|
|
60
|
+
throw new Error("Invalid range: startIndex must be less than endIndex and both must be positive");
|
|
61
|
+
}
|
|
62
|
+
if (!Validator.validateTextStyle(args.format)) {
|
|
63
|
+
throw new Error("Invalid format: at least one formatting property must be specified");
|
|
64
|
+
}
|
|
65
|
+
// Validate colors if provided
|
|
66
|
+
if (args.format.foregroundColor && !Validator.validateColor(args.format.foregroundColor)) {
|
|
67
|
+
throw new Error("Invalid foregroundColor: RGB values must be between 0 and 1");
|
|
68
|
+
}
|
|
69
|
+
if (args.format.backgroundColor && !Validator.validateColor(args.format.backgroundColor)) {
|
|
70
|
+
throw new Error("Invalid backgroundColor: RGB values must be between 0 and 1");
|
|
71
|
+
}
|
|
72
|
+
// Resolve document ID from URL if needed
|
|
73
|
+
const docRef = DocumentIdResolver.resolve(args.documentId);
|
|
74
|
+
const documentId = docRef.id;
|
|
75
|
+
const docs = google.docs("v1");
|
|
76
|
+
// Build text style object
|
|
77
|
+
const textStyle = {};
|
|
78
|
+
if (args.format.bold !== undefined)
|
|
79
|
+
textStyle.bold = args.format.bold;
|
|
80
|
+
if (args.format.italic !== undefined)
|
|
81
|
+
textStyle.italic = args.format.italic;
|
|
82
|
+
if (args.format.underline !== undefined)
|
|
83
|
+
textStyle.underline = args.format.underline;
|
|
84
|
+
if (args.format.fontSize) {
|
|
85
|
+
textStyle.fontSize = {
|
|
86
|
+
magnitude: args.format.fontSize,
|
|
87
|
+
unit: "PT",
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
if (args.format.fontFamily)
|
|
91
|
+
textStyle.weightedFontFamily = { fontFamily: args.format.fontFamily };
|
|
92
|
+
if (args.format.foregroundColor) {
|
|
93
|
+
textStyle.foregroundColor = {
|
|
94
|
+
color: {
|
|
95
|
+
rgbColor: args.format.foregroundColor,
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
if (args.format.backgroundColor) {
|
|
100
|
+
textStyle.backgroundColor = {
|
|
101
|
+
color: {
|
|
102
|
+
rgbColor: args.format.backgroundColor,
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
// Build fields mask
|
|
107
|
+
const fields = Object.keys(textStyle).join(",");
|
|
108
|
+
const response = await docs.documents.batchUpdate({
|
|
109
|
+
documentId,
|
|
110
|
+
requestBody: {
|
|
111
|
+
requests: [
|
|
112
|
+
{
|
|
113
|
+
updateTextStyle: {
|
|
114
|
+
textStyle,
|
|
115
|
+
fields,
|
|
116
|
+
range: {
|
|
117
|
+
startIndex: args.startIndex,
|
|
118
|
+
endIndex: args.endIndex,
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
return ResponseFormatter.success({
|
|
126
|
+
documentId,
|
|
127
|
+
formattedRange: {
|
|
128
|
+
startIndex: args.startIndex,
|
|
129
|
+
endIndex: args.endIndex,
|
|
130
|
+
},
|
|
131
|
+
appliedFormats: Object.keys(args.format),
|
|
132
|
+
}, "Text formatted successfully");
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
return ResponseFormatter.error(error);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -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
|
+
import { Validator } from "../../../lib/validation.js";
|
|
5
|
+
export const schema = {
|
|
6
|
+
name: "gdocs_insert_text",
|
|
7
|
+
description: "Insert text at a specific position in a Google Document. Position is 1-based (1 = start of document).",
|
|
8
|
+
inputSchema: {
|
|
9
|
+
type: "object",
|
|
10
|
+
properties: {
|
|
11
|
+
documentId: {
|
|
12
|
+
type: "string",
|
|
13
|
+
description: "Document ID or full Google Docs URL",
|
|
14
|
+
},
|
|
15
|
+
text: {
|
|
16
|
+
type: "string",
|
|
17
|
+
description: "Text to insert",
|
|
18
|
+
},
|
|
19
|
+
index: {
|
|
20
|
+
type: "number",
|
|
21
|
+
description: "Position to insert text (1-based index, 1 = start of document)",
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
required: ["documentId", "text", "index"],
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
export async function insertText(args) {
|
|
28
|
+
try {
|
|
29
|
+
// Validate inputs
|
|
30
|
+
if (!Validator.validateIndex(args.index)) {
|
|
31
|
+
throw new Error("Index must be a positive integer");
|
|
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
|
+
insertText: {
|
|
43
|
+
text: args.text,
|
|
44
|
+
location: {
|
|
45
|
+
index: args.index,
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
return ResponseFormatter.success({
|
|
53
|
+
documentId,
|
|
54
|
+
insertedText: args.text,
|
|
55
|
+
insertedAt: args.index,
|
|
56
|
+
length: args.text.length,
|
|
57
|
+
}, "Text inserted successfully");
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
return ResponseFormatter.error(error);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -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
|
+
export const schema = {
|
|
5
|
+
name: "gdocs_replace_text",
|
|
6
|
+
description: "Find and replace all occurrences of text in 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
|
+
findText: {
|
|
15
|
+
type: "string",
|
|
16
|
+
description: "Text to find",
|
|
17
|
+
},
|
|
18
|
+
replaceText: {
|
|
19
|
+
type: "string",
|
|
20
|
+
description: "Text to replace with",
|
|
21
|
+
},
|
|
22
|
+
matchCase: {
|
|
23
|
+
type: "boolean",
|
|
24
|
+
description: "Whether to match case (default: false)",
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
required: ["documentId", "findText", "replaceText"],
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
export async function replaceText(args) {
|
|
31
|
+
try {
|
|
32
|
+
// Resolve document ID from URL if needed
|
|
33
|
+
const docRef = DocumentIdResolver.resolve(args.documentId);
|
|
34
|
+
const documentId = docRef.id;
|
|
35
|
+
const docs = google.docs("v1");
|
|
36
|
+
const response = await docs.documents.batchUpdate({
|
|
37
|
+
documentId,
|
|
38
|
+
requestBody: {
|
|
39
|
+
requests: [
|
|
40
|
+
{
|
|
41
|
+
replaceAllText: {
|
|
42
|
+
containsText: {
|
|
43
|
+
text: args.findText,
|
|
44
|
+
matchCase: args.matchCase || false,
|
|
45
|
+
},
|
|
46
|
+
replaceText: args.replaceText,
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
// Get occurrences replaced from response
|
|
53
|
+
const occurrencesReplaced = response.data.replies?.[0]?.replaceAllText?.occurrencesChanged || 0;
|
|
54
|
+
return ResponseFormatter.success({
|
|
55
|
+
documentId,
|
|
56
|
+
findText: args.findText,
|
|
57
|
+
replaceText: args.replaceText,
|
|
58
|
+
occurrencesReplaced,
|
|
59
|
+
}, `Replaced ${occurrencesReplaced} occurrence(s)`);
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
return ResponseFormatter.error(error);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
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_set_alignment",
|
|
7
|
+
description: "Set paragraph alignment (left, center, right, justified) for a 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 align (1-based, inclusive)",
|
|
18
|
+
},
|
|
19
|
+
endIndex: {
|
|
20
|
+
type: "number",
|
|
21
|
+
description: "End position of paragraph to align (1-based, exclusive)",
|
|
22
|
+
},
|
|
23
|
+
alignment: {
|
|
24
|
+
type: "string",
|
|
25
|
+
description: "Alignment type",
|
|
26
|
+
enum: ["START", "CENTER", "END", "JUSTIFIED"],
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
required: ["documentId", "startIndex", "endIndex", "alignment"],
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
export async function setAlignment(args) {
|
|
33
|
+
try {
|
|
34
|
+
// Validate inputs
|
|
35
|
+
if (!Validator.validateRange(args.startIndex, args.endIndex)) {
|
|
36
|
+
throw new Error("Invalid range: startIndex must be less than endIndex and both must be positive");
|
|
37
|
+
}
|
|
38
|
+
if (!Validator.validateAlignment(args.alignment)) {
|
|
39
|
+
throw new Error("alignment must be one of: START, CENTER, END, JUSTIFIED");
|
|
40
|
+
}
|
|
41
|
+
// Resolve document ID from URL if needed
|
|
42
|
+
const docRef = DocumentIdResolver.resolve(args.documentId);
|
|
43
|
+
const documentId = docRef.id;
|
|
44
|
+
const docs = google.docs("v1");
|
|
45
|
+
const response = await docs.documents.batchUpdate({
|
|
46
|
+
documentId,
|
|
47
|
+
requestBody: {
|
|
48
|
+
requests: [
|
|
49
|
+
{
|
|
50
|
+
updateParagraphStyle: {
|
|
51
|
+
paragraphStyle: {
|
|
52
|
+
alignment: args.alignment,
|
|
53
|
+
},
|
|
54
|
+
fields: "alignment",
|
|
55
|
+
range: {
|
|
56
|
+
startIndex: args.startIndex,
|
|
57
|
+
endIndex: args.endIndex,
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
return ResponseFormatter.success({
|
|
65
|
+
documentId,
|
|
66
|
+
alignment: args.alignment,
|
|
67
|
+
range: {
|
|
68
|
+
startIndex: args.startIndex,
|
|
69
|
+
endIndex: args.endIndex,
|
|
70
|
+
},
|
|
71
|
+
}, "Paragraph alignment set successfully");
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
return ResponseFormatter.error(error);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
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_update_text",
|
|
7
|
+
description: "Update (replace) text in a specific range in 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 replace (1-based, inclusive)",
|
|
18
|
+
},
|
|
19
|
+
endIndex: {
|
|
20
|
+
type: "number",
|
|
21
|
+
description: "End position of text to replace (1-based, exclusive)",
|
|
22
|
+
},
|
|
23
|
+
text: {
|
|
24
|
+
type: "string",
|
|
25
|
+
description: "New text to replace the range with",
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
required: ["documentId", "startIndex", "endIndex", "text"],
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
export async function updateText(args) {
|
|
32
|
+
try {
|
|
33
|
+
// Validate inputs
|
|
34
|
+
if (!Validator.validateRange(args.startIndex, args.endIndex)) {
|
|
35
|
+
throw new Error("Invalid range: startIndex must be less than endIndex and both must be positive");
|
|
36
|
+
}
|
|
37
|
+
// 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
|
+
// First delete the range, then insert new text
|
|
42
|
+
const response = await docs.documents.batchUpdate({
|
|
43
|
+
documentId,
|
|
44
|
+
requestBody: {
|
|
45
|
+
requests: [
|
|
46
|
+
{
|
|
47
|
+
deleteContentRange: {
|
|
48
|
+
range: {
|
|
49
|
+
startIndex: args.startIndex,
|
|
50
|
+
endIndex: args.endIndex,
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
insertText: {
|
|
56
|
+
text: args.text,
|
|
57
|
+
location: {
|
|
58
|
+
index: args.startIndex,
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
return ResponseFormatter.success({
|
|
66
|
+
documentId,
|
|
67
|
+
updatedRange: {
|
|
68
|
+
startIndex: args.startIndex,
|
|
69
|
+
endIndex: args.startIndex + args.text.length,
|
|
70
|
+
},
|
|
71
|
+
newText: args.text,
|
|
72
|
+
replacedLength: args.endIndex - args.startIndex,
|
|
73
|
+
}, "Text updated successfully");
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
return ResponseFormatter.error(error);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
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_batch_update",
|
|
6
|
+
description: "Execute multiple update operations on a Google Document in a single API call. Requests are executed in order.",
|
|
7
|
+
inputSchema: {
|
|
8
|
+
type: "object",
|
|
9
|
+
properties: {
|
|
10
|
+
documentId: {
|
|
11
|
+
type: "string",
|
|
12
|
+
description: "Document ID or full Google Docs URL",
|
|
13
|
+
},
|
|
14
|
+
requests: {
|
|
15
|
+
type: "array",
|
|
16
|
+
description: "Array of update requests to execute",
|
|
17
|
+
items: {
|
|
18
|
+
type: "object",
|
|
19
|
+
properties: {
|
|
20
|
+
type: {
|
|
21
|
+
type: "string",
|
|
22
|
+
description: "Type of operation",
|
|
23
|
+
enum: [
|
|
24
|
+
"insertText",
|
|
25
|
+
"deleteContentRange",
|
|
26
|
+
"updateTextStyle",
|
|
27
|
+
"updateParagraphStyle",
|
|
28
|
+
"createParagraphBullets",
|
|
29
|
+
"insertTable",
|
|
30
|
+
"insertInlineImage",
|
|
31
|
+
"insertPageBreak",
|
|
32
|
+
"createHeader",
|
|
33
|
+
"createFooter",
|
|
34
|
+
],
|
|
35
|
+
},
|
|
36
|
+
params: {
|
|
37
|
+
type: "object",
|
|
38
|
+
description: "Parameters for the operation (structure depends on type)",
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
required: ["type", "params"],
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
required: ["documentId", "requests"],
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
export async function batchUpdate(args) {
|
|
49
|
+
try {
|
|
50
|
+
// Resolve document ID from URL if needed
|
|
51
|
+
const docRef = DocumentIdResolver.resolve(args.documentId);
|
|
52
|
+
const documentId = docRef.id;
|
|
53
|
+
const docs = google.docs("v1");
|
|
54
|
+
// Convert our simplified request format to Google Docs API format
|
|
55
|
+
const requests = args.requests.map((req) => {
|
|
56
|
+
const apiRequest = {};
|
|
57
|
+
switch (req.type) {
|
|
58
|
+
case "insertText":
|
|
59
|
+
apiRequest.insertText = req.params;
|
|
60
|
+
break;
|
|
61
|
+
case "deleteContentRange":
|
|
62
|
+
apiRequest.deleteContentRange = req.params;
|
|
63
|
+
break;
|
|
64
|
+
case "updateTextStyle":
|
|
65
|
+
apiRequest.updateTextStyle = req.params;
|
|
66
|
+
break;
|
|
67
|
+
case "updateParagraphStyle":
|
|
68
|
+
apiRequest.updateParagraphStyle = req.params;
|
|
69
|
+
break;
|
|
70
|
+
case "createParagraphBullets":
|
|
71
|
+
apiRequest.createParagraphBullets = req.params;
|
|
72
|
+
break;
|
|
73
|
+
case "insertTable":
|
|
74
|
+
apiRequest.insertTable = req.params;
|
|
75
|
+
break;
|
|
76
|
+
case "insertInlineImage":
|
|
77
|
+
apiRequest.insertInlineImage = req.params;
|
|
78
|
+
break;
|
|
79
|
+
case "insertPageBreak":
|
|
80
|
+
apiRequest.insertPageBreak = req.params;
|
|
81
|
+
break;
|
|
82
|
+
case "createHeader":
|
|
83
|
+
apiRequest.createHeader = req.params;
|
|
84
|
+
break;
|
|
85
|
+
case "createFooter":
|
|
86
|
+
apiRequest.createFooter = req.params;
|
|
87
|
+
break;
|
|
88
|
+
default:
|
|
89
|
+
throw new Error(`Unknown request type: ${req.type}`);
|
|
90
|
+
}
|
|
91
|
+
return apiRequest;
|
|
92
|
+
});
|
|
93
|
+
const response = await docs.documents.batchUpdate({
|
|
94
|
+
documentId,
|
|
95
|
+
requestBody: {
|
|
96
|
+
requests,
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
return ResponseFormatter.success({
|
|
100
|
+
documentId,
|
|
101
|
+
operationsExecuted: args.requests.length,
|
|
102
|
+
requestTypes: args.requests.map((r) => r.type),
|
|
103
|
+
}, `Successfully executed ${args.requests.length} operation(s)`);
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
return ResponseFormatter.error(error);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
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_table",
|
|
7
|
+
description: "Create a table with specified rows and columns 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
|
+
rows: {
|
|
16
|
+
type: "number",
|
|
17
|
+
description: "Number of rows in the table",
|
|
18
|
+
},
|
|
19
|
+
columns: {
|
|
20
|
+
type: "number",
|
|
21
|
+
description: "Number of columns in the table",
|
|
22
|
+
},
|
|
23
|
+
index: {
|
|
24
|
+
type: "number",
|
|
25
|
+
description: "Position to insert table (1-based)",
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
required: ["documentId", "rows", "columns", "index"],
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
export async function createTable(args) {
|
|
32
|
+
try {
|
|
33
|
+
// Validate inputs
|
|
34
|
+
if (!Validator.validateIndex(args.index)) {
|
|
35
|
+
throw new Error("Index must be a positive integer");
|
|
36
|
+
}
|
|
37
|
+
if (!Validator.validateIndex(args.rows) || args.rows < 1 || args.rows > 20) {
|
|
38
|
+
throw new Error("Rows must be between 1 and 20");
|
|
39
|
+
}
|
|
40
|
+
if (!Validator.validateIndex(args.columns) || args.columns < 1 || args.columns > 20) {
|
|
41
|
+
throw new Error("Columns must be between 1 and 20");
|
|
42
|
+
}
|
|
43
|
+
// Resolve document ID from URL if needed
|
|
44
|
+
const docRef = DocumentIdResolver.resolve(args.documentId);
|
|
45
|
+
const documentId = docRef.id;
|
|
46
|
+
const docs = google.docs("v1");
|
|
47
|
+
const response = await docs.documents.batchUpdate({
|
|
48
|
+
documentId,
|
|
49
|
+
requestBody: {
|
|
50
|
+
requests: [
|
|
51
|
+
{
|
|
52
|
+
insertTable: {
|
|
53
|
+
rows: args.rows,
|
|
54
|
+
columns: args.columns,
|
|
55
|
+
location: {
|
|
56
|
+
index: args.index,
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
return ResponseFormatter.success({
|
|
64
|
+
documentId,
|
|
65
|
+
rows: args.rows,
|
|
66
|
+
columns: args.columns,
|
|
67
|
+
insertedAt: args.index,
|
|
68
|
+
}, `Table created successfully (${args.rows}x${args.columns})`);
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
return ResponseFormatter.error(error);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -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_export",
|
|
6
|
+
description: "Export a Google Document to different formats (PDF, DOCX, Plain Text, HTML, EPUB).",
|
|
7
|
+
inputSchema: {
|
|
8
|
+
type: "object",
|
|
9
|
+
properties: {
|
|
10
|
+
documentId: {
|
|
11
|
+
type: "string",
|
|
12
|
+
description: "Document ID or full Google Docs URL",
|
|
13
|
+
},
|
|
14
|
+
mimeType: {
|
|
15
|
+
type: "string",
|
|
16
|
+
description: "Export format",
|
|
17
|
+
enum: [
|
|
18
|
+
"application/pdf",
|
|
19
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
20
|
+
"text/plain",
|
|
21
|
+
"text/html",
|
|
22
|
+
"application/epub+zip",
|
|
23
|
+
],
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
required: ["documentId", "mimeType"],
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
export async function exportDocument(args) {
|
|
30
|
+
try {
|
|
31
|
+
// Resolve document ID from URL if needed
|
|
32
|
+
const docRef = DocumentIdResolver.resolve(args.documentId);
|
|
33
|
+
const documentId = docRef.id;
|
|
34
|
+
const drive = google.drive("v3");
|
|
35
|
+
// Export the document
|
|
36
|
+
const response = await drive.files.export({
|
|
37
|
+
fileId: documentId,
|
|
38
|
+
mimeType: args.mimeType,
|
|
39
|
+
}, { responseType: "arraybuffer" });
|
|
40
|
+
// Get file size
|
|
41
|
+
const fileSize = Buffer.byteLength(response.data);
|
|
42
|
+
// Map MIME type to file extension
|
|
43
|
+
const extensionMap = {
|
|
44
|
+
"application/pdf": "pdf",
|
|
45
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document": "docx",
|
|
46
|
+
"text/plain": "txt",
|
|
47
|
+
"text/html": "html",
|
|
48
|
+
"application/epub+zip": "epub",
|
|
49
|
+
};
|
|
50
|
+
const extension = extensionMap[args.mimeType];
|
|
51
|
+
return ResponseFormatter.success({
|
|
52
|
+
documentId,
|
|
53
|
+
mimeType: args.mimeType,
|
|
54
|
+
extension,
|
|
55
|
+
fileSize,
|
|
56
|
+
note: "Document exported successfully. The exported file data is available in the API response but not shown here for brevity.",
|
|
57
|
+
}, "Document exported successfully");
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
return ResponseFormatter.error(error);
|
|
61
|
+
}
|
|
62
|
+
}
|