UNPKG

aspose.cells.mcp

Version:

Excel MCP - AI-powered Excel automation server for Excel AI, Excel Formula MCP, spreadsheets MCP, and Excel automation using Aspose.Cells for Node.js

250 lines (222 loc) 7.74 kB
/** * Read Data Service And Write Data Service * MCP service for reading data and writing data from Excel worksheets */ import fs from 'fs'; import { getAspose, getAbsolutePath, ensureDirectoryExists, handleExcelFileError } from '../core/utils.js'; interface ReadDataArgs { filepath: string; sheet_name: string; start_cell?: string; end_cell?: string; read_formulas?: boolean; } interface WriteDataArgs { filepath: string; sheet_name: string; data: any[][]; start_cell?: string; apply_formulas?: boolean; } interface ServiceResponse { content: Array<{ type: 'text'; text: string; }>; [key: string]: unknown; } export const readDataService = { // Tool definition definition: { name: 'read_data_from_excel', description: 'Read text from Excel worksheet. Use when user wants to extract/view data from existing Excel files.', inputSchema: { type: 'object', properties: { filepath: { type: 'string', description: 'Path to existing Excel file (.xlsx, .xls, .csv, .ods supported)', }, sheet_name: { type: 'string', description: 'Worksheet name to read from', }, start_cell: { type: 'string', description: 'Starting cell (e.g., A1, default: A1)', default: 'A1', }, end_cell: { type: 'string', description: 'Ending cell to limit range (e.g., D10, optional)', }, read_formulas: { type: 'boolean', description: 'Whether to read formulas (true) or calculated values (false). Default: true', default: true, }, }, required: ['filepath', 'sheet_name'], }, }, // Implementation async handler(args: ReadDataArgs): Promise<ServiceResponse> { let fullPath: string = ''; try { const AsposeCells = getAspose(); fullPath = getAbsolutePath(args.filepath); const workbook = new AsposeCells.Workbook(fullPath); const worksheet = workbook.getWorksheets().get(args.sheet_name); if (!worksheet) { throw new Error(`Sheet "${args.sheet_name}" not found`); } const cells = worksheet.getCells(); // Parse cell range let startRow = 0, startCol = 0; let endRow = cells.getMaxRow(); let endCol = cells.getMaxColumn(); if (args.start_cell) { const startCellRef = cells.get(args.start_cell); startRow = startCellRef.getRow(); startCol = startCellRef.getColumn(); } if (args.end_cell) { const endCellRef = cells.get(args.end_cell); endRow = endCellRef.getRow(); endCol = endCellRef.getColumn(); } // Read data const data: any[][] = []; const readFormulas = args.read_formulas !== false; // Default to true for (let row = startRow; row <= endRow; row++) { const rowData: any[] = []; for (let col = startCol; col <= endCol; col++) { const cell = cells.get(row, col); if (readFormulas) { // Try to get formula first, fallback to value const formula = cell.getFormula(); if (formula && formula.length > 0) { // Ensure we don't double-add the = sign const formulaText = formula.startsWith('=') ? formula : '=' + formula; rowData.push(formulaText); } else { rowData.push(cell.getValue()); } } else { // Get calculated value rowData.push(cell.getValue()); } } data.push(rowData); } return { content: [ { type: 'text', text: JSON.stringify(data, null, 2), }, ], }; } catch (error) { const err = error instanceof Error ? error : new Error(String(error)); handleExcelFileError(err, fullPath || args.filepath, args.filepath, 'reading data'); } } }; export const writeDataService = { // Tool definition definition: { name: 'write_data_to_excel', description: 'Write tabular data(no styles, just text content) to Excel worksheet. Use when user has data (arrays, tables, lists) to put into Excel cells.', inputSchema: { type: 'object', properties: { filepath: { type: 'string', description: 'Path to Excel file (creates if not exists)', }, sheet_name: { type: 'string', description: 'Worksheet name to write data to', }, data: { type: 'array', description: 'Data as array of rows: [["Name","Age"],["John",25],["Jane",30]]. Formulas should start with =', items: { type: 'array', }, }, start_cell: { type: 'string', description: 'Starting cell position (default: A1)', default: 'A1', }, apply_formulas: { type: 'boolean', description: 'Whether to apply formulas (true) or treat them as text (false). Default: true', default: true, }, }, required: ['filepath', 'sheet_name', 'data'], }, }, // Implementation async handler(args: WriteDataArgs): Promise<ServiceResponse> { let fullPath: string = ''; try { const AsposeCells = getAspose(); fullPath = getAbsolutePath(args.filepath); let workbook: any; if (fs.existsSync(fullPath)) { workbook = new AsposeCells.Workbook(fullPath); } else { workbook = new AsposeCells.Workbook(); ensureDirectoryExists(fullPath); } let worksheet = workbook.getWorksheets().get(args.sheet_name); if (!worksheet) { worksheet = workbook.getWorksheets().add(args.sheet_name); } const cells = worksheet.getCells(); // Parse start cell let startRow = 0, startCol = 0; if (args.start_cell) { const startCellRef = cells.get(args.start_cell); startRow = startCellRef.getRow(); startCol = startCellRef.getColumn(); } // Write data const applyFormulas = args.apply_formulas !== false; // Default to true for (let i = 0; i < args.data.length; i++) { const row = args.data[i]; for (let j = 0; j < row.length; j++) { const cell = cells.get(startRow + i, startCol + j); const cellValue = row[j]; if (applyFormulas && typeof cellValue === 'string' && cellValue.startsWith('=')) { // Apply as formula (remove the leading =) cell.setFormula(cellValue.substring(1)); } else { // Apply as regular value cell.putValue(cellValue); } } } // Calculate formulas if they were applied if (applyFormulas) { workbook.calculateFormula(); } workbook.save(fullPath); return { content: [ { type: 'text', text: `Data written to ${args.sheet_name} in ${fullPath}${applyFormulas ? ' (formulas applied)' : ' (formulas as text)'}`, }, ], }; } catch (error) { const err = error instanceof Error ? error : new Error(String(error)); handleExcelFileError(err, fullPath || args.filepath, args.filepath, 'writing data'); } } };