Skip to content

Commit b23d943

Browse files
committed
Connect to remote MCP server
1 parent c93b245 commit b23d943

File tree

5 files changed

+151
-29
lines changed

5 files changed

+151
-29
lines changed
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
"""MCP Agent Cloud workflows commands."""
22

33
from .describe import describe_workflow
4-
from .resume import resume_workflow
4+
from .resume import resume_workflow, suspend_workflow
55
from .cancel import cancel_workflow
66

77
__all__ = [
88
"describe_workflow",
99
"resume_workflow",
10+
"suspend_workflow",
1011
"cancel_workflow",
1112
]

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

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,47 @@
99
from mcp_agent.cli.core.utils import run_async
1010
from mcp_agent.cli.exceptions import CLIError
1111
from mcp_agent.cli.utils.ux import console
12+
from mcp_agent.config import MCPServerSettings, Settings, LoggerSettings
1213
from mcp_agent.mcp.gen_client import gen_client
14+
from ...servers.utils import setup_authenticated_client, resolve_server, handle_server_api_errors
1315

1416

1517
async def _cancel_workflow_async(
18+
server_id_or_url: str,
1619
run_id: str,
1720
reason: Optional[str] = None
1821
) -> None:
19-
"""Cancel a workflow using MCP tool calls."""
20-
# Create a temporary MCP app to connect to temporal server
21-
app = MCPApp(name="workflows_cli")
22+
"""Cancel a workflow using MCP tool calls to a deployed server."""
23+
if server_id_or_url.startswith(('http://', 'https://')):
24+
server_url = server_id_or_url
25+
else:
26+
client = setup_authenticated_client()
27+
server = resolve_server(client, server_id_or_url)
28+
29+
if hasattr(server, 'appServerInfo') and server.appServerInfo:
30+
server_url = server.appServerInfo.serverUrl
31+
else:
32+
raise CLIError(f"Server '{server_id_or_url}' is not deployed or has no server URL")
33+
34+
if not server_url:
35+
raise CLIError(f"No server URL found for server '{server_id_or_url}'")
36+
37+
quiet_settings = Settings(logger=LoggerSettings(level="error"))
38+
app = MCPApp(name="workflows_cli", settings=quiet_settings)
2239

2340
try:
2441
async with app.run() as workflow_app:
25-
async with gen_client("temporal", server_registry=workflow_app.context.server_registry) as client:
42+
context = workflow_app.context
43+
44+
sse_url = f"{server_url.rstrip('/')}/sse" if not server_url.endswith('/sse') else server_url
45+
context.server_registry.registry["workflow_server"] = MCPServerSettings(
46+
name="workflow_server",
47+
description=f"Deployed MCP server {server_url}",
48+
url=sse_url,
49+
transport="sse"
50+
)
51+
52+
async with gen_client("workflow_server", server_registry=context.server_registry) as client:
2653
tool_params = {"run_id": run_id}
2754

2855
result = await client.call_tool("workflows-cancel", tool_params)
@@ -43,7 +70,9 @@ async def _cancel_workflow_async(
4370
raise CLIError(f"Error cancelling workflow with run ID {run_id}: {str(e)}") from e
4471

4572

73+
@handle_server_api_errors
4674
def cancel_workflow(
75+
server_id_or_url: str = typer.Argument(..., help="Server ID or URL hosting the workflow"),
4776
run_id: str = typer.Argument(..., help="Run ID of the workflow to cancel"),
4877
reason: Optional[str] = typer.Option(None, "--reason", help="Optional reason for cancellation"),
4978
) -> None:
@@ -53,7 +82,7 @@ def cancel_workflow(
5382
cannot be resumed and will be marked as cancelled.
5483
5584
Examples:
56-
mcp-agent cloud workflows cancel run_abc123
57-
mcp-agent cloud workflows cancel run_abc123 --reason "User requested cancellation"
85+
mcp-agent cloud workflows cancel app_abc123 run_xyz789
86+
mcp-agent cloud workflows cancel https://server.example.com run_xyz789 --reason "User requested cancellation"
5887
"""
59-
run_async(_cancel_workflow_async(run_id, reason))
88+
run_async(_cancel_workflow_async(server_id_or_url, run_id, reason))

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

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,47 @@
1111
from mcp_agent.cli.core.utils import run_async
1212
from mcp_agent.cli.exceptions import CLIError
1313
from mcp_agent.cli.utils.ux import console
14+
from mcp_agent.config import MCPServerSettings, Settings, LoggerSettings
1415
from mcp_agent.mcp.gen_client import gen_client
16+
from ...servers.utils import setup_authenticated_client, resolve_server, handle_server_api_errors
1517

1618

1719
async def _describe_workflow_async(
20+
server_id_or_url: str,
1821
run_id: str,
1922
format: str = "text"
2023
) -> None:
21-
"""Describe a workflow using MCP tool calls."""
22-
# Create a temporary MCP app to connect to temporal server
23-
app = MCPApp(name="workflows_cli")
24+
"""Describe a workflow using MCP tool calls to a deployed server."""
25+
if server_id_or_url.startswith(('http://', 'https://')):
26+
server_url = server_id_or_url
27+
else:
28+
client = setup_authenticated_client()
29+
server = resolve_server(client, server_id_or_url)
30+
31+
if hasattr(server, 'appServerInfo') and server.appServerInfo:
32+
server_url = server.appServerInfo.serverUrl
33+
else:
34+
raise CLIError(f"Server '{server_id_or_url}' is not deployed or has no server URL")
35+
36+
if not server_url:
37+
raise CLIError(f"No server URL found for server '{server_id_or_url}'")
38+
39+
quiet_settings = Settings(logger=LoggerSettings(level="error"))
40+
app = MCPApp(name="workflows_cli", settings=quiet_settings)
2441

2542
try:
2643
async with app.run() as workflow_app:
27-
async with gen_client("temporal", server_registry=workflow_app.context.server_registry) as client:
44+
context = workflow_app.context
45+
46+
sse_url = f"{server_url}/sse" if not server_url.endswith('/sse') else server_url
47+
context.server_registry.registry["workflow_server"] = MCPServerSettings(
48+
name="workflow_server",
49+
description=f"Deployed MCP server {server_url}",
50+
url=sse_url,
51+
transport="sse"
52+
)
53+
54+
async with gen_client("workflow_server", server_registry=context.server_registry) as client:
2855
result = await client.call_tool("workflows-get_status", {
2956
"run_id": run_id
3057
})
@@ -47,20 +74,26 @@ async def _describe_workflow_async(
4774
raise CLIError(f"Error describing workflow with run ID {run_id}: {str(e)}") from e
4875

4976

77+
@handle_server_api_errors
5078
def describe_workflow(
79+
server_id_or_url: str = typer.Argument(..., help="Server ID or URL hosting the workflow"),
5180
run_id: str = typer.Argument(..., help="Run ID of the workflow to describe"),
5281
format: Optional[str] = typer.Option("text", "--format", help="Output format (text|json|yaml)"),
5382
) -> None:
5483
"""Describe a workflow execution (alias: status).
5584
5685
Shows detailed information about a workflow execution including its current status,
5786
creation time, and other metadata.
87+
88+
Examples:
89+
mcp-agent cloud workflows describe app_abc123 run_xyz789
90+
mcp-agent cloud workflows describe https://server.example.com run_xyz789 --format json
5891
"""
5992
if format not in ["text", "json", "yaml"]:
6093
console.print("[red]Error: --format must be 'text', 'json', or 'yaml'[/red]")
6194
raise typer.Exit(6)
6295

63-
run_async(_describe_workflow_async(run_id, format))
96+
run_async(_describe_workflow_async(server_id_or_url, run_id, format))
6497

6598

6699
def print_workflow_status(workflow_status: dict) -> None:
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
"""MCP Agent Cloud workflow resume command."""
1+
"""MCP Agent Cloud workflow resume and suspend commands."""
22

3-
from .main import resume_workflow
3+
from .main import resume_workflow, suspend_workflow
44

5-
__all__ = ["resume_workflow"]
5+
__all__ = ["resume_workflow", "suspend_workflow"]

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

Lines changed: 72 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,49 @@
99
from mcp_agent.cli.core.utils import run_async
1010
from mcp_agent.cli.exceptions import CLIError
1111
from mcp_agent.cli.utils.ux import console
12+
from mcp_agent.config import MCPServerSettings, Settings, LoggerSettings
1213
from mcp_agent.mcp.gen_client import gen_client
14+
from ...servers.utils import setup_authenticated_client, resolve_server, handle_server_api_errors
1315

1416

15-
async def _resume_workflow_async(
17+
async def _signal_workflow_async(
18+
server_id_or_url: str,
1619
run_id: str,
20+
signal_name: str = "resume",
1721
payload: Optional[str] = None
1822
) -> None:
19-
"""Resume a workflow using MCP tool calls."""
20-
# Create a temporary MCP app to connect to temporal server
21-
app = MCPApp(name="workflows_cli")
23+
"""Send a signal to a workflow using MCP tool calls to a deployed server."""
24+
if server_id_or_url.startswith(('http://', 'https://')):
25+
server_url = server_id_or_url
26+
else:
27+
client = setup_authenticated_client()
28+
server = resolve_server(client, server_id_or_url)
29+
30+
if hasattr(server, 'appServerInfo') and server.appServerInfo:
31+
server_url = server.appServerInfo.serverUrl
32+
else:
33+
raise CLIError(f"Server '{server_id_or_url}' is not deployed or has no server URL")
34+
35+
if not server_url:
36+
raise CLIError(f"No server URL found for server '{server_id_or_url}'")
37+
38+
quiet_settings = Settings(logger=LoggerSettings(level="error"))
39+
app = MCPApp(name="workflows_cli", settings=quiet_settings)
2240

2341
try:
2442
async with app.run() as workflow_app:
25-
async with gen_client("temporal", server_registry=workflow_app.context.server_registry) as client:
26-
tool_params = {"run_id": run_id}
43+
context = workflow_app.context
44+
45+
sse_url = f"{server_url.rstrip('/')}/sse" if not server_url.endswith('/sse') else server_url
46+
context.server_registry.registry["workflow_server"] = MCPServerSettings(
47+
name="workflow_server",
48+
description=f"Deployed MCP server {server_url}",
49+
url=sse_url,
50+
transport="sse"
51+
)
52+
53+
async with gen_client("workflow_server", server_registry=context.server_registry) as client:
54+
tool_params = {"run_id": run_id, "signal_name": signal_name}
2755
if payload:
2856
tool_params["payload"] = payload
2957

@@ -34,16 +62,21 @@ async def _resume_workflow_async(
3462
success = success.lower() == 'true'
3563

3664
if success:
37-
console.print(f"[green]✓[/green] Successfully resumed workflow")
65+
action_past = "resumed" if signal_name == "resume" else "suspended" if signal_name == "suspend" else f"signaled ({signal_name})"
66+
action_color = "green" if signal_name == "resume" else "yellow" if signal_name == "suspend" else "blue"
67+
action_icon = "✓" if signal_name == "resume" else "⏸" if signal_name == "suspend" else "📡"
68+
console.print(f"[{action_color}]{action_icon}[/{action_color}] Successfully {action_past} workflow")
3869
console.print(f" Run ID: [cyan]{run_id}[/cyan]")
3970
else:
40-
raise CLIError(f"Failed to resume workflow with run ID {run_id}")
71+
raise CLIError(f"Failed to {signal_name} workflow with run ID {run_id}")
4172

4273
except Exception as e:
43-
raise CLIError(f"Error resuming workflow with run ID {run_id}: {str(e)}") from e
74+
raise CLIError(f"Error {signal_name}ing workflow with run ID {run_id}: {str(e)}") from e
4475

4576

77+
@handle_server_api_errors
4678
def resume_workflow(
79+
server_id_or_url: str = typer.Argument(..., help="Server ID or URL hosting the workflow"),
4780
run_id: str = typer.Argument(..., help="Run ID of the workflow to resume"),
4881
payload: Optional[str] = typer.Option(None, "--payload", help="JSON or text payload to pass to resumed workflow"),
4982
) -> None:
@@ -53,9 +86,9 @@ def resume_workflow(
5386
a payload (JSON or text) to pass data to the resumed workflow.
5487
5588
Examples:
56-
mcp-agent cloud workflows resume run_abc123
57-
mcp-agent cloud workflows resume run_abc123 --payload '{"data": "value"}'
58-
mcp-agent cloud workflows resume run_abc123 --payload "simple text"
89+
mcp-agent cloud workflows resume app_abc123 run_xyz789
90+
mcp-agent cloud workflows resume https://server.example.com run_xyz789 --payload '{"data": "value"}'
91+
mcp-agent cloud workflows resume app_abc123 run_xyz789 --payload "simple text"
5992
"""
6093
if payload:
6194
try:
@@ -64,4 +97,30 @@ def resume_workflow(
6497
except json.JSONDecodeError:
6598
console.print(f"[dim]Resuming with text payload...[/dim]")
6699

67-
run_async(_resume_workflow_async(run_id, payload))
100+
run_async(_signal_workflow_async(server_id_or_url, run_id, "resume", payload))
101+
102+
103+
@handle_server_api_errors
104+
def suspend_workflow(
105+
server_id_or_url: str = typer.Argument(..., help="Server ID or URL hosting the workflow"),
106+
run_id: str = typer.Argument(..., help="Run ID of the workflow to suspend"),
107+
payload: Optional[str] = typer.Option(None, "--payload", help="JSON or text payload to pass to suspended workflow"),
108+
) -> None:
109+
"""Suspend a workflow execution.
110+
111+
Temporarily pauses a workflow execution, which can later be resumed.
112+
Optionally accepts a payload (JSON or text) to pass data to the suspended workflow.
113+
114+
Examples:
115+
mcp-agent cloud workflows suspend app_abc123 run_xyz789
116+
mcp-agent cloud workflows suspend https://server.example.com run_xyz789 --payload '{"reason": "maintenance"}'
117+
mcp-agent cloud workflows suspend app_abc123 run_xyz789 --payload "paused for review"
118+
"""
119+
if payload:
120+
try:
121+
json.loads(payload)
122+
console.print(f"[dim]Suspending with JSON payload...[/dim]")
123+
except json.JSONDecodeError:
124+
console.print(f"[dim]Suspending with text payload...[/dim]")
125+
126+
run_async(_signal_workflow_async(server_id_or_url, run_id, "suspend", payload))

0 commit comments

Comments
 (0)