Skip to main content
Glama
MausRundung

Project Explorer MCP Server

by MausRundung

search_files

Search for patterns in project files using regex, filtering by file type, size, date, and content. Exclude comments and strings for cleaner code searches.

Instructions

Advanced file and code search tool with comprehensive filtering and matching capabilities. Searches for patterns in files within allowed directories with support for regex patterns, file type filtering, size constraints, date filtering, and content preprocessing. When called without arguments, searches for common patterns in the current directory. Supports excluding comments and string literals for cleaner code searches. Results can be formatted as text, JSON, or structured output with configurable sorting and grouping options.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
patternNoSearch pattern - can be literal text or regex depending on regexMode. Defaults to searching for common file types if not specified.*
searchPathNoDirectory path to search in. Must be within allowed directories. Defaults to first allowed directory if not specified
extensionsNoArray of file extensions to include (e.g., ['.js', '.ts', '.py']). Include the dot prefix
excludeExtensionsNoArray of file extensions to exclude
excludePatternsNoArray of filename patterns to exclude (supports simple wildcards)
regexModeNoWhether to treat pattern as a regular expression
caseSensitiveNoWhether search should be case sensitive
wordBoundaryNoWhether to match whole words only
multilineNoWhether to enable multiline regex matching
maxDepthNoMaximum directory recursion depth. Unlimited if not specified
followSymlinksNoWhether to follow symbolic links
includeBinaryNoWhether to search in binary files
minSizeNoMinimum file size in bytes
maxSizeNoMaximum file size in bytes
modifiedAfterNoOnly include files modified after this date (ISO 8601 format)
modifiedBeforeNoOnly include files modified before this date (ISO 8601 format)
snippetLengthNoLength of text snippet around matches
maxResultsNoMaximum number of match results to return
sortByNoHow to sort the resultsrelevance
groupByFileNoWhether to group results by file
excludeCommentsNoWhether to exclude comments from search (language-aware)
excludeStringsNoWhether to exclude string literals from search
outputFormatNoOutput format for resultstext

Implementation Reference

  • Main handler function for executing the search_files tool. Validates inputs, sets defaults, performs recursive file search, sorts and formats results.
    export async function handleSearch(args: any, allowedDirectories: string[]) {
      // Search files handler
      
      // Set up default options
      const options: SearchOptions = {
        pattern: args.pattern || ".*",
        searchPath: args.searchPath || (allowedDirectories.length > 0 ? allowedDirectories[0] : ""),
        extensions: args.extensions,
        excludeExtensions: args.excludeExtensions,
        excludePatterns: args.excludePatterns || [],
        regexMode: args.regexMode || false,
        caseSensitive: args.caseSensitive || false,
        wordBoundary: args.wordBoundary || false,
        multiline: args.multiline || false,
        maxDepth: args.maxDepth,
        followSymlinks: args.followSymlinks || false,
        includeBinary: args.includeBinary || false,
        minSize: args.minSize,
        maxSize: args.maxSize,
        modifiedAfter: args.modifiedAfter,
        modifiedBefore: args.modifiedBefore,
        snippetLength: args.snippetLength || 50,
        maxResults: args.maxResults || 100,
        sortBy: args.sortBy || 'relevance',
        groupByFile: args.groupByFile !== undefined ? args.groupByFile : true,
        excludeComments: args.excludeComments || false,
        excludeStrings: args.excludeStrings || false,
        outputFormat: args.outputFormat || 'text'
      };
      
      // Validate search path
      if (!options.searchPath) {
        throw new McpError(
          ErrorCode.InvalidRequest,
          "No search path specified and no allowed directories available"
        );
      }
      
      // Ensure searchPath is not empty string
      if (options.searchPath.trim() === "") {
        throw new McpError(
          ErrorCode.InvalidRequest,
          "Search path cannot be empty"
        );
      }
      
      // Normalize search path
      options.searchPath = path.normalize(options.searchPath);
      
      // Check if search path is allowed
      if (!isPathAllowed(options.searchPath, allowedDirectories)) {
        throw new McpError(
          ErrorCode.InvalidRequest,
          `Access denied: The path '${options.searchPath}' is not in the list of allowed directories: ${allowedDirectories.join(', ')}`
        );
      }
      
      // Validate that the search path exists and is a directory
      try {
        const stat = await fs.promises.stat(options.searchPath);
        if (!stat.isDirectory()) {
          throw new McpError(
            ErrorCode.InvalidRequest,
            `The path '${options.searchPath}' is not a directory`
          );
        }
      } catch (error) {
        throw new McpError(
          ErrorCode.InvalidRequest,
          `The path '${options.searchPath}' does not exist or cannot be accessed`
        );
      }
      
      try {
        // Perform the search
        const results = await searchDirectory(options.searchPath, options, allowedDirectories);
        
        // Sort results
        const sortedResults = sortResults(results, options.sortBy || 'relevance');
        
        // Limit results
        const limitedResults = sortedResults.slice(0, options.maxResults);
        
        // Format output
        const formattedResults = formatResults(limitedResults, options);
        
        return {
          content: [
            {
              type: "text",
              text: formattedResults
            }
          ]
        };
      } catch (error) {
        if (error instanceof McpError) {
          throw error;
        }
        
        throw new McpError(
          ErrorCode.InternalError,
          `Error during search: ${error instanceof Error ? error.message : String(error)}`
        );
      }
    }
  • Tool schema definition including name, description, and detailed inputSchema with all parameters for the search_files tool.
    export const searchTool = {
      name: "search_files",
      description: "Advanced file and code search tool with comprehensive filtering and matching capabilities. Searches for patterns in files within allowed directories with support for regex patterns, file type filtering, size constraints, date filtering, and content preprocessing. When called without arguments, searches for common patterns in the current directory. Supports excluding comments and string literals for cleaner code searches. Results can be formatted as text, JSON, or structured output with configurable sorting and grouping options.",
      inputSchema: {
        type: "object",
        properties: {
          pattern: {
            type: "string",
            description: "Search pattern - can be literal text or regex depending on regexMode. Defaults to searching for common file types if not specified",
            default: ".*"
          },
          searchPath: {
            type: "string",
            description: "Directory path to search in. Must be within allowed directories. Defaults to first allowed directory if not specified"
          },
          extensions: {
            type: "array",
            items: { type: "string" },
            description: "Array of file extensions to include (e.g., ['.js', '.ts', '.py']). Include the dot prefix"
          },
          excludeExtensions: {
            type: "array",
            items: { type: "string" },
            description: "Array of file extensions to exclude"
          },
          excludePatterns: {
            type: "array",
            items: { type: "string" },
            description: "Array of filename patterns to exclude (supports simple wildcards)"
          },
          regexMode: {
            type: "boolean",
            description: "Whether to treat pattern as a regular expression",
            default: false
          },
          caseSensitive: {
            type: "boolean",
            description: "Whether search should be case sensitive",
            default: false
          },
          wordBoundary: {
            type: "boolean",
            description: "Whether to match whole words only",
            default: false
          },
          multiline: {
            type: "boolean",
            description: "Whether to enable multiline regex matching",
            default: false
          },
          maxDepth: {
            type: "integer",
            description: "Maximum directory recursion depth. Unlimited if not specified"
          },
          followSymlinks: {
            type: "boolean",
            description: "Whether to follow symbolic links",
            default: false
          },
          includeBinary: {
            type: "boolean",
            description: "Whether to search in binary files",
            default: false
          },
          minSize: {
            type: "integer",
            description: "Minimum file size in bytes"
          },
          maxSize: {
            type: "integer",
            description: "Maximum file size in bytes"
          },
          modifiedAfter: {
            type: "string",
            description: "Only include files modified after this date (ISO 8601 format)"
          },
          modifiedBefore: {
            type: "string",
            description: "Only include files modified before this date (ISO 8601 format)"
          },
          snippetLength: {
            type: "integer",
            description: "Length of text snippet around matches",
            default: 50
          },
          maxResults: {
            type: "integer",
            description: "Maximum number of match results to return",
            default: 100
          },
          sortBy: {
            type: "string",
            enum: ["relevance", "file", "lineNumber", "modified", "size"],
            description: "How to sort the results",
            default: "relevance"
          },
          groupByFile: {
            type: "boolean",
            description: "Whether to group results by file",
            default: true
          },
          excludeComments: {
            type: "boolean",
            description: "Whether to exclude comments from search (language-aware)",
            default: false
          },
          excludeStrings: {
            type: "boolean",
            description: "Whether to exclude string literals from search",
            default: false
          },
          outputFormat: {
            type: "string",
            enum: ["text", "json", "structured"],
            description: "Output format for results",
            default: "text"
          }
        },
        required: []
      }
    };
  • src/index.ts:33-44 (registration)
    Registration of the search_files tool (via searchTool) in the MCP server's listTools handler.
    server.setRequestHandler(ListToolsRequestSchema, async () => {
      return {
        tools: [
          exploreProjectTool,
          listAllowedTool,
          searchTool,
          renameFileTool,
          deleteFileTool,
          checkOutdatedTool
        ]
      };
    });
  • src/index.ts:59-60 (registration)
    Dispatcher case in CallToolRequestSchema handler that routes 'search_files' calls to the handleSearch function.
    case "search_files":
      return await handleSearch(args, ALLOWED_DIRECTORIES);
  • Core helper function that recursively searches directories for files matching the search criteria.
    async function searchDirectory(
      dirPath: string, 
      options: SearchOptions, 
      allowedDirectories: string[],
      currentDepth: number = 0
    ): Promise<SearchResult[]> {
      const results: SearchResult[] = [];
      
      if (options.maxDepth !== undefined && currentDepth > options.maxDepth) {
        return results;
      }
      
      try {
        const entries = await fs.promises.readdir(dirPath, { withFileTypes: true });
        
        for (const entry of entries) {
          const entryPath = path.join(dirPath, entry.name);
          
          // Skip excluded paths
          if (shouldExcludePath(entryPath, options.excludePatterns || [])) {
            continue;
          }
          
          if (entry.isDirectory()) {
            // Recursively search subdirectories
            const subdirResults = await searchDirectory(entryPath, options, allowedDirectories, currentDepth + 1);
            results.push(...subdirResults);
          } else if (entry.isFile() || (entry.isSymbolicLink() && options.followSymlinks)) {
            try {
              const stat = await fs.promises.stat(entryPath);
              
              // Check size criteria
              if (!matchesSize(stat.size, options.minSize, options.maxSize)) {
                continue;
              }
              
              // Check date criteria
              if (!matchesDate(stat, options.modifiedAfter, options.modifiedBefore)) {
                continue;
              }
              
              // Check extension criteria
              if (!matchesExtension(entryPath, options.extensions, options.excludeExtensions)) {
                continue;
              }
              
              // Search in file
              const matches = await searchInFile(entryPath, options);
              
              if (matches.length > 0) {
                const relativePath = options.searchPath ? path.relative(options.searchPath, entryPath) : entryPath;
                
                results.push({
                  filePath: entryPath,
                  relativePath: relativePath,
                  matches: matches,
                  fileSize: stat.size,
                  fileSizeFormatted: formatFileSize(stat.size),
                  lastModified: stat.mtime,
                  fileExtension: path.extname(entryPath).toLowerCase()
                });
              }
            } catch (error) {
              // Error processing file, skip
            }
          }
        }
      } catch (error) {
        // Error searching directory, skip
      }
      
      return results;
    }
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It mentions several behavioral traits: searching 'within allowed directories,' support for regex and filtering, preprocessing options (excluding comments/strings), and output formatting. However, it omits critical details like performance characteristics (e.g., speed, memory usage), error handling, or whether it's read-only/destructive. For a complex 23-parameter tool, this leaves significant gaps in understanding its behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is appropriately sized and front-loaded, starting with the core purpose and key features. It efficiently lists capabilities in a single paragraph without redundancy. However, it could be slightly more structured by separating usage notes from feature lists, and some phrases like 'comprehensive filtering and matching capabilities' are somewhat vague.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's high complexity (23 parameters) and lack of annotations or output schema, the description is moderately complete. It covers the tool's scope and key features but lacks details on behavioral constraints, error cases, or example outputs. For a search tool with many options, more context on limitations (e.g., performance with large directories) would be helpful to fully guide usage.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, meaning all parameters are documented in the input schema. The description adds minimal parameter semantics beyond the schema—it mentions filtering capabilities (e.g., 'file type filtering, size constraints, date filtering') and output options, but these are already covered in the schema's parameter descriptions. Since the schema does the heavy lifting, the baseline score of 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'Advanced file and code search tool with comprehensive filtering and matching capabilities.' It specifies the verb ('searches for patterns in files') and resource ('files within allowed directories'), making the function unambiguous. However, it doesn't explicitly differentiate from sibling tools like 'explore_project' or 'list_allowed_directories' beyond the search focus.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides some usage context: 'When called without arguments, searches for common patterns in the current directory' and mentions capabilities like excluding comments/strings for cleaner code searches. However, it lacks explicit guidance on when to use this tool versus alternatives like 'explore_project' or 'list_allowed_directories', leaving usage scenarios implied rather than clearly defined.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/MausRundung/mcp-explorer'

If you have feedback or need assistance with the MCP directory API, please join our Discord server