diff --git a/package-lock.json b/package-lock.json index fa9ec691..34253af3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1565,6 +1565,13 @@ "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", "dev": true }, + "node_modules/@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/node": { "version": "22.9.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", @@ -5185,6 +5192,7 @@ "@modelcontextprotocol/sdk": "0.5.0", "diff": "^5.1.0", "glob": "^10.3.10", + "minimatch": "^10.0.1", "zod-to-json-schema": "^3.23.5" }, "bin": { @@ -5192,6 +5200,7 @@ }, "devDependencies": { "@types/diff": "^5.0.9", + "@types/minimatch": "^5.1.2", "@types/node": "^20.11.0", "shx": "^0.3.4", "typescript": "^5.3.3" @@ -5246,7 +5255,7 @@ "url": "https://github.com/sponsors/isaacs" } }, - "src/filesystem/node_modules/minimatch": { + "src/filesystem/node_modules/glob/node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", @@ -5261,6 +5270,21 @@ "url": "https://github.com/sponsors/isaacs" } }, + "src/filesystem/node_modules/minimatch": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "src/gdrive": { "name": "@modelcontextprotocol/server-gdrive", "version": "0.6.2", diff --git a/src/filesystem/README.md b/src/filesystem/README.md index 37bc290f..79c2b0f3 100644 --- a/src/filesystem/README.md +++ b/src/filesystem/README.md @@ -82,6 +82,7 @@ Node.js server implementing Model Context Protocol (MCP) for filesystem operatio - Inputs: - `path` (string): Starting directory - `pattern` (string): Search pattern + - `excludePatterns` (string[]): Exclude any patterns. Glob formats are supported. - Case-insensitive matching - Returns full paths to matches diff --git a/src/filesystem/index.ts b/src/filesystem/index.ts index 23d989d0..730721de 100644 --- a/src/filesystem/index.ts +++ b/src/filesystem/index.ts @@ -13,6 +13,7 @@ import os from 'os'; import { z } from "zod"; import { zodToJsonSchema } from "zod-to-json-schema"; import { diffLines, createTwoFilesPatch } from 'diff'; +import { minimatch } from 'minimatch'; // Command line argument parsing const args = process.argv.slice(2); @@ -134,6 +135,7 @@ const MoveFileArgsSchema = z.object({ const SearchFilesArgsSchema = z.object({ path: z.string(), pattern: z.string(), + excludePatterns: z.array(z.string()).optional().default([]) }); const GetFileInfoArgsSchema = z.object({ @@ -183,6 +185,7 @@ async function getFileStats(filePath: string): Promise { async function searchFiles( rootPath: string, pattern: string, + excludePatterns: string[] = [] ): Promise { const results: string[] = []; @@ -191,11 +194,22 @@ async function searchFiles( for (const entry of entries) { const fullPath = path.join(currentPath, entry.name); - + try { // Validate each path before processing await validatePath(fullPath); + // Check if path matches any exclude pattern + const relativePath = path.relative(rootPath, fullPath); + const shouldExclude = excludePatterns.some(pattern => { + const globPattern = pattern.includes('*') ? pattern : `**/${pattern}/**`; + return minimatch(relativePath, globPattern, { dot: true }); + }); + + if (shouldExclude) { + continue; + } + if (entry.name.toLowerCase().includes(pattern.toLowerCase())) { results.push(fullPath); } @@ -522,7 +536,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { throw new Error(`Invalid arguments for search_files: ${parsed.error}`); } const validPath = await validatePath(parsed.data.path); - const results = await searchFiles(validPath, parsed.data.pattern); + const results = await searchFiles(validPath, parsed.data.pattern, parsed.data.excludePatterns); return { content: [{ type: "text", text: results.length > 0 ? results.join("\n") : "No matches found" }], }; @@ -544,9 +558,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { case "list_allowed_directories": { return { - content: [{ - type: "text", - text: `Allowed directories:\n${allowedDirectories.join('\n')}` + content: [{ + type: "text", + text: `Allowed directories:\n${allowedDirectories.join('\n')}` }], }; } @@ -574,4 +588,4 @@ async function runServer() { runServer().catch((error) => { console.error("Fatal error running server:", error); process.exit(1); -}); \ No newline at end of file +}); diff --git a/src/filesystem/package.json b/src/filesystem/package.json index bb9797ce..6f0b223d 100644 --- a/src/filesystem/package.json +++ b/src/filesystem/package.json @@ -22,12 +22,14 @@ "@modelcontextprotocol/sdk": "0.5.0", "diff": "^5.1.0", "glob": "^10.3.10", + "minimatch": "^10.0.1", "zod-to-json-schema": "^3.23.5" }, "devDependencies": { "@types/diff": "^5.0.9", + "@types/minimatch": "^5.1.2", "@types/node": "^20.11.0", "shx": "^0.3.4", "typescript": "^5.3.3" } -} \ No newline at end of file +}