@alanse/mcp-server-google-workspace 0.2.1 → 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/README.md +109 -5
- package/dist/auth.js +1 -0
- package/dist/index.js +1 -1
- 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/index.js +119 -0
- package/package.json +1 -1
|
@@ -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
|
+
}
|
|
@@ -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
|
+
}
|