google-tools-mcp 1.2.11 → 2.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 +21 -21
- package/README.md +555 -276
- package/dist/cachedToolsList.js +52 -52
- package/dist/googleDocsApiHelpers.js +76 -12
- package/dist/helpers.js +572 -252
- package/dist/httpAuth.js +296 -0
- package/dist/index.js +162 -11
- package/dist/logger.js +87 -87
- package/dist/markdown-transformer/docsToMarkdown.js +222 -21
- package/dist/markdown-transformer/index.js +19 -9
- package/dist/markdown-transformer/markdownToDocs.js +458 -28
- package/dist/readTracker.js +136 -17
- package/dist/sessionContext.js +37 -0
- package/dist/setup.js +187 -9
- package/dist/tools/calendar/getBusy.js +64 -64
- package/dist/tools/calendar/getEvents.js +141 -141
- package/dist/tools/calendar/getFree.js +225 -225
- package/dist/tools/calendar/index.js +19 -19
- package/dist/tools/calendar/listCalendars.js +38 -38
- package/dist/tools/calendar/listRecurringInstances.js +83 -83
- package/dist/tools/calendar/manageCalendar.js +121 -121
- package/dist/tools/calendar/manageEvent.js +258 -258
- package/dist/tools/calendar/moveEvent.js +66 -66
- package/dist/tools/docs/addTab.js +10 -12
- package/dist/tools/docs/appendToGoogleDoc.js +9 -4
- package/dist/tools/docs/deleteRange.js +9 -4
- package/dist/tools/docs/findAndReplace.js +9 -4
- package/dist/tools/docs/formatting/applyParagraphStyle.js +4 -1
- package/dist/tools/docs/formatting/getFormatting.js +171 -171
- package/dist/tools/docs/insertImage.js +15 -2
- package/dist/tools/docs/insertPageBreak.js +4 -1
- package/dist/tools/docs/insertTable.js +4 -1
- package/dist/tools/docs/insertTableWithData.js +4 -1
- package/dist/tools/docs/modifyText.js +14 -4
- package/dist/tools/docs/modifyText.test.js +84 -84
- package/dist/tools/docs/readGoogleDoc.js +71 -11
- package/dist/tools/docs/renameTab.js +12 -14
- package/dist/tools/drafts.js +165 -165
- package/dist/tools/drive/createDocument.js +13 -1
- package/dist/tools/drive/downloadFile.js +268 -268
- package/dist/tools/drive/getFileInfo.js +48 -48
- package/dist/tools/drive/getFilePath.js +50 -50
- package/dist/tools/drive/listDriveFiles.js +112 -112
- package/dist/tools/drive/listSharedDrives.js +52 -52
- package/dist/tools/drive/listSharedWithMe.js +97 -97
- package/dist/tools/drive/uploadFile.js +111 -111
- package/dist/tools/extras/index.js +9 -9
- package/dist/tools/extras/readDriveFile.js +212 -206
- package/dist/tools/extras/readFile.js +84 -84
- package/dist/tools/extras/searchFileContents.js +81 -81
- package/dist/tools/forms/batchUpdateForm.js +81 -81
- package/dist/tools/forms/createForm.js +54 -54
- package/dist/tools/forms/getForm.js +118 -118
- package/dist/tools/forms/getFormResponse.js +45 -45
- package/dist/tools/forms/index.js +15 -15
- package/dist/tools/forms/listFormResponses.js +56 -56
- package/dist/tools/forms/setPublishSettings.js +59 -59
- package/dist/tools/gmail/drafts.js +165 -165
- package/dist/tools/gmail/labels.js +54 -83
- package/dist/tools/gmail/messages.js +444 -441
- package/dist/tools/gmail/settings.js +358 -528
- package/dist/tools/gmail/threads.js +286 -164
- package/dist/tools/index.js +511 -473
- package/dist/tools/labels.js +103 -103
- package/dist/tools/legacyAliases.js +426 -0
- package/dist/tools/maps/directions.js +72 -0
- package/dist/tools/maps/geocode.js +16 -0
- package/dist/tools/maps/index.js +15 -0
- package/dist/tools/maps/mapsClient.js +83 -0
- package/dist/tools/maps/placeDetails.js +16 -0
- package/dist/tools/maps/reverseGeocode.js +19 -0
- package/dist/tools/maps/searchNearby.js +54 -0
- package/dist/tools/maps/searchPlaces.js +24 -0
- package/dist/tools/messages.js +448 -448
- package/dist/tools/settings.js +528 -528
- package/dist/tools/slides/createPresentation.js +104 -104
- package/dist/tools/slides/createShape.js +92 -92
- package/dist/tools/slides/createTextBox.js +82 -82
- package/dist/tools/slides/deleteSlide.js +30 -30
- package/dist/tools/slides/duplicateSlide.js +37 -37
- package/dist/tools/slides/exportThumbnail.js +42 -42
- package/dist/tools/slides/formatParagraph.js +72 -72
- package/dist/tools/slides/formatText.js +84 -84
- package/dist/tools/slides/getPresentation.js +87 -87
- package/dist/tools/slides/index.js +33 -33
- package/dist/tools/slides/reorderSlides.js +41 -41
- package/dist/tools/slides/replaceAllText.js +46 -46
- package/dist/tools/slides/setBackground.js +58 -58
- package/dist/tools/slides/speakerNotes.js +102 -102
- package/dist/tools/slides/styleShape.js +111 -111
- package/dist/tools/slides/updatePresentation.js +128 -128
- package/dist/tools/threads.js +145 -145
- package/dist/tools/utils/appendMarkdownToGoogleDoc.js +31 -7
- package/dist/tools/utils/replaceDocumentWithMarkdown.js +72 -9
- package/dist/updateCheck.js +202 -0
- package/dist/workspace.js +122 -0
- package/package.json +82 -80
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
import { register as createPresentation } from './createPresentation.js';
|
|
2
|
-
import { register as updatePresentation } from './updatePresentation.js';
|
|
3
|
-
import { register as getPresentation } from './getPresentation.js';
|
|
4
|
-
import { register as formatText } from './formatText.js';
|
|
5
|
-
import { register as formatParagraph } from './formatParagraph.js';
|
|
6
|
-
import { register as styleShape } from './styleShape.js';
|
|
7
|
-
import { register as setBackground } from './setBackground.js';
|
|
8
|
-
import { register as createTextBox } from './createTextBox.js';
|
|
9
|
-
import { register as createShape } from './createShape.js';
|
|
10
|
-
import { register as speakerNotes } from './speakerNotes.js';
|
|
11
|
-
import { register as deleteSlide } from './deleteSlide.js';
|
|
12
|
-
import { register as duplicateSlide } from './duplicateSlide.js';
|
|
13
|
-
import { register as reorderSlides } from './reorderSlides.js';
|
|
14
|
-
import { register as replaceAllText } from './replaceAllText.js';
|
|
15
|
-
import { register as exportThumbnail } from './exportThumbnail.js';
|
|
16
|
-
|
|
17
|
-
export function registerSlidesTools(server) {
|
|
18
|
-
createPresentation(server);
|
|
19
|
-
updatePresentation(server);
|
|
20
|
-
getPresentation(server);
|
|
21
|
-
formatText(server);
|
|
22
|
-
formatParagraph(server);
|
|
23
|
-
styleShape(server);
|
|
24
|
-
setBackground(server);
|
|
25
|
-
createTextBox(server);
|
|
26
|
-
createShape(server);
|
|
27
|
-
speakerNotes(server);
|
|
28
|
-
deleteSlide(server);
|
|
29
|
-
duplicateSlide(server);
|
|
30
|
-
reorderSlides(server);
|
|
31
|
-
replaceAllText(server);
|
|
32
|
-
exportThumbnail(server);
|
|
33
|
-
}
|
|
1
|
+
import { register as createPresentation } from './createPresentation.js';
|
|
2
|
+
import { register as updatePresentation } from './updatePresentation.js';
|
|
3
|
+
import { register as getPresentation } from './getPresentation.js';
|
|
4
|
+
import { register as formatText } from './formatText.js';
|
|
5
|
+
import { register as formatParagraph } from './formatParagraph.js';
|
|
6
|
+
import { register as styleShape } from './styleShape.js';
|
|
7
|
+
import { register as setBackground } from './setBackground.js';
|
|
8
|
+
import { register as createTextBox } from './createTextBox.js';
|
|
9
|
+
import { register as createShape } from './createShape.js';
|
|
10
|
+
import { register as speakerNotes } from './speakerNotes.js';
|
|
11
|
+
import { register as deleteSlide } from './deleteSlide.js';
|
|
12
|
+
import { register as duplicateSlide } from './duplicateSlide.js';
|
|
13
|
+
import { register as reorderSlides } from './reorderSlides.js';
|
|
14
|
+
import { register as replaceAllText } from './replaceAllText.js';
|
|
15
|
+
import { register as exportThumbnail } from './exportThumbnail.js';
|
|
16
|
+
|
|
17
|
+
export function registerSlidesTools(server) {
|
|
18
|
+
createPresentation(server);
|
|
19
|
+
updatePresentation(server);
|
|
20
|
+
getPresentation(server);
|
|
21
|
+
formatText(server);
|
|
22
|
+
formatParagraph(server);
|
|
23
|
+
styleShape(server);
|
|
24
|
+
setBackground(server);
|
|
25
|
+
createTextBox(server);
|
|
26
|
+
createShape(server);
|
|
27
|
+
speakerNotes(server);
|
|
28
|
+
deleteSlide(server);
|
|
29
|
+
duplicateSlide(server);
|
|
30
|
+
reorderSlides(server);
|
|
31
|
+
replaceAllText(server);
|
|
32
|
+
exportThumbnail(server);
|
|
33
|
+
}
|
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
import { UserError } from 'fastmcp';
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
import { getSlidesClient } from '../../clients.js';
|
|
4
|
-
|
|
5
|
-
export function register(server) {
|
|
6
|
-
server.addTool({
|
|
7
|
-
name: 'reorderSlides',
|
|
8
|
-
description: 'Reorder one or more slides in a Google Slides presentation by moving them to a target index.',
|
|
9
|
-
parameters: z.object({
|
|
10
|
-
presentationId: z.string().describe('Presentation ID'),
|
|
11
|
-
slideObjectIds: z.array(z.string()).min(1).describe('Array of slide object IDs to move'),
|
|
12
|
-
insertionIndex: z.number().int().min(0).describe('Target insertion index (0-based)'),
|
|
13
|
-
}),
|
|
14
|
-
execute: async (args, { log }) => {
|
|
15
|
-
const slidesApi = await getSlidesClient();
|
|
16
|
-
|
|
17
|
-
try {
|
|
18
|
-
await slidesApi.presentations.batchUpdate({
|
|
19
|
-
presentationId: args.presentationId,
|
|
20
|
-
requestBody: {
|
|
21
|
-
requests: [
|
|
22
|
-
{
|
|
23
|
-
updateSlidesPosition: {
|
|
24
|
-
slideObjectIds: args.slideObjectIds,
|
|
25
|
-
insertionIndex: args.insertionIndex,
|
|
26
|
-
},
|
|
27
|
-
},
|
|
28
|
-
],
|
|
29
|
-
},
|
|
30
|
-
});
|
|
31
|
-
return JSON.stringify({
|
|
32
|
-
success: true,
|
|
33
|
-
message: `Reordered ${args.slideObjectIds.length} slide(s) to index ${args.insertionIndex}`,
|
|
34
|
-
});
|
|
35
|
-
} catch (error) {
|
|
36
|
-
log.error(`Error reordering slides: ${error.message || error}`);
|
|
37
|
-
throw new UserError(`Failed to reorder slides: ${error.message || 'Unknown error'}`);
|
|
38
|
-
}
|
|
39
|
-
},
|
|
40
|
-
});
|
|
41
|
-
}
|
|
1
|
+
import { UserError } from 'fastmcp';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { getSlidesClient } from '../../clients.js';
|
|
4
|
+
|
|
5
|
+
export function register(server) {
|
|
6
|
+
server.addTool({
|
|
7
|
+
name: 'reorderSlides',
|
|
8
|
+
description: 'Reorder one or more slides in a Google Slides presentation by moving them to a target index.',
|
|
9
|
+
parameters: z.object({
|
|
10
|
+
presentationId: z.string().describe('Presentation ID'),
|
|
11
|
+
slideObjectIds: z.array(z.string()).min(1).describe('Array of slide object IDs to move'),
|
|
12
|
+
insertionIndex: z.number().int().min(0).describe('Target insertion index (0-based)'),
|
|
13
|
+
}),
|
|
14
|
+
execute: async (args, { log }) => {
|
|
15
|
+
const slidesApi = await getSlidesClient();
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
await slidesApi.presentations.batchUpdate({
|
|
19
|
+
presentationId: args.presentationId,
|
|
20
|
+
requestBody: {
|
|
21
|
+
requests: [
|
|
22
|
+
{
|
|
23
|
+
updateSlidesPosition: {
|
|
24
|
+
slideObjectIds: args.slideObjectIds,
|
|
25
|
+
insertionIndex: args.insertionIndex,
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
return JSON.stringify({
|
|
32
|
+
success: true,
|
|
33
|
+
message: `Reordered ${args.slideObjectIds.length} slide(s) to index ${args.insertionIndex}`,
|
|
34
|
+
});
|
|
35
|
+
} catch (error) {
|
|
36
|
+
log.error(`Error reordering slides: ${error.message || error}`);
|
|
37
|
+
throw new UserError(`Failed to reorder slides: ${error.message || 'Unknown error'}`);
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
}
|
|
@@ -1,46 +1,46 @@
|
|
|
1
|
-
import { UserError } from 'fastmcp';
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
import { getSlidesClient } from '../../clients.js';
|
|
4
|
-
|
|
5
|
-
export function register(server) {
|
|
6
|
-
server.addTool({
|
|
7
|
-
name: 'replaceAllTextInSlides',
|
|
8
|
-
description:
|
|
9
|
-
'Find and replace all matching text across all slides in a Google Slides presentation.',
|
|
10
|
-
parameters: z.object({
|
|
11
|
-
presentationId: z.string().describe('Presentation ID'),
|
|
12
|
-
containsText: z.string().describe('Text to search for'),
|
|
13
|
-
replaceText: z.string().describe('Replacement text'),
|
|
14
|
-
matchCase: z.boolean().optional().default(false).describe('Case-sensitive match'),
|
|
15
|
-
}),
|
|
16
|
-
execute: async (args, { log }) => {
|
|
17
|
-
const slidesApi = await getSlidesClient();
|
|
18
|
-
|
|
19
|
-
try {
|
|
20
|
-
const response = await slidesApi.presentations.batchUpdate({
|
|
21
|
-
presentationId: args.presentationId,
|
|
22
|
-
requestBody: {
|
|
23
|
-
requests: [
|
|
24
|
-
{
|
|
25
|
-
replaceAllText: {
|
|
26
|
-
containsText: { text: args.containsText, matchCase: args.matchCase },
|
|
27
|
-
replaceText: args.replaceText,
|
|
28
|
-
},
|
|
29
|
-
},
|
|
30
|
-
],
|
|
31
|
-
},
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
const count = response.data.replies?.[0]?.replaceAllText?.occurrencesChanged ?? 0;
|
|
35
|
-
return JSON.stringify({
|
|
36
|
-
success: true,
|
|
37
|
-
occurrencesChanged: count,
|
|
38
|
-
message: `Replaced ${count} occurrence(s) of "${args.containsText}"`,
|
|
39
|
-
});
|
|
40
|
-
} catch (error) {
|
|
41
|
-
log.error(`Error replacing text: ${error.message || error}`);
|
|
42
|
-
throw new UserError(`Failed to replace text: ${error.message || 'Unknown error'}`);
|
|
43
|
-
}
|
|
44
|
-
},
|
|
45
|
-
});
|
|
46
|
-
}
|
|
1
|
+
import { UserError } from 'fastmcp';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { getSlidesClient } from '../../clients.js';
|
|
4
|
+
|
|
5
|
+
export function register(server) {
|
|
6
|
+
server.addTool({
|
|
7
|
+
name: 'replaceAllTextInSlides',
|
|
8
|
+
description:
|
|
9
|
+
'Find and replace all matching text across all slides in a Google Slides presentation.',
|
|
10
|
+
parameters: z.object({
|
|
11
|
+
presentationId: z.string().describe('Presentation ID'),
|
|
12
|
+
containsText: z.string().describe('Text to search for'),
|
|
13
|
+
replaceText: z.string().describe('Replacement text'),
|
|
14
|
+
matchCase: z.boolean().optional().default(false).describe('Case-sensitive match'),
|
|
15
|
+
}),
|
|
16
|
+
execute: async (args, { log }) => {
|
|
17
|
+
const slidesApi = await getSlidesClient();
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const response = await slidesApi.presentations.batchUpdate({
|
|
21
|
+
presentationId: args.presentationId,
|
|
22
|
+
requestBody: {
|
|
23
|
+
requests: [
|
|
24
|
+
{
|
|
25
|
+
replaceAllText: {
|
|
26
|
+
containsText: { text: args.containsText, matchCase: args.matchCase },
|
|
27
|
+
replaceText: args.replaceText,
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const count = response.data.replies?.[0]?.replaceAllText?.occurrencesChanged ?? 0;
|
|
35
|
+
return JSON.stringify({
|
|
36
|
+
success: true,
|
|
37
|
+
occurrencesChanged: count,
|
|
38
|
+
message: `Replaced ${count} occurrence(s) of "${args.containsText}"`,
|
|
39
|
+
});
|
|
40
|
+
} catch (error) {
|
|
41
|
+
log.error(`Error replacing text: ${error.message || error}`);
|
|
42
|
+
throw new UserError(`Failed to replace text: ${error.message || 'Unknown error'}`);
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
}
|
|
@@ -1,58 +1,58 @@
|
|
|
1
|
-
import { UserError } from 'fastmcp';
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
import { getSlidesClient } from '../../clients.js';
|
|
4
|
-
|
|
5
|
-
export function register(server) {
|
|
6
|
-
server.addTool({
|
|
7
|
-
name: 'setSlidesBackground',
|
|
8
|
-
description:
|
|
9
|
-
'Set the background color for one or more slides in a Google Slides presentation.',
|
|
10
|
-
parameters: z.object({
|
|
11
|
-
presentationId: z.string().describe('Presentation ID'),
|
|
12
|
-
pageObjectIds: z.array(z.string()).min(1).describe('Array of slide page object IDs to update'),
|
|
13
|
-
backgroundColor: z
|
|
14
|
-
.object({
|
|
15
|
-
red: z.number().min(0).max(1).optional(),
|
|
16
|
-
green: z.number().min(0).max(1).optional(),
|
|
17
|
-
blue: z.number().min(0).max(1).optional(),
|
|
18
|
-
alpha: z.number().min(0).max(1).optional(),
|
|
19
|
-
})
|
|
20
|
-
.describe('Background color (RGBA 0-1)'),
|
|
21
|
-
}),
|
|
22
|
-
execute: async (args, { log }) => {
|
|
23
|
-
const slidesApi = await getSlidesClient();
|
|
24
|
-
|
|
25
|
-
const requests = args.pageObjectIds.map((pageObjectId) => ({
|
|
26
|
-
updatePageProperties: {
|
|
27
|
-
objectId: pageObjectId,
|
|
28
|
-
pageProperties: {
|
|
29
|
-
pageBackgroundFill: {
|
|
30
|
-
solidFill: {
|
|
31
|
-
color: {
|
|
32
|
-
rgbColor: {
|
|
33
|
-
red: args.backgroundColor.red || 0,
|
|
34
|
-
green: args.backgroundColor.green || 0,
|
|
35
|
-
blue: args.backgroundColor.blue || 0,
|
|
36
|
-
},
|
|
37
|
-
},
|
|
38
|
-
alpha: args.backgroundColor.alpha ?? 1,
|
|
39
|
-
},
|
|
40
|
-
},
|
|
41
|
-
},
|
|
42
|
-
fields: 'pageBackgroundFill',
|
|
43
|
-
},
|
|
44
|
-
}));
|
|
45
|
-
|
|
46
|
-
try {
|
|
47
|
-
await slidesApi.presentations.batchUpdate({
|
|
48
|
-
presentationId: args.presentationId,
|
|
49
|
-
requestBody: { requests },
|
|
50
|
-
});
|
|
51
|
-
return JSON.stringify({ success: true, message: `Set background for ${args.pageObjectIds.length} slide(s)` });
|
|
52
|
-
} catch (error) {
|
|
53
|
-
log.error(`Error setting background: ${error.message || error}`);
|
|
54
|
-
throw new UserError(`Failed to set background: ${error.message || 'Unknown error'}`);
|
|
55
|
-
}
|
|
56
|
-
},
|
|
57
|
-
});
|
|
58
|
-
}
|
|
1
|
+
import { UserError } from 'fastmcp';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { getSlidesClient } from '../../clients.js';
|
|
4
|
+
|
|
5
|
+
export function register(server) {
|
|
6
|
+
server.addTool({
|
|
7
|
+
name: 'setSlidesBackground',
|
|
8
|
+
description:
|
|
9
|
+
'Set the background color for one or more slides in a Google Slides presentation.',
|
|
10
|
+
parameters: z.object({
|
|
11
|
+
presentationId: z.string().describe('Presentation ID'),
|
|
12
|
+
pageObjectIds: z.array(z.string()).min(1).describe('Array of slide page object IDs to update'),
|
|
13
|
+
backgroundColor: z
|
|
14
|
+
.object({
|
|
15
|
+
red: z.number().min(0).max(1).optional(),
|
|
16
|
+
green: z.number().min(0).max(1).optional(),
|
|
17
|
+
blue: z.number().min(0).max(1).optional(),
|
|
18
|
+
alpha: z.number().min(0).max(1).optional(),
|
|
19
|
+
})
|
|
20
|
+
.describe('Background color (RGBA 0-1)'),
|
|
21
|
+
}),
|
|
22
|
+
execute: async (args, { log }) => {
|
|
23
|
+
const slidesApi = await getSlidesClient();
|
|
24
|
+
|
|
25
|
+
const requests = args.pageObjectIds.map((pageObjectId) => ({
|
|
26
|
+
updatePageProperties: {
|
|
27
|
+
objectId: pageObjectId,
|
|
28
|
+
pageProperties: {
|
|
29
|
+
pageBackgroundFill: {
|
|
30
|
+
solidFill: {
|
|
31
|
+
color: {
|
|
32
|
+
rgbColor: {
|
|
33
|
+
red: args.backgroundColor.red || 0,
|
|
34
|
+
green: args.backgroundColor.green || 0,
|
|
35
|
+
blue: args.backgroundColor.blue || 0,
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
alpha: args.backgroundColor.alpha ?? 1,
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
fields: 'pageBackgroundFill',
|
|
43
|
+
},
|
|
44
|
+
}));
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
await slidesApi.presentations.batchUpdate({
|
|
48
|
+
presentationId: args.presentationId,
|
|
49
|
+
requestBody: { requests },
|
|
50
|
+
});
|
|
51
|
+
return JSON.stringify({ success: true, message: `Set background for ${args.pageObjectIds.length} slide(s)` });
|
|
52
|
+
} catch (error) {
|
|
53
|
+
log.error(`Error setting background: ${error.message || error}`);
|
|
54
|
+
throw new UserError(`Failed to set background: ${error.message || 'Unknown error'}`);
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
}
|
|
@@ -1,102 +1,102 @@
|
|
|
1
|
-
import { UserError } from 'fastmcp';
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
import { getSlidesClient } from '../../clients.js';
|
|
4
|
-
|
|
5
|
-
export function register(server) {
|
|
6
|
-
// --- Get speaker notes ---
|
|
7
|
-
server.addTool({
|
|
8
|
-
name: 'getSpeakerNotes',
|
|
9
|
-
description: 'Get the speaker notes from a specific slide in a Google Slides presentation.',
|
|
10
|
-
parameters: z.object({
|
|
11
|
-
presentationId: z.string().describe('Presentation ID'),
|
|
12
|
-
slideIndex: z.number().min(0).describe('Slide index (0-based)'),
|
|
13
|
-
}),
|
|
14
|
-
execute: async (args, { log }) => {
|
|
15
|
-
const slidesApi = await getSlidesClient();
|
|
16
|
-
|
|
17
|
-
try {
|
|
18
|
-
const presentation = await slidesApi.presentations.get({ presentationId: args.presentationId });
|
|
19
|
-
const allSlides = presentation.data.slides || [];
|
|
20
|
-
|
|
21
|
-
if (args.slideIndex >= allSlides.length) {
|
|
22
|
-
throw new UserError(`Slide index ${args.slideIndex} out of range (presentation has ${allSlides.length} slides)`);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const slide = allSlides[args.slideIndex];
|
|
26
|
-
const notesObjectId = slide.slideProperties?.notesPage?.notesProperties?.speakerNotesObjectId;
|
|
27
|
-
|
|
28
|
-
if (!notesObjectId) {
|
|
29
|
-
return JSON.stringify({ slideIndex: args.slideIndex, notes: '' });
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const notesPage = slide.slideProperties?.notesPage;
|
|
33
|
-
const notesElement = notesPage?.pageElements?.find((el) => el.objectId === notesObjectId);
|
|
34
|
-
|
|
35
|
-
let notesText = '';
|
|
36
|
-
if (notesElement?.shape?.text?.textElements) {
|
|
37
|
-
for (const te of notesElement.shape.text.textElements) {
|
|
38
|
-
if (te.textRun?.content) notesText += te.textRun.content;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return JSON.stringify({ slideIndex: args.slideIndex, notes: notesText.trim() });
|
|
43
|
-
} catch (error) {
|
|
44
|
-
if (error instanceof UserError) throw error;
|
|
45
|
-
log.error(`Error getting speaker notes: ${error.message || error}`);
|
|
46
|
-
throw new UserError(`Failed to get speaker notes: ${error.message || 'Unknown error'}`);
|
|
47
|
-
}
|
|
48
|
-
},
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
// --- Update speaker notes ---
|
|
52
|
-
server.addTool({
|
|
53
|
-
name: 'updateSpeakerNotes',
|
|
54
|
-
description: 'Update the speaker notes for a specific slide in a Google Slides presentation.',
|
|
55
|
-
parameters: z.object({
|
|
56
|
-
presentationId: z.string().describe('Presentation ID'),
|
|
57
|
-
slideIndex: z.number().min(0).describe('Slide index (0-based)'),
|
|
58
|
-
notes: z.string().describe('Speaker notes content'),
|
|
59
|
-
}),
|
|
60
|
-
execute: async (args, { log }) => {
|
|
61
|
-
const slidesApi = await getSlidesClient();
|
|
62
|
-
|
|
63
|
-
try {
|
|
64
|
-
const presentation = await slidesApi.presentations.get({ presentationId: args.presentationId });
|
|
65
|
-
const allSlides = presentation.data.slides || [];
|
|
66
|
-
|
|
67
|
-
if (args.slideIndex >= allSlides.length) {
|
|
68
|
-
throw new UserError(`Slide index ${args.slideIndex} out of range (presentation has ${allSlides.length} slides)`);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const slide = allSlides[args.slideIndex];
|
|
72
|
-
const notesObjectId = slide.slideProperties?.notesPage?.notesProperties?.speakerNotesObjectId;
|
|
73
|
-
|
|
74
|
-
if (!notesObjectId) {
|
|
75
|
-
throw new UserError('This slide does not have a speaker notes object.');
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// Check if there's existing text to delete first
|
|
79
|
-
const notesPage = slide.slideProperties?.notesPage;
|
|
80
|
-
const notesElement = notesPage?.pageElements?.find((el) => el.objectId === notesObjectId);
|
|
81
|
-
const hasExistingText = notesElement?.shape?.text?.textElements?.some((el) => el.textRun?.content);
|
|
82
|
-
|
|
83
|
-
const requests = [];
|
|
84
|
-
if (hasExistingText) {
|
|
85
|
-
requests.push({ deleteText: { objectId: notesObjectId, textRange: { type: 'ALL' } } });
|
|
86
|
-
}
|
|
87
|
-
requests.push({ insertText: { objectId: notesObjectId, text: args.notes, insertionIndex: 0 } });
|
|
88
|
-
|
|
89
|
-
await slidesApi.presentations.batchUpdate({
|
|
90
|
-
presentationId: args.presentationId,
|
|
91
|
-
requestBody: { requests },
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
return JSON.stringify({ success: true, message: `Updated speaker notes for slide ${args.slideIndex}` });
|
|
95
|
-
} catch (error) {
|
|
96
|
-
if (error instanceof UserError) throw error;
|
|
97
|
-
log.error(`Error updating speaker notes: ${error.message || error}`);
|
|
98
|
-
throw new UserError(`Failed to update speaker notes: ${error.message || 'Unknown error'}`);
|
|
99
|
-
}
|
|
100
|
-
},
|
|
101
|
-
});
|
|
102
|
-
}
|
|
1
|
+
import { UserError } from 'fastmcp';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { getSlidesClient } from '../../clients.js';
|
|
4
|
+
|
|
5
|
+
export function register(server) {
|
|
6
|
+
// --- Get speaker notes ---
|
|
7
|
+
server.addTool({
|
|
8
|
+
name: 'getSpeakerNotes',
|
|
9
|
+
description: 'Get the speaker notes from a specific slide in a Google Slides presentation.',
|
|
10
|
+
parameters: z.object({
|
|
11
|
+
presentationId: z.string().describe('Presentation ID'),
|
|
12
|
+
slideIndex: z.number().min(0).describe('Slide index (0-based)'),
|
|
13
|
+
}),
|
|
14
|
+
execute: async (args, { log }) => {
|
|
15
|
+
const slidesApi = await getSlidesClient();
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
const presentation = await slidesApi.presentations.get({ presentationId: args.presentationId });
|
|
19
|
+
const allSlides = presentation.data.slides || [];
|
|
20
|
+
|
|
21
|
+
if (args.slideIndex >= allSlides.length) {
|
|
22
|
+
throw new UserError(`Slide index ${args.slideIndex} out of range (presentation has ${allSlides.length} slides)`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const slide = allSlides[args.slideIndex];
|
|
26
|
+
const notesObjectId = slide.slideProperties?.notesPage?.notesProperties?.speakerNotesObjectId;
|
|
27
|
+
|
|
28
|
+
if (!notesObjectId) {
|
|
29
|
+
return JSON.stringify({ slideIndex: args.slideIndex, notes: '' });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const notesPage = slide.slideProperties?.notesPage;
|
|
33
|
+
const notesElement = notesPage?.pageElements?.find((el) => el.objectId === notesObjectId);
|
|
34
|
+
|
|
35
|
+
let notesText = '';
|
|
36
|
+
if (notesElement?.shape?.text?.textElements) {
|
|
37
|
+
for (const te of notesElement.shape.text.textElements) {
|
|
38
|
+
if (te.textRun?.content) notesText += te.textRun.content;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return JSON.stringify({ slideIndex: args.slideIndex, notes: notesText.trim() });
|
|
43
|
+
} catch (error) {
|
|
44
|
+
if (error instanceof UserError) throw error;
|
|
45
|
+
log.error(`Error getting speaker notes: ${error.message || error}`);
|
|
46
|
+
throw new UserError(`Failed to get speaker notes: ${error.message || 'Unknown error'}`);
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// --- Update speaker notes ---
|
|
52
|
+
server.addTool({
|
|
53
|
+
name: 'updateSpeakerNotes',
|
|
54
|
+
description: 'Update the speaker notes for a specific slide in a Google Slides presentation.',
|
|
55
|
+
parameters: z.object({
|
|
56
|
+
presentationId: z.string().describe('Presentation ID'),
|
|
57
|
+
slideIndex: z.number().min(0).describe('Slide index (0-based)'),
|
|
58
|
+
notes: z.string().describe('Speaker notes content'),
|
|
59
|
+
}),
|
|
60
|
+
execute: async (args, { log }) => {
|
|
61
|
+
const slidesApi = await getSlidesClient();
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
const presentation = await slidesApi.presentations.get({ presentationId: args.presentationId });
|
|
65
|
+
const allSlides = presentation.data.slides || [];
|
|
66
|
+
|
|
67
|
+
if (args.slideIndex >= allSlides.length) {
|
|
68
|
+
throw new UserError(`Slide index ${args.slideIndex} out of range (presentation has ${allSlides.length} slides)`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const slide = allSlides[args.slideIndex];
|
|
72
|
+
const notesObjectId = slide.slideProperties?.notesPage?.notesProperties?.speakerNotesObjectId;
|
|
73
|
+
|
|
74
|
+
if (!notesObjectId) {
|
|
75
|
+
throw new UserError('This slide does not have a speaker notes object.');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Check if there's existing text to delete first
|
|
79
|
+
const notesPage = slide.slideProperties?.notesPage;
|
|
80
|
+
const notesElement = notesPage?.pageElements?.find((el) => el.objectId === notesObjectId);
|
|
81
|
+
const hasExistingText = notesElement?.shape?.text?.textElements?.some((el) => el.textRun?.content);
|
|
82
|
+
|
|
83
|
+
const requests = [];
|
|
84
|
+
if (hasExistingText) {
|
|
85
|
+
requests.push({ deleteText: { objectId: notesObjectId, textRange: { type: 'ALL' } } });
|
|
86
|
+
}
|
|
87
|
+
requests.push({ insertText: { objectId: notesObjectId, text: args.notes, insertionIndex: 0 } });
|
|
88
|
+
|
|
89
|
+
await slidesApi.presentations.batchUpdate({
|
|
90
|
+
presentationId: args.presentationId,
|
|
91
|
+
requestBody: { requests },
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
return JSON.stringify({ success: true, message: `Updated speaker notes for slide ${args.slideIndex}` });
|
|
95
|
+
} catch (error) {
|
|
96
|
+
if (error instanceof UserError) throw error;
|
|
97
|
+
log.error(`Error updating speaker notes: ${error.message || error}`);
|
|
98
|
+
throw new UserError(`Failed to update speaker notes: ${error.message || 'Unknown error'}`);
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
}
|