Skip to main content
Glama
lofcz

MCP Smart Filesystem Server

by lofcz

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

TableJSON Schema
NameRequiredDescriptionDefault
patternYesRegex pattern to search. For multiple alternatives use: (class|struct|interface) to match any
pathNoLimit search to specific directory (e.g., 'src/components'). Omit to search entire workspace.
filePatternNoFile glob pattern (ripgrep -g flag). Examples: '*.js', '*.{ts,tsx}', '!*test*' (exclude). Can specify multiple patterns separated by comma.
caseInsensitiveNoIgnore case in search (ripgrep -i). Default: true for LLM-friendly searching
contextLinesNoLines of context before/after match (ripgrep -C)
maxResultsNoMaximum number of results to return (per page). Default: 100. Configure via MCP_MAX_SEARCH_RESULTS env var.
pageNoPage number for paginated results (1-based). Use to get more results beyond maxResults.
literalStringNoTreat pattern as literal string, not regex (ripgrep -F)
wordBoundaryNoMatch whole words only (ripgrep -w)

Implementation Reference

  • 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) }] }; }
  • 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'] } },
  • 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; }
  • 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 }));

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/lofcz/mcp-filesystem-smart'

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