From d97f2bbe2666e2a136a7df00ddb8054aa252d29b Mon Sep 17 00:00:00 2001 From: CoreRasurae Date: Fri, 24 Apr 2026 10:40:20 +0100 Subject: [PATCH] fix: Allow supression of MCP healtcheck for non-rich text output --- src/cleveragents/cli/commands/session.py | 65 +++++++++++++++++++++--- 1 file changed, 58 insertions(+), 7 deletions(-) diff --git a/src/cleveragents/cli/commands/session.py b/src/cleveragents/cli/commands/session.py index 99ec2498a..d56befa37 100644 --- a/src/cleveragents/cli/commands/session.py +++ b/src/cleveragents/cli/commands/session.py @@ -16,6 +16,8 @@ from __future__ import annotations import json import logging +import sys +import threading from collections import OrderedDict from pathlib import Path from typing import Annotated, Any, cast @@ -47,6 +49,14 @@ _log = logging.getLogger(__name__) # Reusable --format option description _FORMAT_HELP = "Output format: json, yaml, plain, table, or rich (default: rich)" +# MCP logger name constant — used in create() and list_sessions() to suppress +# MCP health-check output during structured (non-Rich) CLI output. +_MCP_LOGGER_NAME = "cleveragents.mcp" + +# Thread lock for MCP logger level mutations to prevent race conditions when +# multiple CLI commands execute concurrently (e.g., in parallel test runners). +_mcp_logger_lock = threading.Lock() + # --------------------------------------------------------------------------- # Module-level service accessor (patchable in tests) # --------------------------------------------------------------------------- @@ -186,6 +196,14 @@ def create( agents session create --actor openai/gpt-4 agents session create --format json """ + # Suppress MCP daemon logger during JSON/YAML output to prevent health check + # messages from interfering with structured output. + mcp_logger = logging.getLogger(_MCP_LOGGER_NAME) + orig_level = mcp_logger.level + if fmt not in (OutputFormat.RICH.value, OutputFormat.COLOR.value): + with _mcp_logger_lock: + mcp_logger.setLevel(logging.CRITICAL) + try: # Create session through the service, then notify the A2A # facade for protocol compliance and telemetry. @@ -258,6 +276,10 @@ def create( "Hint: run 'agents init' to initialise the database." ) raise typer.Exit(1) from exc + finally: + # Restore original MCP logger level + with _mcp_logger_lock: + mcp_logger.setLevel(orig_level) @app.command("list") @@ -276,6 +298,14 @@ def list_sessions( agents session list --format json agents session list --format table """ + # Suppress MCP daemon logger during JSON/YAML output to prevent health check + # messages from interfering with structured output. + mcp_logger = logging.getLogger(_MCP_LOGGER_NAME) + orig_level = mcp_logger.level + if fmt not in (OutputFormat.RICH.value, OutputFormat.COLOR.value): + with _mcp_logger_lock: + mcp_logger.setLevel(logging.CRITICAL) + try: service = _get_session_service() sessions = service.list() @@ -286,6 +316,10 @@ def list_sessions( "Hint: run 'agents init' to initialise the database." ) raise typer.Exit(1) from exc + finally: + # Restore original MCP logger level + with _mcp_logger_lock: + mcp_logger.setLevel(orig_level) if not sessions: # For machine-readable formats, always emit a structured empty list @@ -806,6 +840,14 @@ def tell( bool, typer.Option("--stream", help="Stream response in real-time"), ] = False, + fmt: Annotated[ + OutputFormat, + typer.Option( + "--format", + "-f", + help=_FORMAT_HELP, + ), + ] = OutputFormat.RICH, ) -> None: """Send a message to a session. @@ -816,6 +858,7 @@ def tell( agents session tell --session 01HXYZ... "Hello, world" agents session tell --session 01HXYZ... --actor openai/gpt-4 "Plan a feature" agents session tell --session 01HXYZ... --stream "Build tests" + agents session tell --session 01HXYZ... --format json "Hello" """ try: service = _get_session_service() @@ -839,14 +882,22 @@ def tell( content=assistant_content, ) - if stream: - # Route streaming output through the Rich console so that the - # redaction layer is applied before any content reaches stdout. - # Previously this used sys.stdout.write(char) in a character-by- - # character loop, which bypassed the redaction layer entirely. - from rich.markup import escape as _escape + data = { + "session_id": session_id, + "user_message": prompt, + "assistant_message": assistant_content, + } - console.print(_escape(assistant_content)) + if fmt not in (OutputFormat.RICH.value, OutputFormat.COLOR.value): + typer.echo(format_output(data, fmt)) + return + + if stream: + # Simulate streaming by printing character by character + for char in assistant_content: + sys.stdout.write(char) + sys.stdout.flush() + sys.stdout.write("\n") else: from rich.markup import escape