@makeshkumar/mcp-xl-reader 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +110 -29
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -133,13 +133,12 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
133
133
|
},
|
|
134
134
|
{
|
|
135
135
|
name: "find_excel_files",
|
|
136
|
-
description: "Finds all .xlsx files in a given directory.",
|
|
136
|
+
description: "Finds all .xlsx files in a given directory. If directoryPath is omitted, it automatically searches the allowed directories or current working directory.",
|
|
137
137
|
inputSchema: {
|
|
138
138
|
type: "object",
|
|
139
139
|
properties: {
|
|
140
|
-
directoryPath: { type: "string", description: "Absolute path to the directory to search." }
|
|
141
|
-
}
|
|
142
|
-
required: ["directoryPath"]
|
|
140
|
+
directoryPath: { type: "string", description: "Absolute path to the directory to search (optional)." }
|
|
141
|
+
}
|
|
143
142
|
}
|
|
144
143
|
},
|
|
145
144
|
{
|
|
@@ -183,6 +182,34 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
183
182
|
},
|
|
184
183
|
required: ["filePath", "cellAddress", "newValue"]
|
|
185
184
|
}
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
name: "add_worksheet_with_data",
|
|
188
|
+
description: "Creates a new worksheet in an existing workbook and populates it with an array of JSON rows. Great for generating summary reports.",
|
|
189
|
+
inputSchema: {
|
|
190
|
+
type: "object",
|
|
191
|
+
properties: {
|
|
192
|
+
filePath: { type: "string" },
|
|
193
|
+
sheetName: { type: "string", description: "Name of the new sheet to create." },
|
|
194
|
+
columns: {
|
|
195
|
+
type: "array",
|
|
196
|
+
items: {
|
|
197
|
+
type: "object",
|
|
198
|
+
properties: {
|
|
199
|
+
header: { type: "string" },
|
|
200
|
+
key: { type: "string" }
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
description: "Array of column definitions, e.g., [{header: 'Name', key: 'name'}]"
|
|
204
|
+
},
|
|
205
|
+
rows: {
|
|
206
|
+
type: "array",
|
|
207
|
+
items: { type: "object" },
|
|
208
|
+
description: "Array of JSON objects representing the rows to insert."
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
required: ["filePath", "sheetName", "columns", "rows"]
|
|
212
|
+
}
|
|
186
213
|
}
|
|
187
214
|
]
|
|
188
215
|
};
|
|
@@ -195,15 +222,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
195
222
|
content: [{ type: "text", text: "Invalid arguments provided." }]
|
|
196
223
|
};
|
|
197
224
|
}
|
|
198
|
-
if (name
|
|
199
|
-
if (!args.directoryPath) {
|
|
200
|
-
return {
|
|
201
|
-
isError: true,
|
|
202
|
-
content: [{ type: "text", text: "Missing required directoryPath parameter." }]
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
else {
|
|
225
|
+
if (name !== "find_excel_files") {
|
|
207
226
|
if (!args.filePath) {
|
|
208
227
|
return {
|
|
209
228
|
isError: true,
|
|
@@ -211,12 +230,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
211
230
|
};
|
|
212
231
|
}
|
|
213
232
|
}
|
|
214
|
-
let absolutePath;
|
|
233
|
+
let absolutePath = "";
|
|
215
234
|
try {
|
|
216
|
-
if (name
|
|
217
|
-
absolutePath = await validateDirectoryPath(args.directoryPath);
|
|
218
|
-
}
|
|
219
|
-
else {
|
|
235
|
+
if (name !== "find_excel_files") {
|
|
220
236
|
absolutePath = await validateFilePath(args.filePath);
|
|
221
237
|
}
|
|
222
238
|
}
|
|
@@ -230,18 +246,52 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
230
246
|
const workbook = new ExcelJS.Workbook();
|
|
231
247
|
switch (name) {
|
|
232
248
|
case "find_excel_files": {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
.
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
249
|
+
let directoriesToSearch = [];
|
|
250
|
+
if (args && args.directoryPath) {
|
|
251
|
+
directoriesToSearch.push(await validateDirectoryPath(args.directoryPath));
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
const allowedDirectories = process.env.ALLOWED_DIRECTORIES ? process.env.ALLOWED_DIRECTORIES.split(',') : [];
|
|
255
|
+
if (allowedDirectories.length > 0) {
|
|
256
|
+
for (const dir of allowedDirectories) {
|
|
257
|
+
try {
|
|
258
|
+
directoriesToSearch.push(await validateDirectoryPath(dir.trim()));
|
|
259
|
+
}
|
|
260
|
+
catch (e) {
|
|
261
|
+
// ignore invalid ones
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
// fallback to current working directory
|
|
267
|
+
directoriesToSearch.push(process.cwd());
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
if (directoriesToSearch.length === 0) {
|
|
271
|
+
return {
|
|
272
|
+
isError: true,
|
|
273
|
+
content: [{ type: "text", text: "No valid directories to search." }]
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
const allExcelFiles = [];
|
|
277
|
+
for (const dir of directoriesToSearch) {
|
|
278
|
+
try {
|
|
279
|
+
const files = await fs.readdir(dir, { recursive: true, withFileTypes: true });
|
|
280
|
+
const excelFiles = files
|
|
281
|
+
.filter(dirent => dirent.isFile() && dirent.name.toLowerCase().endsWith('.xlsx'))
|
|
282
|
+
.map(dirent => {
|
|
283
|
+
const d = dirent;
|
|
284
|
+
const parentPath = d.parentPath || d.path || dir;
|
|
285
|
+
return path.resolve(parentPath, dirent.name);
|
|
286
|
+
});
|
|
287
|
+
allExcelFiles.push(...excelFiles);
|
|
288
|
+
}
|
|
289
|
+
catch (e) {
|
|
290
|
+
// Skip if one directory fails
|
|
291
|
+
}
|
|
292
|
+
}
|
|
243
293
|
return {
|
|
244
|
-
content: [{ type: "text", text: JSON.stringify(
|
|
294
|
+
content: [{ type: "text", text: JSON.stringify(allExcelFiles, null, 2) }]
|
|
245
295
|
};
|
|
246
296
|
}
|
|
247
297
|
case "list_sheets": {
|
|
@@ -376,6 +426,37 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
376
426
|
content: [{ type: "text", text: `Successfully updated cell ${cellAddress} to ${newValue}` }]
|
|
377
427
|
};
|
|
378
428
|
}
|
|
429
|
+
case "add_worksheet_with_data": {
|
|
430
|
+
const { sheetName, columns, rows } = args;
|
|
431
|
+
await workbook.xlsx.readFile(absolutePath);
|
|
432
|
+
// Check if sheet exists to avoid crashing
|
|
433
|
+
if (workbook.getWorksheet(sheetName)) {
|
|
434
|
+
throw new Error(`A worksheet named '${sheetName}' already exists in this file.`);
|
|
435
|
+
}
|
|
436
|
+
const ws = workbook.addWorksheet(sheetName);
|
|
437
|
+
// Set columns
|
|
438
|
+
if (Array.isArray(columns)) {
|
|
439
|
+
ws.columns = columns.map(col => ({
|
|
440
|
+
header: col.header,
|
|
441
|
+
key: col.key,
|
|
442
|
+
width: 20 // Default reasonable width
|
|
443
|
+
}));
|
|
444
|
+
}
|
|
445
|
+
// Add data rows
|
|
446
|
+
if (Array.isArray(rows)) {
|
|
447
|
+
rows.forEach(rowData => {
|
|
448
|
+
ws.addRow(rowData);
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
// Make the header bold to look professional
|
|
452
|
+
const headerRow = ws.getRow(1);
|
|
453
|
+
headerRow.font = { bold: true };
|
|
454
|
+
headerRow.commit();
|
|
455
|
+
await workbook.xlsx.writeFile(absolutePath);
|
|
456
|
+
return {
|
|
457
|
+
content: [{ type: "text", text: `Successfully generated new reporting sheet '${sheetName}' with ${rows.length} rows of data and saved it to the file.` }]
|
|
458
|
+
};
|
|
459
|
+
}
|
|
379
460
|
default:
|
|
380
461
|
throw new Error(`Unknown tool: ${name}`);
|
|
381
462
|
}
|