diff --git a/src/serena/cli.py b/src/serena/cli.py index 844aa6ef..444a0538 100644 --- a/src/serena/cli.py +++ b/src/serena/cli.py @@ -16,6 +16,7 @@ from serena.config.serena_config import ProjectConfig, SerenaConfig from serena.constants import ( DEFAULT_CONTEXT, DEFAULT_MODES, + SERENA_LOG_FORMAT, SERENA_MANAGED_DIR_IN_HOME, SERENAS_OWN_CONTEXT_YAMLS_DIR, SERENAS_OWN_MODE_YAMLS_DIR, @@ -138,11 +139,17 @@ class TopLevelCommands(AutoRegisteringGroup): tool_timeout: float | None, ) -> None: # initialize logging, using INFO level initially (will later be adjusted by SerenaAgent according to the config) + # * memory log handler (for use by GUI/Dashboard) + # * stream handler for stderr (for direct console output, which will also be captured by clients like Claude Desktop) + # (Note that stdout must never be used for logging, as it is used by the MCP server to communicate with the client.) Logger.root.setLevel(logging.INFO) memory_log_handler = MemoryLogHandler() Logger.root.addHandler(memory_log_handler) + stderr_handler = logging.StreamHandler(stream=sys.stderr) + stderr_handler.formatter = logging.Formatter(SERENA_LOG_FORMAT) + Logger.root.addHandler(stderr_handler) - log.info("Starting Serena MCP server") + log.info("Initializing Serena MCP server") project_file = project_file_arg or project factory = SerenaMCPFactorySingleProcess(context=context, project=project_file, memory_log_handler=memory_log_handler) server = factory.create_mcp_server( diff --git a/src/serena/mcp.py b/src/serena/mcp.py index c7d6f8bd..ff8a11ba 100644 --- a/src/serena/mcp.py +++ b/src/serena/mcp.py @@ -7,7 +7,6 @@ from abc import abstractmethod from collections.abc import AsyncIterator, Iterator, Sequence from contextlib import asynccontextmanager from dataclasses import dataclass -from logging import Formatter, Logger, StreamHandler from typing import Any, Literal, cast import docstring_parser @@ -22,22 +21,19 @@ from serena.agent import ( SerenaConfig, ) from serena.config.context_mode import SerenaAgentContext, SerenaAgentMode -from serena.constants import DEFAULT_CONTEXT, DEFAULT_MODES +from serena.constants import DEFAULT_CONTEXT, DEFAULT_MODES, SERENA_LOG_FORMAT from serena.tools import Tool from serena.util.exception import show_fatal_exception_safe from serena.util.logging import MemoryLogHandler log = logging.getLogger(__name__) -LOG_FORMAT = "%(levelname)-5s %(asctime)-15s %(name)s:%(funcName)s:%(lineno)d - %(message)s" -LOG_LEVEL = logging.INFO def configure_logging(*args, **kwargs) -> None: # type: ignore - # configure logging to stderr (will be captured by Claude Desktop); stdio is the MCP communication stream and cannot be used! - Logger.root.setLevel(LOG_LEVEL) - handler = StreamHandler(stream=sys.stderr) - handler.formatter = Formatter(LOG_FORMAT) - Logger.root.addHandler(handler) + # We only do something here if logging has not yet been configured. + # Normally, logging is configured in the MCP server startup script. + if not logging.is_enabled(): + logging.basicConfig(level=logging.INFO, stream=sys.stderr, format=SERENA_LOG_FORMAT) # patch the logging configuration function in fastmcp, because it's hard-coded and broken