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
text/typescript
/**
* 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');
}
}
};