Skip to content

Commit e4740a6

Browse files
committed
feat(slides): add slides.create, slides.batchUpdate, and slides.createFromJson
slides.createFromJson is a high-level agent-friendly tool that accepts a JSON blueprint and translates it into Slides API batch requests in one call. Avoids requiring agents to know the raw Slides API request format. Blueprint format: { "slides": [{ "elements": [...] }, ...] } — multiple slides { "elements": [...] } — single slide (shorthand) Each element supports: type (text|shape|image), position (x,y,w,h in PT on a 720x405 grid), content, shape_type, url, layer (z-index), and a style object with: size, bold, italic, underline, strikethrough, font_family, align, vertical_align, color, bg_color, border_color, border_weight, no_border, bold_phrases, bold_until, links. Hardcoded Google Sans replaced with configurable font_family (defaults to Arial for cross-platform compatibility). Layers are sorted before rendering: shapes → images → text, then by layer value, so background shapes reliably render beneath text. Also adds: - slides.create: create a blank presentation (returns ID + URL) - slides.batchUpdate: execute raw Slides API request arrays (escape hatch for operations not covered by createFromJson) Complements #237 which adds further write tools (addSlide, insertText, replaceText, deleteSlide). The batchUpdate and create implementations here are intentionally minimal; #237's versions may supersede them on merge.
1 parent 3ab1b79 commit e4740a6

2 files changed

Lines changed: 617 additions & 0 deletions

File tree

workspace-server/src/index.ts

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,157 @@ async function main() {
458458
slidesService.getSlideThumbnail,
459459
);
460460

461+
server.registerTool(
462+
'slides.create',
463+
{
464+
description:
465+
'Creates a new blank Google Slides presentation. Returns the presentation ID and URL.',
466+
inputSchema: {
467+
title: z.string().describe('The title for the new presentation.'),
468+
},
469+
},
470+
slidesService.create,
471+
);
472+
473+
server.registerTool(
474+
'slides.batchUpdate',
475+
{
476+
description:
477+
'Executes a batch of updates (create, modify, delete) on a Google Slides presentation. Takes an array of raw Slides API request objects.',
478+
inputSchema: {
479+
presentationId: z
480+
.string()
481+
.describe('The ID or URL of the presentation to modify.'),
482+
requests: z
483+
.string()
484+
.describe(
485+
'JSON string of an array of Slides API request objects (e.g., [{"createSlide":{}}, {"createShape":{...}}]). Will be parsed server-side.',
486+
),
487+
},
488+
},
489+
slidesService.batchUpdate,
490+
);
491+
492+
// Shared element schema for createFromJson
493+
const slideElementSchema = z.object({
494+
type: z.enum(['text', 'shape', 'image']).describe('Element type.'),
495+
content: z
496+
.string()
497+
.optional()
498+
.describe('Text content (for text elements).'),
499+
shape_type: z
500+
.string()
501+
.optional()
502+
.describe(
503+
'Shape type (e.g., RECTANGLE, RIGHT_ARROW, TEXT_BOX). Default: RECTANGLE.',
504+
),
505+
url: z.string().optional().describe('Image URL (for image elements).'),
506+
layer: z
507+
.number()
508+
.optional()
509+
.describe(
510+
'Z-index layer for rendering order. Lower layers render first.',
511+
),
512+
position: z
513+
.object({
514+
x: z.number().describe('X position in points.'),
515+
y: z.number().describe('Y position in points.'),
516+
w: z.number().describe('Width in points.'),
517+
h: z.number().describe('Height in points.'),
518+
})
519+
.describe('Position and size on a 720x405 point grid.'),
520+
style: z
521+
.object({
522+
size: z.number().optional().describe('Font size in points.'),
523+
bold: z.boolean().optional().describe('Bold text.'),
524+
italic: z.boolean().optional().describe('Italic text.'),
525+
align: z
526+
.enum(['START', 'CENTER', 'END'])
527+
.optional()
528+
.describe('Horizontal text alignment.'),
529+
vertical_align: z
530+
.enum(['TOP', 'MIDDLE', 'BOTTOM'])
531+
.optional()
532+
.describe('Vertical content alignment.'),
533+
color: z
534+
.object({
535+
red: z.number(),
536+
green: z.number(),
537+
blue: z.number(),
538+
})
539+
.optional()
540+
.describe('Text color (RGB 0-1).'),
541+
bg_color: z
542+
.object({
543+
red: z.number(),
544+
green: z.number(),
545+
blue: z.number(),
546+
})
547+
.optional()
548+
.describe('Shape background color (RGB 0-1).'),
549+
border_color: z
550+
.object({
551+
red: z.number(),
552+
green: z.number(),
553+
blue: z.number(),
554+
})
555+
.optional()
556+
.describe('Shape border color (RGB 0-1).'),
557+
border_weight: z
558+
.number()
559+
.optional()
560+
.describe('Border weight in points.'),
561+
no_border: z
562+
.boolean()
563+
.optional()
564+
.describe('Remove border from shape.'),
565+
font_family: z
566+
.string()
567+
.optional()
568+
.describe('Font family name (e.g. "Arial", "Roboto"). Defaults to "Arial".'),
569+
underline: z.boolean().optional().describe('Underline text.'),
570+
strikethrough: z.boolean().optional().describe('Strikethrough text.'),
571+
bold_phrases: z
572+
.array(z.string())
573+
.optional()
574+
.describe('Phrases within content to bold.'),
575+
bold_until: z
576+
.number()
577+
.optional()
578+
.describe('Bold text from start to this character index.'),
579+
links: z
580+
.array(
581+
z.object({
582+
text: z.string().describe('Link text to find in content.'),
583+
url: z.string().describe('URL to link to.'),
584+
}),
585+
)
586+
.optional()
587+
.describe('Hyperlinks to apply to matching text.'),
588+
})
589+
.optional()
590+
.describe('Styling options for the element.'),
591+
});
592+
593+
server.registerTool(
594+
'slides.createFromJson',
595+
{
596+
description:
597+
'Creates one or more slides in a presentation from a JSON blueprint. Supports two formats: (1) { "slides": [{ "elements": [...] }, ...] } for multiple slides, or (2) { "elements": [...] } for a single slide. Elements are positioned on a 720x405 point grid. Each element has: type ("text"|"shape"|"image"), position ({x,y,w,h} in points), optional content (string), optional shape_type (e.g. "RECTANGLE","TEXT_BOX"), optional url (for images), optional layer (z-index), optional style ({size,bold,italic,underline,strikethrough,align,vertical_align,color,bg_color,border_color,border_weight,no_border,font_family,bold_phrases,bold_until,links}). Colors are RGB 0-1 objects ({red,green,blue}).',
598+
inputSchema: {
599+
presentationId: z
600+
.string()
601+
.describe('The ID or URL of the presentation to add slides to.'),
602+
slideJson: z
603+
.string()
604+
.describe(
605+
'JSON string of the slide blueprint. Use {"slides":[{"elements":[...]},...]} for multiple slides or {"elements":[...]} for one slide. Will be parsed server-side.',
606+
),
607+
},
608+
},
609+
slidesService.createFromJson,
610+
);
611+
461612
// Sheets tools
462613
registerTool(
463614
'sheets.getText',

0 commit comments

Comments
 (0)