fix: preserve CLI exit codes, log tracebacks, sanitize search errors, validate fixture

This commit is contained in:
Igor Lins e Silva
2026-04-07 17:38:53 -03:00
parent c9135aad67
commit 5ac4947d02
5 changed files with 46 additions and 15 deletions
+4 -1
View File
@@ -97,9 +97,10 @@ def cmd_mine(args):
def cmd_search(args): def cmd_search(args):
from .searcher import search from .searcher import search, SearchError
palace_path = os.path.expanduser(args.palace) if args.palace else MempalaceConfig().palace_path palace_path = os.path.expanduser(args.palace) if args.palace else MempalaceConfig().palace_path
try:
search( search(
query=args.query, query=args.query,
palace_path=palace_path, palace_path=palace_path,
@@ -107,6 +108,8 @@ def cmd_search(args):
room=args.room, room=args.room,
n_results=args.results, n_results=args.results,
) )
except SearchError:
sys.exit(1)
def cmd_wakeup(args): def cmd_wakeup(args):
+7 -3
View File
@@ -743,9 +743,13 @@ def handle_request(request):
"id": req_id, "id": req_id,
"result": {"content": [{"type": "text", "text": json.dumps(result, indent=2)}]}, "result": {"content": [{"type": "text", "text": json.dumps(result, indent=2)}]},
} }
except Exception as e: except Exception:
logger.error(f"Tool error in {tool_name}: {e}") logger.exception(f"Tool error in {tool_name}")
return {"jsonrpc": "2.0", "id": req_id, "error": {"code": -32000, "message": "Internal tool error"}} return {
"jsonrpc": "2.0",
"id": req_id,
"error": {"code": -32000, "message": "Internal tool error"},
}
return { return {
"jsonrpc": "2.0", "jsonrpc": "2.0",
+14 -4
View File
@@ -6,11 +6,17 @@ Semantic search against the palace.
Returns verbatim text — the actual words, never summaries. Returns verbatim text — the actual words, never summaries.
""" """
import logging
from pathlib import Path from pathlib import Path
import chromadb import chromadb
logger = logging.getLogger("mempalace_mcp")
class SearchError(Exception):
"""Raised when search cannot proceed (e.g. no palace found)."""
def search(query: str, palace_path: str, wing: str = None, room: str = None, n_results: int = 5): def search(query: str, palace_path: str, wing: str = None, room: str = None, n_results: int = 5):
""" """
@@ -23,7 +29,7 @@ def search(query: str, palace_path: str, wing: str = None, room: str = None, n_r
except Exception: except Exception:
print(f"\n No palace found at {palace_path}") print(f"\n No palace found at {palace_path}")
print(" Run: mempalace init <dir> then mempalace mine <dir>") print(" Run: mempalace init <dir> then mempalace mine <dir>")
return raise SearchError(f"No palace found at {palace_path}")
# Build where filter # Build where filter
where = {} where = {}
@@ -47,7 +53,7 @@ def search(query: str, palace_path: str, wing: str = None, room: str = None, n_r
except Exception as e: except Exception as e:
print(f"\n Search error: {e}") print(f"\n Search error: {e}")
return raise SearchError(f"Search error: {e}") from e
docs = results["documents"][0] docs = results["documents"][0]
metas = results["metadatas"][0] metas = results["metadatas"][0]
@@ -95,7 +101,11 @@ def search_memories(
client = chromadb.PersistentClient(path=palace_path) client = chromadb.PersistentClient(path=palace_path)
col = client.get_collection("mempalace_drawers") col = client.get_collection("mempalace_drawers")
except Exception as e: except Exception as e:
return {"error": f"No palace found at {palace_path}: {e}"} logger.error("No palace found at %s: %s", palace_path, e)
return {
"error": "No palace found",
"hint": "Run: mempalace init <dir> && mempalace mine <dir>",
}
# Build where filter # Build where filter
where = {} where = {}
+4
View File
@@ -13,6 +13,9 @@ def _patch_mcp_server(monkeypatch, config, palace_path, kg):
"""Patch the mcp_server module globals to use test fixtures.""" """Patch the mcp_server module globals to use test fixtures."""
from mempalace import mcp_server from mempalace import mcp_server
assert getattr(config, "palace_path", None) == palace_path, (
f"config.palace_path ({getattr(config, 'palace_path', None)!r}) does not match palace_path fixture ({palace_path!r})"
)
monkeypatch.setattr(mcp_server, "_config", config) monkeypatch.setattr(mcp_server, "_config", config)
monkeypatch.setattr(mcp_server, "_kg", kg) monkeypatch.setattr(mcp_server, "_kg", kg)
@@ -149,6 +152,7 @@ class TestReadTools:
assert result["taxonomy"]["notes"]["planning"] == 1 assert result["taxonomy"]["notes"]["planning"] == 1
def test_no_palace_returns_error(self, monkeypatch, config, kg): def test_no_palace_returns_error(self, monkeypatch, config, kg):
config._file_config["palace_path"] = "/nonexistent/path"
_patch_mcp_server(monkeypatch, config, "/nonexistent/path", kg) _patch_mcp_server(monkeypatch, config, "/nonexistent/path", kg)
from mempalace.mcp_server import tool_status from mempalace.mcp_server import tool_status
Generated
+10
View File
@@ -966,6 +966,13 @@ dependencies = [
{ name = "pyyaml" }, { name = "pyyaml" },
] ]
[package.optional-dependencies]
dev = [
{ name = "pytest", version = "8.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
{ name = "pytest", version = "9.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
{ name = "ruff" },
]
[package.dev-dependencies] [package.dev-dependencies]
dev = [ dev = [
{ name = "pytest", version = "8.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "pytest", version = "8.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
@@ -976,8 +983,11 @@ dev = [
[package.metadata] [package.metadata]
requires-dist = [ requires-dist = [
{ name = "chromadb", specifier = ">=0.4.0,<1" }, { name = "chromadb", specifier = ">=0.4.0,<1" },
{ name = "pytest", marker = "extra == 'dev'", specifier = ">=7.0" },
{ name = "pyyaml", specifier = ">=6.0" }, { name = "pyyaml", specifier = ">=6.0" },
{ name = "ruff", marker = "extra == 'dev'", specifier = ">=0.4.0" },
] ]
provides-extras = ["dev"]
[package.metadata.requires-dev] [package.metadata.requires-dev]
dev = [ dev = [