Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/mcp_agent/cli/cloud/commands/logger/tail/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

from mcp_agent.cli.exceptions import CLIError
from mcp_agent.cli.auth import load_credentials, UserCredentials
from mcp_agent.cli.cloud.commands.servers.utils import setup_authenticated_client
from mcp_agent.cli.cloud.commands.utils import setup_authenticated_client
from mcp_agent.cli.core.api_client import UnauthenticatedError
from mcp_agent.cli.core.utils import parse_app_identifier, resolve_server_url
from mcp_agent.cli.utils.ux import print_error
Expand Down
2 changes: 1 addition & 1 deletion src/mcp_agent/cli/cloud/commands/servers/delete/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from mcp_agent.cli.core.utils import run_async
from mcp_agent.cli.exceptions import CLIError
from mcp_agent.cli.mcp_app.api_client import MCPApp
from ..utils import (
from ...utils import (
setup_authenticated_client,
resolve_server,
handle_server_api_errors,
Expand Down
2 changes: 1 addition & 1 deletion src/mcp_agent/cli/cloud/commands/servers/describe/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from mcp_agent.cli.exceptions import CLIError
from mcp_agent.cli.mcp_app.api_client import MCPApp, MCPAppConfiguration
from ..utils import (
from ...utils import (
setup_authenticated_client,
validate_output_format,
resolve_server,
Expand Down
2 changes: 1 addition & 1 deletion src/mcp_agent/cli/cloud/commands/servers/list/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from mcp_agent.cli.core.utils import run_async
from mcp_agent.cli.mcp_app.api_client import MCPApp, MCPAppConfiguration
from ..utils import (
from ...utils import (
setup_authenticated_client,
validate_output_format,
handle_server_api_errors,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Shared utilities for server commands."""
"""Shared utilities for cloud commands."""

from functools import wraps
from typing import Union
Expand Down
5 changes: 0 additions & 5 deletions src/mcp_agent/cli/cloud/commands/workflow/__init__.py

This file was deleted.

5 changes: 0 additions & 5 deletions src/mcp_agent/cli/cloud/commands/workflow/status/__init__.py

This file was deleted.

106 changes: 0 additions & 106 deletions src/mcp_agent/cli/cloud/commands/workflow/status/main.py

This file was deleted.

12 changes: 12 additions & 0 deletions src/mcp_agent/cli/cloud/commands/workflows/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
"""MCP Agent Cloud workflows commands."""

from .describe import describe_workflow
from .resume import resume_workflow, suspend_workflow
from .cancel import cancel_workflow

__all__ = [
"describe_workflow",
"resume_workflow",
"suspend_workflow",
"cancel_workflow",
]
5 changes: 5 additions & 0 deletions src/mcp_agent/cli/cloud/commands/workflows/cancel/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"""MCP Agent Cloud workflow cancel command."""

from .main import cancel_workflow

__all__ = ["cancel_workflow"]
87 changes: 87 additions & 0 deletions src/mcp_agent/cli/cloud/commands/workflows/cancel/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
"""Workflow cancel command implementation."""

from typing import Optional

import typer

from mcp_agent.app import MCPApp
from mcp_agent.cli.core.utils import run_async
from mcp_agent.cli.exceptions import CLIError
from mcp_agent.cli.utils.ux import console
from mcp_agent.config import MCPServerSettings, Settings, LoggerSettings
from mcp_agent.mcp.gen_client import gen_client
from ...utils import setup_authenticated_client, resolve_server, handle_server_api_errors


async def _cancel_workflow_async(
server_id_or_url: str,
run_id: str,
reason: Optional[str] = None
) -> None:
"""Cancel a workflow using MCP tool calls to a deployed server."""
if server_id_or_url.startswith(('http://', 'https://')):
server_url = server_id_or_url
else:
client = setup_authenticated_client()
server = resolve_server(client, server_id_or_url)

if hasattr(server, 'appServerInfo') and server.appServerInfo:
server_url = server.appServerInfo.serverUrl
else:
raise CLIError(f"Server '{server_id_or_url}' is not deployed or has no server URL")

if not server_url:
raise CLIError(f"No server URL found for server '{server_id_or_url}'")

quiet_settings = Settings(logger=LoggerSettings(level="error"))
app = MCPApp(name="workflows_cli", settings=quiet_settings)

try:
async with app.run() as workflow_app:
context = workflow_app.context

sse_url = f"{server_url.rstrip('/')}/sse" if not server_url.endswith('/sse') else server_url
context.server_registry.registry["workflow_server"] = MCPServerSettings(
name="workflow_server",
description=f"Deployed MCP server {server_url}",
url=sse_url,
transport="sse"
)

async with gen_client("workflow_server", server_registry=context.server_registry) as client:
tool_params = {"run_id": run_id}

result = await client.call_tool("workflows-cancel", tool_params)

Comment on lines +52 to +55
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix: forward --reason to server tool call

The CLI accepts --reason but doesn’t forward it, so the server never receives it.

Apply:

-                tool_params = {"run_id": run_id}
+                tool_params = {"run_id": run_id}
+                if reason is not None:
+                    tool_params["reason"] = reason
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
tool_params = {"run_id": run_id}
result = await client.call_tool("workflows-cancel", tool_params)
tool_params = {"run_id": run_id}
if reason is not None:
tool_params["reason"] = reason
result = await client.call_tool("workflows-cancel", tool_params)
🤖 Prompt for AI Agents
In src/mcp_agent/cli/cloud/commands/workflows/cancel/main.py around lines 52 to
55, the CLI parses a --reason flag but doesn’t include it in the payload sent to
the server; update the tool_params construction to include the reason (e.g., add
"reason": reason or conditionally add when reason is not None) so the await
client.call_tool("workflows-cancel", tool_params) forwards the reason to the
server.

success = result.content[0].text if result.content else False
if isinstance(success, str):
success = success.lower() == 'true'

Comment on lines +56 to +59
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Harden tool response parsing (accept bool/JSON/text “ok”)

Parsing assumes a text "true". Make it robust to boolean or JSON payloads.

Apply:

-                success = result.content[0].text if result.content else False
-                if isinstance(success, str):
-                    success = success.lower() == 'true'
+                success = False
+                if result.content:
+                    item = result.content[0]
+                    payload = getattr(item, "json", None) or getattr(item, "data", None) or getattr(item, "text", None)
+                    if isinstance(payload, dict) and "success" in payload:
+                        success = bool(payload["success"])
+                    elif isinstance(payload, str):
+                        success = payload.strip().lower() in {"true", "ok", "success", "1", "yes"}

Committable suggestion skipped: line range outside the PR's diff.

if success:
console.print("[yellow]⚠[/yellow] Successfully cancelled workflow")
console.print(f" Run ID: [cyan]{run_id}[/cyan]")
if reason:
console.print(f" Reason: [dim]{reason}[/dim]")
else:
raise CLIError(f"Failed to cancel workflow with run ID {run_id}")

except Exception as e:
raise CLIError(f"Error cancelling workflow with run ID {run_id}: {str(e)}") from e
Comment on lines +68 to +69
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Preserve CLIError semantics (avoid double-wrapping)

Re-raise CLIError as-is so exit codes/messages are preserved.

Apply:

-    except Exception as e:
-        raise CLIError(f"Error cancelling workflow with run ID {run_id}: {str(e)}") from e
+    except CLIError:
+        raise
+    except Exception as e:
+        raise CLIError(f"Error cancelling workflow with run ID {run_id}: {e}") from e
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
except Exception as e:
raise CLIError(f"Error cancelling workflow with run ID {run_id}: {str(e)}") from e
except CLIError:
raise
except Exception as e:
raise CLIError(f"Error cancelling workflow with run ID {run_id}: {e}") from e
🤖 Prompt for AI Agents
In src/mcp_agent/cli/cloud/commands/workflows/cancel/main.py around lines 68-69,
the except block rewraps every Exception into a new CLIError which double-wraps
existing CLIError instances; change the handler to check if the caught exception
is already a CLIError and re-raise it unchanged (raise e), otherwise wrap
non-CLIError exceptions with CLIError(f"Error cancelling workflow with run ID
{run_id}: {str(e)}") from e so exit codes/messages are preserved.



@handle_server_api_errors
def cancel_workflow(
server_id_or_url: str = typer.Argument(..., help="Server ID or URL hosting the workflow"),
run_id: str = typer.Argument(..., help="Run ID of the workflow to cancel"),
reason: Optional[str] = typer.Option(None, "--reason", help="Optional reason for cancellation"),
) -> None:
"""Cancel a workflow execution.

Permanently stops a workflow execution. Unlike suspend, a cancelled workflow
cannot be resumed and will be marked as cancelled.

Examples:
mcp-agent cloud workflows cancel app_abc123 run_xyz789
mcp-agent cloud workflows cancel https://server.example.com run_xyz789 --reason "User requested cancellation"
"""
run_async(_cancel_workflow_async(server_id_or_url, run_id, reason))
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"""MCP Agent Cloud workflow describe command."""

from .main import describe_workflow

__all__ = ["describe_workflow"]
Loading
Loading