Skip to main content
Glama
MausRundung

Project Explorer MCP Server

by MausRundung

search_files

Search files using pattern matching with regex, file type, size, date filters, and exclude comments or string literals for cleaner code results.

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

  • src/index.ts:33-43 (registration)
    Registers searchTool (which has name 'search_files') in the list of available MCP tools exposed by the server.
    server.setRequestHandler(ListToolsRequestSchema, async () => {
      return {
        tools: [
          exploreProjectTool,
          listAllowedTool,
          searchTool,
          renameFileTool,
          deleteFileTool,
          checkOutdatedTool
        ]
      };
  • src/index.ts:59-60 (registration)
    Routes the 'search_files' tool request to the handleSearch handler.
    case "search_files":
      return await handleSearch(args, ALLOWED_DIRECTORIES);
  • Schema definition and tool metadata for 'search_files' tool, defining all input parameters with types, descriptions, and defaults.
    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: []
      }
    };
  • Main handler function for the 'search_files' tool. Parses arguments, validates the search path against allowed directories, performs recursive directory search, sorts results, limits output, and formats the response.
    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)}`
        );
      }
    }
  • Core recursive directory search function. Walks the directory tree, applies all filters (size, date, extension, exclude patterns, depth), delegates per-file searching to searchInFile(), and collects SearchResult objects.
    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;
    }
Behavior4/5

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

With no annotations, the description carries full behavioral burden. It discloses many behavioral traits: regex mode, file/case filtering, exclusion of comments/strings, output formats, and grouping. It could mention performance limitations, but overall provides substantial transparency.

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

Conciseness5/5

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

The description is concise (4 sentences) and well-structured, front-loading the primary purpose. Each sentence adds new information: capabilities, default behavior, comment exclusion, and output options, with no unnecessary words.

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

Completeness4/5

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

Given the complexity (23 parameters) and absence of output schema, the description covers a wide range of features and default behavior. It could be more complete by describing the output structure, but it is fairly thorough for a search tool.

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 coverage is 100%, so baseline is 3. The description summarizes the tool's purpose but does not add significant per-parameter detail beyond the existing schema descriptions. It serves as a high-level overview rather than enhancing parameter understanding.

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

Purpose5/5

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

The description clearly identifies the tool as an advanced file and code search tool, specifying its capabilities (regex, filtering, etc.) and default behavior. It distinguishes itself from sibling tools which are unrelated (e.g., delete_file, rename_file), leaving no ambiguity about its purpose.

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

Usage Guidelines4/5

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

The description provides clear context for when to use the tool (for file/code search) and mentions default behavior when called without arguments. However, it does not explicitly exclude usage for other tasks or compare with alternatives, though siblings are distinct enough to avoid confusion.

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