diff --git a/src/mcp_knowledge_base/obsidian.py b/src/mcp_knowledge_base/obsidian.py index 6b6f094..a817f9e 100644 --- a/src/mcp_knowledge_base/obsidian.py +++ b/src/mcp_knowledge_base/obsidian.py @@ -114,4 +114,18 @@ class Obsidian(): response.raise_for_status() return None + return self._safe_call(call_fn) + + 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) + response.raise_for_status() + return response.json() + return self._safe_call(call_fn) \ No newline at end of file diff --git a/src/mcp_knowledge_base/server.py b/src/mcp_knowledge_base/server.py index c3e0936..0493d1b 100644 --- a/src/mcp_knowledge_base/server.py +++ b/src/mcp_knowledge_base/server.py @@ -50,6 +50,7 @@ add_tool_handler(tools.GetFileContentsToolHandler()) add_tool_handler(tools.SearchToolHandler()) add_tool_handler(tools.PatchContentToolHandler()) add_tool_handler(tools.AppendContentToolHandler()) +add_tool_handler(tools.ComplexSearchToolHandler()) #@app.list_resources() #async def list_resources() -> list[Resource]: diff --git a/src/mcp_knowledge_base/tools.py b/src/mcp_knowledge_base/tools.py index b923250..1f4c11c 100644 --- a/src/mcp_knowledge_base/tools.py +++ b/src/mcp_knowledge_base/tools.py @@ -129,18 +129,19 @@ class GetFileContentsToolHandler(ToolHandler): class SearchToolHandler(ToolHandler): def __init__(self): - super().__init__("search") + super().__init__("simple_search") def get_tool_description(self): return Tool( name=self.name, - description="Search for documents matching a specified text query across all files in the vault", + description="""Simple search for documents matching a specified text query across all files in the vault. + Use this tool when you want to do a simple text search""", inputSchema={ "type": "object", "properties": { "query": { "type": "string", - "description": "Text to search for in the vault" + "description": "Text to a simple search for in the vault." }, "context_length": { "type": "integer", @@ -285,4 +286,42 @@ class PatchContentToolHandler(ToolHandler): type="text", text=f"Successfully patched content in {args['filepath']}" ) + ] + +class ComplexSearchToolHandler(ToolHandler): + def __init__(self): + super().__init__("complex_search") + + 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. + """, + inputSchema={ + "type": "object", + "properties": { + "query": { + "type": "object", + "description": "JsonLogic query object. Example: {\"glob\": [\"*.md\", {\"var\": \"path\"}]} matches all markdown files" + } + }, + "required": ["query"] + } + ) + + def run_tool(self, args: dict) -> Sequence[TextContent | ImageContent | EmbeddedResource]: + if "query" not in args: + raise RuntimeError("query argument missing in arguments") + + api = obsidian.Obsidian(api_key=api_key) + results = api.search_json(args["query"]) + + return [ + TextContent( + type="text", + text=json.dumps(results, indent=2) + ) ] \ No newline at end of file