Skip to content

Commit 1e42e75

Browse files
committed
Add cloud servers
1 parent 24463b2 commit 1e42e75

File tree

17 files changed

+1177
-96
lines changed

17 files changed

+1177
-96
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
from mcp_agent.cli.exceptions import CLIError
1919
from mcp_agent.cli.auth import load_credentials, UserCredentials
2020
from mcp_agent.cli.core.constants import DEFAULT_API_BASE_URL
21-
from mcp_agent.cli.cloud.commands.logger.utils import parse_app_identifier, resolve_server_url
21+
from mcp_agent.cli.core.utils import parse_app_identifier, resolve_server_url
2222

2323
console = Console()
2424

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

Lines changed: 0 additions & 94 deletions
This file was deleted.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
"""Server management commands for MCP Agent Cloud."""
2+
3+
from .list.main import list_servers
4+
from .describe.main import describe_server
5+
from .stats.main import get_server_stats
6+
from .delete.main import delete_server
7+
from .workflows.main import list_server_workflows
8+
9+
__all__ = [
10+
"list_servers",
11+
"describe_server",
12+
"get_server_stats",
13+
"delete_server",
14+
"list_server_workflows",
15+
]

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

Whitespace-only changes.
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
2+
import typer
3+
from rich.panel import Panel
4+
5+
from mcp_agent.cli.core.utils import run_async
6+
from mcp_agent.cli.exceptions import CLIError
7+
from mcp_agent.cli.mcp_app.api_client import MCPApp
8+
from ..utils import (
9+
setup_authenticated_client,
10+
resolve_server,
11+
handle_server_api_errors,
12+
get_server_name,
13+
get_server_id,
14+
)
15+
from mcp_agent.cli.utils.ux import console, print_info
16+
17+
18+
@handle_server_api_errors
19+
def delete_server(
20+
id_or_url: str = typer.Argument(..., help="Server ID or URL to delete"),
21+
force: bool = typer.Option(False, "--force", "-f", help="Force deletion without confirmation prompt"),
22+
) -> None:
23+
"""Delete a specific MCP Server."""
24+
client = setup_authenticated_client()
25+
server = resolve_server(client, id_or_url)
26+
27+
# Determine server type and delete function
28+
if isinstance(server, MCPApp):
29+
server_type = "Deployed Server"
30+
delete_function = client.delete_app
31+
else:
32+
server_type = "Configured Server"
33+
delete_function = client.delete_app_configuration
34+
35+
server_name = get_server_name(server)
36+
server_id = get_server_id(server)
37+
38+
if not force:
39+
console.print(
40+
Panel(
41+
f"Name: [cyan]{server_name}[/cyan]\n"
42+
f"Type: [cyan]{server_type}[/cyan]\n"
43+
f"ID: [cyan]{server_id}[/cyan]\n\n"
44+
f"[bold red]⚠️ This action cannot be undone![/bold red]",
45+
title="Server to Delete",
46+
border_style="red",
47+
expand=False,
48+
)
49+
)
50+
51+
confirm = typer.confirm(f"\nAre you sure you want to delete this {server_type.lower()}?")
52+
if not confirm:
53+
print_info("Deletion cancelled.")
54+
return
55+
56+
if isinstance(server, MCPApp):
57+
can_delete = run_async(client.can_delete_app(server_id))
58+
else:
59+
can_delete = run_async(client.can_delete_app_configuration(server_id))
60+
61+
if not can_delete:
62+
raise CLIError(
63+
f"You do not have permission to delete this {server_type.lower()}. "
64+
f"You can only delete servers that you created."
65+
)
66+
deleted_id = run_async(delete_function(server_id))
67+
68+
console.print(
69+
Panel(
70+
f"[green]✅ Successfully deleted {server_type.lower()}[/green]\n\n"
71+
f"Name: [cyan]{server_name}[/cyan]\n"
72+
f"ID: [cyan]{deleted_id}[/cyan]",
73+
title="Deletion Complete",
74+
border_style="green",
75+
expand=False,
76+
)
77+
)
78+

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

Whitespace-only changes.
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
import json
2+
from typing import Optional, Union
3+
4+
import typer
5+
import yaml
6+
from rich.panel import Panel
7+
8+
from mcp_agent.cli.exceptions import CLIError
9+
from mcp_agent.cli.mcp_app.api_client import MCPApp, MCPAppConfiguration
10+
from ..utils import (
11+
setup_authenticated_client,
12+
validate_output_format,
13+
resolve_server,
14+
handle_server_api_errors,
15+
clean_server_status,
16+
)
17+
from mcp_agent.cli.utils.ux import console
18+
19+
20+
@handle_server_api_errors
21+
def describe_server(
22+
id_or_url: str = typer.Argument(..., help="Server ID or URL to describe"),
23+
format: Optional[str] = typer.Option("text", "--format", help="Output format (text|json|yaml)"),
24+
) -> None:
25+
"""Describe a specific MCP Server."""
26+
validate_output_format(format)
27+
client = setup_authenticated_client()
28+
server = resolve_server(client, id_or_url)
29+
print_server_description(server, format)
30+
31+
32+
def print_server_description(server: Union[MCPApp, MCPAppConfiguration], output_format: str = "text") -> None:
33+
"""Print detailed description information for a server."""
34+
35+
valid_formats = ["text", "json", "yaml"]
36+
if output_format not in valid_formats:
37+
raise CLIError(f"Invalid format '{output_format}'. Valid options are: {', '.join(valid_formats)}")
38+
39+
if output_format == "json":
40+
_print_server_json(server)
41+
elif output_format == "yaml":
42+
_print_server_yaml(server)
43+
else:
44+
_print_server_text(server)
45+
46+
47+
def _print_server_json(server: Union[MCPApp, MCPAppConfiguration]) -> None:
48+
"""Print server in JSON format."""
49+
server_data = _server_to_dict(server)
50+
print(json.dumps(server_data, indent=2, default=str))
51+
52+
53+
def _print_server_yaml(server: Union[MCPApp, MCPAppConfiguration]) -> None:
54+
"""Print server in YAML format."""
55+
server_data = _server_to_dict(server)
56+
print(yaml.dump(server_data, default_flow_style=False))
57+
58+
59+
def _server_to_dict(server: Union[MCPApp, MCPAppConfiguration]) -> dict:
60+
"""Convert server to dictionary."""
61+
if isinstance(server, MCPApp):
62+
server_type = "deployed"
63+
server_id = server.appId
64+
server_name = server.name
65+
server_description = server.description
66+
created_at = server.createdAt
67+
server_info = server.appServerInfo
68+
creator_id = server.creatorId
69+
underlying_app = None
70+
else:
71+
server_type = "configured"
72+
server_id = server.appConfigurationId
73+
server_name = server.app.name if server.app else "Unnamed"
74+
server_description = server.app.description if server.app else None
75+
created_at = server.createdAt
76+
server_info = server.appServerInfo
77+
creator_id = server.creatorId
78+
underlying_app = {
79+
"app_id": server.app.appId,
80+
"name": server.app.name
81+
} if server.app else None
82+
83+
status_raw = server_info.status if server_info else "APP_SERVER_STATUS_OFFLINE"
84+
server_url = server_info.serverUrl if server_info else None
85+
86+
data = {
87+
"id": server_id,
88+
"name": server_name,
89+
"type": server_type,
90+
"status": clean_server_status(status_raw),
91+
"server_url": server_url,
92+
"description": server_description,
93+
"creator_id": creator_id,
94+
"created_at": created_at.isoformat() if created_at else None
95+
}
96+
97+
if underlying_app:
98+
data["underlying_app"] = underlying_app
99+
100+
return data
101+
102+
103+
104+
105+
def _print_server_text(server: Union[MCPApp, MCPAppConfiguration]) -> None:
106+
"""Print server in text format."""
107+
if isinstance(server, MCPApp):
108+
server_type = "Deployed Server"
109+
server_id = server.appId
110+
server_name = server.name
111+
server_description = server.description
112+
created_at = server.createdAt
113+
server_info = server.appServerInfo
114+
creator_id = server.creatorId
115+
else:
116+
server_type = "Configured Server"
117+
server_id = server.appConfigurationId
118+
server_name = server.app.name if server.app else "Unnamed"
119+
server_description = server.app.description if server.app else None
120+
created_at = server.createdAt
121+
server_info = server.appServerInfo
122+
creator_id = server.creatorId
123+
124+
status_text = "❓ Unknown"
125+
server_url = "N/A"
126+
127+
if server_info:
128+
status_text = _server_status_text(server_info.status)
129+
server_url = server_info.serverUrl
130+
content_lines = [
131+
f"Name: [cyan]{server_name}[/cyan]",
132+
f"Type: [cyan]{server_type}[/cyan]",
133+
f"ID: [cyan]{server_id}[/cyan]",
134+
f"Status: {status_text}",
135+
f"Server URL: [cyan]{server_url}[/cyan]",
136+
]
137+
138+
if server_description:
139+
content_lines.append(f"Description: [cyan]{server_description}[/cyan]")
140+
141+
content_lines.append(f"Creator: [cyan]{creator_id}[/cyan]")
142+
143+
if created_at:
144+
content_lines.append(f"Created: [cyan]{created_at.strftime('%Y-%m-%d %H:%M:%S')}[/cyan]")
145+
146+
if isinstance(server, MCPAppConfiguration) and server.app:
147+
content_lines.extend([
148+
"",
149+
"[bold]Underlying App:[/bold]",
150+
f" App ID: [cyan]{server.app.appId}[/cyan]",
151+
f" App Name: [cyan]{server.app.name}[/cyan]",
152+
])
153+
154+
console.print(
155+
Panel(
156+
"\n".join(content_lines),
157+
title="Server Description",
158+
border_style="blue",
159+
expand=False,
160+
)
161+
)
162+
163+
164+
def _server_status_text(status: str) -> str:
165+
"""Convert server status code to emoji and text."""
166+
if status == "APP_SERVER_STATUS_ONLINE":
167+
return "[green]🟢 Active[/green]"
168+
elif status == "APP_SERVER_STATUS_OFFLINE":
169+
return "[red]🔴 Offline[/red]"
170+
else:
171+
return "❓ Unknown"

src/mcp_agent/cli/cloud/commands/servers/list/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)