Skip to main content
Glama
247arjun

Grep MCP Server

by 247arjun

grep_count

Count pattern matches in files or directories using regex or plain text, with options for case sensitivity, whole words, per-file results, and file extensions.

Instructions

Count the number of matches for a pattern

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
patternYesRegular expression pattern or plain text to count
targetYesFile or directory path to search in
case_sensitiveNoWhether the search should be case sensitive
whole_wordsNoMatch whole words only
by_fileNoShow count per file when searching directories
file_extensionsNoOnly search files with these extensions

Implementation Reference

  • The handler function for the 'grep_count' tool. It validates the input path, constructs the grep command with the '-c' flag for counting matches, handles options like case sensitivity, whole words, recursion for directories, file filters, executes the command using executeGrep helper, and formats the output.
    async ({ pattern, target, case_sensitive, whole_words, by_file, file_extensions }) => {
      // Validate target path
      const pathValidation = validatePath(target);
      if (!pathValidation.isValid) {
        return {
          content: [
            {
              type: "text",
              text: `Error: ${pathValidation.error}`,
            },
          ],
        };
      }
    
      const args = ['grep'];
      
      // Add count option
      args.push('-c');
      
      // Use extended regex
      args.push('-E');
      args.push(pattern);
      
      // Add case sensitivity option
      if (!case_sensitive) {
        args.push('-i');
      }
      
      // Add whole words option
      if (whole_words) {
        args.push('-w');
      }
      
      // Add recursive search if target is directory
      if (pathValidation.isDirectory) {
        args.push('-r');
        
        // Add filename display for directory searches
        if (by_file) {
          args.push('-H');
        }
      }
      
      // Add file extension filters
      if (file_extensions && file_extensions.length > 0 && pathValidation.isDirectory) {
        file_extensions.forEach(ext => {
          args.push('--include', `*.${ext}`);
        });
      }
      
      // Add target path
      args.push(target);
      
      try {
        const result = await executeGrep(args);
        
        return {
          content: [
            {
              type: "text",
              text: `Pattern: ${pattern}\nExit Code: ${result.exitCode}\n\nMatch Counts:\n${result.stdout}${result.stderr ? `\n\nErrors:\n${result.stderr}` : ''}`,
            },
          ],
        };
      } catch (error) {
        return {
          content: [
            {
              type: "text",
              text: `Error executing grep: ${error instanceof Error ? error.message : String(error)}`,
            },
          ],
        };
      }
    }
  • Zod schema defining the input parameters for the 'grep_count' tool, including pattern, target path, and various search options.
    {
      pattern: z.string().describe("Regular expression pattern or plain text to count"),
      target: z.string().describe("File or directory path to search in"),
      case_sensitive: z.boolean().optional().default(false).describe("Whether the search should be case sensitive"),
      whole_words: z.boolean().optional().default(false).describe("Match whole words only"),
      by_file: z.boolean().optional().default(false).describe("Show count per file when searching directories"),
      file_extensions: z.array(z.string()).optional().describe("Only search files with these extensions"),
    },
  • src/index.ts:339-425 (registration)
    Registration of the 'grep_count' tool on the MCP server using server.tool(), specifying name, description, input schema, and handler function.
    server.tool(
      "grep_count",
      "Count the number of matches for a pattern",
      {
        pattern: z.string().describe("Regular expression pattern or plain text to count"),
        target: z.string().describe("File or directory path to search in"),
        case_sensitive: z.boolean().optional().default(false).describe("Whether the search should be case sensitive"),
        whole_words: z.boolean().optional().default(false).describe("Match whole words only"),
        by_file: z.boolean().optional().default(false).describe("Show count per file when searching directories"),
        file_extensions: z.array(z.string()).optional().describe("Only search files with these extensions"),
      },
      async ({ pattern, target, case_sensitive, whole_words, by_file, file_extensions }) => {
        // Validate target path
        const pathValidation = validatePath(target);
        if (!pathValidation.isValid) {
          return {
            content: [
              {
                type: "text",
                text: `Error: ${pathValidation.error}`,
              },
            ],
          };
        }
    
        const args = ['grep'];
        
        // Add count option
        args.push('-c');
        
        // Use extended regex
        args.push('-E');
        args.push(pattern);
        
        // Add case sensitivity option
        if (!case_sensitive) {
          args.push('-i');
        }
        
        // Add whole words option
        if (whole_words) {
          args.push('-w');
        }
        
        // Add recursive search if target is directory
        if (pathValidation.isDirectory) {
          args.push('-r');
          
          // Add filename display for directory searches
          if (by_file) {
            args.push('-H');
          }
        }
        
        // Add file extension filters
        if (file_extensions && file_extensions.length > 0 && pathValidation.isDirectory) {
          file_extensions.forEach(ext => {
            args.push('--include', `*.${ext}`);
          });
        }
        
        // Add target path
        args.push(target);
        
        try {
          const result = await executeGrep(args);
          
          return {
            content: [
              {
                type: "text",
                text: `Pattern: ${pattern}\nExit Code: ${result.exitCode}\n\nMatch Counts:\n${result.stdout}${result.stderr ? `\n\nErrors:\n${result.stderr}` : ''}`,
              },
            ],
          };
        } catch (error) {
          return {
            content: [
              {
                type: "text",
                text: `Error executing grep: ${error instanceof Error ? error.message : String(error)}`,
              },
            ],
          };
        }
      }
    );
  • Shared helper function to execute the grep command safely using child_process.spawn, capturing stdout, stderr, and exit code. Used by grep_count and other tools.
    async function executeGrep(args: string[]): Promise<{ stdout: string; stderr: string; exitCode: number }> {
      return new Promise((resolve) => {
        // Ensure we're only calling grep with safe arguments
        if (!args.includes('grep')) {
          args.unshift('grep');
        }
        
        const child = spawn('grep', args.slice(1), {
          stdio: ['pipe', 'pipe', 'pipe'],
          shell: false,
        });
    
        let stdout = '';
        let stderr = '';
    
        child.stdout.on('data', (data) => {
          stdout += data.toString();
        });
    
        child.stderr.on('data', (data) => {
          stderr += data.toString();
        });
    
        child.on('close', (code) => {
          resolve({
            stdout,
            stderr,
            exitCode: code || 0,
          });
        });
    
        child.on('error', (error) => {
          resolve({
            stdout: '',
            stderr: error.message,
            exitCode: 1,
          });
        });
      });
    }
  • Shared helper function to validate if a path exists and determine if it's a directory. Used by grep_count and other tools for input validation.
    function validatePath(path: string): { isValid: boolean; isDirectory: boolean; error?: string } {
      try {
        const resolvedPath = resolve(path);
        if (!existsSync(resolvedPath)) {
          return { isValid: false, isDirectory: false, error: `Path does not exist: ${path}` };
        }
        const stats = statSync(resolvedPath);
        return { isValid: true, isDirectory: stats.isDirectory() };
      } catch (error) {
        return { isValid: false, isDirectory: false, error: `Invalid path: ${path}` };
      }
    }

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/247arjun/mcp-grep'

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