search_file
Find regex patterns in a file and display matching lines with specified context lines before and after each match for precise file analysis.
Instructions
Search for regex patterns in a file and show matching lines with context.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| file_path | Yes | Absolute path to the file | |
| lines_after | No | Number of lines to show after each match | |
| lines_before | No | Number of lines to show before each match | |
| regexp | Yes | Regular expression pattern to search for |
Input Schema (JSON Schema)
{
"$schema": "http://json-schema.org/draft-07/schema#",
"additionalProperties": false,
"properties": {
"file_path": {
"description": "Absolute path to the file",
"type": "string"
},
"lines_after": {
"description": "Number of lines to show after each match",
"minimum": 0,
"type": "integer"
},
"lines_before": {
"description": "Number of lines to show before each match",
"minimum": 0,
"type": "integer"
},
"regexp": {
"description": "Regular expression pattern to search for",
"type": "string"
}
},
"required": [
"file_path",
"regexp"
],
"type": "object"
}
Implementation Reference
- src/index.ts:444-480 (handler)The main handler function for the 'search_file' tool. It validates the file path, reads the file contents, splits into lines, searches each line using the provided regex, and for each match, includes context lines before and after with line numbers and a '>' marker on the matching line. Returns formatted matches or no-match message.execute: async ({ file_path, regexp, lines_before, lines_after }) => { const absolutePath = validateAbsolutePath(file_path, 'file_path'); validateFileExists(absolutePath); try { const content = fs.readFileSync(absolutePath, 'utf-8'); const lines = content.split('\n'); const regex = new RegExp(regexp); const matches: string[] = []; lines.forEach((line, index) => { if (regex.test(line)) { const lineNumber = index + 1; // 1-based const startLine = Math.max(1, lineNumber - (lines_before || 0)); const endLine = Math.min(lines.length, lineNumber + (lines_after || 0)); const contextLines = lines.slice(startLine - 1, endLine); const context = contextLines.map((ctxLine, ctxIndex) => { const ctxLineNumber = startLine + ctxIndex; const marker = ctxLineNumber === lineNumber ? '>' : ' '; return `${marker} ${ctxLineNumber} | ${ctxLine}`; }).join('\n'); matches.push(`Match at line ${lineNumber}:\n${context}`); } }); if (matches.length === 0) { return `No matches found for pattern "${regexp}" in file "${absolutePath}".`; } return matches.join('\n\n'); } catch (error: any) { if (error instanceof UserError) throw error; throw new UserError(`Error searching file "${absolutePath}": ${error.message}`); } }
- src/index.ts:438-443 (schema)Zod schema defining the input parameters for the 'search_file' tool: file_path (required string), regexp (required string), lines_before and lines_after (optional non-negative integers).parameters: z.object({ file_path: z.string().describe('Absolute path to the file'), regexp: z.string().describe('Regular expression pattern to search for'), lines_before: z.number().int().min(0).optional().describe('Number of lines to show before each match'), lines_after: z.number().int().min(0).optional().describe('Number of lines to show after each match') }),
- src/index.ts:435-481 (registration)The registration of the 'search_file' tool via server.addTool(), including name, description, parameters schema, and inline handler.server.addTool({ name: 'search_file', description: 'Search for regex patterns in a file and show matching lines with context.', parameters: z.object({ file_path: z.string().describe('Absolute path to the file'), regexp: z.string().describe('Regular expression pattern to search for'), lines_before: z.number().int().min(0).optional().describe('Number of lines to show before each match'), lines_after: z.number().int().min(0).optional().describe('Number of lines to show after each match') }), execute: async ({ file_path, regexp, lines_before, lines_after }) => { const absolutePath = validateAbsolutePath(file_path, 'file_path'); validateFileExists(absolutePath); try { const content = fs.readFileSync(absolutePath, 'utf-8'); const lines = content.split('\n'); const regex = new RegExp(regexp); const matches: string[] = []; lines.forEach((line, index) => { if (regex.test(line)) { const lineNumber = index + 1; // 1-based const startLine = Math.max(1, lineNumber - (lines_before || 0)); const endLine = Math.min(lines.length, lineNumber + (lines_after || 0)); const contextLines = lines.slice(startLine - 1, endLine); const context = contextLines.map((ctxLine, ctxIndex) => { const ctxLineNumber = startLine + ctxIndex; const marker = ctxLineNumber === lineNumber ? '>' : ' '; return `${marker} ${ctxLineNumber} | ${ctxLine}`; }).join('\n'); matches.push(`Match at line ${lineNumber}:\n${context}`); } }); if (matches.length === 0) { return `No matches found for pattern "${regexp}" in file "${absolutePath}".`; } return matches.join('\n\n'); } catch (error: any) { if (error instanceof UserError) throw error; throw new UserError(`Error searching file "${absolutePath}": ${error.message}`); } } });