@makeshkumar/mcp-xl-reader 1.0.0 → 1.0.1
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 +77 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -42,6 +42,33 @@ async function validateFilePath(filePath) {
|
|
|
42
42
|
}
|
|
43
43
|
return absolutePath;
|
|
44
44
|
}
|
|
45
|
+
// Helper for security and directory existence check
|
|
46
|
+
async function validateDirectoryPath(directoryPath) {
|
|
47
|
+
const absolutePath = path.resolve(directoryPath);
|
|
48
|
+
try {
|
|
49
|
+
const stats = await fs.stat(absolutePath);
|
|
50
|
+
if (!stats.isDirectory()) {
|
|
51
|
+
throw new Error(`Path exists but is not a directory: ${absolutePath}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
if (error.code === 'ENOENT') {
|
|
56
|
+
throw new Error(`Directory Not Found: ${absolutePath}`);
|
|
57
|
+
}
|
|
58
|
+
else if (error.code === 'EACCES') {
|
|
59
|
+
throw new Error(`Permission Denied: ${absolutePath}`);
|
|
60
|
+
}
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
const allowedDirectories = process.env.ALLOWED_DIRECTORIES ? process.env.ALLOWED_DIRECTORIES.split(',') : [];
|
|
64
|
+
if (allowedDirectories.length > 0) {
|
|
65
|
+
const isAllowed = allowedDirectories.some(dir => absolutePath.startsWith(path.resolve(dir)));
|
|
66
|
+
if (!isAllowed) {
|
|
67
|
+
throw new Error(`Security Violation: Access to ${absolutePath} is not within allowed directories.`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return absolutePath;
|
|
71
|
+
}
|
|
45
72
|
// Helper to format cell values properly
|
|
46
73
|
function formatCellValue(cell) {
|
|
47
74
|
if (cell.value === null || cell.value === undefined) {
|
|
@@ -104,6 +131,17 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
104
131
|
required: ["filePath"]
|
|
105
132
|
}
|
|
106
133
|
},
|
|
134
|
+
{
|
|
135
|
+
name: "find_excel_files",
|
|
136
|
+
description: "Finds all .xlsx files in a given directory.",
|
|
137
|
+
inputSchema: {
|
|
138
|
+
type: "object",
|
|
139
|
+
properties: {
|
|
140
|
+
directoryPath: { type: "string", description: "Absolute path to the directory to search." }
|
|
141
|
+
},
|
|
142
|
+
required: ["directoryPath"]
|
|
143
|
+
}
|
|
144
|
+
},
|
|
107
145
|
{
|
|
108
146
|
name: "read_spreadsheet",
|
|
109
147
|
description: "Returns JSON representation of rows. Streams using worksheet.eachRow for memory efficiency.",
|
|
@@ -151,16 +189,36 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
151
189
|
});
|
|
152
190
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
153
191
|
const { name, arguments: args } = request.params;
|
|
154
|
-
if (!args || typeof args !== 'object'
|
|
192
|
+
if (!args || typeof args !== 'object') {
|
|
155
193
|
return {
|
|
156
194
|
isError: true,
|
|
157
|
-
content: [{ type: "text", text: "
|
|
195
|
+
content: [{ type: "text", text: "Invalid arguments provided." }]
|
|
158
196
|
};
|
|
159
197
|
}
|
|
160
|
-
|
|
198
|
+
if (name === "find_excel_files") {
|
|
199
|
+
if (!args.directoryPath) {
|
|
200
|
+
return {
|
|
201
|
+
isError: true,
|
|
202
|
+
content: [{ type: "text", text: "Missing required directoryPath parameter." }]
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
if (!args.filePath) {
|
|
208
|
+
return {
|
|
209
|
+
isError: true,
|
|
210
|
+
content: [{ type: "text", text: "Missing required filePath parameter." }]
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
}
|
|
161
214
|
let absolutePath;
|
|
162
215
|
try {
|
|
163
|
-
|
|
216
|
+
if (name === "find_excel_files") {
|
|
217
|
+
absolutePath = await validateDirectoryPath(args.directoryPath);
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
absolutePath = await validateFilePath(args.filePath);
|
|
221
|
+
}
|
|
164
222
|
}
|
|
165
223
|
catch (error) {
|
|
166
224
|
return {
|
|
@@ -171,6 +229,21 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
171
229
|
try {
|
|
172
230
|
const workbook = new ExcelJS.Workbook();
|
|
173
231
|
switch (name) {
|
|
232
|
+
case "find_excel_files": {
|
|
233
|
+
// Read all files recursively in the given directory
|
|
234
|
+
const files = await fs.readdir(absolutePath, { recursive: true, withFileTypes: true });
|
|
235
|
+
const excelFiles = files
|
|
236
|
+
.filter(dirent => dirent.isFile() && dirent.name.toLowerCase().endsWith('.xlsx'))
|
|
237
|
+
// fs.readdir with recursive=true returns relative paths in dirent.name or dirent.parentPath
|
|
238
|
+
.map(dirent => {
|
|
239
|
+
const d = dirent;
|
|
240
|
+
const parentPath = d.parentPath || d.path || absolutePath;
|
|
241
|
+
return path.resolve(parentPath, dirent.name);
|
|
242
|
+
});
|
|
243
|
+
return {
|
|
244
|
+
content: [{ type: "text", text: JSON.stringify(excelFiles, null, 2) }]
|
|
245
|
+
};
|
|
246
|
+
}
|
|
174
247
|
case "list_sheets": {
|
|
175
248
|
await workbook.xlsx.readFile(absolutePath);
|
|
176
249
|
const sheets = workbook.worksheets.map(ws => ws.name);
|