Use a separate process to call webbrowser open,

redirecting output at the OS level to avoid stdout contamination #257
This commit is contained in:
Dominik Jain authored and Dominik Jain committed 2025-07-03 12:23:56 +02:00
1 parent 8910b38c02
commit 80a4caf36a
1 file changed
+17 -6
+17 -6
View File
@@ -2,7 +2,7 @@
The Serena Model Context Protocol (MCP) Server
"""
import contextlib
import multiprocessing
import os
import platform
import sys
@@ -273,11 +273,10 @@ class SerenaAgent:
Logger.root.addHandler(dashboard_log_handler)
self._dashboard_thread, port = SerenaDashboardAPI(dashboard_log_handler, tool_names).run_in_thread()
if self.serena_config.web_dashboard_open_on_launch:
# open the dashboard URL in the default web browser, making sure to redirect output,
# as this can print to stdout (contaminating the MCP server stream)
with open(os.devnull, "w") as fnull:
with contextlib.redirect_stdout(fnull), contextlib.redirect_stderr(fnull):
webbrowser.open(f"http://localhost:{port}/dashboard/index.html")
# open the dashboard URL in the default web browser (using a separate process to control
# output redirection)
process = multiprocessing.Process(target=self._open_dashboard, args=(port,))
process.start()
# log fundamental information
log.info(f"Starting Serena server (version={serena_version()}, process id={os.getpid()}, parent process id={os.getppid()})")
@@ -326,6 +325,18 @@ class SerenaAgent:
"You should now pass either --project <project_name> or --project <project_root>."
)
@staticmethod
def _open_dashboard(port: int) -> None:
# Redirect stdout and stderr file descriptors to /dev/null,
# making sure that nothing can be written to stdout/stderr, even by subprocesses
null_fd = os.open(os.devnull, os.O_WRONLY)
os.dup2(null_fd, sys.stdout.fileno())
os.dup2(null_fd, sys.stderr.fileno())
os.close(null_fd)
# open the dashboard URL in the default web browser
webbrowser.open(f"http://localhost:{port}/dashboard/index.html")
def get_project_root(self) -> str:
"""
:return: the root directory of the active project (if any); raises a ValueError if there is no active project