diff --git a/temporal_mcp/handlers/workflow_handlers.py b/temporal_mcp/handlers/workflow_handlers.py index 49383aa..9544558 100644 --- a/temporal_mcp/handlers/workflow_handlers.py +++ b/temporal_mcp/handlers/workflow_handlers.py @@ -215,15 +215,16 @@ async def get_workflow_history(client: Client, args: dict) -> list[TextContent]: Args: client: Connected Temporal client - args: Arguments containing workflow_id and optional limit + args: Arguments containing workflow_id and optional limit, run_id Returns: Workflow history events """ workflow_id = args["workflow_id"] limit = args.get("limit", 1000) + run_id = args.get("run_id") - handle = client.get_workflow_handle(workflow_id) + handle = client.get_workflow_handle(workflow_id, run_id=run_id) events = [] scheduled_activities: dict[int, dict[str, Any]] = {} @@ -249,7 +250,7 @@ async def get_workflow_history(client: Client, args: dict) -> list[TextContent]: if count >= limit: break - return [TextContent(type="text", text=json.dumps({"workflow_id": workflow_id, "events": events, "count": len(events)}, indent=2))] + return [TextContent(type="text", text=json.dumps({"workflow_id": workflow_id, "run_id": run_id, "events": events, "count": len(events)}, indent=2))] def _workflow_history_event_to_dict(event: Any, scheduled_activities: dict[int, dict[str, Any]], initiated_child_workflows: dict[int, dict[str, Any]]) -> dict[str, Any]: diff --git a/temporal_mcp/tools/tool_definitions.py b/temporal_mcp/tools/tool_definitions.py index dc46e02..d99815b 100644 --- a/temporal_mcp/tools/tool_definitions.py +++ b/temporal_mcp/tools/tool_definitions.py @@ -93,6 +93,7 @@ def get_all_tools() -> list[Tool]: "type": "object", "properties": { "workflow_id": {"type": "string", "description": "The workflow execution ID"}, + "run_id": {"type": "string", "description": "Optional run ID for the workflow execution; omit to target the latest run"}, "limit": {"type": "number", "description": "Maximum number of history events to return (default: 1000)"}, }, "required": ["workflow_id"], diff --git a/tests/test_workflow_handlers.py b/tests/test_workflow_handlers.py index 4b76c37..a66e979 100644 --- a/tests/test_workflow_handlers.py +++ b/tests/test_workflow_handlers.py @@ -282,6 +282,44 @@ async def mock_fetch_history_events(): assert "__raw__" not in failed_attrs assert "result" not in failed_attrs + @pytest.mark.asyncio + async def test_get_workflow_history_passes_run_id_when_provided(self, mock_client): + async def mock_fetch_history_events(): + if False: + yield None + + mock_handle = AsyncMock() + mock_handle.fetch_history_events = mock_fetch_history_events + mock_client.get_workflow_handle = MagicMock(return_value=mock_handle) + + result = await workflow_handlers.get_workflow_history(mock_client, {"workflow_id": "test-workflow-123", "run_id": "run-xyz"}) + + response = json.loads(result[0].text) + assert response["workflow_id"] == "test-workflow-123" + assert response["run_id"] == "run-xyz" + assert response["events"] == [] + assert response["count"] == 0 + mock_client.get_workflow_handle.assert_called_once_with("test-workflow-123", run_id="run-xyz") + + @pytest.mark.asyncio + async def test_get_workflow_history_defaults_run_id_to_none(self, mock_client): + async def mock_fetch_history_events(): + if False: + yield None + + mock_handle = AsyncMock() + mock_handle.fetch_history_events = mock_fetch_history_events + mock_client.get_workflow_handle = MagicMock(return_value=mock_handle) + + result = await workflow_handlers.get_workflow_history(mock_client, {"workflow_id": "test-workflow-123"}) + + response = json.loads(result[0].text) + assert response["workflow_id"] == "test-workflow-123" + assert response["run_id"] is None + assert response["events"] == [] + assert response["count"] == 0 + mock_client.get_workflow_handle.assert_called_once_with("test-workflow-123", run_id=None) + class TestGetWorkflowEvent: @pytest.mark.asyncio