Skip to content

Commit 6456278

Browse files
committed
do not allow unsupported server URLs as arguments
1 parent 8a35062 commit 6456278

File tree

9 files changed

+52
-84
lines changed

9 files changed

+52
-84
lines changed

src/mcp_agent/cli/cloud/commands/app/status/main.py

Lines changed: 6 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
)
2828
from mcp_agent.cli.utils.ux import (
2929
console,
30+
print_error,
31+
print_warning,
3032
)
3133

3234

@@ -210,13 +212,7 @@ async def print_server_tools(session: MCPClientSession) -> None:
210212
console.print(Panel(Group(*panels), title="Server Tools", border_style="blue"))
211213

212214
except Exception as e:
213-
console.print(
214-
Panel(
215-
f"[red]Error fetching tools: {str(e)}[/red]",
216-
title="Server Tools",
217-
border_style="red",
218-
)
219-
)
215+
print_error(f"Error fetching tools: {str(e)}")
220216

221217

222218
async def print_server_prompts(session: MCPClientSession) -> None:
@@ -263,13 +259,7 @@ async def print_server_prompts(session: MCPClientSession) -> None:
263259
Panel(Group(*panels), title="Server Prompts", border_style="blue")
264260
)
265261
except Exception as e:
266-
console.print(
267-
Panel(
268-
f"[red]Error fetching prompts: {str(e)}[/red]",
269-
title="Server Prompts",
270-
border_style="red",
271-
)
272-
)
262+
print_error(f"Error fetching prompts: {str(e)}")
273263

274264

275265
async def print_server_resources(session: MCPClientSession) -> None:
@@ -304,13 +294,7 @@ async def print_server_resources(session: MCPClientSession) -> None:
304294
)
305295
console.print(Panel(table, title="Server Resources", border_style="blue"))
306296
except Exception as e:
307-
console.print(
308-
Panel(
309-
f"[red]Error fetching resources: {str(e)}[/red]",
310-
title="Server Resources",
311-
border_style="red",
312-
)
313-
)
297+
print_error(f"Error fetching resources: {str(e)}")
314298

315299

316300
async def print_server_workflows(session: MCPClientSession) -> None:
@@ -347,10 +331,4 @@ async def print_server_workflows(session: MCPClientSession) -> None:
347331
Panel(Group(*panels), title="Server Workflows", border_style="blue")
348332
)
349333
except Exception as e:
350-
console.print(
351-
Panel(
352-
f"[red]Error fetching workflows: {str(e)}[/red]",
353-
title="Server Workflows",
354-
border_style="red",
355-
)
356-
)
334+
print_error(f"Error fetching workflows: {str(e)}")

src/mcp_agent/cli/cloud/commands/app/workflows/main.py

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
)
3131
from mcp_agent.cli.utils.ux import (
3232
console,
33+
print_error,
34+
print_warning,
3335
)
3436

3537

@@ -212,13 +214,7 @@ async def print_workflows_list(session: MCPClientSession) -> None:
212214
console.print(Panel(Group(*panels), title="Workflows", border_style="blue"))
213215

214216
except Exception as e:
215-
console.print(
216-
Panel(
217-
f"[red]Error fetching workflows: {str(e)}[/red]",
218-
title="Workflows",
219-
border_style="red",
220-
)
221-
)
217+
print_error(f"Error fetching workflows: {str(e)}")
222218

223219

224220
async def print_runs_list(session: MCPClientSession) -> None:
@@ -293,10 +289,4 @@ def get_start_time(run: WorkflowRun):
293289
console.print(table)
294290

295291
except Exception as e:
296-
console.print(
297-
Panel(
298-
f"[red]Error fetching workflow runs: {str(e)}[/red]",
299-
title="Workflow Runs",
300-
border_style="red",
301-
)
302-
)
292+
print_error(f"Error fetching workflow runs: {str(e)}")

src/mcp_agent/cli/cloud/commands/logger/configure/main.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from rich.panel import Panel
1111

1212
from mcp_agent.cli.exceptions import CLIError
13+
from mcp_agent.cli.utils.ux import print_error
1314

1415
console = Console()
1516

@@ -42,7 +43,7 @@ def configure_logger(
4243
mcp-agent cloud logger configure --test # Test current configuration
4344
"""
4445
if not endpoint and not test:
45-
console.print("[red]Error: Must specify endpoint or use --test[/red]")
46+
print_error("Must specify endpoint or use --test")
4647
raise typer.Exit(1)
4748

4849
config_path = _find_config_file()
@@ -64,7 +65,7 @@ def configure_logger(
6465
key, value = header_pair.strip().split("=", 1)
6566
headers_dict[key.strip()] = value.strip()
6667
except ValueError:
67-
console.print("[red]Error: Headers must be in format 'key=value,key2=value2'[/red]")
68+
print_error("Headers must be in format 'key=value,key2=value2'")
6869
raise typer.Exit(1)
6970

7071
if endpoint:
@@ -83,7 +84,7 @@ def configure_logger(
8384
console.print(f"[yellow]⚠ Got status {response.status_code}, but endpoint is reachable[/yellow]")
8485

8586
except httpx.RequestError as e:
86-
console.print(f"[red]✗ Connection failed: {e}[/red]")
87+
print_error(f"✗ Connection failed: {e}")
8788
if not test:
8889
console.print("[yellow]Configuration will be saved anyway. Check your endpoint URL and network connection.[/yellow]")
8990

@@ -113,8 +114,7 @@ def configure_logger(
113114
))
114115

115116
except Exception as e:
116-
console.print(f"[red]Error saving configuration: {e}[/red]")
117-
raise typer.Exit(1)
117+
raise CLIError(f"Error saving configuration: {e}")
118118

119119

120120
def _find_config_file() -> Optional[Path]:

src/mcp_agent/cli/cloud/commands/logger/tail/main.py

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828

2929
def tail_logs(
3030
app_identifier: str = typer.Argument(
31-
help="Server ID, URL, or app configuration ID to retrieve logs for"
31+
help="App ID or app configuration ID to retrieve logs for"
3232
),
3333
since: Optional[str] = typer.Option(
3434
None,
@@ -83,7 +83,7 @@ def tail_logs(
8383
mcp-agent cloud logger tail app_abc123 --limit 50
8484
8585
# Stream logs continuously
86-
mcp-agent cloud logger tail https://app.mcpac.dev/abc123 --follow
86+
mcp-agent cloud logger tail app_abc123 --follow
8787
8888
# Show logs from the last hour with error filtering
8989
mcp-agent cloud logger tail app_abc123 --since 1h --grep "ERROR|WARN"
@@ -94,49 +94,48 @@ def tail_logs(
9494

9595
credentials = load_credentials()
9696
if not credentials:
97-
console.print("[red]Error: Not authenticated. Run 'mcp-agent login' first.[/red]")
97+
print_error("Not authenticated. Run 'mcp-agent login' first.")
9898
raise typer.Exit(4)
9999

100100
# Validate conflicting options
101101
if follow and since:
102-
console.print("[red]Error: --since cannot be used with --follow (streaming mode)[/red]")
102+
print_error("--since cannot be used with --follow (streaming mode)")
103103
raise typer.Exit(6)
104104

105105
if follow and limit != DEFAULT_LOG_LIMIT:
106-
console.print("[red]Error: --limit cannot be used with --follow (streaming mode)[/red]")
106+
print_error("--limit cannot be used with --follow (streaming mode)")
107107
raise typer.Exit(6)
108108

109109
if follow and order_by:
110-
console.print("[red]Error: --order-by cannot be used with --follow (streaming mode)[/red]")
110+
print_error("--order-by cannot be used with --follow (streaming mode)")
111111
raise typer.Exit(6)
112112

113113
if follow and (asc or desc):
114-
console.print("[red]Error: --asc/--desc cannot be used with --follow (streaming mode)[/red]")
114+
print_error("--asc/--desc cannot be used with --follow (streaming mode)")
115115
raise typer.Exit(6)
116116

117117
# Validate order_by values
118118
if order_by and order_by not in ["timestamp", "severity"]:
119-
console.print("[red]Error: --order-by must be 'timestamp' or 'severity'[/red]")
119+
print_error("--order-by must be 'timestamp' or 'severity'")
120120
raise typer.Exit(6)
121121

122122
# Validate that both --asc and --desc are not used together
123123
if asc and desc:
124-
console.print("[red]Error: Cannot use both --asc and --desc together[/red]")
124+
print_error("Cannot use both --asc and --desc together")
125125
raise typer.Exit(6)
126126

127127
# Validate format values
128128
if format and format not in ["text", "json", "yaml"]:
129-
console.print("[red]Error: --format must be 'text', 'json', or 'yaml'[/red]")
129+
print_error("--format must be 'text', 'json', or 'yaml'")
130130
raise typer.Exit(6)
131131

132-
app_id, config_id, server_url = parse_app_identifier(app_identifier)
132+
app_id, config_id = parse_app_identifier(app_identifier)
133133

134134
try:
135135
if follow:
136136
asyncio.run(_stream_logs(
137137
app_id=app_id,
138138
config_id=config_id,
139-
server_url=server_url,
140139
credentials=credentials,
141140
grep_pattern=grep,
142141
app_identifier=app_identifier,
@@ -146,7 +145,6 @@ def tail_logs(
146145
asyncio.run(_fetch_logs(
147146
app_id=app_id,
148147
config_id=config_id,
149-
server_url=server_url,
150148
credentials=credentials,
151149
since=since,
152150
grep_pattern=grep,
@@ -161,14 +159,12 @@ def tail_logs(
161159
console.print("\n[yellow]Interrupted by user[/yellow]")
162160
sys.exit(0)
163161
except Exception as e:
164-
console.print(f"[red]Error: {e}[/red]")
165-
raise typer.Exit(5)
162+
raise CLIError(str(e))
166163

167164

168165
async def _fetch_logs(
169166
app_id: Optional[str],
170167
config_id: Optional[str],
171-
server_url: Optional[str],
172168
credentials: UserCredentials,
173169
since: Optional[str],
174170
grep_pattern: Optional[str],
@@ -241,17 +237,15 @@ async def _fetch_logs(
241237

242238
async def _stream_logs(
243239
app_id: Optional[str],
244-
config_id: Optional[str],
245-
server_url: Optional[str],
240+
config_id: Optional[str],
246241
credentials: UserCredentials,
247242
grep_pattern: Optional[str],
248243
app_identifier: str,
249244
format: str,
250245
) -> None:
251246
"""Stream logs continuously via SSE."""
252247

253-
if not server_url:
254-
server_url = await resolve_server_url(app_id, config_id, credentials)
248+
server_url = await resolve_server_url(app_id, config_id, credentials)
255249

256250
parsed = urlparse(server_url)
257251
stream_url = f"{parsed.scheme}://{parsed.netloc}/logs"

src/mcp_agent/cli/cloud/commands/servers/delete/main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
@handle_server_api_errors
1919
def delete_server(
20-
id_or_url: str = typer.Argument(..., help="Server ID or URL to delete"),
20+
id_or_url: str = typer.Argument(..., help="Server ID or app configuration ID to delete"),
2121
force: bool = typer.Option(False, "--force", "-f", help="Force deletion without confirmation prompt"),
2222
) -> None:
2323
"""Delete a specific MCP Server."""

src/mcp_agent/cli/cloud/commands/servers/describe/main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
@handle_server_api_errors
2121
def describe_server(
22-
id_or_url: str = typer.Argument(..., help="Server ID or URL to describe"),
22+
id_or_url: str = typer.Argument(..., help="Server ID or app configuration ID to describe"),
2323
format: Optional[str] = typer.Option("text", "--format", help="Output format (text|json|yaml)"),
2424
) -> None:
2525
"""Describe a specific MCP Server."""

src/mcp_agent/cli/cloud/commands/servers/utils.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,11 @@ def validate_output_format(format: str) -> None:
5151

5252

5353
def resolve_server(client: MCPAppClient, id_or_url: str) -> Union[MCPApp, MCPAppConfiguration]:
54-
"""Resolve server from ID or URL.
54+
"""Resolve server from ID.
5555
5656
Args:
5757
client: Authenticated MCP App client
58-
id_or_url: Server identifier (ID, config ID, or URL)
58+
id_or_url: Server identifier (app ID or app config ID)
5959
6060
Returns:
6161
Server object (MCPApp or MCPAppConfiguration)
@@ -64,15 +64,15 @@ def resolve_server(client: MCPAppClient, id_or_url: str) -> Union[MCPApp, MCPApp
6464
CLIError: If server resolution fails
6565
"""
6666
try:
67-
app_id, config_id, server_url = parse_app_identifier(id_or_url)
67+
app_id, config_id = parse_app_identifier(id_or_url)
6868

69-
if server_url:
70-
return run_async(client.get_app(server_url=server_url))
71-
elif config_id:
69+
if config_id:
7270
return run_async(client.get_app_configuration(app_config_id=config_id))
7371
else:
7472
return run_async(client.get_app(app_id=app_id))
7573

74+
except ValueError as e:
75+
raise CLIError(str(e)) from e
7676
except Exception as e:
7777
raise CLIError(f"Failed to resolve server '{id_or_url}': {str(e)}") from e
7878

src/mcp_agent/cli/core/utils.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,26 @@ def run_async(coro):
2626
raise
2727

2828

29-
def parse_app_identifier(identifier: str) -> Tuple[Optional[str], Optional[str], Optional[str]]:
30-
"""Parse app identifier to extract app ID, config ID, and server URL."""
29+
def parse_app_identifier(identifier: str) -> Tuple[Optional[str], Optional[str]]:
30+
"""Parse app identifier to extract app ID and config ID.
3131
32-
if identifier.startswith(('http://', 'https://')):
33-
return None, None, identifier
32+
Args:
33+
identifier: App identifier (must be app_... or apcnf_...)
34+
35+
Returns:
36+
Tuple of (app_id, config_id)
37+
38+
Raises:
39+
ValueError: If identifier format is not recognized
40+
"""
3441

3542
if identifier.startswith('apcnf_'):
36-
return None, identifier, None
43+
return None, identifier
3744

3845
if identifier.startswith('app_'):
39-
return identifier, None, None
46+
return identifier, None
4047

41-
return identifier, None, None
48+
raise ValueError(f"Invalid identifier format: '{identifier}'. Must be an app ID (app_...) or app configuration ID (apcnf_...)")
4249

4350

4451
async def resolve_server_url(

src/mcp_agent/cli/mcp_app/api_client.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,11 @@ class Config:
118118
class GetAppLogsResponse(BaseModel):
119119
"""Response from get_app_logs API endpoint."""
120120
logEntries: Optional[List[LogEntry]] = []
121-
log_entries: Optional[List[LogEntry]] = []
122121

123122
@property
124123
def log_entries_list(self) -> List[LogEntry]:
125124
"""Get log entries regardless of field name format."""
126-
return self.logEntries or self.log_entries or []
125+
return self.logEntries or []
127126

128127

129128
class MCPAppClient(APIClient):

0 commit comments

Comments
 (0)