@piotr-agier/google-drive-mcp 1.4.0 → 1.5.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 CHANGED
@@ -569,9 +569,41 @@ Add the server to your Claude Desktop configuration:
569
569
  - `valueInputOption`: `RAW` or `USER_ENTERED` (optional, default: USER_ENTERED)
570
570
 
571
571
  - **addSpreadsheetSheet** - Add a new sheet/tab to an existing spreadsheet
572
+ - **addSheet** - Alias for `addSpreadsheetSheet`
572
573
  - `spreadsheetId`: Spreadsheet ID
573
574
  - `sheetTitle`: Title for the new sheet
574
575
 
576
+ - **listSheets** - List tabs/sheets in a spreadsheet
577
+ - `spreadsheetId`: Spreadsheet ID
578
+
579
+ - **renameSheet** - Rename a sheet/tab by `sheetId`
580
+ - `spreadsheetId`: Spreadsheet ID
581
+ - `sheetId`: Sheet ID
582
+ - `newTitle`: New title
583
+
584
+ - **deleteSheet** - Delete a sheet/tab by `sheetId`
585
+ - `spreadsheetId`: Spreadsheet ID
586
+ - `sheetId`: Sheet ID
587
+
588
+ - **addDataValidation** - Add data validation rules to a range
589
+ - `spreadsheetId`: Spreadsheet ID
590
+ - `range`: A1 range (e.g., `Sheet1!A1:A10`)
591
+ - `conditionType`: `ONE_OF_LIST`, `NUMBER_GREATER`, `NUMBER_LESS`, or `TEXT_CONTAINS`
592
+ - `values`: Condition values (e.g. list items, threshold)
593
+ - `strict`: Reject invalid values (optional, default: `true`)
594
+ - `showCustomUi`: Show dropdown/custom UI (optional, default: `true`)
595
+
596
+ - **protectRange** - Protect a range in a spreadsheet
597
+ - `spreadsheetId`: Spreadsheet ID
598
+ - `range`: A1 range
599
+ - `description`: Protection description (optional)
600
+ - `warningOnly`: Warn instead of enforce (optional, default: `false`)
601
+
602
+ - **addNamedRange** - Create a named range
603
+ - `spreadsheetId`: Spreadsheet ID
604
+ - `name`: Named range name
605
+ - `range`: A1 range
606
+
575
607
  - **listGoogleSheets** - List Google Spreadsheets with optional filtering
576
608
  - `query`: Search query to filter by name or content (optional)
577
609
  - `maxResults`: Maximum spreadsheets to return, 1-100 (optional, default: 20)
@@ -694,6 +726,32 @@ Add the server to your Claude Desktop configuration:
694
726
  - `slideIndex`: Slide index (0-based)
695
727
  - `notes`: The speaker notes content to set
696
728
 
729
+ #### Slide Operations and Templating
730
+ - **deleteGoogleSlide** - Delete a slide by object ID
731
+ - `presentationId`: Presentation ID
732
+ - `slideObjectId`: Slide object ID
733
+
734
+ - **duplicateSlide** - Duplicate a slide by object ID
735
+ - `presentationId`: Presentation ID
736
+ - `slideObjectId`: Slide object ID
737
+
738
+ - **reorderSlides** - Reorder slides by object IDs and insertion index
739
+ - `presentationId`: Presentation ID
740
+ - `slideObjectIds`: Array of slide object IDs to move
741
+ - `insertionIndex`: Target insertion index
742
+
743
+ - **replaceAllTextInSlides** - Replace text across a presentation
744
+ - `presentationId`: Presentation ID
745
+ - `containsText`: Text to find
746
+ - `replaceText`: Replacement text
747
+ - `matchCase`: Match case (optional, default: `false`)
748
+
749
+ - **exportSlideThumbnail** - Export a slide thumbnail URL (PNG/JPEG, SMALL/MEDIUM/LARGE)
750
+ - `presentationId`: Presentation ID
751
+ - `slideObjectId`: Slide object ID
752
+ - `mimeType`: `PNG` or `JPEG` (optional, default: `PNG`)
753
+ - `size`: `SMALL`, `MEDIUM`, or `LARGE` (optional, default: `LARGE`)
754
+
697
755
  ### Google Calendar
698
756
  - **listCalendars** - List all accessible Google Calendars
699
757
  - `showHidden`: Include hidden calendars (optional, default: false)
package/dist/index.js CHANGED
@@ -1589,7 +1589,7 @@ Link: ${response.data.webViewLink}` }],
1589
1589
  const data = validation.data;
1590
1590
  const response = await ctx.getDrive().permissions.list({
1591
1591
  fileId: data.fileId,
1592
- fields: "permissions(id,type,role,emailAddress,domain,displayName)",
1592
+ fields: "permissions(id,type,role,emailAddress,domain,displayName,permissionDetails(inherited,inheritedFrom,permissionType))",
1593
1593
  supportsAllDrives: true
1594
1594
  });
1595
1595
  const permissions = response.data.permissions || [];
@@ -1598,7 +1598,10 @@ Link: ${response.data.webViewLink}` }],
1598
1598
  }
1599
1599
  const lines = permissions.map((p) => {
1600
1600
  const who = p.emailAddress || p.domain || p.displayName || p.type || "unknown";
1601
- return `- ${p.id}: ${who} (${p.type}) => ${p.role}`;
1601
+ const inherited = p.permissionDetails?.some((d) => d.inherited === true) ?? false;
1602
+ const inheritedFrom = p.permissionDetails?.find((d) => d.inheritedFrom)?.inheritedFrom;
1603
+ const inheritedMarker = inherited ? ` [inherited${inheritedFrom ? ` from ${inheritedFrom}` : ""}]` : " [direct]";
1604
+ return `- ${p.id}: ${who} (${p.type}) => ${p.role}${inheritedMarker}`;
1602
1605
  });
1603
1606
  return { content: [{ type: "text", text: `Permissions for file ${data.fileId}:
1604
1607
  ${lines.join("\n")}` }], isError: false };
@@ -3776,6 +3779,41 @@ var AddSpreadsheetSheetSchema = z3.object({
3776
3779
  spreadsheetId: z3.string().min(1, "Spreadsheet ID is required"),
3777
3780
  sheetTitle: z3.string().min(1, "Sheet title is required")
3778
3781
  });
3782
+ var AddSheetSchema = z3.object({
3783
+ spreadsheetId: z3.string().min(1, "Spreadsheet ID is required"),
3784
+ title: z3.string().min(1, "Sheet title is required")
3785
+ });
3786
+ var ListSheetsSchema = z3.object({
3787
+ spreadsheetId: z3.string().min(1, "Spreadsheet ID is required")
3788
+ });
3789
+ var RenameSheetSchema = z3.object({
3790
+ spreadsheetId: z3.string().min(1, "Spreadsheet ID is required"),
3791
+ sheetId: z3.number().int(),
3792
+ newTitle: z3.string().min(1, "New title is required")
3793
+ });
3794
+ var DeleteSheetSchema = z3.object({
3795
+ spreadsheetId: z3.string().min(1, "Spreadsheet ID is required"),
3796
+ sheetId: z3.number().int()
3797
+ });
3798
+ var AddDataValidationSchema = z3.object({
3799
+ spreadsheetId: z3.string().min(1, "Spreadsheet ID is required"),
3800
+ range: z3.string().min(1, "Range is required"),
3801
+ conditionType: z3.enum(["ONE_OF_LIST", "NUMBER_GREATER", "NUMBER_LESS", "TEXT_CONTAINS"]),
3802
+ values: z3.array(z3.string()).min(1, "At least one value is required"),
3803
+ strict: z3.boolean().optional().default(true),
3804
+ showCustomUi: z3.boolean().optional().default(true)
3805
+ });
3806
+ var ProtectRangeSchema = z3.object({
3807
+ spreadsheetId: z3.string().min(1, "Spreadsheet ID is required"),
3808
+ range: z3.string().min(1, "Range is required"),
3809
+ description: z3.string().optional(),
3810
+ warningOnly: z3.boolean().optional().default(false)
3811
+ });
3812
+ var AddNamedRangeSchema = z3.object({
3813
+ spreadsheetId: z3.string().min(1, "Spreadsheet ID is required"),
3814
+ name: z3.string().min(1, "Name is required"),
3815
+ range: z3.string().min(1, "Range is required")
3816
+ });
3779
3817
  var ListGoogleSheetsSchema = z3.object({
3780
3818
  maxResults: z3.number().int().min(1).max(100).optional().default(20),
3781
3819
  query: z3.string().optional(),
@@ -4061,6 +4099,97 @@ var toolDefinitions3 = [
4061
4099
  required: ["spreadsheetId", "sheetTitle"]
4062
4100
  }
4063
4101
  },
4102
+ {
4103
+ name: "addSheet",
4104
+ description: "Alias for addSpreadsheetSheet (adds a new sheet/tab)",
4105
+ inputSchema: {
4106
+ type: "object",
4107
+ properties: {
4108
+ spreadsheetId: { type: "string", description: "Spreadsheet ID" },
4109
+ title: { type: "string", description: "Title for the new sheet/tab" }
4110
+ },
4111
+ required: ["spreadsheetId", "title"]
4112
+ }
4113
+ },
4114
+ {
4115
+ name: "listSheets",
4116
+ description: "List tabs/sheets in a Google Spreadsheet",
4117
+ inputSchema: {
4118
+ type: "object",
4119
+ properties: {
4120
+ spreadsheetId: { type: "string", description: "Spreadsheet ID" }
4121
+ },
4122
+ required: ["spreadsheetId"]
4123
+ }
4124
+ },
4125
+ {
4126
+ name: "renameSheet",
4127
+ description: "Rename a sheet/tab by sheetId",
4128
+ inputSchema: {
4129
+ type: "object",
4130
+ properties: {
4131
+ spreadsheetId: { type: "string", description: "Spreadsheet ID" },
4132
+ sheetId: { type: "number", description: "Sheet ID" },
4133
+ newTitle: { type: "string", description: "New title" }
4134
+ },
4135
+ required: ["spreadsheetId", "sheetId", "newTitle"]
4136
+ }
4137
+ },
4138
+ {
4139
+ name: "deleteSheet",
4140
+ description: "Delete a sheet/tab by sheetId",
4141
+ inputSchema: {
4142
+ type: "object",
4143
+ properties: {
4144
+ spreadsheetId: { type: "string", description: "Spreadsheet ID" },
4145
+ sheetId: { type: "number", description: "Sheet ID" }
4146
+ },
4147
+ required: ["spreadsheetId", "sheetId"]
4148
+ }
4149
+ },
4150
+ {
4151
+ name: "addDataValidation",
4152
+ description: "Add data validation rules to a sheet range",
4153
+ inputSchema: {
4154
+ type: "object",
4155
+ properties: {
4156
+ spreadsheetId: { type: "string", description: "Spreadsheet ID" },
4157
+ range: { type: "string", description: "A1 range" },
4158
+ conditionType: { type: "string", enum: ["ONE_OF_LIST", "NUMBER_GREATER", "NUMBER_LESS", "TEXT_CONTAINS"], description: "Validation condition type" },
4159
+ values: { type: "array", items: { type: "string" }, description: "Condition values (e.g. list items, threshold)" },
4160
+ strict: { type: "boolean", description: "Reject invalid values" },
4161
+ showCustomUi: { type: "boolean", description: "Show dropdown/custom UI" }
4162
+ },
4163
+ required: ["spreadsheetId", "range", "conditionType", "values"]
4164
+ }
4165
+ },
4166
+ {
4167
+ name: "protectRange",
4168
+ description: "Protect a range in a spreadsheet",
4169
+ inputSchema: {
4170
+ type: "object",
4171
+ properties: {
4172
+ spreadsheetId: { type: "string", description: "Spreadsheet ID" },
4173
+ range: { type: "string", description: "A1 range" },
4174
+ description: { type: "string", description: "Protection description" },
4175
+ warningOnly: { type: "boolean", description: "Warn instead of enforce" }
4176
+ },
4177
+ required: ["spreadsheetId", "range"]
4178
+ }
4179
+ },
4180
+ {
4181
+ name: "addNamedRange",
4182
+ description: "Create a named range",
4183
+ inputSchema: {
4184
+ type: "object",
4185
+ properties: {
4186
+ spreadsheetId: { type: "string", description: "Spreadsheet ID" },
4187
+ name: { type: "string", description: "Named range name" },
4188
+ range: { type: "string", description: "A1 range" }
4189
+ },
4190
+ required: ["spreadsheetId", "name", "range"]
4191
+ }
4192
+ },
4064
4193
  {
4065
4194
  name: "listGoogleSheets",
4066
4195
  description: "Lists Google Spreadsheets from your Google Drive with optional filtering",
@@ -4075,6 +4204,19 @@ var toolDefinitions3 = [
4075
4204
  }
4076
4205
  }
4077
4206
  ];
4207
+ async function resolveGridRange(sheetsService, spreadsheetId, range) {
4208
+ const rangeData = await sheetsService.spreadsheets.get({
4209
+ spreadsheetId,
4210
+ ranges: [range],
4211
+ fields: "sheets(properties(sheetId,title))"
4212
+ });
4213
+ const { sheetName, cellRange: a1Range } = parseA1Range(range);
4214
+ const sheet = rangeData.data.sheets?.find((s) => s.properties?.title === sheetName);
4215
+ if (!sheet || sheet.properties?.sheetId === void 0 || sheet.properties?.sheetId === null) {
4216
+ return `Sheet "${sheetName}" not found`;
4217
+ }
4218
+ return convertA1ToGridRange(a1Range, sheet.properties.sheetId);
4219
+ }
4078
4220
  async function handleTool3(toolName, args, ctx) {
4079
4221
  switch (toolName) {
4080
4222
  case "createGoogleSheet": {
@@ -4569,20 +4711,23 @@ ID: ${spreadsheet.data.spreadsheetId}` }],
4569
4711
  isError: false
4570
4712
  };
4571
4713
  }
4572
- case "addSpreadsheetSheet": {
4573
- const validation = AddSpreadsheetSheetSchema.safeParse(args);
4714
+ case "addSpreadsheetSheet":
4715
+ case "addSheet": {
4716
+ const isAlias = toolName === "addSheet";
4717
+ const validation = isAlias ? AddSheetSchema.safeParse(args) : AddSpreadsheetSheetSchema.safeParse(args);
4574
4718
  if (!validation.success) {
4575
4719
  return errorResponse(validation.error.errors[0].message);
4576
4720
  }
4577
- const a = validation.data;
4721
+ const spreadsheetId = validation.data.spreadsheetId;
4722
+ const sheetTitle = isAlias ? validation.data.title : validation.data.sheetTitle;
4578
4723
  const sheets = ctx.google.sheets({ version: "v4", auth: ctx.authClient });
4579
4724
  const response = await sheets.spreadsheets.batchUpdate({
4580
- spreadsheetId: a.spreadsheetId,
4725
+ spreadsheetId,
4581
4726
  requestBody: {
4582
4727
  requests: [{
4583
4728
  addSheet: {
4584
4729
  properties: {
4585
- title: a.sheetTitle
4730
+ title: sheetTitle
4586
4731
  }
4587
4732
  }
4588
4733
  }]
@@ -4597,6 +4742,131 @@ ID: ${spreadsheet.data.spreadsheetId}` }],
4597
4742
  isError: false
4598
4743
  };
4599
4744
  }
4745
+ case "listSheets": {
4746
+ const validation = ListSheetsSchema.safeParse(args);
4747
+ if (!validation.success) return errorResponse(validation.error.errors[0].message);
4748
+ const a = validation.data;
4749
+ const sheets = ctx.google.sheets({ version: "v4", auth: ctx.authClient });
4750
+ const response = await sheets.spreadsheets.get({
4751
+ spreadsheetId: a.spreadsheetId,
4752
+ fields: "sheets.properties(sheetId,title,index,hidden)"
4753
+ });
4754
+ const tabs = response.data.sheets || [];
4755
+ if (tabs.length === 0) {
4756
+ return { content: [{ type: "text", text: "No sheets found." }], isError: false };
4757
+ }
4758
+ const lines = tabs.map((s) => `- ${s.properties?.title} (id: ${s.properties?.sheetId}, index: ${s.properties?.index}${s.properties?.hidden ? ", hidden" : ""})`);
4759
+ return { content: [{ type: "text", text: `Sheets in spreadsheet ${a.spreadsheetId}:
4760
+ ${lines.join("\n")}` }], isError: false };
4761
+ }
4762
+ case "renameSheet": {
4763
+ const validation = RenameSheetSchema.safeParse(args);
4764
+ if (!validation.success) return errorResponse(validation.error.errors[0].message);
4765
+ const a = validation.data;
4766
+ const sheets = ctx.google.sheets({ version: "v4", auth: ctx.authClient });
4767
+ await sheets.spreadsheets.batchUpdate({
4768
+ spreadsheetId: a.spreadsheetId,
4769
+ requestBody: {
4770
+ requests: [{
4771
+ updateSheetProperties: {
4772
+ properties: { sheetId: a.sheetId, title: a.newTitle },
4773
+ fields: "title"
4774
+ }
4775
+ }]
4776
+ }
4777
+ });
4778
+ return { content: [{ type: "text", text: `Renamed sheet ${a.sheetId} to "${a.newTitle}".` }], isError: false };
4779
+ }
4780
+ case "deleteSheet": {
4781
+ const validation = DeleteSheetSchema.safeParse(args);
4782
+ if (!validation.success) return errorResponse(validation.error.errors[0].message);
4783
+ const a = validation.data;
4784
+ const sheets = ctx.google.sheets({ version: "v4", auth: ctx.authClient });
4785
+ await sheets.spreadsheets.batchUpdate({
4786
+ spreadsheetId: a.spreadsheetId,
4787
+ requestBody: {
4788
+ requests: [{
4789
+ deleteSheet: { sheetId: a.sheetId }
4790
+ }]
4791
+ }
4792
+ });
4793
+ return { content: [{ type: "text", text: `Deleted sheet ${a.sheetId}.` }], isError: false };
4794
+ }
4795
+ case "addDataValidation": {
4796
+ const validation = AddDataValidationSchema.safeParse(args);
4797
+ if (!validation.success) return errorResponse(validation.error.errors[0].message);
4798
+ const a = validation.data;
4799
+ const sheets = ctx.google.sheets({ version: "v4", auth: ctx.authClient });
4800
+ const gridRange = await resolveGridRange(sheets, a.spreadsheetId, a.range);
4801
+ if (typeof gridRange === "string") return errorResponse(gridRange);
4802
+ const conditionValues = a.values.map((v) => ({ userEnteredValue: v }));
4803
+ await sheets.spreadsheets.batchUpdate({
4804
+ spreadsheetId: a.spreadsheetId,
4805
+ requestBody: {
4806
+ requests: [{
4807
+ setDataValidation: {
4808
+ range: gridRange,
4809
+ rule: {
4810
+ condition: {
4811
+ type: a.conditionType,
4812
+ values: conditionValues
4813
+ },
4814
+ strict: a.strict,
4815
+ showCustomUi: a.showCustomUi
4816
+ }
4817
+ }
4818
+ }]
4819
+ }
4820
+ });
4821
+ return { content: [{ type: "text", text: `Added data validation (${a.conditionType}) to ${a.range}.` }], isError: false };
4822
+ }
4823
+ case "protectRange": {
4824
+ const validation = ProtectRangeSchema.safeParse(args);
4825
+ if (!validation.success) return errorResponse(validation.error.errors[0].message);
4826
+ const a = validation.data;
4827
+ const sheets = ctx.google.sheets({ version: "v4", auth: ctx.authClient });
4828
+ const gridRange = await resolveGridRange(sheets, a.spreadsheetId, a.range);
4829
+ if (typeof gridRange === "string") return errorResponse(gridRange);
4830
+ const response = await sheets.spreadsheets.batchUpdate({
4831
+ spreadsheetId: a.spreadsheetId,
4832
+ requestBody: {
4833
+ requests: [{
4834
+ addProtectedRange: {
4835
+ protectedRange: {
4836
+ range: gridRange,
4837
+ description: a.description,
4838
+ warningOnly: a.warningOnly
4839
+ }
4840
+ }
4841
+ }]
4842
+ }
4843
+ });
4844
+ const protectedRangeId = response.data.replies?.[0]?.addProtectedRange?.protectedRange?.protectedRangeId;
4845
+ return { content: [{ type: "text", text: `Protected range ${a.range}${protectedRangeId ? ` (id: ${protectedRangeId})` : ""}.` }], isError: false };
4846
+ }
4847
+ case "addNamedRange": {
4848
+ const validation = AddNamedRangeSchema.safeParse(args);
4849
+ if (!validation.success) return errorResponse(validation.error.errors[0].message);
4850
+ const a = validation.data;
4851
+ const sheets = ctx.google.sheets({ version: "v4", auth: ctx.authClient });
4852
+ const gridRange = await resolveGridRange(sheets, a.spreadsheetId, a.range);
4853
+ if (typeof gridRange === "string") return errorResponse(gridRange);
4854
+ const response = await sheets.spreadsheets.batchUpdate({
4855
+ spreadsheetId: a.spreadsheetId,
4856
+ requestBody: {
4857
+ requests: [{
4858
+ addNamedRange: {
4859
+ namedRange: {
4860
+ name: a.name,
4861
+ range: gridRange
4862
+ }
4863
+ }
4864
+ }]
4865
+ }
4866
+ });
4867
+ const namedRangeId = response.data.replies?.[0]?.addNamedRange?.namedRange?.namedRangeId;
4868
+ return { content: [{ type: "text", text: `Added named range "${a.name}" for ${a.range}${namedRangeId ? ` (id: ${namedRangeId})` : ""}.` }], isError: false };
4869
+ }
4600
4870
  case "listGoogleSheets": {
4601
4871
  const validation = ListGoogleSheetsSchema.safeParse(args);
4602
4872
  if (!validation.success) {
@@ -4766,6 +5036,31 @@ var UpdateGoogleSlidesSpeakerNotesSchema = z4.object({
4766
5036
  slideIndex: z4.number().min(0, "Slide index must be non-negative"),
4767
5037
  notes: z4.string()
4768
5038
  });
5039
+ var DeleteGoogleSlideSchema = z4.object({
5040
+ presentationId: z4.string().min(1, "Presentation ID is required"),
5041
+ slideObjectId: z4.string().min(1, "Slide object ID is required")
5042
+ });
5043
+ var DuplicateSlideSchema = z4.object({
5044
+ presentationId: z4.string().min(1, "Presentation ID is required"),
5045
+ slideObjectId: z4.string().min(1, "Slide object ID is required")
5046
+ });
5047
+ var ReorderSlidesSchema = z4.object({
5048
+ presentationId: z4.string().min(1, "Presentation ID is required"),
5049
+ slideObjectIds: z4.array(z4.string().min(1)).min(1, "At least one slide object ID is required"),
5050
+ insertionIndex: z4.number().int().min(0)
5051
+ });
5052
+ var ReplaceAllTextInSlidesSchema = z4.object({
5053
+ presentationId: z4.string().min(1, "Presentation ID is required"),
5054
+ containsText: z4.string().min(1, "containsText is required"),
5055
+ replaceText: z4.string(),
5056
+ matchCase: z4.boolean().optional().default(false)
5057
+ });
5058
+ var ExportSlideThumbnailSchema = z4.object({
5059
+ presentationId: z4.string().min(1, "Presentation ID is required"),
5060
+ slideObjectId: z4.string().min(1, "Slide object ID is required"),
5061
+ mimeType: z4.enum(["PNG", "JPEG"]).optional().default("PNG"),
5062
+ size: z4.enum(["SMALL", "MEDIUM", "LARGE"]).optional().default("LARGE")
5063
+ });
4769
5064
  var toolDefinitions4 = [
4770
5065
  {
4771
5066
  name: "createGoogleSlides",
@@ -5014,6 +5309,71 @@ var toolDefinitions4 = [
5014
5309
  },
5015
5310
  required: ["presentationId", "slideIndex", "notes"]
5016
5311
  }
5312
+ },
5313
+ {
5314
+ name: "deleteGoogleSlide",
5315
+ description: "Delete a slide from a presentation",
5316
+ inputSchema: {
5317
+ type: "object",
5318
+ properties: {
5319
+ presentationId: { type: "string", description: "Presentation ID" },
5320
+ slideObjectId: { type: "string", description: "Slide object ID" }
5321
+ },
5322
+ required: ["presentationId", "slideObjectId"]
5323
+ }
5324
+ },
5325
+ {
5326
+ name: "duplicateSlide",
5327
+ description: "Duplicate a slide in a presentation",
5328
+ inputSchema: {
5329
+ type: "object",
5330
+ properties: {
5331
+ presentationId: { type: "string", description: "Presentation ID" },
5332
+ slideObjectId: { type: "string", description: "Slide object ID" }
5333
+ },
5334
+ required: ["presentationId", "slideObjectId"]
5335
+ }
5336
+ },
5337
+ {
5338
+ name: "reorderSlides",
5339
+ description: "Reorder one or more slides in a presentation",
5340
+ inputSchema: {
5341
+ type: "object",
5342
+ properties: {
5343
+ presentationId: { type: "string", description: "Presentation ID" },
5344
+ slideObjectIds: { type: "array", items: { type: "string" }, description: "Slide object IDs to move" },
5345
+ insertionIndex: { type: "number", description: "Target insertion index" }
5346
+ },
5347
+ required: ["presentationId", "slideObjectIds", "insertionIndex"]
5348
+ }
5349
+ },
5350
+ {
5351
+ name: "replaceAllTextInSlides",
5352
+ description: "Replace all matching text across presentation slides",
5353
+ inputSchema: {
5354
+ type: "object",
5355
+ properties: {
5356
+ presentationId: { type: "string", description: "Presentation ID" },
5357
+ containsText: { type: "string", description: "Text to find" },
5358
+ replaceText: { type: "string", description: "Replacement text" },
5359
+ matchCase: { type: "boolean", description: "Case-sensitive match" }
5360
+ },
5361
+ required: ["presentationId", "containsText", "replaceText"]
5362
+ }
5363
+ },
5364
+ {
5365
+ name: "exportSlideThumbnail",
5366
+ description: "Export a slide thumbnail URL",
5367
+ inputSchema: {
5368
+ type: "object",
5369
+ properties: {
5370
+ presentationId: { type: "string", description: "Presentation ID" },
5371
+ slideObjectId: { type: "string", description: "Slide object ID" },
5372
+ mimeType: { type: "string", enum: ["PNG", "JPEG"], description: "Thumbnail MIME type" },
5373
+ size: { type: "string", enum: ["SMALL", "MEDIUM", "LARGE"], description: "Thumbnail size" }
5374
+ },
5375
+ required: ["presentationId", "slideObjectId"]
5376
+ }
5017
5377
  }
5018
5378
  ];
5019
5379
  async function handleTool4(toolName, args, ctx) {
@@ -5758,6 +6118,103 @@ Slide ${a.slideIndex ?? index} (ID: ${slide.objectId}):
5758
6118
  isError: false
5759
6119
  };
5760
6120
  }
6121
+ case "deleteGoogleSlide": {
6122
+ const validation = DeleteGoogleSlideSchema.safeParse(args);
6123
+ if (!validation.success) return errorResponse(validation.error.errors[0].message);
6124
+ const a = validation.data;
6125
+ const slidesService = ctx.google.slides({ version: "v1", auth: ctx.authClient });
6126
+ await slidesService.presentations.batchUpdate({
6127
+ presentationId: a.presentationId,
6128
+ requestBody: {
6129
+ requests: [{ deleteObject: { objectId: a.slideObjectId } }]
6130
+ }
6131
+ });
6132
+ return {
6133
+ content: [{ type: "text", text: `Deleted slide ${a.slideObjectId}` }],
6134
+ isError: false
6135
+ };
6136
+ }
6137
+ case "duplicateSlide": {
6138
+ const validation = DuplicateSlideSchema.safeParse(args);
6139
+ if (!validation.success) return errorResponse(validation.error.errors[0].message);
6140
+ const a = validation.data;
6141
+ const slidesService = ctx.google.slides({ version: "v1", auth: ctx.authClient });
6142
+ const response = await slidesService.presentations.batchUpdate({
6143
+ presentationId: a.presentationId,
6144
+ requestBody: {
6145
+ requests: [{ duplicateObject: { objectId: a.slideObjectId } }]
6146
+ }
6147
+ });
6148
+ const dupId = response.data.replies?.[0]?.duplicateObject?.objectId;
6149
+ return {
6150
+ content: [{ type: "text", text: `Duplicated slide ${a.slideObjectId}${dupId ? ` -> ${dupId}` : ""}` }],
6151
+ isError: false
6152
+ };
6153
+ }
6154
+ case "reorderSlides": {
6155
+ const validation = ReorderSlidesSchema.safeParse(args);
6156
+ if (!validation.success) return errorResponse(validation.error.errors[0].message);
6157
+ const a = validation.data;
6158
+ const slidesService = ctx.google.slides({ version: "v1", auth: ctx.authClient });
6159
+ await slidesService.presentations.batchUpdate({
6160
+ presentationId: a.presentationId,
6161
+ requestBody: {
6162
+ requests: [{
6163
+ updateSlidesPosition: {
6164
+ slideObjectIds: a.slideObjectIds,
6165
+ insertionIndex: a.insertionIndex
6166
+ }
6167
+ }]
6168
+ }
6169
+ });
6170
+ return {
6171
+ content: [{ type: "text", text: `Reordered ${a.slideObjectIds.length} slide(s) to index ${a.insertionIndex}` }],
6172
+ isError: false
6173
+ };
6174
+ }
6175
+ case "replaceAllTextInSlides": {
6176
+ const validation = ReplaceAllTextInSlidesSchema.safeParse(args);
6177
+ if (!validation.success) return errorResponse(validation.error.errors[0].message);
6178
+ const a = validation.data;
6179
+ const slidesService = ctx.google.slides({ version: "v1", auth: ctx.authClient });
6180
+ const response = await slidesService.presentations.batchUpdate({
6181
+ presentationId: a.presentationId,
6182
+ requestBody: {
6183
+ requests: [{
6184
+ replaceAllText: {
6185
+ containsText: {
6186
+ text: a.containsText,
6187
+ matchCase: a.matchCase
6188
+ },
6189
+ replaceText: a.replaceText
6190
+ }
6191
+ }]
6192
+ }
6193
+ });
6194
+ const count = response.data.replies?.[0]?.replaceAllText?.occurrencesChanged ?? 0;
6195
+ return {
6196
+ content: [{ type: "text", text: `Replaced ${count} occurrence(s) of "${a.containsText}" in slides.` }],
6197
+ isError: false
6198
+ };
6199
+ }
6200
+ case "exportSlideThumbnail": {
6201
+ const validation = ExportSlideThumbnailSchema.safeParse(args);
6202
+ if (!validation.success) return errorResponse(validation.error.errors[0].message);
6203
+ const a = validation.data;
6204
+ const slidesService = ctx.google.slides({ version: "v1", auth: ctx.authClient });
6205
+ const response = await slidesService.presentations.pages.getThumbnail({
6206
+ presentationId: a.presentationId,
6207
+ pageObjectId: a.slideObjectId,
6208
+ "thumbnailProperties.mimeType": a.mimeType,
6209
+ "thumbnailProperties.thumbnailSize": a.size
6210
+ });
6211
+ const url = response.data?.contentUrl;
6212
+ if (!url) return errorResponse("No thumbnail URL returned by Google Slides API.");
6213
+ return {
6214
+ content: [{ type: "text", text: `Slide thumbnail URL (${a.mimeType}, ${a.size}): ${url}` }],
6215
+ isError: false
6216
+ };
6217
+ }
5761
6218
  default:
5762
6219
  return null;
5763
6220
  }