search_notes
Search notes by content, tags, or category to find information in your knowledge base. Filter results using specific paths or tags for targeted retrieval.
Instructions
Search through all notes by query, category path, or tags
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| query | No | Search term (searches title, content, tags) - case insensitive | |
| category_path | No | Optional category path filter (e.g., 'work/clients'). Searches in this path and all subcategories by default. | |
| tags | No | Optional comma-separated tags to filter by (matches any) | |
| recursive | No | If true, search in subcategories too (default: true) |
Implementation Reference
- src/knowledge_base_mcp/server.py:443-464 (handler)The handler function for the 'search_notes' MCP tool. Parses input arguments, handles tag parsing, and delegates to the KnowledgeBaseSearch engine to perform the search and format results.async def handle_search_notes(arguments: dict) -> list[TextContent]: """Handle search_notes tool call.""" query = arguments.get("query", "") category_path = arguments.get("category_path") tags_str = arguments.get("tags") recursive = arguments.get("recursive", True) # Parse tags tags = None if tags_str: tags = [tag.strip() for tag in tags_str.split(",") if tag.strip()] # Search result = search_engine.search_formatted( query=query, category_path=category_path, tags=tags, recursive=recursive ) return [TextContent(type="text", text=result)]
- Input schema (JSON Schema) defining parameters for the search_notes tool: query, category_path, tags, and recursive.inputSchema={ "type": "object", "properties": { "query": { "type": "string", "description": "Search term (searches title, content, tags) - case insensitive", "default": "", }, "category_path": { "type": "string", "description": "Optional category path filter (e.g., 'work/clients'). Searches in this path and all subcategories by default.", }, "tags": { "type": "string", "description": "Optional comma-separated tags to filter by (matches any)", }, "recursive": { "type": "boolean", "description": "If true, search in subcategories too (default: true)", "default": True, }, }, },
- src/knowledge_base_mcp/server.py:165-191 (registration)Registers the search_notes tool in the MCP server's list_tools() method, including name, description, and schema.Tool( name="search_notes", description="Search through all notes by query, category path, or tags", inputSchema={ "type": "object", "properties": { "query": { "type": "string", "description": "Search term (searches title, content, tags) - case insensitive", "default": "", }, "category_path": { "type": "string", "description": "Optional category path filter (e.g., 'work/clients'). Searches in this path and all subcategories by default.", }, "tags": { "type": "string", "description": "Optional comma-separated tags to filter by (matches any)", }, "recursive": { "type": "boolean", "description": "If true, search in subcategories too (default: true)", "default": True, }, }, }, ),
- Helper method in KnowledgeBaseSearch that performs the search and formats the results as a readable string for the tool response.def search_formatted( self, query: str = "", category_path: Optional[str] = None, tags: Optional[list[str]] = None, recursive: bool = True ) -> str: """ Search and return formatted results as a string. Args: query: Search term category_path: Optional category path filter (e.g., "work/clients") tags: Optional list of tags to filter by recursive: If True, search in subcategories too (default: True) Returns: Formatted search results string """ results = self.search( query=query, category_path=category_path, tags=tags, recursive=recursive ) if not results: return "No results found." # Format results output_lines = [f"Found {len(results)} result(s):\n"] for result in results: output_lines.append(str(result)) output_lines.append("") # Blank line between results return "\n".join(output_lines)
- Core search logic: retrieves notes, applies filters for tags and category, computes relevance scores, sorts results.def search( self, query: str = "", category_path: Optional[str] = None, tags: Optional[list[str]] = None, recursive: bool = True ) -> list[SearchResult]: """ Search through all notes. Args: query: Search term (searches title, content, tags) - case insensitive category_path: Optional category path filter (e.g., "work/clients") tags: Optional list of tags to filter by (matches any) recursive: If True, search in subcategories too (default: True) Returns: List of SearchResult objects sorted by relevance """ # Get all notes (potentially filtered by category path) all_notes = self.storage.list_notes( category_path=category_path, recursive=recursive ) results = [] for note in all_notes: # Apply tag filter if specified (match any tag) if tags: note_tags_lower = [t.lower() for t in note.frontmatter.tags] search_tags_lower = [t.lower() for t in tags] if not any(tag in note_tags_lower for tag in search_tags_lower): continue # Calculate relevance score relevance = self._calculate_relevance(note, query) # Include all notes if no query, or only matches if query provided if not query or relevance > 0: results.append(SearchResult(note=note, relevance_score=relevance)) # Sort by relevance (highest first) results.sort(key=lambda r: r.relevance_score, reverse=True) return results