You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
bm mcp startup imports the full local FastAPI app (api/__init__.py), ~60% of MCP import time; concurrent starts exceed client connect timeouts #1731
bm mcp spends most of its startup time importing the local FastAPI app and every v2 router, even when the server will never serve the local API (for example, when BASIC_MEMORY_FORCE_CLOUD=true routes every tool call to cloud). When several MCP clients start servers at the same moment, those CPU-bound imports compete for cores, and startup can exceed the client's MCP connect timeout. Claude Code's default is about 30 s, and when it is exceeded the client reports MCP servers failed to connect: basic-memory.
Root cause
src/basic_memory/api/__init__.py imports the app eagerly:
About 0.67 s of the 1.06 s for basic_memory.mcp.tools is basic_memory.api.app, roughly 63%. A warm import of the full set took 1.7 s, and a cold one 5.5 s.
On a slower machine (Apple M1, 4 performance + 4 efficiency cores; a 0.22.x checkout with the same chain), I timed bm mcp --project main with BASIC_MEMORY_FORCE_CLOUD=true and BASIC_MEMORY_EXPLICIT_ROUTING=true, from spawn to the initialize / tools/list response, with N servers started at once:
N started together
initialize median / worst
tools/list worst
1
11.2 s / 11.3 s
11.3 s
2
11.7 s / 12.1 s
12.2 s
3
12.8 s / 15.4 s
15.5 s
5
21.0 s / 24.6 s
24.8 s
About 10 s of the 11 s solo start comes before the first log line (MCP server constrained to project). That time is imports. /usr/bin/time showed it as CPU (user about 10 s), not I/O. api.app and its routers account for about 4.9 s.
DB init, project reconciliation and the watcher take 0.3–1.4 s at every N. Setting BASIC_MEMORY_INDEX_CHANGES=false did not change the N = 5 result (19.8 / 20.2 s), so SQLite and watcher contention are not the cause.
In real bursts, where the MCP client itself is also starting, 3 concurrent sessions took about 35 s and missed a 30 s connect timeout. Raising the client timeout to 90 s works around it.
Proposed fix
Make basic_memory/api/__init__.py stop importing .app eagerly. Use a lazy __getattr__, or have callers import basic_memory.api.app directly, so that basic_memory.api.container no longer pulls in FastAPI and the routers.
Check that the MCP startup path (mcp.server and its lifespan) only imports the API app when the local API is actually needed.
Add a startup-time regression check, for example asserting that basic_memory.api.app is not in sys.modules after import basic_memory.mcp.tools, or a -X importtime budget.
Related
Slow startup time #740: slow CLI startup, an eager import graph. The last comment called for a dedicated lazy-import refactor with startup benchmarks. This issue is the MCP-server slice of that.
Component: MCP server startup (import graph). Not Cloud-specific, even though FORCE_CLOUD makes the cost most visible.
Priority: Medium-High. Concurrent MCP client starts can exceed the ~30 s connect timeout, so the server fails to connect.
Complexity: Simple to Medium. The root cause is confirmed: src/basic_memory/api/__init__.py does from .app import app eagerly, so importing basic_memory.api.container pulls in FastAPI and every v2 router. The fix is a lazy __getattr__ in api/__init__.py or direct basic_memory.api.app imports in callers. The remaining work is checking the mcp.server lifespan and adding an import regression test, such as basic_memory.api.app not in sys.modules after import basic_memory.mcp.tools.
Duplicates: The search found none. This is related to #740, #886 and #1713, which you already linked.
Reproduction: The issue already has -X importtime numbers and a concurrency table, so nothing more is needed.
This is triage only. I haven't made any code changes.
Importing basic_memory.api.container first initializes the basic_memory.api package, which eagerly imported the FastAPI app. This pulled the app and its routers into the MCP startup import chain.
The package now loads and caches the FastAPI instance when its app export is requested. It also preserves that export when the app submodule is imported first, so callers receive the instance in either import order.
Keeping this open. The plan is to fix it with Python 3.15 explicit lazy imports (PEP 810) once we support 3.15. #1740 was declined in favor of that, to avoid the module __getattr__ / __class__ machinery.
Activity
claude commentedon Oct 10, 2026
Claude finished @groksrc's task in 15s —— View job
Triage
bug.FORCE_CLOUDmakes the cost most visible.src/basic_memory/api/__init__.pydoesfrom .app import appeagerly, so importingbasic_memory.api.containerpulls in FastAPI and every v2 router. The fix is a lazy__getattr__inapi/__init__.pyor directbasic_memory.api.appimports in callers. The remaining work is checking themcp.serverlifespan and adding an import regression test, such asbasic_memory.api.app not in sys.modulesafterimport basic_memory.mcp.tools.Duplicates: The search found none. This is related to #740, #886 and #1713, which you already linked.
Reproduction: The issue already has
-X importtimenumbers and a concurrency table, so nothing more is needed.This is triage only. I haven't made any code changes.
mvanhorn commentedon Oct 11, 2026
Importing basic_memory.api.container first initializes the basic_memory.api package, which eagerly imported the FastAPI app. This pulled the app and its routers into the MCP startup import chain.
The package now loads and caches the FastAPI instance when its app export is requested. It also preserves that export when the app submodule is imported first, so callers receive the instance in either import order.
#1740
phernandez commentedon Oct 11, 2026
Keeping this open. The plan is to fix it with Python 3.15 explicit lazy imports (PEP 810) once we support 3.15. #1740 was declined in favor of that, to avoid the module
__getattr__/__class__machinery.