search_code
Search code patterns using regex to find functions, classes, methods, and declarations across your codebase with advanced filtering options.
Instructions
Search for code patterns using ripgrep (very fast). Supports regex patterns and advanced filtering.
PATTERN EXAMPLES:
Exact text: "functionName"
Multiple options: "\b(class|struct|record|interface|enum)\s+TypeName\b" (finds: class TypeName, record TypeName, interface TypeName, etc.)
Regex: "async.Promise<.>" (finds async functions returning Promise)
Any declaration: "\b(public|private|protected)\s+\w+\s+methodName"
COMMON USE CASES:
Find type declaration: "\b(class|struct|interface|record|enum)\s+TypeName\b"
Find method: "\b(public|private|protected|internal).\s+methodName\s\("
Find property: "\bpublic\s+\w+\s+propertyName\s*\{"
Find async methods: "async.*Task<"
Find implementations: ":\s*IInterfaceName\b"
TIPS:
Use \b for word boundaries
Use \s+ for whitespace
Combine alternatives with (opt1|opt2|opt3)
Escape special chars: \( \) \{ \}
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| pattern | Yes | Regex pattern to search. For multiple alternatives use: (class|struct|interface) to match any | |
| path | No | Limit search to specific directory (e.g., 'src/components'). Omit to search entire workspace. | |
| filePattern | No | File glob pattern (ripgrep -g flag). Examples: '*.js', '*.{ts,tsx}', '!*test*' (exclude). Can specify multiple patterns separated by comma. | |
| caseInsensitive | No | Ignore case in search (ripgrep -i). Default: true for LLM-friendly searching | |
| contextLines | No | Lines of context before/after match (ripgrep -C) | |
| maxResults | No | Maximum number of results to return (per page). Default: 100. Configure via MCP_MAX_SEARCH_RESULTS env var. | |
| page | No | Page number for paginated results (1-based). Use to get more results beyond maxResults. | |
| literalString | No | Treat pattern as literal string, not regex (ripgrep -F) | |
| wordBoundary | No | Match whole words only (ripgrep -w) |
Implementation Reference
- index.ts:340-362 (handler)MCP CallTool handler for search_code: validates input parameters using Zod schema and delegates to ripgrepSearch helper function.case 'search_code': { const schema = z.object({ pattern: z.string(), path: z.string().optional(), filePattern: z.string().optional(), caseInsensitive: z.boolean().optional().default(true), contextLines: z.number().optional().default(2), maxResults: z.number().optional().default(DEFAULT_MAX_RESULTS), page: z.number().optional().default(1), literalString: z.boolean().optional().default(false), wordBoundary: z.boolean().optional().default(false) }); const options = schema.parse(args); const result = await ripgrepSearch(options, getAllowedDirectories()); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; }
- index.ts:97-168 (schema)Tool definition for 'search_code' including comprehensive input schema with descriptions, defaults, and examples for ripgrep-powered code search.{ name: 'search_code', description: `Search for code patterns using ripgrep (very fast). Supports regex patterns and advanced filtering. PATTERN EXAMPLES: - Exact text: "functionName" - Multiple options: "\\\\b(class|struct|record|interface|enum)\\\\s+TypeName\\\\b" (finds: class TypeName, record TypeName, interface TypeName, etc.) - Regex: "async.*Promise<.*>" (finds async functions returning Promise) - Any declaration: "\\\\b(public|private|protected)\\\\s+\\\\w+\\\\s+methodName" COMMON USE CASES: - Find type declaration: "\\\\b(class|struct|interface|record|enum)\\\\s+TypeName\\\\b" - Find method: "\\\\b(public|private|protected|internal).*\\\\s+methodName\\\\s*\\\\(" - Find property: "\\\\bpublic\\\\s+\\\\w+\\\\s+propertyName\\\\s*\\\\{" - Find async methods: "async.*Task<" - Find implementations: ":\\\\s*IInterfaceName\\\\b" TIPS: - Use \\\\b for word boundaries - Use \\\\s+ for whitespace - Combine alternatives with (opt1|opt2|opt3) - Escape special chars: \\\\( \\\\) \\\\{ \\\\}`, inputSchema: { type: 'object', properties: { pattern: { type: 'string', description: 'Regex pattern to search. For multiple alternatives use: (class|struct|interface) to match any' }, path: { type: 'string', description: "Limit search to specific directory (e.g., 'src/components'). Omit to search entire workspace." }, filePattern: { type: 'string', description: "File glob pattern (ripgrep -g flag). Examples: '*.js', '*.{ts,tsx}', '!*test*' (exclude). Can specify multiple patterns separated by comma." }, caseInsensitive: { type: 'boolean', description: 'Ignore case in search (ripgrep -i). Default: true for LLM-friendly searching', default: true }, contextLines: { type: 'number', description: 'Lines of context before/after match (ripgrep -C)', default: 2 }, maxResults: { type: 'number', description: `Maximum number of results to return (per page). Default: ${DEFAULT_MAX_RESULTS}. Configure via MCP_MAX_SEARCH_RESULTS env var.`, default: DEFAULT_MAX_RESULTS }, page: { type: 'number', description: 'Page number for paginated results (1-based). Use to get more results beyond maxResults.', default: 1 }, literalString: { type: 'boolean', description: 'Treat pattern as literal string, not regex (ripgrep -F)', default: false }, wordBoundary: { type: 'boolean', description: 'Match whole words only (ripgrep -w)', default: false } }, required: ['pattern'] } },
- ripgrep.ts:58-166 (helper)Core helper function implementing the search_code logic: constructs and executes ripgrep command, parses output, handles pagination and suggestions.export async function ripgrepSearch( options: RipgrepSearchOptions, allowedDirs: string[] ): Promise<RipgrepResult> { const startTime = Date.now(); const args: string[] = ['--json', '--no-config']; // Context lines if (options.contextLines !== undefined && options.contextLines > 0) { args.push('-C', options.contextLines.toString()); } // Case sensitivity if (options.caseInsensitive) { args.push('-i'); } // Literal string (not regex) if (options.literalString) { args.push('-F'); } // Word boundary if (options.wordBoundary) { args.push('-w'); } // File pattern (glob) if (options.filePattern) { // Support multiple patterns separated by comma const patterns = options.filePattern.split(',').map(p => p.trim()); patterns.forEach(pattern => { args.push('-g', pattern); }); } // Note: We don't use ripgrep's -m flag for pagination // Instead, we collect ALL matches and paginate in memory // This allows proper pagination across all results // Pattern args.push(options.pattern); // Search paths (validated) let searchPaths: string[]; if (options.path) { try { const validated = await validatePath(path.join(allowedDirs[0], options.path)); searchPaths = [validated]; } catch (error) { throw new Error(`Invalid search path: ${error instanceof Error ? error.message : String(error)}`); } } else { searchPaths = allowedDirs; } args.push(...searchPaths); const result = await executeRipgrep(args, options.pattern, searchPaths); result.summary.searchTimeMs = Date.now() - startTime; // Apply pagination const page = options.page || 1; const pageSize = options.maxResults || 100; const totalMatches = result.matches.length; const startIdx = (page - 1) * pageSize; const endIdx = startIdx + pageSize; // Paginate matches const paginatedMatches = result.matches.slice(startIdx, endIdx); const totalPages = Math.ceil(totalMatches / pageSize); result.matches = paginatedMatches; result.pagination = { page, pageSize, totalPages, hasMore: endIdx < totalMatches }; // Update summary to reflect total (not paginated) result.summary.totalMatches = totalMatches; // Add helpful suggestions if (totalMatches === 0) { result.suggestions = [ 'No matches found. Try:', '- Using case-insensitive search (caseInsensitive: true)', '- Simplifying the pattern', '- Checking if the file pattern is correct' ]; } else if (result.pagination.hasMore) { result.suggestions = [ `Showing page ${page} of ${totalPages} (${paginatedMatches.length} of ${totalMatches} total matches)`, `To see more results, request page ${page + 1}`, 'Or narrow your search with:', '- More specific pattern', '- File pattern filter (filePattern)', '- Path restriction (path parameter)' ]; } else if (page > 1) { result.suggestions = [ `Showing final page ${page} of ${totalPages} (${paginatedMatches.length} matches on this page, ${totalMatches} total)` ]; } return result; }
- ripgrep.ts:9-19 (schema)TypeScript interface defining RipgrepSearchOptions, mirroring the input schema for type safety in the helper functions.export interface RipgrepSearchOptions { pattern: string; path?: string; filePattern?: string; // -g flag caseInsensitive?: boolean; // -i flag contextLines?: number; // -C flag maxResults?: number; // Results per page page?: number; // Page number (1-based) literalString?: boolean; // -F flag wordBoundary?: boolean; // -w flag }
- index.ts:259-262 (registration)Registration of all tools including search_code via the ListToolsRequestSchema handler that returns the static tools array.// Tool handlers server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools }));