Skip to main content
Glama
index.ts19.6 kB
import { Tool } from '@modelcontextprotocol/sdk/types.js'; import { GoogleSheetsService } from '../services/google-sheets-service.js'; import { AuthService } from '../auth/auth-service.js'; import { ListFilesParamsSchema, ReadDataParamsSchema, ReadRangeParamsSchema, SearchParamsSchema, UpdateCellParamsSchema, UpdateRangeParamsSchema, SmartReplaceParamsSchema, AppendRowsParamsSchema, CreateSheetParamsSchema, DeleteSheetParamsSchema, RenameSheetParamsSchema, } from '../types/schemas.js'; import { ValidationError } from '../types/errors.js'; /** * Create all MCP tools for Google Sheets operations */ export function createTools(googleSheetsService: GoogleSheetsService, authService: AuthService): Tool[] { return [ // Auth tools createAuthTool(authService), // Read tools createListFilesTool(googleSheetsService), createListSheetsTool(googleSheetsService), createReadDataTool(googleSheetsService), createReadRangeTool(googleSheetsService), createSearchTool(googleSheetsService), // Update tools createUpdateCellTool(googleSheetsService), createUpdateRangeTool(googleSheetsService), createSmartReplaceTool(googleSheetsService), createAppendRowsTool(googleSheetsService), // Management tools createCreateSheetTool(googleSheetsService), createDeleteSheetTool(googleSheetsService), createRenameSheetTool(googleSheetsService), ]; } /** * List all accessible spreadsheet files */ function createListFilesTool(googleSheetsService: GoogleSheetsService): Tool { return { name: 'sheets_list_files', description: 'List all spreadsheet files accessible to the authenticated user', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Optional query to filter files by name', }, }, }, handler: async (args: any) => { try { const params = ListFilesParamsSchema.parse(args); const files = await googleSheetsService.listFiles(params.query); return { files: files.map(file => ({ id: file.id, name: file.name, modifiedTime: file.modifiedTime, webViewLink: file.webViewLink, owners: file.owners, })), totalCount: files.length, }; } catch (error) { if (error instanceof Error) { throw new ValidationError(error.message); } throw error; } }, }; } /** * List all sheets in a spreadsheet */ function createListSheetsTool(googleSheetsService: GoogleSheetsService): Tool { return { name: 'sheets_list_sheets', description: 'List all sheets in a specific spreadsheet', inputSchema: { type: 'object', properties: { spreadsheetId: { type: 'string', description: 'The spreadsheet ID or full URL', }, }, required: ['spreadsheetId'], }, handler: async (args: any) => { try { const sheets = await googleSheetsService.listSheets(args.spreadsheetId); return { sheets: sheets.map(sheet => ({ sheetId: sheet.sheetId, title: sheet.title, index: sheet.index, sheetType: sheet.sheetType, rowCount: sheet.gridProperties?.rowCount, columnCount: sheet.gridProperties?.columnCount, })), totalCount: sheets.length, }; } catch (error) { if (error instanceof Error) { throw new ValidationError(error.message); } throw error; } }, }; } /** * Read all data from a sheet */ function createReadDataTool(googleSheetsService: GoogleSheetsService): Tool { return { name: 'sheets_read_data', description: 'Read all data from a specific sheet', inputSchema: { type: 'object', properties: { spreadsheetId: { type: 'string', description: 'The spreadsheet ID or full URL', }, sheetName: { type: 'string', description: 'Sheet name (defaults to first sheet)', }, includeFormats: { type: 'boolean', description: 'Include cell formatting information', default: false, }, }, required: ['spreadsheetId'], }, handler: async (args: any) => { try { const params = ReadDataParamsSchema.parse(args); const data = await googleSheetsService.readData( params.spreadsheetId, params.sheetName, params.includeFormats ); return { data, rowCount: data.length, columnCount: data[0]?.length || 0, sheetName: params.sheetName || 'first sheet', }; } catch (error) { if (error instanceof Error) { throw new ValidationError(error.message); } throw error; } }, }; } /** * Read data from a specific range */ function createReadRangeTool(googleSheetsService: GoogleSheetsService): Tool { return { name: 'sheets_read_range', description: 'Read data from a specific range in a sheet', inputSchema: { type: 'object', properties: { spreadsheetId: { type: 'string', description: 'The spreadsheet ID or full URL', }, sheetName: { type: 'string', description: 'Sheet name', }, range: { type: 'string', description: 'A1 notation range (e.g., "A1:D10")', }, }, required: ['spreadsheetId', 'sheetName', 'range'], }, handler: async (args: any) => { try { const params = ReadRangeParamsSchema.parse(args); const data = await googleSheetsService.readRange( params.spreadsheetId, params.sheetName, params.range ); return { data, range: params.range, rowCount: data.length, columnCount: data[0]?.length || 0, sheetName: params.sheetName, }; } catch (error) { if (error instanceof Error) { throw new ValidationError(error.message); } throw error; } }, }; } /** * Search for text in a sheet */ function createSearchTool(googleSheetsService: GoogleSheetsService): Tool { return { name: 'sheets_search', description: 'Search for cells containing specific text', inputSchema: { type: 'object', properties: { spreadsheetId: { type: 'string', description: 'The spreadsheet ID or full URL', }, sheetName: { type: 'string', description: 'Sheet name', }, searchText: { type: 'string', description: 'Text to search for', }, searchColumns: { type: 'array', items: { type: 'string' }, description: 'Limit search to specific columns (e.g., ["A", "B"])', }, }, required: ['spreadsheetId', 'sheetName', 'searchText'], }, handler: async (args: any) => { try { const params = SearchParamsSchema.parse(args); const results = await googleSheetsService.search( params.spreadsheetId, params.sheetName, params.searchText, params.searchColumns ); return { results, totalMatches: results.length, searchText: params.searchText, sheetName: params.sheetName, searchColumns: params.searchColumns, }; } catch (error) { if (error instanceof Error) { throw new ValidationError(error.message); } throw error; } }, }; } /** * Update a single cell */ function createUpdateCellTool(googleSheetsService: GoogleSheetsService): Tool { return { name: 'sheets_update_cell', description: 'Update a single cell value', inputSchema: { type: 'object', properties: { spreadsheetId: { type: 'string', description: 'The spreadsheet ID or full URL', }, sheetName: { type: 'string', description: 'Sheet name', }, cell: { type: 'string', description: 'Cell reference (e.g., "B5")', }, value: { description: 'New value for the cell', }, }, required: ['spreadsheetId', 'sheetName', 'cell', 'value'], }, handler: async (args: any) => { try { const params = UpdateCellParamsSchema.parse(args); const result = await googleSheetsService.updateCell( params.spreadsheetId, params.sheetName, params.cell, params.value ); return { success: true, updatedCells: result.updatedCells, updatedRange: result.updatedRange, message: `Cell ${params.cell} updated successfully`, }; } catch (error) { if (error instanceof Error) { throw new ValidationError(error.message); } throw error; } }, }; } /** * Update multiple cells in a range */ function createUpdateRangeTool(googleSheetsService: GoogleSheetsService): Tool { return { name: 'sheets_update_range', description: 'Update multiple cells in a range', inputSchema: { type: 'object', properties: { spreadsheetId: { type: 'string', description: 'The spreadsheet ID or full URL', }, sheetName: { type: 'string', description: 'Sheet name', }, range: { type: 'string', description: 'A1 notation range (e.g., "A1:B10")', }, values: { type: 'array', items: { type: 'array', items: {}, }, description: '2D array of values to update', }, preserveFormulas: { type: 'boolean', description: 'Keep existing formulas (default: false)', default: false, }, }, required: ['spreadsheetId', 'sheetName', 'range', 'values'], }, handler: async (args: any) => { try { const params = UpdateRangeParamsSchema.parse(args); const result = await googleSheetsService.updateRange( params.spreadsheetId, params.sheetName, params.range, params.values, params.preserveFormulas ); return { success: true, updatedCells: result.updatedCells, updatedRange: result.updatedRange, message: `Range ${params.range} updated successfully`, }; } catch (error) { if (error instanceof Error) { throw new ValidationError(error.message); } throw error; } }, }; } /** * Smart replace text in cells */ function createSmartReplaceTool(googleSheetsService: GoogleSheetsService): Tool { return { name: 'sheets_smart_replace', description: 'Replace text within cells while preserving surrounding content', inputSchema: { type: 'object', properties: { spreadsheetId: { type: 'string', description: 'The spreadsheet ID or full URL', }, sheetName: { type: 'string', description: 'Sheet name', }, findText: { type: 'string', description: 'Text to find and replace', }, replaceText: { type: 'string', description: 'Replacement text', }, range: { type: 'string', description: 'Limit replacement to specific range (optional)', }, matchCase: { type: 'boolean', description: 'Case-sensitive matching (default: false)', default: false, }, matchEntireCell: { type: 'boolean', description: 'Match entire cell content (default: false)', default: false, }, }, required: ['spreadsheetId', 'sheetName', 'findText', 'replaceText'], }, handler: async (args: any) => { try { const params = SmartReplaceParamsSchema.parse(args); const result = await googleSheetsService.smartReplace( params.spreadsheetId, params.sheetName, params.findText, params.replaceText, params.range, params.matchCase, params.matchEntireCell ); return { success: true, modifiedCells: result.modifiedCells, replacements: result.replacements, message: `Smart replace completed: ${result.modifiedCells} cells modified`, }; } catch (error) { if (error instanceof Error) { throw new ValidationError(error.message); } throw error; } }, }; } /** * Append rows to a sheet */ function createAppendRowsTool(googleSheetsService: GoogleSheetsService): Tool { return { name: 'sheets_append_rows', description: 'Append new rows to the end of a sheet', inputSchema: { type: 'object', properties: { spreadsheetId: { type: 'string', description: 'The spreadsheet ID or full URL', }, sheetName: { type: 'string', description: 'Sheet name', }, values: { type: 'array', items: { type: 'array', items: {}, }, description: '2D array of row values to append', }, }, required: ['spreadsheetId', 'sheetName', 'values'], }, handler: async (args: any) => { try { const params = AppendRowsParamsSchema.parse(args); const result = await googleSheetsService.appendRows( params.spreadsheetId, params.sheetName, params.values ); return { success: true, appendedRange: result.appendedRange, rowsAppended: params.values.length, message: `${params.values.length} rows appended successfully`, }; } catch (error) { if (error instanceof Error) { throw new ValidationError(error.message); } throw error; } }, }; } /** * Create a new sheet */ function createCreateSheetTool(googleSheetsService: GoogleSheetsService): Tool { return { name: 'sheets_create_sheet', description: 'Create a new sheet within a spreadsheet', inputSchema: { type: 'object', properties: { spreadsheetId: { type: 'string', description: 'The spreadsheet ID or full URL', }, sheetName: { type: 'string', description: 'Name for the new sheet', }, }, required: ['spreadsheetId', 'sheetName'], }, handler: async (args: any) => { try { const params = CreateSheetParamsSchema.parse(args); const result = await googleSheetsService.createSheet( params.spreadsheetId, params.sheetName ); return { success: true, sheetId: result.sheetId, sheetName: params.sheetName, message: `Sheet "${params.sheetName}" created successfully`, }; } catch (error) { if (error instanceof Error) { throw new ValidationError(error.message); } throw error; } }, }; } /** * Delete a sheet */ function createDeleteSheetTool(googleSheetsService: GoogleSheetsService): Tool { return { name: 'sheets_delete_sheet', description: 'Delete a sheet from a spreadsheet', inputSchema: { type: 'object', properties: { spreadsheetId: { type: 'string', description: 'The spreadsheet ID or full URL', }, sheetName: { type: 'string', description: 'Sheet name to delete', }, }, required: ['spreadsheetId', 'sheetName'], }, handler: async (args: any) => { try { const params = DeleteSheetParamsSchema.parse(args); await googleSheetsService.deleteSheet( params.spreadsheetId, params.sheetName ); return { success: true, sheetName: params.sheetName, message: `Sheet "${params.sheetName}" deleted successfully`, }; } catch (error) { if (error instanceof Error) { throw new ValidationError(error.message); } throw error; } }, }; } /** * Rename a sheet */ function createRenameSheetTool(googleSheetsService: GoogleSheetsService): Tool { return { name: 'sheets_rename_sheet', description: 'Rename a sheet', inputSchema: { type: 'object', properties: { spreadsheetId: { type: 'string', description: 'The spreadsheet ID or full URL', }, oldName: { type: 'string', description: 'Current sheet name', }, newName: { type: 'string', description: 'New sheet name', }, }, required: ['spreadsheetId', 'oldName', 'newName'], }, handler: async (args: any) => { try { const params = RenameSheetParamsSchema.parse(args); await googleSheetsService.renameSheet( params.spreadsheetId, params.oldName, params.newName ); return { success: true, oldName: params.oldName, newName: params.newName, message: `Sheet renamed from "${params.oldName}" to "${params.newName}"`, }; } catch (error) { if (error instanceof Error) { throw new ValidationError(error.message); } throw error; } }, }; } /** * Tool for handling authentication */ function createAuthTool(authService: AuthService): Tool { return { name: 'sheets_authenticate', description: 'Authenticate with Google to access sheets', inputSchema: { type: 'object', properties: { command: { type: 'string', description: 'The authentication command to execute', enum: ['get_auth_url', 'exchange_code'], }, code: { type: 'string', description: 'The authorization code to exchange for tokens', }, }, required: ['command'], }, handler: async (args: any) => { try { if (args.command === 'get_auth_url') { const authUrl = authService.getAuthUrl(); return { authUrl, message: 'Please visit this URL to authenticate', }; } else if (args.command === 'exchange_code') { if (!args.code) { throw new ValidationError('Missing required parameter: code'); } await authService.exchangeCodeForTokens(args.code); return { success: true, message: 'Authentication successful', }; } else { throw new ValidationError(`Invalid command: ${args.command}`); } } catch (error) { if (error instanceof Error) { throw new ValidationError(error.message); } throw error; } }, }; }

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/ainetwork-ai/google-sheet-mcp'

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