fix: route MCP server log notifications to loguru instead of TUI#1637
fix: route MCP server log notifications to loguru instead of TUI#1637he-yufeng wants to merge 1 commit intoMoonshotAI:mainfrom
Conversation
fastmcp's default log_handler uses RichHandler(stderr=True), which dumps MCP server log notifications (e.g. SearXNG startup messages) directly into the TUI. Pass a custom handler that routes through loguru at DEBUG level so these messages go to the log file only. Fixes MoonshotAI#1624
| data = message.data | ||
| msg = data.get("message") or data.get("msg") or str(data) |
There was a problem hiding this comment.
🔴 AttributeError when MCP server sends non-dict log data
The _mcp_log_handler calls data.get("message") on message.data, but per the MCP spec, data is typed as Any and can be any JSON-serializable value — including a plain string, number, or list. If an MCP server sends a log notification with a string data (e.g. "Server started"), calling .get() on it raises AttributeError: 'str' object has no attribute 'get'. This exception propagates up from mcp.client.session:_received_notification (line 431 of mcp/client/session.py) and can crash the MCP session, potentially disconnecting the server.
Suggested fix
Check if data is a dict before calling .get():
data = message.data
if isinstance(data, dict):
msg = data.get("message") or data.get("msg") or str(data)
else:
msg = str(data)| data = message.data | |
| msg = data.get("message") or data.get("msg") or str(data) | |
| data = message.data | |
| msg = data.get("message") or data.get("msg") or str(data) if isinstance(data, dict) else str(data) |
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f438cca947
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| async def _mcp_log_handler(message: LogMessage) -> None: | ||
| """Route MCP server log notifications to loguru instead of rich stderr.""" | ||
| data = message.data | ||
| msg = data.get("message") or data.get("msg") or str(data) |
There was a problem hiding this comment.
Handle non-object MCP log payloads safely
The new _mcp_log_handler assumes message.data is a mapping and calls .get(...), but MCP log notification payloads can be arbitrary JSON values (for example a plain string or null). In those cases this line raises AttributeError, which can propagate out of the notification callback and disrupt MCP client handling for servers that emit non-object log payloads. Add a type guard (e.g., isinstance(data, dict)) before using .get, and fall back to str(data) otherwise.
Useful? React with 👍 / 👎.
Problem
MCP servers (e.g. SearXNG) send log notifications on each request.
fastmcp.Clientdefaults todefault_log_handler, which usesRichHandler(stderr=True)— these messages end up dumped into the TUI:Root Cause
fastmcp/client/logging.pyhas adefault_log_handlerthat routes MCPLoggingMessageNotificationthrough Python's standardloggingmodule with aRichHandlertargeting stderr. When kimi-cli createsfastmcp.Client(MCPConfig(...))without specifyinglog_handler, this default kicks in and the messages bypass loguru entirely.Fix
Pass a custom
log_handlertofastmcp.Clientthat routes MCP server log notifications through kimi-cli's loguru logger at DEBUG level. The messages still go tokimi.logfor debugging, but no longer pollute the TUI.The handler also properly extracts the
messagefield from the notification data (SearXNG usesdata.message, notdata.msg), avoiding the rawstr(message)fallback that produced the uglymeta=None level='info' ...output.Fixes #1624