@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.
Files changed (2) hide show
  1. package/dist/index.js +77 -4
  2. 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' || !args.filePath) {
192
+ if (!args || typeof args !== 'object') {
155
193
  return {
156
194
  isError: true,
157
- content: [{ type: "text", text: "Missing required filePath parameter." }]
195
+ content: [{ type: "text", text: "Invalid arguments provided." }]
158
196
  };
159
197
  }
160
- const { filePath } = args;
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
- absolutePath = await validateFilePath(filePath);
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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@makeshkumar/mcp-xl-reader",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "main": "dist/index.js",
5
5
  "type": "module",
6
6
  "scripts": {