99from mcp_agent .cli .core .utils import run_async
1010from mcp_agent .cli .exceptions import CLIError
1111from mcp_agent .cli .utils .ux import console
12+ from mcp_agent .config import MCPServerSettings , Settings , LoggerSettings
1213from 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
4678def 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