Skip to main content
Glama
index.ts9.59 kB
#!/usr/bin/env node import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, ToolSchema, CallToolResult, } from '@modelcontextprotocol/sdk/types.js'; import * as dotenv from 'dotenv'; import { GoogleSlidesAuth, GoogleSlidesConfig } from './auth.js'; import { GoogleSlidesService } from './slides.js'; // Load environment variables dotenv.config(); interface McpError extends Error { code?: string; } class GoogleSlidesMCPServer { private server: Server; private auth: GoogleSlidesAuth; private slidesService: GoogleSlidesService; constructor() { this.server = new Server( { name: 'google-slides-mcp-server', version: '1.0.0', }, { capabilities: { tools: {}, }, } ); // Initialize Google Slides authentication const config: GoogleSlidesConfig = { clientId: process.env.GOOGLE_CLIENT_ID || '', clientSecret: process.env.GOOGLE_CLIENT_SECRET || '', redirectUri: process.env.GOOGLE_REDIRECT_URI || 'http://localhost:3000/oauth2callback', }; this.auth = new GoogleSlidesAuth(config); this.slidesService = new GoogleSlidesService(this.auth); this.setupHandlers(); } private setupHandlers(): void { // List available tools this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: 'create_slide', description: 'Create a new slide in a Google Slides presentation', inputSchema: { type: 'object', properties: { presentationId: { type: 'string', description: 'The ID of the Google Slides presentation', }, insertionIndex: { type: 'number', description: 'Position where to insert the slide (optional, defaults to 0)', }, }, required: ['presentationId'], }, }, { name: 'add_rectangle', description: 'Add a rectangle to a slide with 20% of slide dimensions', inputSchema: { type: 'object', properties: { presentationId: { type: 'string', description: 'The ID of the Google Slides presentation', }, slideId: { type: 'string', description: 'The ID of the slide to add the rectangle to', }, x: { type: 'number', description: 'X position of the rectangle (optional, defaults to center)', }, y: { type: 'number', description: 'Y position of the rectangle (optional, defaults to center)', }, width: { type: 'number', description: 'Width of the rectangle (optional, defaults to 20% of slide width)', }, height: { type: 'number', description: 'Height of the rectangle (optional, defaults to 20% of slide height)', }, }, required: ['presentationId', 'slideId'], }, }, { name: 'get_presentation_info', description: 'Get information about a Google Slides presentation', inputSchema: { type: 'object', properties: { presentationId: { type: 'string', description: 'The ID of the Google Slides presentation', }, }, required: ['presentationId'], }, }, { name: 'list_slides', description: 'List all slides in a Google Slides presentation', inputSchema: { type: 'object', properties: { presentationId: { type: 'string', description: 'The ID of the Google Slides presentation', }, }, required: ['presentationId'], }, }, { name: 'get_auth_url', description: 'Get the OAuth2 authorization URL for Google Slides access', inputSchema: { type: 'object', properties: {}, }, }, { name: 'authenticate', description: 'Complete OAuth2 authentication with authorization code', inputSchema: { type: 'object', properties: { code: { type: 'string', description: 'Authorization code from OAuth2 flow', }, }, required: ['code'], }, }, ], }; }); // Handle tool calls this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { switch (name) { case 'get_auth_url': return await this.handleGetAuthUrl(); case 'authenticate': return await this.handleAuthenticate(args as { code: string }); case 'create_slide': return await this.handleCreateSlide(args as { presentationId: string; insertionIndex?: number; }); case 'add_rectangle': return await this.handleAddRectangle(args as { presentationId: string; slideId: string; x?: number; y?: number; width?: number; height?: number; }); case 'get_presentation_info': return await this.handleGetPresentationInfo(args as { presentationId: string; }); case 'list_slides': return await this.handleListSlides(args as { presentationId: string; }); default: throw new Error(`Unknown tool: ${name}`); } } catch (error) { const mcpError = error as McpError; return { content: [ { type: 'text', text: `Error: ${mcpError.message}`, }, ], isError: true, }; } }); } private async handleGetAuthUrl(): Promise<CallToolResult> { const authUrl = this.auth.getAuthUrl(); return { content: [ { type: 'text', text: `Please visit this URL to authorize the application:\n${authUrl}`, }, ], }; } private async handleAuthenticate(args: { code: string }): Promise<CallToolResult> { await this.auth.getTokens(args.code); return { content: [ { type: 'text', text: 'Authentication successful! You can now use Google Slides tools.', }, ], }; } private async handleCreateSlide(args: { presentationId: string; insertionIndex?: number; }): Promise<CallToolResult> { const slideInfo = await this.slidesService.createSlide( args.presentationId, args.insertionIndex ); return { content: [ { type: 'text', text: `Successfully created new slide with ID: ${slideInfo.slideId}`, }, ], }; } private async handleAddRectangle(args: { presentationId: string; slideId: string; x?: number; y?: number; width?: number; height?: number; }): Promise<CallToolResult> { const rectangleId = await this.slidesService.addRectangle(args.presentationId, { slideId: args.slideId, x: args.x, y: args.y, width: args.width, height: args.height, }); return { content: [ { type: 'text', text: `Successfully added rectangle with ID: ${rectangleId}. Rectangle dimensions are 20% of slide size.`, }, ], }; } private async handleGetPresentationInfo(args: { presentationId: string; }): Promise<CallToolResult> { const info = await this.slidesService.getPresentationInfo(args.presentationId); return { content: [ { type: 'text', text: `Presentation: ${info.title}\nSlide count: ${info.slideCount}\nPage size: ${info.pageSize.width.magnitude} x ${info.pageSize.height.magnitude} ${info.pageSize.width.unit}`, }, ], }; } private async handleListSlides(args: { presentationId: string; }): Promise<CallToolResult> { const slides = await this.slidesService.listSlides(args.presentationId); const slidesList = slides .map((slide) => `- ${slide.title} (ID: ${slide.slideId})`) .join('\n'); return { content: [ { type: 'text', text: `Slides in presentation:\n${slidesList}`, }, ], }; } async run(): Promise<void> { // Try to load existing tokens await this.auth.loadTokens(); const transport = new StdioServerTransport(); await this.server.connect(transport); } } // Start the server const server = new GoogleSlidesMCPServer(); server.run().catch((error) => { console.error('Server error:', error); process.exit(1); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/vamsikiran353-gif/google-slides-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server