From 590d10b7da50b9a0ead8bccf0d3a4877d4dd7bef Mon Sep 17 00:00:00 2001 From: HumbleTurtle Date: Sun, 19 Jul 2026 15:18:29 -0300 Subject: [PATCH] feat(memory): resolve paths in context reads --- resources/scripts/napseer_mcp_server.py | 13 ++++++++----- tests/test_mcp_memory_ergonomics_helpers.py | 10 ++++++---- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/resources/scripts/napseer_mcp_server.py b/resources/scripts/napseer_mcp_server.py index 7b4a3e6..f13233c 100755 --- a/resources/scripts/napseer_mcp_server.py +++ b/resources/scripts/napseer_mcp_server.py @@ -9765,12 +9765,11 @@ def classify_memory_query(args): def get_memory_context(args): - if args.get("path") or args.get("paths"): - raise RuntimeError("paths are index/routes only; resolve them with nap_node_by_path, then use node_id/uri inputs") raw_node_ids = args.get("node_ids") or ([args["node_id"]] if args.get("node_id") else []) raw_uris = args.get("uris") or ([args["uri"]] if args.get("uri") else []) - if not raw_node_ids and not raw_uris: - raise RuntimeError("node_id, node_ids, uri, or uris is required") + raw_paths = args.get("paths") or ([args["path"]] if args.get("path") else []) + if not raw_node_ids and not raw_uris and not raw_paths: + raise RuntimeError("path, paths, node_id, node_ids, uri, or uris is required") max_nodes = max(1, min(int(args.get("max_nodes", 20)), 100)) depth = max(0, min(int(args.get("depth", 1)), 2)) default_view = "summary" @@ -9780,6 +9779,8 @@ def get_memory_context(args): queue.append(("node", get_node_by_id({"node_id": node_id}))) for uri in raw_uris: queue.append(("node", node_by_uri({"uri": uri})["node"])) + for path in raw_paths: + queue.append(("path", normalize_node_path(path))) nodes = [] edges = [] seen_paths = set() @@ -11562,10 +11563,12 @@ def raw_tools(): *kanban_workflow_tool_schemas(), { "name": "nap_context", - "description": "Fetch one or more nodes plus bounded outgoing links and backlinks for surrounding context.", + "description": "Resolve one or more paths, node IDs, or nap:// URIs and fetch bounded outgoing links and backlinks in one call.", "inputSchema": { "type": "object", "properties": { + "path": {"type": "string", "description": "Readable route to resolve internally; canonical identity is returned in the result."}, + "paths": {"type": "array", "items": {"type": "string"}}, "node_id": {"type": "string"}, "node_ids": {"type": "array", "items": {"type": "string"}}, "uri": {"type": "string"}, diff --git a/tests/test_mcp_memory_ergonomics_helpers.py b/tests/test_mcp_memory_ergonomics_helpers.py index 5bc269e..67b432b 100644 --- a/tests/test_mcp_memory_ergonomics_helpers.py +++ b/tests/test_mcp_memory_ergonomics_helpers.py @@ -329,8 +329,8 @@ def test_tools_include_node_by_path_descriptor(mod): assert "node_ids" in context_schema["properties"] assert "uri" in context_schema["properties"] assert "uris" in context_schema["properties"] - assert "path" not in context_schema["properties"] - assert "paths" not in context_schema["properties"] + assert "path" in context_schema["properties"] + assert "paths" in context_schema["properties"] related_schema = next(item for item in mod.tools() if item["name"] == "nap_find_related")["inputSchema"] assert "node_id" in related_schema["properties"] @@ -599,6 +599,8 @@ def test_context_and_related_use_identity_inputs(mod): raise AssertionError(f"unexpected node_id {node_id}") def fake_read_by_path(args, allow_agent=False): + if args["path"] == "/notes/root": + return root if args["path"] == "/notes/linked": return linked if args["path"] == "/notes/backlink": @@ -627,8 +629,8 @@ def test_context_and_related_use_identity_inputs(mod): assert related["target"]["full_path"] == "/notes/root" assert {item["full_path"] for item in related["items"]} >= {"/notes/backlink", "/notes/linked"} - with pytest.raises(RuntimeError, match="paths are index/routes only"): - mod.get_memory_context({"path": "/notes/root"}) + path_context = mod.get_memory_context({"path": "/notes/root", "depth": 0, "view": "paths"}) + assert [item["full_path"] for item in path_context["items"]] == ["/notes/root"] with pytest.raises(RuntimeError, match="path is an index/route only"): mod.find_related_nodes({"path": "/notes/root"})