search
Search files for patterns using ripgrep (rg). Define patterns, specify paths, filter by file type, and control case sensitivity. Ideal for high-performance text searches across directories or specific files.
Instructions
Search files for patterns using ripgrep (rg)
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| caseSensitive | No | Use case sensitive search (default: auto) | |
| context | No | Show N lines before and after each match | |
| filePattern | No | Filter by file type or glob | |
| maxResults | No | Limit the number of matching lines | |
| path | Yes | Directory or file(s) to search. | |
| pattern | Yes | The search pattern (regex by default) | |
| useColors | No | Use colors in output (default: false) |
Implementation Reference
- src/index.ts:194-260 (handler)The handler logic for the 'search' tool. It builds a ripgrep (rg) command using the provided parameters like pattern, path, case sensitivity, etc., escapes arguments, executes the command via the exec helper, processes the output (stripping ANSI if no colors), and returns the result or error.case "search": { const pattern = String(args.pattern || ""); const path = String(args.path); const caseSensitive = typeof args.caseSensitive === 'boolean' ? args.caseSensitive : undefined; const filePattern = args.filePattern ? String(args.filePattern) : undefined; const maxResults = typeof args.maxResults === 'number' ? args.maxResults : undefined; const context = typeof args.context === 'number' ? args.context : undefined; const useColors = typeof args.useColors === 'boolean' ? args.useColors : false; if (!pattern) { return { isError: true, content: [{ type: "text", text: "Error: Pattern is required" }] }; } // Build the rg command with flags let command = "rg"; // Add case sensitivity flag if specified if (caseSensitive === true) { command += " -s"; // Case sensitive } else if (caseSensitive === false) { command += " -i"; // Case insensitive } // Add file pattern if specified if (filePattern) { command += ` -g ${escapeShellArg(filePattern)}`; } // Add max results if specified if (maxResults !== undefined && maxResults > 0) { command += ` -m ${maxResults}`; } // Add context lines if specified if (context !== undefined && context > 0) { command += ` -C ${context}`; } // Add line numbers command += " -n"; // Add color setting command += useColors ? " --color always" : " --color never"; // Add pattern and path command += ` ${escapeShellArg(pattern)} ${escapeShellArg(path)}`; console.error(`Executing: ${command}`); const { stdout, stderr } = await exec(command); // If there's anything in stderr, log it for debugging if (stderr) { console.error(`ripgrep stderr: ${stderr}`); } return { content: [ { type: "text", text: processOutput(stdout, useColors) || "No matches found" } ] }; }
- src/index.ts:101-111 (schema)The input schema defining parameters for the 'search' tool, including required pattern and path, and optional flags for case sensitivity, file patterns, limits, context, and colors.type: "object", properties: { pattern: { type: "string", description: "The search pattern (regex by default)" }, path: { type: "string", description: "Directory or file(s) to search." }, caseSensitive: { type: "boolean", description: "Use case sensitive search (default: auto)" }, filePattern: { type: "string", description: "Filter by file type or glob" }, maxResults: { type: "number", description: "Limit the number of matching lines" }, context: { type: "number", description: "Show N lines before and after each match" }, useColors: { type: "boolean", description: "Use colors in output (default: false)" } }, required: ["pattern", "path"]
- src/index.ts:97-113 (registration)The registration of the 'search' tool in the listTools response, including name, description, and input schema.{ name: "search", description: "Search files for patterns using ripgrep (rg)", inputSchema: { type: "object", properties: { pattern: { type: "string", description: "The search pattern (regex by default)" }, path: { type: "string", description: "Directory or file(s) to search." }, caseSensitive: { type: "boolean", description: "Use case sensitive search (default: auto)" }, filePattern: { type: "string", description: "Filter by file type or glob" }, maxResults: { type: "number", description: "Limit the number of matching lines" }, context: { type: "number", description: "Show N lines before and after each match" }, useColors: { type: "boolean", description: "Use colors in output (default: false)" } }, required: ["pattern", "path"] } },
- src/index.ts:51-91 (helper)Helper function to execute shell commands safely using node's spawn, capturing stdout/stderr, handling ripgrep exit code 1 as success (no matches). Used by the search handler.function exec(command: string): Promise<{ stdout: string; stderr: string }> { return new Promise((resolve, reject) => { const parts = command.split(" "); const program = parts[0]; const args = parts.slice(1).filter(arg => arg.length > 0); // Use spawn with explicit stdio control const child = spawn(program, args, { shell: true, // Use shell to handle quotes and escaping }); let stdout = ""; let stderr = ""; child.stdout.setEncoding("utf8"); child.stderr.setEncoding("utf8"); child.stdout.on("data", (data) => { stdout += data; }); child.stderr.on("data", (data) => { stderr += data; }); child.on("close", (code) => { if (code === 0 || code === 1) { // Code 1 is "no matches" for ripgrep resolve({ stdout, stderr }); } else { const error = new Error(`Command failed with exit code ${code}`); Object.assign(error, { code, stdout, stderr }); reject(error); } }); // Handle process errors child.on("error", (error) => { reject(error); }); }); }
- src/index.ts:41-45 (helper)Helper function to safely escape shell arguments for use in rg commands, preventing injection.function escapeShellArg(arg: string): string { // Replace all single quotes with the sequence: '"'"' // This ensures the argument is properly quoted in shell commands return `'${arg.replace(/'/g, "'\"'\"'")}'`; }