diff --git a/src/basic_memory/cli/commands/mcp.py b/src/basic_memory/cli/commands/mcp.py index 764cd468..02f440bd 100644 --- a/src/basic_memory/cli/commands/mcp.py +++ b/src/basic_memory/cli/commands/mcp.py @@ -1,21 +1,24 @@ """MCP server command with streamable HTTP transport.""" import os +from typing import Any, Optional + import typer -from typing import Optional +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 +class _DeferredMcpServer: + def run(self, *args: Any, **kwargs: Any) -> None: # pragma: no cover + from basic_memory.mcp.server import mcp as live_mcp_server -# Import prompts to register them -import basic_memory.mcp.prompts # noqa: F401 # pragma: no cover -from loguru import logger + live_mcp_server.run(*args, **kwargs) + + +# Keep module-level attribute for tests/monkeypatching while deferring heavy import. +mcp_server = _DeferredMcpServer() @app.command() @@ -47,6 +50,10 @@ 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" + # 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 4f5e38b2..16289a24 100644 --- a/src/basic_memory/cli/main.py +++ b/src/basic_memory/cli/main.py @@ -1,26 +1,33 @@ """Main CLI entry point for basic-memory.""" # pragma: no cover +import sys +import warnings + 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, - doctor, - import_chatgpt, - import_claude_conversations, - import_claude_projects, - import_memory_json, - mcp, - project, - schema, - status, - tool, -) +def _version_only_invocation(argv: list[str]) -> bool: + # Trigger: invocation is exactly `bm --version` or `bm -v` + # Why: avoid importing command modules on the hot version path + # Outcome: eager version callback exits quickly with minimal startup work + return len(argv) == 1 and argv[0] in {"--version", "-v"} -# Re-apply warning filter AFTER all imports -# (authlib adds a DeprecationWarning filter that overrides ours) -import warnings # pragma: no cover + +if not _version_only_invocation(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, + doctor, + import_chatgpt, + import_claude_conversations, + import_claude_projects, + import_memory_json, + mcp, + project, + schema, + status, + tool, + ) warnings.filterwarnings("ignore") # pragma: no cover