Skip to content

Commit 5a6e1e2

Browse files
niechenclaude
andcommitted
Improve run command logging and console output
- Split logging configuration into general and dependency-specific parts - Add rich console output to run --http commands matching share style - Control uvicorn logging across all HTTP commands (run, share) - Extract debug utilities to common location for consistency - Show clean HTTP URLs and server lists in run commands - Move verbose logging to debug level with MCPM_DEBUG control 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 505eebe commit 5a6e1e2

File tree

6 files changed

+208
-59
lines changed

6 files changed

+208
-59
lines changed

src/mcpm/cli.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
from mcpm import __version__
1111
from mcpm.clients.client_config import ClientConfigManager
12+
from mcpm.utils.logging_config import setup_logging
1213
from mcpm.commands import (
1314
add,
1415
client,
@@ -30,6 +31,9 @@
3031
console = Console()
3132
client_config_manager = ClientConfigManager()
3233

34+
# Setup Rich logging early - this runs when the module is imported
35+
setup_logging()
36+
3337
# Set -h as an alias for --help but we'll handle it ourselves
3438
CONTEXT_SETTINGS = dict(help_option_names=[])
3539

src/mcpm/commands/profile/run.py

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,16 @@
44
import logging
55

66
import click
7+
from rich.console import Console
78

89
from mcpm.fastmcp_integration.proxy import create_mcpm_proxy
910
from mcpm.profile.profile_config import ProfileConfigManager
1011
from mcpm.utils.config import DEFAULT_PORT
12+
from mcpm.utils.logging_config import setup_dependency_logging, ensure_dependency_logging_suppressed, get_uvicorn_log_level
1113

1214
profile_config_manager = ProfileConfigManager()
1315
logger = logging.getLogger(__name__)
16+
console = Console()
1417

1518

1619
async def find_available_port(preferred_port, max_attempts=10):
@@ -47,6 +50,12 @@ async def run_profile_fastmcp(profile_servers, profile_name, http_mode=False, po
4750
)
4851

4952
logger.debug(f"FastMCP proxy initialized with: {[s.name for s in profile_servers]}")
53+
54+
# Set up dependency logging for FastMCP/MCP libraries
55+
setup_dependency_logging()
56+
57+
# Re-suppress library logging after FastMCP initialization
58+
ensure_dependency_logging_suppressed()
5059

5160
# Record profile usage
5261
from mcpm.commands.usage import record_profile_usage
@@ -57,13 +66,24 @@ async def run_profile_fastmcp(profile_servers, profile_name, http_mode=False, po
5766
# Try to find an available port if the requested one is taken
5867
actual_port = await find_available_port(port)
5968
if actual_port != port:
60-
logger.warning(f"Port {port} is busy, using port {actual_port} instead")
61-
62-
logger.info(f"Starting profile '{profile_name}' on HTTP port {actual_port}")
63-
logger.info("Press Ctrl+C to stop the profile.")
64-
65-
# Run the aggregated proxy over HTTP
66-
await proxy.run_http_async(host="127.0.0.1", port=actual_port)
69+
logger.debug(f"Port {port} is busy, using port {actual_port} instead")
70+
71+
# Display server information
72+
http_url = f"http://127.0.0.1:{actual_port}/mcp/"
73+
console.print(f"[bold green]Profile '{profile_name}' is now running at:[/]")
74+
console.print(f"[cyan]{http_url}[/]")
75+
76+
# Show servers in the profile
77+
console.print(f"[bold green]Servers:[/]")
78+
for server_config in profile_servers:
79+
console.print(f" • [cyan]{server_config.name}[/]")
80+
81+
console.print("[dim]Press Ctrl+C to stop the profile[/]")
82+
83+
logger.debug(f"Starting FastMCP proxy for profile '{profile_name}' on port {actual_port}")
84+
85+
# Run the aggregated proxy over HTTP with uvicorn logging control
86+
await proxy.run_http_async(host="127.0.0.1", port=actual_port, uvicorn_config={"log_level": get_uvicorn_log_level()})
6787
else:
6888
# Run the aggregated proxy over stdio (default)
6989
logger.info(f"Starting profile '{profile_name}' over stdio")

src/mcpm/commands/profile/share.py

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Profile share command."""
22

33
import asyncio
4+
import logging
45
import secrets
56

67
import click
@@ -10,9 +11,11 @@
1011
from mcpm.fastmcp_integration.proxy import create_mcpm_proxy
1112
from mcpm.profile.profile_config import ProfileConfigManager
1213
from mcpm.utils.config import DEFAULT_SHARE_ADDRESS, DEFAULT_PORT
14+
from mcpm.utils.logging_config import setup_dependency_logging, ensure_dependency_logging_suppressed, get_uvicorn_log_level
1315

1416
console = Console()
1517
profile_config_manager = ProfileConfigManager()
18+
logger = logging.getLogger(__name__)
1619

1720

1821
async def find_available_port(preferred_port, max_attempts=10):
@@ -47,7 +50,8 @@ async def share_profile_fastmcp(profile_servers, profile_name, port, address, ht
4750
config_manager.save_auth_config(api_key)
4851
console.print(f"[green]Generated new API key:[/] [cyan]{api_key}[/]")
4952
try:
50-
console.print(f"[cyan]Creating FastMCP proxy for profile '{profile_name}'...[/]")
53+
server_count = len(profile_servers)
54+
logger.debug(f"Creating FastMCP proxy for profile '{profile_name}' with {server_count} server(s)")
5155

5256
# Create FastMCP proxy for profile servers (HTTP mode - disable auth)
5357
proxy = await create_mcpm_proxy(
@@ -58,30 +62,36 @@ async def share_profile_fastmcp(profile_servers, profile_name, port, address, ht
5862
api_key=api_key,
5963
)
6064

61-
server_count = len(profile_servers)
62-
console.print(f"[green]FastMCP proxy created with {server_count} server(s)[/]")
65+
logger.debug(f"FastMCP proxy created with {server_count} server(s)")
66+
67+
# Set up dependency logging for FastMCP/MCP libraries
68+
setup_dependency_logging()
69+
70+
# Re-suppress library logging after FastMCP initialization
71+
ensure_dependency_logging_suppressed()
6372

6473
# Use default port if none specified, then find available port
6574
preferred_port = port or DEFAULT_PORT
6675
actual_port = await find_available_port(preferred_port)
6776
if actual_port != preferred_port:
68-
console.print(f"[yellow]Port {preferred_port} is busy, using port {actual_port} instead[/]")
77+
logger.debug(f"Port {preferred_port} is busy, using port {actual_port} instead")
6978

70-
console.print(f"[cyan]Starting streamable HTTP server on port {actual_port}...[/]")
79+
logger.debug(f"Starting HTTP server on port {actual_port}")
7180

7281
# Start the FastMCP proxy as a streamable HTTP server in a background task
73-
server_task = asyncio.create_task(proxy.run_http_async(host="127.0.0.1", port=actual_port))
82+
server_task = asyncio.create_task(proxy.run_http_async(host="127.0.0.1", port=actual_port, uvicorn_config={"log_level": get_uvicorn_log_level()}))
7483

7584
# Wait a moment for server to start
7685
await asyncio.sleep(2)
7786

78-
console.print(f"[green]FastMCP proxy running on port {actual_port}[/]")
87+
logger.debug(f"FastMCP proxy running on port {actual_port}")
7988

8089
# Create tunnel to make it publicly accessible
8190
if not address:
8291
address = DEFAULT_SHARE_ADDRESS
8392

8493
remote_host, remote_port = address.split(":")
94+
logger.debug(f"Creating tunnel from localhost:{actual_port} to {remote_host}:{remote_port}")
8595

8696
# Generate a random share token
8797
share_token = secrets.token_urlsafe(32)
@@ -96,26 +106,25 @@ async def share_profile_fastmcp(profile_servers, profile_name, port, address, ht
96106
share_server_tls_certificate=None,
97107
)
98108

99-
console.print("[cyan]Creating secure tunnel...[/]")
100109
public_url = tunnel.start_tunnel()
101110

102111
if public_url:
103-
console.print(f"[bold green]Profile '{profile_name}' is now publicly accessible![/]")
104-
console.print(f"[cyan]Public URL:[/] {public_url}")
112+
# Display critical information only
113+
http_url = f"{public_url}/mcp/"
114+
console.print(f"[bold green]Profile '{profile_name}' is now shared at:[/]")
115+
console.print(f"[cyan]{http_url}[/]")
116+
105117
if not no_auth and api_key:
106118
console.print(f"[bold green]API Key:[/] [cyan]{api_key}[/]")
107-
console.print(f"[dim]Provide this key in the 'Authorization' header as a Bearer token.[/]")
108119
else:
109120
console.print("[bold red]Warning:[/] Anyone with the URL can access your servers.")
110-
console.print()
111-
console.print("[bold]Connection URL:[/]")
112-
console.print(f" • HTTP: [cyan]{public_url}/mcp/[/]")
113-
console.print()
114-
console.print("[bold]Available servers in this profile:[/]")
121+
122+
# Show available servers
123+
console.print(f"[bold green]Shared servers:[/]")
115124
for server_config in profile_servers:
116-
console.print(f" • {server_config.name}")
117-
console.print()
118-
console.print("[dim]Press Ctrl+C to stop sharing...[/]")
125+
console.print(f" • [cyan]{server_config.name}[/]")
126+
127+
console.print("[dim]Press Ctrl+C to stop sharing[/]")
119128

120129
# Keep running until interrupted
121130
try:
@@ -127,6 +136,7 @@ async def share_profile_fastmcp(profile_servers, profile_name, port, address, ht
127136
tunnel.kill()
128137
else:
129138
console.print("[red]Failed to create tunnel[/]")
139+
logger.error("Failed to create tunnel")
130140
server_task.cancel()
131141
# Wait for cancellation to complete
132142
await asyncio.gather(server_task, return_exceptions=True)
@@ -135,10 +145,11 @@ async def share_profile_fastmcp(profile_servers, profile_name, port, address, ht
135145
return 0
136146

137147
except KeyboardInterrupt:
138-
console.print("\n[yellow]Stopping profile sharing...[/]")
148+
console.print("\n[yellow]Stopping...[/]")
139149
return 130
140150
except Exception as e:
141-
console.print(f"[red]Error sharing profile: {e}[/]")
151+
console.print(f"[red]Error: {e}[/]")
152+
logger.exception("Detailed error information")
142153
return 1
143154

144155

src/mcpm/commands/run.py

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,16 @@
77
import sys
88

99
import click
10+
from rich.console import Console
1011

1112
from mcpm.fastmcp_integration.proxy import create_mcpm_proxy
1213
from mcpm.global_config import GlobalConfigManager
1314
from mcpm.utils.config import DEFAULT_PORT
15+
from mcpm.utils.logging_config import setup_dependency_logging, ensure_dependency_logging_suppressed, get_uvicorn_log_level
1416

1517
global_config_manager = GlobalConfigManager()
1618
logger = logging.getLogger(__name__)
19+
console = Console()
1720

1821

1922
def find_installed_server(server_name):
@@ -95,18 +98,29 @@ async def run_server_with_fastmcp(server_config, server_name, http_mode=False, p
9598
name=f"mcpm-run-{server_name}",
9699
stdio_mode=not http_mode, # stdio_mode=False for HTTP
97100
)
101+
102+
# Set up dependency logging for FastMCP/MCP libraries
103+
setup_dependency_logging()
104+
105+
# Re-suppress library logging after FastMCP initialization
106+
ensure_dependency_logging_suppressed()
98107

99108
if http_mode:
100-
logger.info(f"Starting server '{server_name}' on HTTP port {port}...")
101-
logger.info("Press Ctrl+C to stop the server.")
102-
103109
# Try to find an available port if the requested one is taken
104110
actual_port = await find_available_port(port)
105111
if actual_port != port:
106-
logger.warning(f"Port {port} is busy, using port {actual_port} instead")
107-
108-
# Run FastMCP proxy in HTTP mode
109-
await proxy.run_http_async(host="127.0.0.1", port=actual_port)
112+
logger.debug(f"Port {port} is busy, using port {actual_port} instead")
113+
114+
# Display server information
115+
http_url = f"http://127.0.0.1:{actual_port}/mcp/"
116+
console.print(f"[bold green]Server '{server_name}' is now running at:[/]")
117+
console.print(f"[cyan]{http_url}[/]")
118+
console.print("[dim]Press Ctrl+C to stop the server[/]")
119+
120+
logger.debug(f"Starting FastMCP proxy for server '{server_name}' on port {actual_port}")
121+
122+
# Run FastMCP proxy in HTTP mode with uvicorn logging control
123+
await proxy.run_http_async(host="127.0.0.1", port=actual_port, uvicorn_config={"log_level": get_uvicorn_log_level()})
110124
else:
111125
# Run FastMCP proxy in stdio mode (default)
112126
logger.info(f"Starting server '{server_name}' over stdio")
@@ -180,15 +194,13 @@ def run(server_name, http, port):
180194
logger.info(" • Run 'mcpm install {name}' to install a server")
181195
sys.exit(1)
182196

183-
# Show debug info in verbose mode or if MCPM_DEBUG is set
184-
debug = os.getenv("MCPM_DEBUG", "").lower() in ("1", "true", "yes")
185-
if debug:
186-
logging.basicConfig(level=logging.DEBUG)
187-
logger.debug(f"Running server '{server_name}' from {location} configuration")
188-
logger.debug(f"Command: {server_config.command} {' '.join(server_config.args or [])}")
189-
logger.debug(f"Mode: {'HTTP' if http else 'stdio'}")
190-
if http:
191-
logger.debug(f"Port: {port}")
197+
# Debug logging is now handled by the Rich logging setup in CLI
198+
# Just log debug info - the level is controlled centrally
199+
logger.debug(f"Running server '{server_name}' from {location} configuration")
200+
logger.debug(f"Command: {server_config.command} {' '.join(server_config.args or [])}")
201+
logger.debug(f"Mode: {'HTTP' if http else 'stdio'}")
202+
if http:
203+
logger.debug(f"Port: {port}")
192204

193205
# Choose execution method
194206
if http:

0 commit comments

Comments
 (0)