Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 1 addition & 34 deletions src/mcp_agent/cli/cloud/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,7 @@
from typing import Optional
from importlib.metadata import version as metadata_version

import click
import typer
from rich.console import Console
from rich.panel import Panel
from typer.core import TyperGroup

from mcp_agent.cli.cloud.commands import (
configure_app,
Expand Down Expand Up @@ -40,8 +36,7 @@
describe_server,
delete_server,
)
from mcp_agent.cli.exceptions import CLIError
from mcp_agent.cli.utils.ux import print_error
from mcp_agent.cli.utils.typer_utils import HelpfulTyperGroup

# Setup file logging
LOG_DIR = Path.home() / ".mcp-agent" / "logs"
Expand All @@ -63,34 +58,6 @@
logging.basicConfig(level=logging.INFO, handlers=[file_handler])


class HelpfulTyperGroup(TyperGroup):
"""Typer group that shows help before usage errors for better UX."""

def resolve_command(self, ctx, args):
try:
return super().resolve_command(ctx, args)
except click.UsageError as e:
click.echo(ctx.get_help())

console = Console(stderr=True)
error_panel = Panel(
str(e),
title="Error",
title_align="left",
border_style="red",
expand=True,
)
console.print(error_panel)
ctx.exit(2)

def invoke(self, ctx):
try:
return super().invoke(ctx)
except CLIError as e:
# Handle CLIError cleanly - show error message and exit
logging.error(f"CLI error: {str(e)}")
print_error(str(e))
ctx.exit(e.exit_code)


# Root typer for `mcp-agent` CLI commands
Expand Down
3 changes: 2 additions & 1 deletion src/mcp_agent/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@

from __future__ import annotations


import typer
from rich.console import Console

Expand All @@ -32,12 +31,14 @@
)

from mcp_agent.cli.cloud.commands import deploy_config, login, logout, whoami
from mcp_agent.cli.utils.typer_utils import HelpfulTyperGroup

app = typer.Typer(
help="mcp-agent CLI",
add_completion=False,
no_args_is_help=False,
context_settings={"help_option_names": ["-h", "--help"]},
cls=HelpfulTyperGroup,
)


Expand Down
40 changes: 40 additions & 0 deletions src/mcp_agent/cli/utils/typer_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"""Shared Typer utilities for MCP Agent CLI."""

import logging
import click
from rich.console import Console
from rich.panel import Panel
from typer.core import TyperGroup

from mcp_agent.cli.exceptions import CLIError
from mcp_agent.cli.utils.ux import print_error


class HelpfulTyperGroup(TyperGroup):
"""Typer group that shows help before usage errors for better UX."""

def resolve_command(self, ctx, args):
try:
return super().resolve_command(ctx, args)
except click.UsageError as e:
click.echo(ctx.get_help())

console = Console(stderr=True)
error_panel = Panel(
str(e),
title="Error",
title_align="left",
border_style="red",
expand=True,
)
console.print(error_panel)
ctx.exit(2)

def invoke(self, ctx):
try:
return super().invoke(ctx)
except CLIError as e:
# Handle CLIError cleanly - show error message and exit
logging.error(f"CLI error: {str(e)}")
print_error(str(e))
ctx.exit(e.exit_code)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Avoid double-logging and handle unexpected exceptions here for consistent UX.

Prevents duplicate log lines and ensures unhandled exceptions are rendered cleanly across all CLIs without per-file wrappers.

     def invoke(self, ctx):
         try:
             return super().invoke(ctx)
         except CLIError as e:
-            # Handle CLIError cleanly - show error message and exit
-            logging.error(f"CLI error: {str(e)}")
-            print_error(str(e))
-            ctx.exit(e.exit_code)
+            # Expected CLI errors: log once and print cleanly
+            logging.error(str(e))
+            print_error(str(e), log=False)
+            ctx.exit(e.exit_code)
+        except Exception as e:
+            # Unexpected errors: include traceback in logs, concise console message
+            logging.exception("Unhandled exception in CLI")
+            print_error(f"An unexpected error occurred: {e}", log=False)
+            ctx.exit(1)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def invoke(self, ctx):
try:
return super().invoke(ctx)
except CLIError as e:
# Handle CLIError cleanly - show error message and exit
logging.error(f"CLI error: {str(e)}")
print_error(str(e))
ctx.exit(e.exit_code)
def invoke(self, ctx):
try:
return super().invoke(ctx)
except CLIError as e:
# Expected CLI errors: log once and print cleanly
logging.error(str(e))
print_error(str(e), log=False)
ctx.exit(e.exit_code)
except Exception as e:
# Unexpected errors: include traceback in logs, concise console message
logging.exception("Unhandled exception in CLI")
print_error(f"An unexpected error occurred: {e}", log=False)
ctx.exit(1)
🤖 Prompt for AI Agents
In src/mcp_agent/cli/utils/typer_utils.py around lines 33 to 40, the current
invoke() handler logs CLIError via logging.error and also prints the error which
can produce duplicate messages and it doesn't handle unexpected exceptions;
change it so the CLIError branch only prints a user-facing message
(print_error(str(e))) and exits with ctx.exit(e.exit_code) (remove the
logging.error call to avoid double-logging), and add a second broad except
Exception as e that calls logging.exception("Unhandled CLI exception") to
capture the traceback, prints a concise user-facing message (e.g.
print_error("Unexpected error occurred")) and exits with a non-zero code
(ctx.exit(1)).

Loading