Skip to main content
Glama
wrale

mcp-server-tree-sitter

by wrale

find_text

Search for specific text patterns within project files using customizable filters like file type, regex, case sensitivity, and whole-word matching. Supports context lines for better result understanding.

Instructions

Search for text pattern in project files.

    Args:
        project: Project name
        pattern: Text pattern to search for
        file_pattern: Optional glob pattern (e.g., "**/*.py")
        max_results: Maximum number of results
        case_sensitive: Whether to do case-sensitive matching
        whole_word: Whether to match whole words only
        use_regex: Whether to treat pattern as a regular expression
        context_lines: Number of context lines to include

    Returns:
        List of matches with file, line number, and text
    

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
case_sensitiveNo
context_linesNo
file_patternNo
max_resultsNo
patternYes
projectYes
use_regexNo
whole_wordNo

Implementation Reference

  • The MCP tool handler function for 'find_text', decorated with @mcp_server.tool() for registration. It handles dependency injection, project resolution, and delegates to the core search_text helper.
    def find_text(
        project: str,
        pattern: str,
        file_pattern: Optional[str] = None,
        max_results: int = 100,
        case_sensitive: bool = False,
        whole_word: bool = False,
        use_regex: bool = False,
        context_lines: int = 2,
    ) -> List[Dict[str, Any]]:
        """Search for text pattern in project files.
    
        Args:
            project: Project name
            pattern: Text pattern to search for
            file_pattern: Optional glob pattern (e.g., "**/*.py")
            max_results: Maximum number of results
            case_sensitive: Whether to do case-sensitive matching
            whole_word: Whether to match whole words only
            use_regex: Whether to treat pattern as a regular expression
            context_lines: Number of context lines to include
    
        Returns:
            List of matches with file, line number, and text
        """
        from ..tools.search import search_text
    
        config = config_manager.get_config()
    
        return search_text(
            project_registry.get_project(project),
            pattern,
            file_pattern,
            max_results if max_results is not None else config.max_results_default,
            case_sensitive,
            whole_word,
            use_regex,
            context_lines,
        )
  • Core helper function implementing the text search logic: prepares regex or string patterns, scans files using glob, processes matches with context lines in parallel using ThreadPoolExecutor, and returns structured results.
    def search_text(
        project: Any,
        pattern: str,
        file_pattern: Optional[str] = None,
        max_results: int = 100,
        case_sensitive: bool = False,
        whole_word: bool = False,
        use_regex: bool = False,
        context_lines: int = 0,
    ) -> List[Dict[str, Any]]:
        """
        Search for text pattern in project files.
    
        Args:
            project: Project object
            pattern: Text pattern to search for
            file_pattern: Optional glob pattern to filter files (e.g. "**/*.py")
            max_results: Maximum number of results to return
            case_sensitive: Whether to do case-sensitive matching
            whole_word: Whether to match whole words only
            use_regex: Whether to treat pattern as a regular expression
            context_lines: Number of context lines to include before/after matches
    
        Returns:
            List of matches with file, line number, and text
        """
        root = project.root_path
    
        results: List[Dict[str, Any]] = []
        pattern_obj = None
    
        # Prepare the pattern
        if use_regex:
            try:
                flags = 0 if case_sensitive else re.IGNORECASE
                pattern_obj = re.compile(pattern, flags)
            except re.error as e:
                raise ValueError(f"Invalid regular expression: {e}") from e
        elif whole_word:
            # Escape pattern for use in regex and add word boundary markers
            pattern_escaped = re.escape(pattern)
            flags = 0 if case_sensitive else re.IGNORECASE
            pattern_obj = re.compile(rf"\b{pattern_escaped}\b", flags)
        elif not case_sensitive:
            # For simple case-insensitive search
            pattern = pattern.lower()
    
        file_pattern = file_pattern or "**/*"
    
        # Process files in parallel
        def process_file(file_path: Path) -> List[Dict[str, Any]]:
            file_results = []
            try:
                validate_file_access(file_path, root)
    
                with open(file_path, "r", encoding="utf-8", errors="replace") as f:
                    lines = f.readlines()
    
                for i, line in enumerate(lines, 1):
                    match = False
    
                    if pattern_obj:
                        # Using regex pattern
                        match_result = pattern_obj.search(line)
                        match = bool(match_result)
                    elif case_sensitive:
                        # Simple case-sensitive search - check both original and stripped versions
                        match = pattern in line or pattern.strip() in line.strip()
                    else:
                        # Simple case-insensitive search - check both original and stripped versions
                        line_lower = line.lower()
                        pattern_lower = pattern.lower()
                        match = pattern_lower in line_lower or pattern_lower.strip() in line_lower.strip()
    
                    if match:
                        # Calculate context lines
                        start = max(0, i - 1 - context_lines)
                        end = min(len(lines), i + context_lines)
    
                        context = []
                        for ctx_i in range(start, end):
                            ctx_line = lines[ctx_i].rstrip("\n")
                            context.append(
                                {
                                    "line": ctx_i + 1,
                                    "text": ctx_line,
                                    "is_match": ctx_i == i - 1,
                                }
                            )
    
                        file_results.append(
                            {
                                "file": str(file_path.relative_to(root)),
                                "line": i,
                                "text": line.rstrip("\n"),
                                "context": context,
                            }
                        )
    
                        if len(file_results) >= max_results:
                            break
            except Exception:
                # Skip files that can't be read
                pass
    
            return file_results
    
        # Collect files to process
        files_to_process = []
        for path in root.glob(file_pattern):
            if path.is_file():
                files_to_process.append(path)
    
        # Process files in parallel
        with concurrent.futures.ThreadPoolExecutor() as executor:
            futures = [executor.submit(process_file, f) for f in files_to_process]
            for future in concurrent.futures.as_completed(futures):
                results.extend(future.result())
                if len(results) >= max_results:
                    # Cancel any pending futures
                    for f in futures:
                        f.cancel()
                    break
    
        return results[:max_results]

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/wrale/mcp-server-tree-sitter'

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