feat(mcp): add MCP server log panel backend (#2308)

- Add log buffer (_log_buffer: deque with maxlen=500) to RuntimeMCPSession
- Capture stderr from Box managed process in monitor_process_health loop
- Add get_mcp_server_logs service method with limit and level filtering
- Add GET /servers/<server_name>/logs HTTP endpoint
- Parse log level from stderr lines (error/warning/debug/info)
- Tests passing: 211 passed, 12 skipped

Co-authored-by: dadachann <185672915+dadachann@users.noreply.github.com>
This commit is contained in:
Hyu
2026-07-03 16:05:23 +08:00
committed by GitHub
parent 28bffdef21
commit 85b5b5b54b
4 changed files with 63 additions and 0 deletions
@@ -297,6 +297,33 @@ class BoxStdioSessionRuntime:
)
if consecutive_errors >= self.owner._MONITOR_MAX_CONSECUTIVE_ERRORS:
return
# Capture stderr logs from the managed process
if isinstance(info, dict):
stderr_text = info.get('stderr', '') or info.get('stderr_preview', '')
else:
stderr_text = getattr(info, 'stderr', '') or getattr(info, 'stderr_preview', '')
if stderr_text and stderr_text != self.owner._last_stderr_text:
# Find new lines not in the previous snapshot
old_lines = set(self.owner._last_stderr_text.splitlines()) if self.owner._last_stderr_text else set()
new_lines = [l for l in stderr_text.splitlines() if l and l not in old_lines]
self.owner._last_stderr_text = stderr_text
import time as _time
for line in new_lines:
level = (
'error'
if any(k in line.upper() for k in ('ERROR', 'CRITICAL'))
else 'warning'
if 'WARNING' in line.upper()
else 'debug'
if 'DEBUG' in line.upper()
else 'info'
)
self.owner._log_buffer.append({'ts': _time.time(), 'level': level, 'text': line})
await asyncio.sleep(self.owner._MONITOR_POLL_INTERVAL)
async def _managed_process_is_running(self) -> bool: