From 9844fcb3606a7a1af9c9c3166e89ccdcda1648ea Mon Sep 17 00:00:00 2001 From: phernandez Date: Sat, 31 Jan 2026 20:31:36 -0600 Subject: [PATCH 1/2] perf: speed up bm --version startup Signed-off-by: phernandez --- src/basic_memory/cli/commands/mcp.py | 21 ++++++++--------- src/basic_memory/cli/main.py | 34 +++++++++++++++++----------- 2 files changed, 31 insertions(+), 24 deletions(-) diff --git a/src/basic_memory/cli/commands/mcp.py b/src/basic_memory/cli/commands/mcp.py index 764cd468..5e7de048 100644 --- a/src/basic_memory/cli/commands/mcp.py +++ b/src/basic_memory/cli/commands/mcp.py @@ -1,22 +1,14 @@ """MCP server command with streamable HTTP transport.""" import os -import typer from typing import Optional +import typer +from loguru import logger + from basic_memory.cli.app import app from basic_memory.config import ConfigManager, init_mcp_logging -# Import mcp instance (has lifespan that handles initialization and file sync) -from basic_memory.mcp.server import mcp as mcp_server # pragma: no cover - -# Import mcp tools to register them -import basic_memory.mcp.tools # noqa: F401 # pragma: no cover - -# Import prompts to register them -import basic_memory.mcp.prompts # noqa: F401 # pragma: no cover -from loguru import logger - @app.command() def mcp( @@ -47,6 +39,13 @@ def mcp( # Even when cloud_mode_enabled is True, stdio MCP runs locally and needs local API access. os.environ["BASIC_MEMORY_FORCE_LOCAL"] = "true" + # Deferred imports to avoid heavy startup cost for unrelated CLI commands + from basic_memory.mcp.server import mcp as mcp_server # pragma: no cover + + # Import mcp tools/prompts to register them with the server + import basic_memory.mcp.tools # noqa: F401 # pragma: no cover + import basic_memory.mcp.prompts # noqa: F401 # pragma: no cover + # Initialize logging for MCP (file only, stdout breaks protocol) init_mcp_logging() diff --git a/src/basic_memory/cli/main.py b/src/basic_memory/cli/main.py index 38f4dd03..4f208361 100644 --- a/src/basic_memory/cli/main.py +++ b/src/basic_memory/cli/main.py @@ -1,20 +1,28 @@ """Main CLI entry point for basic-memory.""" # pragma: no cover +import sys + from basic_memory.cli.app import app # pragma: no cover -# Register commands -from basic_memory.cli.commands import ( # noqa: F401 # pragma: no cover - cloud, - db, - import_chatgpt, - import_claude_conversations, - import_claude_projects, - import_memory_json, - mcp, - project, - status, - tool, -) + +def _version_flag_present(argv: list[str]) -> bool: + return any(flag in argv for flag in ("--version", "-v")) + + +if not _version_flag_present(sys.argv[1:]): + # Register commands only when not short-circuiting for --version + from basic_memory.cli.commands import ( # noqa: F401 # pragma: no cover + cloud, + db, + import_chatgpt, + import_claude_conversations, + import_claude_projects, + import_memory_json, + mcp, + project, + status, + tool, + ) # Re-apply warning filter AFTER all imports # (authlib adds a DeprecationWarning filter that overrides ours) From 51e251ac83b320c74a3042c021ac1f978961c293 Mon Sep 17 00:00:00 2001 From: phernandez Date: Mon, 2 Feb 2026 19:54:34 -0600 Subject: [PATCH 2/2] fix: restore legacy /projects/projects endpoint for older CLI versions Older versions of basic-memory CLI (v0.17.4 and earlier) call GET /projects/projects to list projects. This endpoint was removed when we migrated to v2 routers. Re-add the legacy route by mounting v2_project router at /projects prefix, which provides /projects/projects/ for backwards compatibility. Co-Authored-By: Claude Opus 4.5 Signed-off-by: phernandez --- src/basic_memory/api/app.py | 3 +++ tests/api/v2/test_project_router.py | 23 +++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/basic_memory/api/app.py b/src/basic_memory/api/app.py index 71641321..ba1d6931 100644 --- a/src/basic_memory/api/app.py +++ b/src/basic_memory/api/app.py @@ -82,6 +82,9 @@ async def lifespan(app: FastAPI): # pragma: no cover # Legacy web app proxy paths (compat with /proxy/projects/projects) app.include_router(v2_project, prefix="/proxy/projects") +# Legacy v1 compat: older CLI versions call GET /projects/projects +app.include_router(v2_project, prefix="/projects") + # V2 routers are the only public API surface diff --git a/tests/api/v2/test_project_router.py b/tests/api/v2/test_project_router.py index 9e10f6fa..5e7ba02e 100644 --- a/tests/api/v2/test_project_router.py +++ b/tests/api/v2/test_project_router.py @@ -340,3 +340,26 @@ async def test_resolve_project_empty_identifier(client: AsyncClient, v2_projects response = await client.post(f"{v2_projects_url}/resolve", json=resolve_data) assert response.status_code == 422 # Validation error + + +# --- Legacy v1 compatibility tests --- + + +@pytest.mark.asyncio +async def test_legacy_v1_list_projects_endpoint(client: AsyncClient, test_project: Project): + """Test that the legacy /projects/projects endpoint still works for older CLI versions. + + This endpoint was removed when we migrated to v2 but older versions of + basic-memory-cloud CLI still call it for `bm project list`. + """ + # The legacy v1 endpoint was at /projects/projects + response = await client.get("/projects/projects/") + + assert response.status_code == 200 + data = response.json() + assert "projects" in data + assert "default_project" in data + + # Verify the test project is in the list + project_names = [p["name"] for p in data["projects"]] + assert test_project.name in project_names