search_notes
Search your Obsidian vault by content, title, or tags to quickly find relevant notes and information across your knowledge base.
Instructions
Search the vault by content, title, tags, or all
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| folder | No | ||
| limit | No | ||
| query | Yes | ||
| search_type | No | all |
Implementation Reference
- src/obsidian_mcp/server.py:173-230 (handler)The primary handler for the 'search_notes' MCP tool. It handles input validation, invokes the VaultSearch.search method, formats the search results into a readable string, and includes error handling.@mcp.tool(name="search_notes", description="Search the vault by content, title, tags, or all") async def search_notes( query: str, search_type: SearchType = "all", folder: str = "", limit: int = 20 ) -> str: """ Search for notes in the vault. Args: query: Search query string search_type: Type of search - "content", "title", "tags", or "all" folder: Optional folder to limit search (e.g., "Projects") limit: Maximum number of results (default: 20) Returns: Formatted list of search results with snippets """ # Validate input if not query or not query.strip(): return "Error: Query cannot be empty" if len(query) > 500: return "Error: Query too long" if limit <= 0 or limit > 1000: return "Error: Limit must be between 1 and 1000" context = _get_context() try: results = await context.search.search( query=query, search_type=search_type, folder=folder, limit=limit ) if not results: return f"No results found for query: {query}" # Format results output = f"Found {len(results)} results for '{query}':\n\n" for i, result in enumerate(results, 1): output += f"{i}. **{result.name}**\n" output += f" Path: `{result.path}`\n" output += f" Score: {result.score:.1f}\n" if result.snippet: output += f" Snippet: {result.snippet}\n" if result.matched_tags: output += f" Tags: {', '.join(result.matched_tags)}\n" output += "\n" return output except VaultSecurityError as e: return f"Error: Security violation: {e}" except Exception as e: logger.exception("Error searching notes") return f"Error searching notes: {e}"
- src/obsidian_mcp/server.py:173-173 (registration)The @mcp.tool decorator registers the 'search_notes' function as an MCP tool with the specified name and description.@mcp.tool(name="search_notes", description="Search the vault by content, title, tags, or all")
- src/obsidian_mcp/search.py:9-9 (schema)Type definition for the search_type parameter used in the search_notes tool, defining valid literal values for search types.SearchType = Literal["content", "title", "tags", "all"]
- src/obsidian_mcp/search.py:151-197 (helper)The core search logic in VaultSearch.search method, called by the search_notes handler. Performs the actual searching based on type (content, title, tags, or all) and returns sorted SearchResult list.async def search( self, query: str, search_type: SearchType = "all", folder: str = "", limit: int = 20 ) -> list[SearchResult]: """ Search the vault. Args: query: Search query string search_type: Type of search to perform folder: Limit search to this folder limit: Maximum number of results Returns: List of search results sorted by relevance """ logger.debug( f"Searching for '{query}' (type={search_type}, folder={folder}, limit={limit})" ) if not query: return [] if search_type == "content": return await self._search_in_content(query, limit, folder) elif search_type == "title": return self._search_by_title(query, limit, folder) elif search_type == "tags": return self._search_by_tags(query, limit, folder) else: # "all" # Combine results from all search types (each with full limit to ensure enough results) title_results = self._search_by_title(query, limit, folder) tag_results = self._search_by_tags(query, limit, folder) content_results = await self._search_in_content(query, limit, folder) # Merge and deduplicate by path seen_paths = set() all_results = [] for result in title_results + tag_results + content_results: if result.path not in seen_paths: seen_paths.add(result.path) all_results.append(result) # Sort by score and return top results up to limit all_results.sort(key=lambda r: r.score, reverse=True) return all_results[:limit]
- src/obsidian_mcp/search.py:14-23 (helper)Dataclass defining the structure of each search result returned by the search functionality.@dataclass(slots=True, frozen=True) class SearchResult: """A search result with context (immutable).""" path: str name: str score: float snippet: str | None = None matched_tags: list[str] | None = None