Fix agent shutdown lifecycle: no-op shutdown, orphaned LS processes, missing MCP cleanup

Three related bugs preventing proper cleanup of language server processes:

1. SerenaAgent.shutdown() checked `hasattr(self, "_is_initialized")` but that
   attribute was never set, making shutdown always return early (no-op).
   Replace with `hasattr(self, "_active_project")` which is set early in
   __init__ and correctly guards against __del__ on partial construction.

2. _activate_project() replaced self._active_project without shutting down
   the previous project first, orphaning its language server processes.
   Now calls old_project.shutdown() before switching.

3. MCP server_lifespan() logged "shutting down" but never called
   agent.shutdown(), so language servers were orphaned on MCP server exit.
   Now calls agent.shutdown() in a try/finally block.
This commit is contained in:
asher authored and Asher Haig committed 2026-03-21 23:20:30 -04:00
1 parent 0c915bd18d
commit 48025c7ddf
2 files changed
+13 -3

No files matched your search

+7 -1
View File
@@ -699,6 +699,11 @@ class SerenaAgent:
f"(2) Configure one MCP server per backend in your client."
)
# shut down the previously active project to release its language server processes
if self._active_project is not None:
log.info(f"Shutting down previously active project '{self._active_project.project_name}' before switching")
self._active_project.shutdown()
self._active_project = project
project.set_agent(self)
@@ -845,7 +850,8 @@ class SerenaAgent:
"""
Shuts down the agent, freeing resources and stopping background tasks.
"""
if not hasattr(self, "_is_initialized"):
# guard against __del__ being called on a partially constructed instance
if not hasattr(self, "_active_project"):
return
log.info("SerenaAgent is shutting down ...")
if self._active_project is not None:
+6 -2
View File
@@ -341,8 +341,12 @@ class SerenaMCPFactory:
openai_tool_compatible = self.context.name in ["chatgpt", "codex", "oaicompat-agent"]
self._set_mcp_tools(mcp_server, openai_tool_compatible=openai_tool_compatible)
log.info("MCP server lifetime setup complete")
yield
log.info("MCP server shutting down")
try:
yield
finally:
log.info("MCP server shutting down")
if self.agent is not None:
self.agent.shutdown()
def _get_initial_instructions(self) -> str:
assert self.agent is not None