arcade-mcp/libs/arcade-mcp-server/arcade_mcp_server/logging_utils.py
Eric Gustin 36584942f7
Fix runtime warning (#771)
When `python -m arcade_mcp_server` was executed, we would get the
following Runtime Warning:

```
<frozen runpy>:128: RuntimeWarning: 'arcade_mcp_server.__main__' found in sys.modules after import of package 'arcade_mcp_server', but prior to execution of 'arcade_mcp_server.__main__'; this may result in unpredictable behaviour
```

This PR resolves this. This PR is mainly just moving existing functions
to new locations; a refactor


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Primarily a module-organization refactor with minimal behavior change;
main risk is import-path regressions for internal callers and stdio/CLI
startup wiring.
> 
> **Overview**
> Fixes the `python -m arcade_mcp_server` runtime warning by refactoring
`arcade_mcp_server.__main__` to be a thin CLI entrypoint and moving its
reusable logic into import-safe modules.
> 
> Extracts stdio execution and tool discovery into a new
`arcade_mcp_server.stdio_runner` (`initialize_tool_catalog`,
`run_stdio_server`) and moves `setup_logging` into `logging_utils`,
updating `MCPApp`, the FastAPI `worker`, and tests to import from the
new locations. Bumps package version to `1.17.3`.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
210475acea7c5df44fc66be2bde06f1f0c806c4e. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
2026-02-25 09:55:37 -08:00

56 lines
1.7 KiB
Python

"""Shared logging utilities for MCP server."""
import logging
import sys
from loguru import logger
class LoguruInterceptHandler(logging.Handler):
"""Intercept standard logging and route to Loguru.
This handler bridges the standard Python logging module with Loguru,
ensuring all logs (from both systems) use the same formatting.
"""
def emit(self, record: logging.LogRecord) -> None:
try:
level = logger.level(record.levelname).name
except ValueError:
level = str(record.levelno)
logger.opt(exception=record.exc_info).log(level, record.getMessage())
def intercept_standard_logging() -> None:
"""Configure standard logging to route through Loguru.
This should be called after Loguru is configured to ensure all
standard logging calls are intercepted and formatted consistently.
"""
logging.basicConfig(handlers=[LoguruInterceptHandler()], level=0, force=True)
def setup_logging(level: str = "INFO", stdio_mode: bool = False) -> None:
"""Configure logging with Loguru."""
logger.remove()
# In stdio mode, use stderr (stdout is reserved for JSON-RPC)
sink = sys.stderr if stdio_mode else sys.stdout
if level == "DEBUG":
format_str = "<level>{level: <8}</level> | <green>{time:HH:mm:ss}</green> | <cyan>{name}:{line}</cyan> | <level>{message}</level>"
else:
format_str = (
"<level>{level: <8}</level> | <green>{time:HH:mm:ss}</green> | <level>{message}</level>"
)
logger.add(
sink,
format=format_str,
level=level,
colorize=(not stdio_mode),
diagnose=(level == "DEBUG"),
)
intercept_standard_logging()