Skip to content

Commit b97af6d

Browse files
authored
Allow logger to also take URLs (#451)
### TL;DR Unified server resolution logic across CLI commands by implementing a centralized `get_app_or_config` method. ### What changed? - Removed the redundant `resolve_server` and `parse_app_identifier` utility functions - Replaced all instances of these functions with direct calls to `client.get_app_or_config` - Updated the log tailing functionality to work with server URLs directly - Simplified server URL resolution in workflow commands - Enhanced the `tail_logs` command to accept server URLs as input - Updated help text and examples to reflect the new capabilities ### How to test? 1. Test the logger tail command with different identifier types: ``` mcp-agent cloud logger tail app_abc123 --follow mcp-agent cloud logger tail apcnf_xyz789 --follow mcp-agent cloud logger tail https://abc123.mcpcloud.ai --follow ``` 2. Test server commands with different identifier types: ``` mcp-agent cloud servers describe app_abc123 mcp-agent cloud servers describe https://abc123.mcpcloud.ai ``` 3. Test workflow commands with server URLs: ``` mcp-agent cloud workflows list https://abc123.mcpcloud.ai ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - New Features - Logger tail now supports using a direct server URL in addition to identifiers. - Refactor - Unified server resolution across logger, servers, and workflows commands for more consistent behavior. - Log fetching and streaming now operate directly on server objects, simplifying CLI flows. - Documentation - Added an example showing how to tail logs using a server URL. - Chores - Removed legacy helper utilities for server parsing/resolution to reduce complexity. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent 9fb3daf commit b97af6d

File tree

10 files changed

+39
-159
lines changed

10 files changed

+39
-159
lines changed

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

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import signal
77
import sys
88
from datetime import datetime, timezone
9-
from typing import Optional, Dict, Any, List
9+
from typing import Optional, Dict, Any, List, Union
1010
from urllib.parse import urlparse
1111

1212
import httpx
@@ -19,8 +19,9 @@
1919
from mcp_agent.cli.auth import load_credentials, UserCredentials
2020
from mcp_agent.cli.cloud.commands.utils import setup_authenticated_client
2121
from mcp_agent.cli.core.api_client import UnauthenticatedError
22-
from mcp_agent.cli.core.utils import parse_app_identifier, resolve_server_url
22+
from mcp_agent.cli.core.utils import run_async
2323
from mcp_agent.cli.utils.ux import print_error
24+
from mcp_agent.cli.mcp_app.api_client import MCPApp, MCPAppConfiguration
2425

2526
console = Console()
2627

@@ -29,7 +30,7 @@
2930

3031
def tail_logs(
3132
app_identifier: str = typer.Argument(
32-
help="App ID or app configuration ID to retrieve logs for"
33+
help="App ID, app configuration ID, or server URL to retrieve logs for"
3334
),
3435
since: Optional[str] = typer.Option(
3536
None,
@@ -91,6 +92,9 @@ def tail_logs(
9192
9293
# Follow logs and filter for specific patterns
9394
mcp-agent cloud logger tail app_abc123 --follow --grep "authentication.*failed"
95+
96+
# Use server URL instead of app ID
97+
mcp-agent cloud logger tail https://abc123.mcpcloud.ai --follow
9498
"""
9599

96100
credentials = load_credentials()
@@ -130,14 +134,14 @@ def tail_logs(
130134
print_error("--format must be 'text', 'json', or 'yaml'")
131135
raise typer.Exit(6)
132136

133-
app_id, config_id = parse_app_identifier(app_identifier)
134-
137+
client = setup_authenticated_client()
138+
server = run_async(client.get_app_or_config(app_identifier))
139+
135140
try:
136141
if follow:
137142
asyncio.run(
138143
_stream_logs(
139-
app_id=app_id,
140-
config_id=config_id,
144+
server=server,
141145
credentials=credentials,
142146
grep_pattern=grep,
143147
app_identifier=app_identifier,
@@ -147,16 +151,15 @@ def tail_logs(
147151
else:
148152
asyncio.run(
149153
_fetch_logs(
150-
app_id=app_id,
151-
config_id=config_id,
152-
credentials=credentials,
154+
server=server,
153155
since=since,
154156
grep_pattern=grep,
155157
limit=limit,
156158
order_by=order_by,
157159
asc=asc,
158160
desc=desc,
159161
format=format,
162+
app_identifier=app_identifier,
160163
)
161164
)
162165

@@ -168,19 +171,26 @@ def tail_logs(
168171

169172

170173
async def _fetch_logs(
171-
app_id: Optional[str],
172-
config_id: Optional[str],
173-
credentials: UserCredentials,
174+
server: Union[MCPApp, MCPAppConfiguration],
174175
since: Optional[str],
175176
grep_pattern: Optional[str],
176177
limit: int,
177178
order_by: Optional[str],
178179
asc: bool,
179180
desc: bool,
180181
format: str,
182+
app_identifier: str,
181183
) -> None:
182184
"""Fetch logs one-time via HTTP API."""
183185

186+
# Extract app_id and config_id from the server object
187+
if hasattr(server, 'appId'): # MCPApp
188+
app_id = server.appId
189+
config_id = None
190+
else: # MCPAppConfiguration
191+
app_id = None
192+
config_id = server.appConfigurationId
193+
184194
client = setup_authenticated_client()
185195

186196
# Map order_by parameter from CLI to API format
@@ -240,20 +250,23 @@ async def _fetch_logs(
240250
console.print("[yellow]No logs found matching the criteria[/yellow]")
241251
return
242252

243-
_display_logs(filtered_logs, title=f"Logs for {app_id or config_id}", format=format)
253+
_display_logs(filtered_logs, title=f"Logs for {app_identifier}", format=format)
244254

245255

246256
async def _stream_logs(
247-
app_id: Optional[str],
248-
config_id: Optional[str],
257+
server: Union[MCPApp, MCPAppConfiguration],
249258
credentials: UserCredentials,
250259
grep_pattern: Optional[str],
251260
app_identifier: str,
252261
format: str,
253262
) -> None:
254263
"""Stream logs continuously via SSE."""
255264

256-
server_url = await resolve_server_url(app_id, config_id, credentials)
265+
# Get server URL directly from the server object
266+
if not server.appServerInfo or not server.appServerInfo.serverUrl:
267+
raise CLIError("Server URL not available - server may not be deployed")
268+
269+
server_url = server.appServerInfo.serverUrl
257270

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

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
from mcp_agent.cli.mcp_app.api_client import MCPApp
77
from ...utils import (
88
setup_authenticated_client,
9-
resolve_server,
109
handle_server_api_errors,
1110
get_server_name,
1211
get_server_id,
@@ -25,7 +24,7 @@ def delete_server(
2524
) -> None:
2625
"""Delete a specific MCP Server."""
2726
client = setup_authenticated_client()
28-
server = resolve_server(client, id_or_url)
27+
server = run_async(client.get_app_or_config(id_or_url))
2928

3029
# Determine server type and delete function
3130
if isinstance(server, MCPApp):

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@
1010
from ...utils import (
1111
setup_authenticated_client,
1212
validate_output_format,
13-
resolve_server,
1413
handle_server_api_errors,
1514
clean_server_status,
1615
)
16+
from mcp_agent.cli.core.utils import run_async
1717
from mcp_agent.cli.utils.ux import console
1818

1919

@@ -29,7 +29,7 @@ def describe_server(
2929
"""Describe a specific MCP Server."""
3030
validate_output_format(format)
3131
client = setup_authenticated_client()
32-
server = resolve_server(client, id_or_url)
32+
server = run_async(client.get_app_or_config(id_or_url))
3333
print_server_description(server, format)
3434

3535

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

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
from mcp_agent.cli.auth import load_api_key_credentials
77
from mcp_agent.cli.core.api_client import UnauthenticatedError
88
from mcp_agent.cli.core.constants import DEFAULT_API_BASE_URL
9-
from mcp_agent.cli.core.utils import parse_app_identifier, run_async
109
from mcp_agent.cli.exceptions import CLIError
1110
from mcp_agent.cli.mcp_app.api_client import (
1211
MCPApp,
@@ -48,35 +47,6 @@ def validate_output_format(format: str) -> None:
4847
)
4948

5049

51-
def resolve_server(
52-
client: MCPAppClient, id_or_url: str
53-
) -> Union[MCPApp, MCPAppConfiguration]:
54-
"""Resolve server from ID.
55-
56-
Args:
57-
client: Authenticated MCP App client
58-
id_or_url: Server identifier (app ID or app config ID)
59-
60-
Returns:
61-
Server object (MCPApp or MCPAppConfiguration)
62-
63-
Raises:
64-
CLIError: If server resolution fails
65-
"""
66-
try:
67-
app_id, config_id = parse_app_identifier(id_or_url)
68-
69-
if config_id:
70-
return run_async(client.get_app_configuration(app_config_id=config_id))
71-
else:
72-
return run_async(client.get_app(app_id=app_id))
73-
74-
except ValueError as e:
75-
raise CLIError(str(e)) from e
76-
except Exception as e:
77-
raise CLIError(f"Failed to resolve server '{id_or_url}': {str(e)}") from e
78-
79-
8050
def handle_server_api_errors(func):
8151
"""Decorator to handle common API errors for server commands.
8252

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
from mcp_agent.mcp.gen_client import gen_client
1313
from ...utils import (
1414
setup_authenticated_client,
15-
resolve_server,
1615
handle_server_api_errors,
1716
)
1817

@@ -25,7 +24,7 @@ async def _cancel_workflow_async(
2524
server_url = server_id_or_url
2625
else:
2726
client = setup_authenticated_client()
28-
server = resolve_server(client, server_id_or_url)
27+
server = run_async(client.get_app_or_config(server_id_or_url))
2928

3029
if hasattr(server, "appServerInfo") and server.appServerInfo:
3130
server_url = server.appServerInfo.serverUrl

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
from mcp_agent.mcp.gen_client import gen_client
1515
from ...utils import (
1616
setup_authenticated_client,
17-
resolve_server,
1817
handle_server_api_errors,
1918
)
2019

@@ -27,7 +26,7 @@ async def _describe_workflow_async(
2726
server_url = server_id_or_url
2827
else:
2928
client = setup_authenticated_client()
30-
server = resolve_server(client, server_id_or_url)
29+
server = run_async(client.get_app_or_config(server_id_or_url))
3130

3231
if hasattr(server, "appServerInfo") and server.appServerInfo:
3332
server_url = server.appServerInfo.serverUrl

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
from mcp_agent.mcp.gen_client import gen_client
1616
from ...utils import (
1717
setup_authenticated_client,
18-
resolve_server,
1918
handle_server_api_errors,
2019
validate_output_format,
2120
)
@@ -27,7 +26,7 @@ async def _list_workflows_async(server_id_or_url: str, format: str = "text") ->
2726
server_url = server_id_or_url
2827
else:
2928
client = setup_authenticated_client()
30-
server = resolve_server(client, server_id_or_url)
29+
server = run_async(client.get_app_or_config(server_id_or_url))
3130

3231
if hasattr(server, "appServerInfo") and server.appServerInfo:
3332
server_url = server.appServerInfo.serverUrl

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
from mcp_agent.mcp.gen_client import gen_client
1414
from ...utils import (
1515
setup_authenticated_client,
16-
resolve_server,
1716
handle_server_api_errors,
1817
)
1918

@@ -29,7 +28,7 @@ async def _signal_workflow_async(
2928
server_url = server_id_or_url
3029
else:
3130
client = setup_authenticated_client()
32-
server = resolve_server(client, server_id_or_url)
31+
server = run_async(client.get_app_or_config(server_id_or_url))
3332

3433
if hasattr(server, "appServerInfo") and server.appServerInfo:
3534
server_url = server.appServerInfo.serverUrl

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
from ...utils import (
1515
setup_authenticated_client,
1616
validate_output_format,
17-
resolve_server,
1817
)
1918
from mcp_agent.cli.utils.ux import console, print_info
2019

@@ -27,7 +26,7 @@ async def _list_workflow_runs_async(
2726
server_url = server_id_or_url
2827
else:
2928
client = setup_authenticated_client()
30-
server = resolve_server(client, server_id_or_url)
29+
server = run_async(client.get_app_or_config(server_id_or_url))
3130

3231
if hasattr(server, "appServerInfo") and server.appServerInfo:
3332
server_url = server.appServerInfo.serverUrl

0 commit comments

Comments
 (0)