From 48025c7ddfed17b0ac69f071b4726d20ae8028bb Mon Sep 17 00:00:00 2001 From: asher Date: Fri, 20 Mar 2026 19:44:13 -0400 Subject: [PATCH] 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. --- src/serena/agent.py | 8 +++++++- src/serena/mcp.py | 8 ++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/serena/agent.py b/src/serena/agent.py index 671d2eb2..b714f679 100644 --- a/src/serena/agent.py +++ b/src/serena/agent.py @@ -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: diff --git a/src/serena/mcp.py b/src/serena/mcp.py index 83b8278b..c83dadc7 100644 --- a/src/serena/mcp.py +++ b/src/serena/mcp.py @@ -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