Skip to main content
Glama
cwente25

Knowledge Base MCP Server

by cwente25

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
NameRequiredDescriptionDefault
queryNoSearch term (searches title, content, tags) - case insensitive
category_pathNoOptional category path filter (e.g., 'work/clients'). Searches in this path and all subcategories by default.
tagsNoOptional comma-separated tags to filter by (matches any)
recursiveNoIf true, search in subcategories too (default: true)

Implementation Reference

  • 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,
            },
        },
    },
  • 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
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It mentions search functionality but lacks details on permissions, rate limits, pagination, or error handling. For a search tool with zero annotation coverage, this leaves significant gaps in understanding how the tool behaves in practice.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, efficient sentence that directly states the tool's function without unnecessary words. It's front-loaded with the core action and resource, making it easy to parse quickly, with no wasted information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's moderate complexity (search with multiple filters) and lack of annotations or output schema, the description is adequate but incomplete. It covers what the tool does but misses behavioral aspects like result format or limitations. For a search tool, this leaves room for improvement in guiding the agent effectively.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema fully documents all four parameters. The description adds minimal value by listing the search criteria ('query, category path, or tags') but doesn't provide additional syntax, examples, or constraints beyond what's in the schema, meeting the baseline for high coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('Search through') and resource ('all notes') with specific search criteria ('by query, category path, or tags'), making the purpose unambiguous. However, it doesn't explicitly differentiate from sibling tools like 'list_notes' or 'get_note', which could also retrieve notes, so it doesn't reach the highest score.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives like 'list_notes' or 'get_note'. It mentions search capabilities but doesn't specify scenarios where searching is preferable to listing or retrieving specific notes, leaving the agent to infer usage from context alone.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

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/cwente25/KnowledgeBaseMCP'

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