Skip to main content
Glama

search_context

Search codebases using natural language queries to locate relevant code snippets. Automatically indexes projects to provide up-to-date results with file paths and line numbers for development workflows.

Instructions

Search for relevant code context based on a query within a specific project. This tool automatically performs incremental indexing before searching, ensuring results are always up-to-date. Returns formatted text snippets from the codebase that are semantically related to your query. IMPORTANT: Use forward slashes (/) as path separators in project_root_path, even on Windows.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
project_root_pathYesAbsolute path to the project root directory. Use forward slashes (/) as separators. Example: C:/Users/username/projects/myproject
queryYesProvide a clear natural language description of the code behavior, workflow, or issue you want to locate. You may also add optional keywords to improve semantic matching. Recommended format: Natural language description + optional keywords Examples: “I want to find where the server handles chunk merging in the file upload process. Keywords: upload chunk merge, file service” “Locate where the system refreshes cached data after user permissions are updated. Keywords: permission update, cache refresh” “Find the initialization flow of message queue consumers during startup. Keywords: mq consumer init, subscribe” “Show me how configuration hot-reload is triggered and applied in the code. Keywords: config reload, hot update”

Implementation Reference

  • Primary handler function for the 'search_context' MCP tool. Validates input arguments, initializes shared IndexManager, calls search_context on it, and formats the result as MCP content.
    async def search_context_tool(arguments: dict[str, Any]) -> dict[str, Any]:
        """Search for code context based on query.
    
        Args:
            arguments: Tool arguments containing:
                - project_root_path: Absolute path to the project root directory
                - query: Search query string
    
        Returns:
            Dictionary containing search results
    
        """
        try:
            project_root_path = arguments.get("project_root_path")
            query = arguments.get("query")
    
            if not project_root_path:
                return {"type": "text", "text": "Error: project_root_path is required"}
    
            if not query:
                return {"type": "text", "text": "Error: query is required"}
    
            logger.info(f"Tool invoked: search_context for project {project_root_path} with query: {query}")
    
            index_manager = await _get_index_manager()
            result = await index_manager.search_context(project_root_path, query)
    
            return {"type": "text", "text": result}
    
        except Exception as e:
            logger.exception("Error in search_context_tool")
            return {"type": "text", "text": f"Error: {e!s}"}
  • MCP server registration of the 'search_context' tool via @app.list_tools(), defining its metadata, description, and full input schema.
    @app.list_tools()
    async def list_tools() -> list[Tool]:
        """List available MCP tools.
    
        Returns:
            List of available tools
    
        """
        return [
            Tool(
                name="search_context",
                description="Search for relevant code context based on a query within a specific project. "
                "This tool automatically performs incremental indexing before searching, "
                "ensuring results are always up-to-date. "
                "Returns formatted text snippets from the codebase that are semantically related to your query. "
                "IMPORTANT: Use forward slashes (/) as path separators in project_root_path, even on Windows.",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "project_root_path": {
                            "type": "string",
                            "description": "Absolute path to the project root directory. Use forward slashes (/) as separators. Example: C:/Users/username/projects/myproject",
                        },
                        "query": {
                            "type": "string",
                            "description": """Provide a clear natural language description of the code behavior, workflow, or issue you want to locate. You may also add optional keywords to improve semantic matching.
    Recommended format:
    Natural language description + optional keywords
    Examples:
    “I want to find where the server handles chunk merging in the file upload process. Keywords: upload chunk merge, file service”
    “Locate where the system refreshes cached data after user permissions are updated. Keywords: permission update, cache refresh”
    “Find the initialization flow of message queue consumers during startup. Keywords: mq consumer init, subscribe”
    “Show me how configuration hot-reload is triggered and applied in the code. Keywords: config reload, hot update”""",
                        },
                    },
                    "required": ["project_root_path", "query"],
                },
            ),
        ]
  • MCP server dispatcher for tool calls via @app.call_tool(), routing 'search_context' invocations to the search_context_tool handler.
    @app.call_tool()
    async def call_tool(name: str, arguments: dict) -> dict:
        """Handle tool calls.
    
        Args:
            name: Tool name
            arguments: Tool arguments
    
        Returns:
            Tool execution results
    
        """
        logger.info(f"Tool called: {name} with arguments: {arguments}")
    
        if name == "search_context":
            return await search_context_tool(arguments)
    
        return {"type": "text", "text": f"Unknown tool: {name}"}
  • Core helper method in IndexManager that performs automatic incremental indexing followed by semantic search via external API, excluding failed blobs, and returns formatted code context snippets.
    async def search_context(self, project_root_path: str, query: str) -> str:
        """Search for code context based on query with automatic incremental indexing.
    
        This method automatically performs incremental indexing before searching,
        ensuring the search is always performed on the latest codebase.
    
        Args:
            project_root_path: Absolute path to the project root directory
            query: Search query string
    
        Returns:
            Formatted retrieval result
    
        """
        normalized_path = self._normalize_path(project_root_path)
        logger.info(f"Searching context in project {normalized_path} with query: {query}")
    
        try:
            # Step 1: Automatically perform incremental indexing
            logger.info(f"Auto-indexing project {normalized_path} before search...")
            index_result = await self.index_project(project_root_path)
    
            if index_result["status"] == "error":
                return f"Error: Failed to index project before search. {index_result['message']}"
    
            # Log indexing stats
            if "stats" in index_result:
                stats = index_result["stats"]
                logger.info(f"Auto-indexing completed: total={stats['total_blobs']}, existing={stats['existing_blobs']}, new={stats['new_blobs']}")
    
            # Step 2: Load indexed blob names and exclude failed blobs
            projects = self._load_projects()
            all_blob_names = projects.get(normalized_path, [])
    
            if not all_blob_names:
                return f"Error: No blobs found for project {normalized_path} after indexing."
    
            # Get failed blob hashes and exclude them from search
            failed_blob_hashes = self._get_failed_blob_hashes(normalized_path)
            blob_names = [blob_hash for blob_hash in all_blob_names if blob_hash not in failed_blob_hashes]
    
            excluded_count = len(all_blob_names) - len(blob_names)
            if excluded_count > 0:
                logger.info(f"Excluded {excluded_count} failed blobs from search (total available: {len(all_blob_names)}, searching: {len(blob_names)})")
    
            if not blob_names:
                return f"Error: No valid blobs available for search in project {normalized_path}. All {len(all_blob_names)} blobs have failed upload."
    
            # Step 3: Perform search
            logger.info(f"Performing search with {len(blob_names)} blobs (excluded {excluded_count} failed blobs)...")
            payload = {
                "information_request": query,
                "blobs": {
                    "checkpoint_id": None,
                    "added_blobs": blob_names,
                    "deleted_blobs": [],
                },
                "dialog": [],
                "max_output_length": 0,
                "disable_codebase_retrieval": False,
                "enable_commit_retrieval": False,
            }
    
            client = self._get_client()
    
            async def search_request():
                response = await client.post(
                    f"{self.base_url}/agents/codebase-retrieval",
                    headers={"Authorization": f"Bearer {self.token}"},
                    json=payload,
                )
                response.raise_for_status()
                return response.json()
    
            # Retry up to 3 times with exponential backoff
            try:
                result = await self._retry_request(search_request, max_retries=3, retry_delay=2.0)
            except Exception as e:
                logger.error(f"Search request failed after retries: {e}")
                return f"Error: Search request failed after 3 retries. {e!s}"
    
            formatted_retrieval = result.get("formatted_retrieval", "")
    
            if not formatted_retrieval:
                logger.warning(f"Search returned empty result for project {normalized_path}")
                return "No relevant code context found for your query."
    
            logger.info(f"Search completed for project {normalized_path}")
            return formatted_retrieval
    
        except Exception as e:
            logger.exception(f"Failed to search context in project {normalized_path}")
            return f"Error: {e!s}"
  • Shared IndexManager singleton factory used by the tool handler to lazily initialize the index manager with configuration.
    async def _get_index_manager() -> IndexManager:
        """Create or return the shared IndexManager instance."""
        global _index_manager, _index_manager_lock
    
        if _index_manager is not None:
            return _index_manager
    
        if _index_manager_lock is None:
            _index_manager_lock = asyncio.Lock()
    
        async with _index_manager_lock:
            if _index_manager is None:
                config = get_config()
                _index_manager = IndexManager(
                    config.index_storage_path,
                    config.base_url,
                    config.token,
                    config.text_extensions,
                    config.batch_size,
                    config.max_lines_per_blob,
                    config.exclude_patterns,
                )
    
        return _index_manager

Tool Definition Quality

Score is being calculated. Check back soon.

Install Server

Other 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/qy527145/acemcp'

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