search_code
Search code in files by specifying a path and pattern, with options for case sensitivity, hidden files, and context lines.
Instructions
Search code
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes | ||
| pattern | Yes | ||
| ignoreCase | No | ||
| includeHidden | No | ||
| contextLines | No | ||
| maxResults | No | ||
| timeoutMs | No |
Implementation Reference
- src/tools/search_tools.js:25-210 (handler)Main handler function that executes the 'search_code' tool logic using ripgrep, with fallbacks to grep and Node.js file scanning.
async function searchCode(searchPath, pattern, options = {}) { try { // Validate inputs if (!searchPath || !pattern) { return { success: false, message: 'Both path and pattern are required' }; } // Resolve the search path const resolvedPath = path.resolve(searchPath); // Check if path exists if (!fs.existsSync(resolvedPath)) { return { success: false, message: `Path not found: ${resolvedPath}` }; } // Check if pattern looks like a file pattern (e.g., *.js, *.py) let searchPattern = pattern; let filePattern = options.filePattern; if (pattern.startsWith('*.') || pattern.includes('*.')) { // This is likely a file pattern, not a search pattern filePattern = pattern; searchPattern = '.'; // Search for any character (i.e., any line) } // Build ripgrep arguments const args = []; // Add search pattern args.push(searchPattern); // Add path args.push(resolvedPath); // Add options if (options.ignoreCase !== false) { args.push('-i'); // Case insensitive by default } if (filePattern) { args.push('-g', filePattern); } if (options.contextLines && options.contextLines > 0) { args.push('-C', options.contextLines.toString()); } if (options.includeHidden) { args.push('--hidden'); } // Always include line numbers args.push('-n'); // Add JSON output for easier parsing args.push('--json'); // Limit results const maxResults = options.maxResults || 100; args.push('--max-count', maxResults.toString()); // Set timeout const timeout = options.timeoutMs || 30000; // Execute ripgrep const results = await new Promise((resolve, reject) => { const rg = spawn(ripgrepPath, args, { cwd: process.cwd(), timeout }); let stdout = ''; let stderr = ''; rg.stdout.on('data', (data) => { stdout += data.toString(); }); rg.stderr.on('data', (data) => { stderr += data.toString(); }); rg.on('error', (error) => { if (error.code === 'ENOENT') { reject(new Error('ripgrep not found. Please install ripgrep to use code search.')); } else { reject(error); } }); rg.on('close', (code) => { if (code === 0 || code === 1) { // 0 = found matches, 1 = no matches resolve({ stdout, stderr, code }); } else { reject(new Error(`ripgrep exited with code ${code}: ${stderr}`)); } }); // Handle timeout setTimeout(() => { rg.kill('SIGTERM'); reject(new Error('Search timeout exceeded')); }, timeout); }); // Parse results const matches = []; const lines = results.stdout.split('\n').filter(line => line.trim()); for (const line of lines) { try { const json = JSON.parse(line); if (json.type === 'match') { const match = { path: json.data.path.text, lineNumber: json.data.line_number, line: json.data.lines.text.trim(), column: json.data.submatches[0]?.start || 0, matchText: json.data.submatches[0]?.match.text || '' }; // Make path relative to search path for cleaner output match.relativePath = path.relative(resolvedPath, match.path); matches.push(match); } } catch (e) { // Skip invalid JSON lines } } // Format content for display let content = ''; if (matches.length === 0) { content = `No matches found for pattern "${pattern}" in ${resolvedPath}`; } else { const displayMatches = matches.slice(0, maxResults); content = `Found ${matches.length} matches for pattern "${pattern}":\n\n`; for (const match of displayMatches) { content += `${match.relativePath || match.path}:${match.lineNumber}: ${match.line}\n`; } if (matches.length > maxResults) { content += `\n... and ${matches.length - maxResults} more matches (truncated)`; } } return { success: true, content, searchPath: resolvedPath, pattern: searchPattern, filePattern, matchCount: matches.length, matches: matches.slice(0, maxResults), truncated: matches.length > maxResults, usingRipgrep: true, ripgrepPath }; } catch (error) { logger.error(`Error in searchCode: ${error.message}`); // Check if it's a ripgrep not found error if (error.message.includes('ripgrep not found') || error.message.includes('ENOENT')) { // Try platform-specific fallback if (process.platform === 'win32') { return searchCodeWindowsFallback(searchPath, pattern, options); } return searchCodeFallback(searchPath, pattern, options); } return { success: false, message: error.message }; } } - src/mcp/server.js:104-104 (schema)Register the 'search_code' tool with its input schema (path, pattern, ignoreCase, includeHidden, contextLines, maxResults, timeoutMs).
{ name:'search_code', description:'Search code', inputSchema:{ type:'object', properties:{ path:{type:'string'}, pattern:{type:'string'}, ignoreCase:{type:'boolean'}, includeHidden:{type:'boolean'}, contextLines:{type:'number'}, maxResults:{type:'number'}, timeoutMs:{type:'number'} }, required:['path','pattern'] } }, - src/mcp/server.js:104-137 (registration)Tool listed in the tools array returned by 'tools/list' handler.
{ name:'search_code', description:'Search code', inputSchema:{ type:'object', properties:{ path:{type:'string'}, pattern:{type:'string'}, ignoreCase:{type:'boolean'}, includeHidden:{type:'boolean'}, contextLines:{type:'number'}, maxResults:{type:'number'}, timeoutMs:{type:'number'} }, required:['path','pattern'] } }, { name:'edit_block', description:'Edit block', inputSchema:{ type:'object', properties:{ file_path:{type:'string'}, old_string:{type:'string'}, new_string:{type:'string'}, expected_replacements:{type:'number'}, normalize_whitespace:{type:'boolean'} }, required:['file_path','old_string','new_string'] } }, // Enhanced FS { name:'read_url', description:'Read URL', inputSchema:{ type:'object', properties:{ path:{type:'string'}, timeout:{type:'number'} }, required:['path'] } }, // Watcher { name:'start_file_watcher', description:'Start file watcher', inputSchema:{ type:'object', properties:{ debounceDelay:{type:'number'} } } }, { name:'stop_file_watcher', description:'Stop file watcher', inputSchema:{ type:'object', properties:{} } }, { name:'file_watcher_status', description:'File watcher status', inputSchema:{ type:'object', properties:{} } }, { name:'force_sync', description:'Force sync task files', inputSchema:{ type:'object', properties:{} } }, // Priority ops { name:'bump_task_priority', description:'Bump task priority', inputSchema:{ type:'object', properties:{ id:{type:'string'}, amount:{type:'number'} }, required:['id'] } }, { name:'defer_task_priority', description:'Defer task priority', inputSchema:{ type:'object', properties:{ id:{type:'string'}, amount:{type:'number'} }, required:['id'] } }, { name:'prioritize_task', description:'Set high priority', inputSchema:{ type:'object', properties:{ id:{type:'string'}, priority:{type:'number'} }, required:['id'] } }, { name:'deprioritize_task', description:'Set low priority', inputSchema:{ type:'object', properties:{ id:{type:'string'}, priority:{type:'number'} }, required:['id'] } }, // Algorithm config { name:'configure_time_decay', description:'Configure time decay', inputSchema:{ type:'object', properties:{ enabled:{type:'boolean'}, halfLifeDays:{type:'number'} } } }, { name:'configure_effort_weighting', description:'Configure effort weighting', inputSchema:{ type:'object', properties:{ enabled:{type:'boolean'}, weight:{type:'number'} } } }, { name:'show_algorithm_config', description:'Show algorithm config', inputSchema:{ type:'object', properties:{} } }, // Browser & AppleScript { name:'browser_navigate', description:'Navigate URL', inputSchema:{ type:'object', properties:{ url:{type:'string'} }, required:['url'] } }, { name:'applescript_execute', description:'Run AppleScript (macOS)', inputSchema:{ type:'object', properties:{ code_snippet:{type:'string'}, timeout:{type:'number'} }, required:['code_snippet'] } } ]; // Add extended browser tool schemas to exceed 50 tools (matches prior capability set) const browserExtras = [ { n:'browser_navigate_back' }, { n:'browser_navigate_forward' }, { n:'browser_hover' }, { n:'browser_drag' }, { n:'browser_select_option' }, { n:'browser_press_key' }, { n:'browser_snapshot' }, { n:'browser_console_messages' }, { n:'browser_network_requests' }, { n:'browser_tab_list' }, { n:'browser_tab_new' }, { n:'browser_tab_select' }, { n:'browser_tab_close' }, { n:'browser_file_upload' }, { n:'browser_wait' }, { n:'browser_wait_for' }, { n:'browser_resize' }, { n:'browser_handle_dialog' } ]; for (const b of browserExtras) { tools.push({ name: b.n, description: b.n.replace(/_/g,' '), inputSchema: { type:'object', properties:{} } }); } sendResponse(id, { tools }); - src/mcp/server.js:267-267 (registration)Switch case dispatching 'search_code' tool calls to the searchTools.searchCode function.
case 'search_code': data = await searchTools.searchCode(args.path, args.pattern, args); break; - src/tools/search_tools.js:405-407 (helper)Module exports exposing the searchCode function for use by the MCP server.
module.exports = { searchCode };