// Google Slides MCP tools
import { z } from 'zod';
import type { SlidesService } from '../services/slides.js';
// ─────────────────────────────────────────────────────────────────────────────
// SCHEMAS
// ─────────────────────────────────────────────────────────────────────────────
const ColorSchema = z.object({
red: z.number().min(0).max(1).optional(),
green: z.number().min(0).max(1).optional(),
blue: z.number().min(0).max(1).optional(),
});
const PositionSchema = z.object({
x: z.number().describe('X position in points'),
y: z.number().describe('Y position in points'),
width: z.number().describe('Width in points'),
height: z.number().describe('Height in points'),
});
export const CreateGoogleSlidesSchema = z.object({
name: z.string().describe('Presentation name'),
slides: z.array(z.object({
title: z.string().describe('Slide title'),
body: z.string().describe('Slide body content'),
})).describe('Array of slides with title and body'),
parentFolder: z.string().optional().describe('Parent folder ID or path'),
});
export const GetGoogleSlidesContentSchema = z.object({
presentationId: z.string().describe('Presentation ID'),
});
export const UpdateGoogleSlidesSchema = z.object({
presentationId: z.string().describe('Presentation ID'),
slideId: z.string().describe('Slide object ID'),
title: z.string().optional().describe('New title'),
body: z.string().optional().describe('New body content'),
});
export const CreateGoogleSlidesTextBoxSchema = z.object({
presentationId: z.string().describe('Presentation ID'),
slideId: z.string().describe('Slide object ID'),
text: z.string().describe('Text content'),
position: PositionSchema.describe('Position and size'),
});
export const CreateGoogleSlidesShapeSchema = z.object({
presentationId: z.string().describe('Presentation ID'),
slideId: z.string().describe('Slide object ID'),
shapeType: z.string().describe('Shape type (RECTANGLE, ELLIPSE, etc)'),
position: PositionSchema.describe('Position and size'),
fillColor: ColorSchema.optional().describe('Fill color (RGB 0-1)'),
outlineColor: ColorSchema.optional().describe('Outline color (RGB 0-1)'),
});
export const FormatGoogleSlidesTextSchema = z.object({
presentationId: z.string().describe('Presentation ID'),
objectId: z.string().describe('Object ID (text box or shape)'),
bold: z.boolean().optional(),
italic: z.boolean().optional(),
underline: z.boolean().optional(),
fontSize: z.number().optional(),
foregroundColor: ColorSchema.optional(),
});
export const SetGoogleSlidesBackgroundSchema = z.object({
presentationId: z.string().describe('Presentation ID'),
slideId: z.string().describe('Slide object ID'),
color: ColorSchema.describe('Background color (RGB 0-1)'),
});
// ─────────────────────────────────────────────────────────────────────────────
// TOOL DEFINITIONS
// ─────────────────────────────────────────────────────────────────────────────
export const slidesTools = [
{
name: 'createGoogleSlides',
description: 'Create a new Google Slides presentation with slides',
inputSchema: {
type: 'object' as const,
properties: {
name: { type: 'string', description: 'Presentation name' },
slides: {
type: 'array',
description: 'Array of slides',
items: {
type: 'object',
properties: {
title: { type: 'string', description: 'Slide title' },
body: { type: 'string', description: 'Slide body content' },
},
required: ['title', 'body'],
},
},
parentFolder: { type: 'string', description: 'Parent folder ID or path' },
},
required: ['name', 'slides'],
},
},
{
name: 'getGoogleSlidesContent',
description: 'Get content of a Google Slides presentation',
inputSchema: {
type: 'object' as const,
properties: {
presentationId: { type: 'string', description: 'Presentation ID' },
},
required: ['presentationId'],
},
},
{
name: 'updateGoogleSlides',
description: 'Update content of a specific slide',
inputSchema: {
type: 'object' as const,
properties: {
presentationId: { type: 'string', description: 'Presentation ID' },
slideId: { type: 'string', description: 'Slide object ID' },
title: { type: 'string', description: 'New title' },
body: { type: 'string', description: 'New body content' },
},
required: ['presentationId', 'slideId'],
},
},
{
name: 'createGoogleSlidesTextBox',
description: 'Create a text box on a slide',
inputSchema: {
type: 'object' as const,
properties: {
presentationId: { type: 'string', description: 'Presentation ID' },
slideId: { type: 'string', description: 'Slide object ID' },
text: { type: 'string', description: 'Text content' },
position: {
type: 'object',
description: 'Position and size in points',
properties: {
x: { type: 'number' },
y: { type: 'number' },
width: { type: 'number' },
height: { type: 'number' },
},
required: ['x', 'y', 'width', 'height'],
},
},
required: ['presentationId', 'slideId', 'text', 'position'],
},
},
{
name: 'createGoogleSlidesShape',
description: 'Create a shape on a slide',
inputSchema: {
type: 'object' as const,
properties: {
presentationId: { type: 'string', description: 'Presentation ID' },
slideId: { type: 'string', description: 'Slide object ID' },
shapeType: { type: 'string', description: 'Shape type (RECTANGLE, ELLIPSE, TRIANGLE, etc)' },
position: {
type: 'object',
properties: {
x: { type: 'number' },
y: { type: 'number' },
width: { type: 'number' },
height: { type: 'number' },
},
required: ['x', 'y', 'width', 'height'],
},
fillColor: { type: 'object', properties: { red: { type: 'number' }, green: { type: 'number' }, blue: { type: 'number' } } },
outlineColor: { type: 'object', properties: { red: { type: 'number' }, green: { type: 'number' }, blue: { type: 'number' } } },
},
required: ['presentationId', 'slideId', 'shapeType', 'position'],
},
},
{
name: 'formatGoogleSlidesText',
description: 'Format text in a shape or text box',
inputSchema: {
type: 'object' as const,
properties: {
presentationId: { type: 'string', description: 'Presentation ID' },
objectId: { type: 'string', description: 'Object ID' },
bold: { type: 'boolean' },
italic: { type: 'boolean' },
underline: { type: 'boolean' },
fontSize: { type: 'number' },
foregroundColor: { type: 'object', properties: { red: { type: 'number' }, green: { type: 'number' }, blue: { type: 'number' } } },
},
required: ['presentationId', 'objectId'],
},
},
{
name: 'setGoogleSlidesBackground',
description: 'Set background color of a slide',
inputSchema: {
type: 'object' as const,
properties: {
presentationId: { type: 'string', description: 'Presentation ID' },
slideId: { type: 'string', description: 'Slide object ID' },
color: {
type: 'object',
description: 'Background color (RGB 0-1)',
properties: { red: { type: 'number' }, green: { type: 'number' }, blue: { type: 'number' } },
},
},
required: ['presentationId', 'slideId', 'color'],
},
},
];
// ─────────────────────────────────────────────────────────────────────────────
// HANDLERS
// ─────────────────────────────────────────────────────────────────────────────
export function createSlidesHandlers(slidesService: SlidesService) {
return {
createGoogleSlides: async (args: z.infer<typeof CreateGoogleSlidesSchema>) => {
const params = CreateGoogleSlidesSchema.parse(args);
return slidesService.createPresentation(params.name, params.slides, params.parentFolder);
},
getGoogleSlidesContent: async (args: z.infer<typeof GetGoogleSlidesContentSchema>) => {
const params = GetGoogleSlidesContentSchema.parse(args);
return slidesService.getPresentationContent(params.presentationId);
},
updateGoogleSlides: async (args: z.infer<typeof UpdateGoogleSlidesSchema>) => {
const params = UpdateGoogleSlidesSchema.parse(args);
await slidesService.updateSlide(params.presentationId, params.slideId, {
title: params.title,
body: params.body,
});
return { success: true };
},
createGoogleSlidesTextBox: async (args: z.infer<typeof CreateGoogleSlidesTextBoxSchema>) => {
const params = CreateGoogleSlidesTextBoxSchema.parse(args);
return slidesService.createTextBox(
params.presentationId,
params.slideId,
params.text,
params.position
);
},
createGoogleSlidesShape: async (args: z.infer<typeof CreateGoogleSlidesShapeSchema>) => {
const params = CreateGoogleSlidesShapeSchema.parse(args);
return slidesService.createShape(
params.presentationId,
params.slideId,
params.shapeType,
params.position,
{
fillColor: params.fillColor,
outlineColor: params.outlineColor,
}
);
},
formatGoogleSlidesText: async (args: z.infer<typeof FormatGoogleSlidesTextSchema>) => {
const params = FormatGoogleSlidesTextSchema.parse(args);
await slidesService.formatText(params.presentationId, params.objectId, {
bold: params.bold,
italic: params.italic,
underline: params.underline,
fontSize: params.fontSize,
foregroundColor: params.foregroundColor,
});
return { success: true };
},
setGoogleSlidesBackground: async (args: z.infer<typeof SetGoogleSlidesBackgroundSchema>) => {
const params = SetGoogleSlidesBackgroundSchema.parse(args);
await slidesService.setSlideBackground(params.presentationId, params.slideId, params.color);
return { success: true };
},
};
}