Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 36 additions & 4 deletions mempalace/mcp_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,11 @@ def _parse_args():
os.environ["MEMPALACE_PALACE_PATH"] = os.path.abspath(_args.palace)

_config = MempalaceConfig()
if _args.palace:
_kg = KnowledgeGraph(db_path=os.path.join(_config.palace_path, "knowledge_graph.sqlite3"))
else:
_kg = KnowledgeGraph()
# Always derive KG path from palace_path to match CLI expectations.
# Previously, the no-arg fallback used DEFAULT_KG_PATH (~/.mempalace/knowledge_graph.sqlite3)
# while CLI reads from palace_path/knowledge.db — causing KG writes via MCP to be
# invisible to CLI search/query commands.
_kg = KnowledgeGraph(db_path=os.path.join(_config.palace_path, "knowledge.db"))


_client_cache = None
Expand Down Expand Up @@ -921,6 +922,37 @@ def handle_request(request):
}


def _shutdown():
"""Flush KG WAL and release ChromaDB client before exit.

Prevents data loss when the stdio transport closes — ChromaDB's
PersistentClient uses an in-memory WAL that may not flush if the
process exits before the background thread completes.

Note: atexit handlers do not run on SIGKILL. For maximum robustness,
consider PRAGMA journal_mode=DELETE at startup (trades write
concurrency for crash safety). WAL checkpoint on clean exit covers
the common case (SIGTERM, SIGINT, normal exit).
"""
try:
if _kg and _kg._connection:
_kg._connection.execute("PRAGMA wal_checkpoint(TRUNCATE)")
_kg._connection.close()
logger.info("KG WAL checkpointed and connection closed")
except Exception:
pass
try:
if _client_cache:
del _client_cache
logger.info("ChromaDB client released")
except Exception:
pass


import atexit
atexit.register(_shutdown)


def main():
logger.info("MemPalace MCP Server starting...")
while True:
Expand Down