Skip to content

Commit e8e2f09

Browse files
authored
Merge pull request #253 from iann0036/fix-subapp-lifespan
fix: Initialize and track lifespans for sub-apps made during hot reload
2 parents 13ce3b5 + d88a23c commit e8e2f09

File tree

1 file changed

+33
-9
lines changed

1 file changed

+33
-9
lines changed

src/mcpo/main.py

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -178,17 +178,21 @@ def mount_config_servers(main_app: FastAPI, config_data: Dict[str, Any],
178178

179179
def unmount_servers(main_app: FastAPI, path_prefix: str, server_names: list):
180180
"""Unmount specific MCP servers."""
181+
active_lifespans = getattr(main_app.state, 'active_lifespans', {})
182+
181183
for server_name in server_names:
182184
mount_path = f"{path_prefix}{server_name}"
183-
# Find and remove the mount
184-
routes_to_remove = []
185-
for route in main_app.router.routes:
186-
if hasattr(route, 'path') and route.path == mount_path:
187-
routes_to_remove.append(route)
188-
189-
for route in routes_to_remove:
190-
main_app.router.routes.remove(route)
191-
logger.info(f"Unmounted server: {server_name}")
185+
186+
# Clean up lifespan context if it exists
187+
if server_name in active_lifespans:
188+
lifespan_context = active_lifespans[server_name]
189+
try:
190+
# Schedule cleanup of the lifespan context
191+
asyncio.create_task(lifespan_context.__aexit__(None, None, None))
192+
except Exception as e:
193+
logger.warning(f"Error cleaning up lifespan for {server_name}: {e}")
194+
finally:
195+
del active_lifespans[server_name]
192196

193197

194198
async def reload_config_handler(main_app: FastAPI, new_config_data: Dict[str, Any]):
@@ -236,6 +240,11 @@ async def reload_config_handler(main_app: FastAPI, new_config_data: Dict[str, An
236240
# Add new servers and updated servers
237241
if servers_to_add:
238242
logger.info(f"Adding servers: {list(servers_to_add)}")
243+
244+
# Store lifespan contexts for cleanup
245+
if not hasattr(main_app.state, 'active_lifespans'):
246+
main_app.state.active_lifespans = {}
247+
239248
for server_name in servers_to_add:
240249
server_cfg = new_config_data["mcpServers"][server_name]
241250
try:
@@ -244,6 +253,21 @@ async def reload_config_handler(main_app: FastAPI, new_config_data: Dict[str, An
244253
strict_auth, api_dependency, connection_timeout, lifespan
245254
)
246255
main_app.mount(f"{path_prefix}{server_name}", sub_app)
256+
257+
# Start the lifespan for the new sub-app
258+
lifespan_context = sub_app.router.lifespan_context(sub_app)
259+
await lifespan_context.__aenter__()
260+
261+
# Store the context manager for cleanup later
262+
main_app.state.active_lifespans[server_name] = lifespan_context
263+
264+
# Check if connection was successful
265+
is_connected = getattr(sub_app.state, "is_connected", False)
266+
if is_connected:
267+
logger.info(f"Successfully connected to new server: '{server_name}'")
268+
else:
269+
logger.warning(f"Failed to connect to new server: '{server_name}'")
270+
247271
except Exception as e:
248272
logger.error(f"Failed to create server '{server_name}': {e}")
249273
# Rollback on failure

0 commit comments

Comments
 (0)