22
33import asyncio
44import typer
5- import uvicorn
65
76from basic_memory .cli .app import app
87
1615import basic_memory .mcp .prompts # noqa: F401 # pragma: no cover
1716from loguru import logger
1817
18+
1919@app .command ()
2020def mcp (
21- transport : str = typer .Option (
22- "stdio" , help = "Transport type: stdio, streamable-http, or sse"
23- ),
21+ transport : str = typer .Option ("stdio" , help = "Transport type: stdio, streamable-http, or sse" ),
2422 host : str = typer .Option (
2523 "0.0.0.0" , help = "Host for HTTP transports (use 0.0.0.0 to allow external connections)"
2624 ),
@@ -35,9 +33,10 @@ def mcp(
3533 - streamable-http: Recommended for web deployments (default)
3634 - sse: Server-Sent Events (for compatibility with existing clients)
3735 """
38-
36+
3937 # Check if OAuth is enabled
4038 import os
39+
4140 auth_enabled = os .getenv ("FASTMCP_AUTH_ENABLED" , "false" ).lower () == "true"
4241 if auth_enabled :
4342 logger .info ("OAuth authentication is ENABLED" )
@@ -52,70 +51,39 @@ def mcp(
5251
5352 # Start the MCP server with the specified transport
5453
55-
56- if transport == "streamable-http" :
57- # For HTTP transports, we can use the ASGI app approach to control the event loop
58- async def main ():
59- """Run HTTP transport with file sync support."""
60-
61- sync_task = None
62- logger .info (f"Sync changes enabled: { app_config .sync_changes } " )
63- if app_config .sync_changes :
64- # Start file sync task in background
65- sync_task = asyncio .create_task (initialize_file_sync (app_config ))
66-
67- # Create ASGI app
68- app = mcp_server .http_app (path = path , transport = "streamable-http" )
69-
70- logger .info (f"Starting MCP server with { transport .upper ()} transport on http://{ host } :{ port } { path } " )
71-
72- # Run with uvicorn
73- config = uvicorn .Config (
74- app = app ,
75- host = host ,
76- port = port ,
77- log_level = "info" ,
78- )
79- server = uvicorn .Server (config )
80-
81- try :
82- await server .serve ()
83- finally :
84- # Cancel sync task on shutdown
85- if sync_task :
86- sync_task .cancel ()
87- try :
88- await sync_task
89- except asyncio .CancelledError :
90- pass
91-
92- # Run the async main function
93- asyncio .run (main ())
94-
95- else :
96- logger .info ("Starting MCP server with stdio transport" )
97-
98- # For stdio, we'll run the file sync in a separate thread since the
99- # MCP server will create its own event loop
100- import threading
101-
102- def run_file_sync ():
103- """Run file sync in a separate thread with its own event loop."""
104- loop = asyncio .new_event_loop ()
105- asyncio .set_event_loop (loop )
106- try :
107- loop .run_until_complete (initialize_file_sync (app_config ))
108- except Exception as e :
109- logger .error (f"File sync error: { e } " , err = True )
110- finally :
111- loop .close ()
112-
113- logger .info (f"Sync changes enabled: { app_config .sync_changes } " )
114- if app_config .sync_changes :
115- # Start the sync thread
116- sync_thread = threading .Thread (target = run_file_sync , daemon = True )
117- sync_thread .start ()
118- logger .info ("Started file sync in background" )
119-
120- # Now run the MCP server (blocks)
121- mcp_server .run (transport = "stdio" )
54+ # Use unified thread-based sync approach for both transports
55+ import threading
56+
57+ def run_file_sync ():
58+ """Run file sync in a separate thread with its own event loop."""
59+ loop = asyncio .new_event_loop ()
60+ asyncio .set_event_loop (loop )
61+ try :
62+ loop .run_until_complete (initialize_file_sync (app_config ))
63+ except Exception as e :
64+ logger .error (f"File sync error: { e } " , err = True )
65+ finally :
66+ loop .close ()
67+
68+ logger .info (f"Sync changes enabled: { app_config .sync_changes } " )
69+ if app_config .sync_changes :
70+ # Start the sync thread
71+ sync_thread = threading .Thread (target = run_file_sync , daemon = True )
72+ sync_thread .start ()
73+ logger .info ("Started file sync in background" )
74+
75+ # Now run the MCP server (blocks)
76+ logger .info (f"Starting MCP server with { transport .upper ()} transport" )
77+
78+ if transport == "stdio" :
79+ mcp_server .run (
80+ transport = transport ,
81+ host = host ,
82+ )
83+ elif transport == "streamable-http" or transport == "sse" :
84+ mcp_server .run (
85+ transport = transport ,
86+ host = host ,
87+ port = port ,
88+ path = path ,
89+ )
0 commit comments