55"""
66
77from pathlib import Path
8+ from datetime import datetime , timezone
89from typing import Optional , Union
10+ import json
911
1012import typer
11- from rich .panel import Panel
1213from rich .progress import Progress , SpinnerColumn , TextColumn
1314
1415from mcp_agent .cli .auth import load_api_key_credentials
3536 print_configuration_header ,
3637 print_info ,
3738 print_success ,
39+ print_verbose ,
40+ LOG_VERBOSE ,
3841)
3942
4043
4144def configure_app (
45+ ctx : typer .Context ,
4246 app_server_url : str = typer .Option (
4347 None ,
4448 "--id" ,
@@ -84,6 +88,12 @@ def configure_app(
8488 help = "API key for authentication. Defaults to MCP_API_KEY environment variable." ,
8589 envvar = ENV_API_KEY ,
8690 ),
91+ verbose : bool = typer .Option (
92+ False ,
93+ "--verbose" ,
94+ "-v" ,
95+ help = "Enable verbose output for this command" ,
96+ ),
8797) -> str :
8898 """Configure an MCP app with the required params (e.g. user secrets).
8999
@@ -98,6 +108,9 @@ def configure_app(
98108 Returns:
99109 Configured app ID.
100110 """
111+ if verbose :
112+ LOG_VERBOSE .set (True )
113+
101114 # Check what params the app requires (doubles as an access check)
102115 if not app_server_url :
103116 raise CLIError ("You must provide a server URL to configure." )
@@ -110,8 +123,7 @@ def configure_app(
110123
111124 client : Union [MockMCPAppClient , MCPAppClient ]
112125 if dry_run :
113- # Use the mock api client in dry run mode
114- print_info ("Using MOCK API client for dry run" )
126+ print_verbose ("Using MOCK API client for dry run" )
115127 client = MockMCPAppClient (
116128 api_url = api_url or DEFAULT_API_BASE_URL , api_key = effective_api_key
117129 )
@@ -162,22 +174,18 @@ def configure_app(
162174
163175 if requires_secrets :
164176 if not secrets_file and secrets_output_file is None :
165- # Set default output file if not specified
166177 secrets_output_file = Path (MCP_CONFIGURED_SECRETS_FILENAME )
167- print_info (f"Using default output path: { secrets_output_file } " )
168-
169- print_configuration_header (secrets_file , secrets_output_file , dry_run )
178+ print_verbose (f"Using default output path: { secrets_output_file } " )
170179
171- print_info (
180+ print_verbose (
172181 f"App { app_server_url } requires the following ({ len (required_params )} ) user secrets: { ', ' .join (required_params )} "
173182 )
174183
175184 try :
176- print_info ("Processing user secrets..." )
185+ print_verbose ("Processing user secrets..." )
177186
178187 if dry_run :
179- # Use the mock client in dry run mode
180- print_info ("Using MOCK Secrets API client for dry run" )
188+ print_verbose ("Using MOCK Secrets API client for dry run" )
181189
182190 # Create the mock client
183191 mock_client = MockSecretsClient (
@@ -210,10 +218,10 @@ def configure_app(
210218 )
211219 )
212220
213- print_success ("User secrets processed successfully" )
221+ print_verbose ("User secrets processed successfully" )
214222
215223 except Exception as e :
216- if settings . VERBOSE :
224+ if LOG_VERBOSE . get () :
217225 import traceback
218226
219227 typer .echo (traceback .format_exc ())
@@ -226,14 +234,35 @@ def configure_app(
226234 f"App { app_server_url } does not require any parameters, but a secrets file was provided: { secrets_file } "
227235 )
228236
237+ print_configuration_header (
238+ app_server_url ,
239+ required_params if requires_secrets else [],
240+ secrets_file ,
241+ secrets_output_file ,
242+ dry_run ,
243+ )
244+
245+ if not dry_run :
246+ proceed = typer .confirm ("Proceed with configuration?" , default = True )
247+ if not proceed :
248+ print_info ("Configuration cancelled." )
249+ return None
250+ else :
251+ print_info ("Running in dry run mode." )
252+
253+ start_time = datetime .now (timezone .utc ).isoformat ().replace ("+00:00" , "Z" )
254+ print_info (f"[{ start_time } ] Starting configuration process..." , highlight = False )
255+
229256 if dry_run :
230257 print_success ("Configuration completed in dry run mode." )
231258 return "dry-run-app-configuration-id"
232259
233- # Finally, configure the app for the user
260+ config = None
261+ spinner_column = SpinnerColumn (spinner_name = "aesthetic" )
234262 with Progress (
235- SpinnerColumn (spinner_name = "arrow3" ),
236- TextColumn ("[progress.description]{task.description}" ),
263+ "" ,
264+ spinner_column ,
265+ TextColumn (" [progress.description]{task.description}" ),
237266 ) as progress :
238267 task = progress .add_task ("Configuring MCP App..." , total = None )
239268
@@ -243,18 +272,52 @@ def configure_app(
243272 app_server_url = app_server_url , config_params = configured_secrets
244273 )
245274 )
246- progress .update (task , description = "✅ MCP App configured successfully!" )
247- console .print (
248- Panel (
249- f"Configured App ID: [cyan]{ config .appConfigurationId } [/cyan]\n "
250- f"Configured App Server URL: [cyan]{ config .appServerInfo .serverUrl if config .appServerInfo else '' } [/cyan]" ,
251- title = "Configuration Complete" ,
252- border_style = "green" ,
253- )
254- )
255-
256- return config .appConfigurationId
275+ spinner_column .spinner .frames = spinner_column .spinner .frames [- 2 :- 1 ]
276+ progress .update (task , description = "MCP App configured successfully!" )
257277
258278 except Exception as e :
259279 progress .update (task , description = "❌ MCP App configuration failed" )
260- raise CLIError (f"Failed to configure app { app_server_url } : { str (e )} " ) from e
280+ end_time = datetime .now (timezone .utc ).isoformat ().replace ("+00:00" , "Z" )
281+ raise CLIError (
282+ f"[{ end_time } ] Failed to configure app { app_server_url } : { str (e )} "
283+ ) from e
284+
285+ # Print results after progress context ends
286+ end_time = datetime .now (timezone .utc ).isoformat ().replace ("+00:00" , "Z" )
287+ if config .app :
288+ print_info (
289+ f"[{ end_time } ] Configuration of '{ config .app .name } ' succeeded. ID: { config .appConfigurationId } " ,
290+ highlight = False ,
291+ )
292+ else :
293+ print_info (
294+ f"[{ end_time } ] Configuration succeeded. ID: { config .appConfigurationId } " ,
295+ highlight = False ,
296+ )
297+
298+ if config .appServerInfo :
299+ server_url = config .appServerInfo .serverUrl
300+ print_info (f"App Server URL: [link={ server_url } ]{ server_url } [/link]" )
301+ print_info (
302+ f"Use this configured app as an MCP server at { server_url } /sse\n \n MCP configuration example:"
303+ )
304+
305+ # Use the app name if available, otherwise use a simple default
306+ app_name = config .app .name if config .app else "configured-app"
307+
308+ mcp_config = {
309+ "mcpServers" : {
310+ app_name : {
311+ "url" : f"{ server_url } /sse" ,
312+ "transport" : "sse" ,
313+ "headers" : {"Authorization" : f"Bearer { effective_api_key } " },
314+ }
315+ }
316+ }
317+
318+ console .print (
319+ f"[bright_black]{ json .dumps (mcp_config , indent = 2 )} [/bright_black]" ,
320+ soft_wrap = True ,
321+ )
322+
323+ return config .appConfigurationId
0 commit comments