Skip to main content
Glama
ToKiDoO

Advanced Obsidian MCP Server

by ToKiDoO

obsidian_complex_search

Perform advanced searches in Obsidian vaults using JsonLogic queries with pattern matching to find documents by tags, content, or file paths.

Instructions

Complex search for documents using a JsonLogic query. Supports standard JsonLogic operators plus 'glob' and 'regexp' for pattern matching. Results must be non-falsy.

       Use this tool when you want to do a complex search, e.g. for all documents with certain tags etc.
       ALWAYS follow query syntax in examples.

       Examples
        1. Match all markdown files
        {"glob": ["*.md", {"var": "path"}]}

        2. Match all markdown files with 1221 substring inside them
        {
          "and": [
            { "glob": ["*.md", {"var": "path"}] },
            { "regexp": [".*1221.*", {"var": "content"}] }
          ]
        }

        3. Match all markdown files in Work folder containing name Keaton
        {
          "and": [
            { "glob": ["*.md", {"var": "path"}] },
            { "regexp": [".*Work.*", {"var": "path"}] },
            { "regexp": ["Keaton", {"var": "content"}] }
          ]
        }
       

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
queryYesJsonLogic query object. ALWAYS follow query syntax in examples. Example 1: {"glob": ["*.md", {"var": "path"}]} matches all markdown files Example 2: {"and": [{"glob": ["*.md", {"var": "path"}]}, {"regexp": [".*1221.*", {"var": "content"}]}]} matches all markdown files with 1221 substring inside them Example 3: {"and": [{"glob": ["*.md", {"var": "path"}]}, {"regexp": [".*Work.*", {"var": "path"}]}, {"regexp": ["Keaton", {"var": "content"}]}]} matches all markdown files in Work folder containing name Keaton

Implementation Reference

  • The run_tool method of ComplexSearchToolHandler that executes the obsidian_complex_search tool by invoking api.search_json with the JsonLogic query argument and returning the results as formatted JSON.
    def run_tool(self, args: dict) -> Sequence[TextContent | ImageContent | EmbeddedResource]:
        if "query" not in args:
            raise RuntimeError("query argument missing in arguments")
    
        results = api.search_json(args.get("query", ""))
    
        return [
            TextContent(
                type="text",
                text=json.dumps(results, indent=2)
            )
        ]
  • The get_tool_description method of ComplexSearchToolHandler providing the tool schema, description, and input validation for the JsonLogic query.
    def get_tool_description(self):
        return Tool(
            name=self.name,
            description="""Complex search for documents using a JsonLogic query. 
            Supports standard JsonLogic operators plus 'glob' and 'regexp' for pattern matching. Results must be non-falsy.
    
            Use this tool when you want to do a complex search, e.g. for all documents with certain tags etc.
            ALWAYS follow query syntax in examples.
    
            Examples
             1. Match all markdown files
             {"glob": ["*.md", {"var": "path"}]}
    
             2. Match all markdown files with 1221 substring inside them
             {
               "and": [
                 { "glob": ["*.md", {"var": "path"}] },
                 { "regexp": [".*1221.*", {"var": "content"}] }
               ]
             }
    
             3. Match all markdown files in Work folder containing name Keaton
             {
               "and": [
                 { "glob": ["*.md", {"var": "path"}] },
                 { "regexp": [".*Work.*", {"var": "path"}] },
                 { "regexp": ["Keaton", {"var": "content"}] }
               ]
             }
            """,
            inputSchema={
                "type": "object",
                "properties": {
                    "query": {
                        "type": "object",
                        "description": "JsonLogic query object. ALWAYS follow query syntax in examples. \
                             Example 1: {\"glob\": [\"*.md\", {\"var\": \"path\"}]} matches all markdown files \
                             Example 2: {\"and\": [{\"glob\": [\"*.md\", {\"var\": \"path\"}]}, {\"regexp\": [\".*1221.*\", {\"var\": \"content\"}]}]} matches all markdown files with 1221 substring inside them \
                             Example 3: {\"and\": [{\"glob\": [\"*.md\", {\"var\": \"path\"}]}, {\"regexp\": [\".*Work.*\", {\"var\": \"path\"}]}, {\"regexp\": [\"Keaton\", {\"var\": \"content\"}]}]} matches all markdown files in Work folder containing name Keaton \
                         "
                    }
                },
                "required": ["query"]
            }
        )
  • TOOL_MAPPING dictionary entry mapping TOOL_COMPLEX_SEARCH ("obsidian_complex_search") to ComplexSearchToolHandler class for tool registration.
    TOOL_MAPPING = {
        tools.TOOL_LIST_FILES_IN_DIR: tools.ListFilesInDirToolHandler,
        tools.TOOL_SIMPLE_SEARCH: tools.SearchToolHandler,
        tools.TOOL_PATCH_CONTENT: tools.PatchContentToolHandler,
        tools.TOOL_PUT_CONTENT: tools.PutContentToolHandler,
        tools.TOOL_APPEND_CONTENT: tools.AppendContentToolHandler,
        tools.TOOL_DELETE_FILE: tools.DeleteFileToolHandler,
        tools.TOOL_COMPLEX_SEARCH: tools.ComplexSearchToolHandler,
        tools.TOOL_BATCH_GET_FILES: tools.BatchGetFilesToolHandler,
        tools.TOOL_PERIODIC_NOTES: tools.PeriodicNotesToolHandler,
        tools.TOOL_RECENT_PERIODIC_NOTES: tools.RecentPeriodicNotesToolHandler,
        tools.TOOL_RECENT_CHANGES: tools.RecentChangesToolHandler,
        tools.TOOL_UNDERSTAND_VAULT: tools.UnderstandVaultToolHandler,
        tools.TOOL_GET_ACTIVE_NOTE: tools.GetActiveNoteToolHandler,
        tools.TOOL_OPEN_FILES: tools.OpenFilesToolHandler,
        tools.TOOL_LIST_COMMANDS: tools.ListCommandsToolHandler,
        tools.TOOL_EXECUTE_COMMANDS: tools.ExecuteCommandsToolHandler,
    }
  • The Obsidian API client's search_json method, which sends the JsonLogic query via POST to the /search/ endpoint and returns the search results. This is the core implementation delegated to by the tool handler.
    def search_json(self, query: dict) -> Any:
        url = f"{self.get_base_url()}/search/"
        
        headers = self._get_headers() | {
            'Content-Type': 'application/vnd.olrapi.jsonlogic+json'
        }
        
        def call_fn():
            response = requests.post(url, headers=headers, json=query, verify=self.verify_ssl, timeout=self.timeout)
            response.raise_for_status()
            return response.json()
    
        return self._safe_call(call_fn)
  • The register_tools function that instantiates handlers from TOOL_MAPPING (including ComplexSearchToolHandler for obsidian_complex_search) and adds them to the tool_handlers dictionary used by list_tools and call_tool.
    """Register the selected tools with the server."""
    tools_to_include = parse_include_tools()
    
    registered_count = 0
    for tool_name in tools_to_include:
        if tool_name in TOOL_MAPPING:
            handler_class = TOOL_MAPPING[tool_name]
            handler_instance = handler_class()
            add_tool_handler(handler_instance)
            registered_count += 1
            logger.debug(f"Registered tool: {tool_name}")
    
    logger.info(f"Successfully registered {registered_count} 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/ToKiDoO/mcp-obsidian-advanced'

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