"""MCP tools for search operations."""
import logging
from ..services.search_service import SearchService
from ..core.constants import DEFAULT_ROOT
logger = logging.getLogger(__name__)
# Default configuration
READ_ONLY = False
MAX_FILE_SIZE_MB = 100
MAX_SEARCH_DEPTH = 10
def register_search_routes(server):
"""Register all search MCP tools."""
@server.tool(
name="search_by_name", description="Search for files and directories by exact name match"
)
def search_by_name(
name: str, path: str = ".", recursive: bool = True, case_sensitive: bool = False
) -> dict:
"""
Search for files and directories by exact name match.
Args:
name: Exact name to search for
path: Starting directory for search (default: root)
recursive: Search subdirectories recursively (default: True)
case_sensitive: Perform case-sensitive matching (default: False)
Returns:
Dictionary with search results
"""
logger.info(
f"API: search_by_name - name={name}, path={path}, "
f"recursive={recursive}, case_sensitive={case_sensitive}"
)
service = SearchService(
root=DEFAULT_ROOT, max_search_depth=MAX_SEARCH_DEPTH, max_file_size_mb=MAX_FILE_SIZE_MB
)
result = service.search_by_name(name, path, recursive, case_sensitive)
logger.debug(f"API: search_by_name - found {result.total_matches} matches")
return result.model_dump()
@server.tool(
name="search_by_glob",
description="Search using glob patterns (*.txt, **/*.py) with wildcard support",
)
def search_by_glob(pattern: str, path: str = ".", recursive: bool = True) -> dict:
"""
Search for files and directories using glob patterns.
Supports wildcards: * (any chars), ** (recursive), ? (single char), [abc] (char class)
Args:
pattern: Glob pattern (e.g., "*.txt", "**/*.py")
path: Starting directory for search (default: root)
recursive: Use recursive globbing (default: True)
Returns:
Dictionary with search results
"""
logger.info(f"API: search_by_glob - pattern={pattern}, path={path}, recursive={recursive}")
service = SearchService(
root=DEFAULT_ROOT, max_search_depth=MAX_SEARCH_DEPTH, max_file_size_mb=MAX_FILE_SIZE_MB
)
result = service.search_by_glob(pattern, path, recursive)
logger.debug(f"API: search_by_glob - found {result.total_matches} matches")
return result.model_dump()
@server.tool(
name="search_by_filename_regex",
description="Search for files and directories using regex pattern on filenames",
)
def search_by_filename_regex(
regex_pattern: str, path: str = ".", recursive: bool = True, case_sensitive: bool = False
) -> dict:
"""
Search for files and directories using regex pattern on filename.
Args:
regex_pattern: Regular expression pattern
path: Starting directory for search (default: root)
recursive: Search subdirectories recursively (default: True)
case_sensitive: Perform case-sensitive matching (default: False)
Returns:
Dictionary with search results
"""
logger.info(
f"API: search_by_filename_regex - regex_pattern={regex_pattern}, "
f"path={path}, recursive={recursive}, case_sensitive={case_sensitive}"
)
service = SearchService(
root=DEFAULT_ROOT, max_search_depth=MAX_SEARCH_DEPTH, max_file_size_mb=MAX_FILE_SIZE_MB
)
result = service.search_by_filename_regex(regex_pattern, path, recursive, case_sensitive)
logger.debug(f"API: search_by_filename_regex - found {result.total_matches} matches")
return result.model_dump()
@server.tool(
name="search_by_content_text",
description="Search for text files containing specific text with context preview",
)
def search_by_content_text(
query: str,
path: str = ".",
recursive: bool = True,
case_sensitive: bool = False,
max_results: int = 100,
) -> dict:
"""
Search for files containing specific text.
Only searches text files (binary files are skipped).
Args:
query: Text to search for
path: Starting directory for search (default: root)
recursive: Search subdirectories recursively (default: True)
case_sensitive: Perform case-sensitive matching (default: False)
max_results: Maximum number of results to return (default: 100)
Returns:
Dictionary with search results
"""
logger.info(
f"API: search_by_content_text - query={query}, path={path}, "
f"recursive={recursive}, case_sensitive={case_sensitive}, max_results={max_results}"
)
service = SearchService(
root=DEFAULT_ROOT, max_search_depth=MAX_SEARCH_DEPTH, max_file_size_mb=MAX_FILE_SIZE_MB
)
result = service.search_by_content_text(query, path, recursive, case_sensitive, max_results)
logger.debug(
f"API: search_by_content_text - found {result.total_matches}"
f" matches, truncated={result.truncated}"
)
return result.model_dump()
@server.tool(
name="search_by_content_regex",
description="Search for text files using regex pattern on content with context preview",
)
def search_by_content_regex(
regex_pattern: str,
path: str = ".",
recursive: bool = True,
case_sensitive: bool = False,
max_results: int = 100,
) -> dict:
"""
Search for files containing text matching a regex pattern.
Only searches text files (binary files are skipped).
Args:
regex_pattern: Regular expression pattern
path: Starting directory for search (default: root)
recursive: Search subdirectories recursively (default: True)
case_sensitive: Perform case-sensitive matching (default: False)
max_results: Maximum number of results to return (default: 100)
Returns:
Dictionary with search results
"""
logger.info(
f"API: search_by_content_regex - regex_pattern={regex_pattern}, "
f"path={path}, recursive={recursive}, case_sensitive={case_sensitive}, "
f"max_results={max_results}"
)
service = SearchService(
root=DEFAULT_ROOT, max_search_depth=MAX_SEARCH_DEPTH, max_file_size_mb=MAX_FILE_SIZE_MB
)
result = service.search_by_content_regex(
regex_pattern, path, recursive, case_sensitive, max_results
)
logger.debug(
f"API: search_by_content_regex - found {result.total_matches} matches, "
f"truncated={result.truncated}"
)
return result.model_dump()