From e899dc79aebb2f29ea305e847fd3e76b5a45d9b9 Mon Sep 17 00:00:00 2001 From: Gamosoft Date: Tue, 16 Jun 2026 18:09:48 +0200 Subject: [PATCH] added mcp fixes --- documentation/MCP.md | 11 +++---- mcp_server/__init__.py | 28 +++++++++++++++++- mcp_server/client.py | 15 +++++++--- mcp_server/server.py | 66 ++++++++++++++++++++++++++++-------------- mcp_server/tools.py | 26 +++++++++++++---- 5 files changed, 108 insertions(+), 38 deletions(-) diff --git a/documentation/MCP.md b/documentation/MCP.md index a164557..818f1ac 100644 --- a/documentation/MCP.md +++ b/documentation/MCP.md @@ -210,20 +210,21 @@ The MCP server provides these tools to AI assistants: | Tool | Description | |------|-------------| | `health_check` | Verify server connectivity | +| `get_config` | Get server config (name, version, enabled features, autosave delay) | ## Tool Details -### Pagination with `max_results` and `offset` +### Pagination with `limit` and `offset` Some tools support optional pagination parameters for large vaults: | Tool | Parameters | Description | |------|------------|-------------| -| `search_notes` | `max_results`, `offset` | Paginate search results | -| `list_notes` | `max_results`, `offset` | Paginate notes list | -| `get_notes_by_tag` | `max_results`, `offset` | Paginate notes by tag | +| `search_notes` | `limit`, `offset` | Paginate search results | +| `list_notes` | `limit`, `offset` | Paginate notes list | +| `get_notes_by_tag` | `limit`, `offset` | Paginate notes by tag | -- `max_results` - Maximum items to return (omit for all) +- `limit` - Maximum items to return (omit for all) - `offset` - Number of items to skip (for pagination) **Example prompts:** diff --git a/mcp_server/__init__.py b/mcp_server/__init__.py index 3f1bcbb..ec9ce36 100644 --- a/mcp_server/__init__.py +++ b/mcp_server/__init__.py @@ -16,7 +16,33 @@ Environment Variables: NOTEDISCOVERY_API_KEY: API key for authentication (optional) """ -__version__ = "1.0.0" +from pathlib import Path + + +def _read_version() -> str: + """ + Resolve the package version. + + Order: + 1. importlib.metadata — works when installed via pip/uvx/setuptools + 2. VERSION file at repo root — works when running from source / Docker + 3. fallback string — so the server still starts cleanly in odd setups + """ + try: + from importlib.metadata import version + return version("notediscovery") + except Exception: + pass + try: + version_file = Path(__file__).resolve().parent.parent / "VERSION" + if version_file.is_file(): + return version_file.read_text(encoding="utf-8").strip() + except Exception: + pass + return "0.0.0+dev" + + +__version__ = _read_version() __author__ = "NoteDiscovery" from .server import main diff --git a/mcp_server/client.py b/mcp_server/client.py index 69012e3..05137ca 100644 --- a/mcp_server/client.py +++ b/mcp_server/client.py @@ -163,16 +163,23 @@ class NoteDiscoveryClient: def get_note(self, path: str) -> APIResponse: """ Get a specific note's content. - + Args: path: Note path (e.g., "folder/note.md") - + Returns: APIResponse with note content and metadata """ - # URL-encode the path + # /api/notes/{path} defaults to include_backlinks=True server-side, + # which scans every note in the vault. The MCP get_note tool only + # surfaces content + metadata, so we skip the backlinks computation. + # Backlinks are still available via the dedicated get_backlinks method. encoded_path = urllib.parse.quote(path, safe="") - return self._request("GET", f"/api/notes/{encoded_path}") + return self._request( + "GET", + f"/api/notes/{encoded_path}", + params={"include_backlinks": "false"}, + ) def create_note(self, path: str, content: str) -> APIResponse: """ diff --git a/mcp_server/server.py b/mcp_server/server.py index e5a1e25..67b964d 100644 --- a/mcp_server/server.py +++ b/mcp_server/server.py @@ -13,6 +13,7 @@ import sys import traceback from typing import Any, Optional +from . import __version__ from .config import load_config, MCPConfig from .client import NoteDiscoveryClient, APIResponse from .tools import TOOLS, get_tool_names @@ -21,10 +22,11 @@ from .tools import TOOLS, get_tool_names # MCP Protocol version MCP_VERSION = "2024-11-05" -# Server info +# Server info: version comes from the package (resolved against the VERSION +# file at repo root, or importlib.metadata when pip-installed). SERVER_INFO = { "name": "notediscovery-mcp", - "version": "1.0.0", + "version": __version__, } @@ -208,14 +210,14 @@ class MCPServer: def _tool_search_notes(self, args: dict) -> str: """Search notes by query.""" query = args.get("query", "").strip() - max_results = args.get("max_results") + limit = args.get("limit") offset = args.get("offset", 0) - + if not query: return "No search term provided. Please specify a query." - - # Use server-side pagination if max_results is specified - response = self.client.search(query, limit=max_results, offset=offset) + + # Use server-side pagination if limit is specified + response = self.client.search(query, limit=limit, offset=offset) if not response.success: return f"Search failed: {response.error}" @@ -252,17 +254,17 @@ class MCPServer: # Add pagination hint if there are more results if pagination and pagination.get("has_more"): - output.append(f"... more results available (use max_results and offset to paginate)") - + output.append(f"... more results available (use limit and offset to paginate)") + return "\n".join(output) - + def _tool_list_notes(self, args: dict) -> str: """List all notes.""" - max_results = args.get("max_results") + limit = args.get("limit") offset = args.get("offset", 0) - - # Use server-side pagination if max_results is specified - response = self.client.list_notes(limit=max_results, offset=offset) + + # Use server-side pagination if limit is specified + response = self.client.list_notes(limit=limit, offset=offset) if not response.success: return f"Failed to list notes: {response.error}" @@ -298,10 +300,10 @@ class MCPServer: # Add pagination hint if there are more results if pagination and pagination.get("has_more"): - output.append(f"... more notes available (use max_results to limit)") - + output.append(f"... more notes available (use limit and offset to paginate)") + return "\n".join(output) - + def _tool_get_note(self, args: dict) -> str: """Get note content.""" path = args.get("path", "") @@ -350,14 +352,14 @@ class MCPServer: def _tool_get_notes_by_tag(self, args: dict) -> str: """Get notes with a specific tag.""" tag = args.get("tag", "") - max_results = args.get("max_results") + limit = args.get("limit") offset = args.get("offset", 0) - + if not tag: return "Error: tag is required" - # Use server-side pagination if max_results is specified - response = self.client.get_notes_by_tag(tag, limit=max_results, offset=offset) + # Use server-side pagination if limit is specified + response = self.client.get_notes_by_tag(tag, limit=limit, offset=offset) if not response.success: return f"Failed to get notes by tag: {response.error}" @@ -382,7 +384,7 @@ class MCPServer: # Add pagination hint if there are more results if pagination and pagination.get("has_more"): - output.append(f"\n... more notes available (use max_results to limit)") + output.append(f"\n... more notes available (use limit and offset to paginate)") return "\n".join(output) @@ -649,6 +651,26 @@ class MCPServer: return f"❌ NoteDiscovery is not reachable: {response.error}" return f"✅ NoteDiscovery is healthy at {self.config.base_url}" + + def _tool_get_config(self, args: dict) -> str: + """Get NoteDiscovery server configuration.""" + response = self.client.get_config() + + if not response.success: + return f"Failed to get config: {response.error}" + + data = response.data or {} + if not data: + return "Server returned empty configuration." + + output = ["NoteDiscovery Server Configuration:\n"] + for key in sorted(data.keys()): + value = data[key] + if isinstance(value, (dict, list)): + value = json.dumps(value, ensure_ascii=False) + output.append(f" {key}: {value}") + + return "\n".join(output) # ========================================================================= # Main Loop diff --git a/mcp_server/tools.py b/mcp_server/tools.py index 360b580..06f5c45 100644 --- a/mcp_server/tools.py +++ b/mcp_server/tools.py @@ -22,13 +22,13 @@ TOOLS: list[dict[str, Any]] = [ "type": "string", "description": "Search query. Can be keywords, phrases, or natural language." }, - "max_results": { + "limit": { "type": "integer", "description": "Maximum number of results to return. Useful for large vaults. If not specified, returns all matches." }, "offset": { "type": "integer", - "description": "Number of results to skip. Use with max_results for pagination." + "description": "Number of results to skip. Use with limit for pagination." } }, "required": ["query"] @@ -40,13 +40,13 @@ TOOLS: list[dict[str, Any]] = [ "inputSchema": { "type": "object", "properties": { - "max_results": { + "limit": { "type": "integer", "description": "Maximum number of notes to return. Useful for large vaults. If not specified, returns all notes." }, "offset": { "type": "integer", - "description": "Number of notes to skip. Use with max_results for pagination." + "description": "Number of notes to skip. Use with limit for pagination." } }, "required": [] @@ -89,13 +89,13 @@ TOOLS: list[dict[str, Any]] = [ "type": "string", "description": "Tag name (without the # symbol)" }, - "max_results": { + "limit": { "type": "integer", "description": "Maximum number of notes to return. Useful for large vaults. If not specified, returns all matches." }, "offset": { "type": "integer", - "description": "Number of notes to skip. Use with max_results for pagination." + "description": "Number of notes to skip. Use with limit for pagination." } }, "required": ["tag"] @@ -298,6 +298,20 @@ TOOLS: list[dict[str, Any]] = [ "required": [] } }, + { + "name": "get_config", + "description": ( + "Get NoteDiscovery server configuration: app name, version, " + "whether search is enabled, authentication mode, autosave delay. " + "Useful for confirming what server you're connected to and what " + "features are available before calling other tools." + ), + "inputSchema": { + "type": "object", + "properties": {}, + "required": [] + } + }, ]