Fix MCP server lifecycle issue

In HTTP/SSE mode, a client disconnection triggered a partial agent shutdown
(project deactivation, dashboard manager & GUI viewer shutdown)
This commit is contained in:
Dominik Jain committed 2026-05-11 19:42:03 +02:00
1 parent 65474d3d3b
commit a1d874b29e
3 files changed
+27 -7

No files matched your search

+2 -1
View File
@@ -7,7 +7,8 @@ Status of the `main` branch. Changes prior to the next official version change w
Instead, they can define `added_modes` to add modes on top of base and default modes.
See updated [documentation on modes](https://oraios.github.io/serena/02-usage/050_configuration.html#modes).
- Serena's default configuration now uses `interactive` and `editing` as `base_modes` instead of as `default_modes`.
- Fixed path validation in `search_for_pattern` tool (thanks to [@dodge1218](https://github.com/dodge1218) for the report)
- Fixed path validation in `search_for_pattern` tool (thanks to [@dodge1218](https://github.com/dodge1218) for the report)
- Fix: In HTTP/SSE mode, a client disconnection triggered a partial agent shutdown (project deactivation, dashboard manager & GUI viewer shutdown)
* JetBrains:
- Add new tools:
+1 -1
View File
@@ -362,7 +362,7 @@ class TopLevelCommands(AutoRegisteringGroup):
if default_modes or added_modes:
mode_selection_def = ModeSelectionDefinitionWithAddedModes(default_modes=default_modes or None, added_modes=added_modes or None)
factory = SerenaMCPFactory(context=context, project=project_file, memory_log_handler=memory_log_handler)
factory = SerenaMCPFactory(transport=transport, context=context, project=project_file, memory_log_handler=memory_log_handler)
server = factory.create_mcp_server(
host=host,
port=port,
+24 -5
View File
@@ -52,14 +52,22 @@ class SerenaMCPFactory:
Factory for the creation of the Serena MCP server with an associated SerenaAgent.
"""
def __init__(self, context: str = DEFAULT_CONTEXT, project: str | None = None, memory_log_handler: MemoryLogHandler | None = None):
def __init__(
self,
transport: Literal["stdio", "sse", "streamable-http"],
context: str = DEFAULT_CONTEXT,
project: str | None = None,
memory_log_handler: MemoryLogHandler | None = None,
):
"""
:param transport: The transport to use for the MCP server.
:param context: The context name or path to context file
:param project: Either an absolute path to the project directory or a name of an already registered project.
If the project passed here hasn't been registered yet, it will be registered automatically and can be activated by its name
afterward.
:param memory_log_handler: the in-memory log handler to use for the agent's logging
"""
self.transport = transport
self.context = SerenaAgentContext.load(context)
self.project = project
self.agent: SerenaAgent | None = None
@@ -342,16 +350,27 @@ class SerenaMCPFactory:
@asynccontextmanager
async def server_lifespan(self, mcp_server: FastMCP) -> AsyncIterator[None]:
"""Manage server startup and shutdown lifecycle."""
"""
Manages the lifespan of MCP server instances and performs necessary setup and teardown.
For stdio transport, there is a single server instance.
For other transports, this is called once per connection!
:param mcp_server: the MCP server instance to configure
"""
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")
try:
yield
finally:
log.info("MCP server shutting down")
if self.agent is not None:
self.agent.on_shutdown()
# Shut down the server if we are running in stdio mode.
# For other transports, we do nothing; the singleton agent instance remains active.
if self.transport == "stdio":
log.info("MCP server shutting down")
if self.agent is not None:
self.agent.on_shutdown()
else:
log.info("Client disconnected")
def _get_initial_instructions(self) -> str:
assert self.agent is not None