Octopus
Agent Triggers Workflow
The start_workflow MCP tool allows an agent to trigger a Flow Studio workflow directly from a conversation. The agent can wait for the result or fire and forget, depending on workflow complexity.
start_workflow Tool Schema
{
"name": "start_workflow",
"description": "Start a Flow Studio workflow and optionally wait for its result. " +
"Use when the user's request requires a structured business process " +
"(approval, multi-step operation, ERP update, notification). " +
"Always confirm the action with the user before calling.",
"inputSchema": {
"type": "object",
"properties": {
"workflow_id": {
"type": "string",
"description": "The workflow identifier to trigger (e.g. 'leave-approval-workflow')"
},
"inputs": {
"type": "object",
"description": "Key-value pairs passed to the workflow as input data"
},
"mode": {
"type": "string",
"enum": ["sync", "async"],
"default": "async",
"description": "sync: wait for completion (up to SyncTimeoutSeconds). async: fire and return reference."
},
"correlation_id": {
"type": "string",
"description": "Optional caller-provided ID to track this execution externally"
}
},
"required": ["workflow_id", "inputs"]
}
}
Sync vs Async Mode
| Mode | Agent waits? | Returns | Use When |
|---|---|---|---|
sync | Yes (up to SyncTimeoutSeconds) | Workflow final output | Short workflows (< 2 min) where the result matters to the user |
async | No | Execution ID + status URL | Long-running approvals, multi-step processes, background tasks |
IWorkflowTriggerService Implementation
public class WorkflowTriggerService : IWorkflowTriggerService
{
private readonly IProcessServerClient _processServer;
private readonly ProcessPluginConfig _config;
private readonly ITenantContext _tenant;
public async Task<string> HandleStartWorkflowAsync(
JsonElement input, ConversationContext ctx, CancellationToken ct)
{
var workflowId = input.GetProperty("workflow_id").GetString()!;
var inputs = input.GetProperty("inputs");
var mode = input.TryGetProperty("mode", out var m)
? m.GetString()! : "async";
var correlationId = input.TryGetProperty("correlation_id", out var c)
? c.GetString() : null;
// Check workflow access policy
if (!_config.WorkflowAccessPolicy.IsAllowed(workflowId))
return JsonSerializer.Serialize(new
{
error = "Workflow not permitted",
reason = $"Workflow '{workflowId}' is not in the allowed workflow list."
});
// Start the workflow on ProcessServer
var startRequest = new StartWorkflowRequest
{
WorkflowId = workflowId,
TenantId = _tenant.TenantId,
InitiatedBy = ctx.UserId,
Inputs = inputs.Deserialize<Dictionary<string, object>>()!,
CorrelationId = correlationId
};
var execution = await _processServer.StartWorkflowAsync(startRequest, ct);
if (mode == "sync")
{
// Poll for completion
var result = await _processServer.WaitForCompletionAsync(
execution.ExecutionId,
TimeSpan.FromSeconds(_config.SyncTimeoutSeconds),
ct);
return JsonSerializer.Serialize(new
{
execution_id = execution.ExecutionId,
status = result.Status,
outputs = result.Outputs
});
}
// Async — return reference immediately
return JsonSerializer.Serialize(new
{
execution_id = execution.ExecutionId,
status = "running",
tracking_url = $"/workflows/executions/{execution.ExecutionId}"
});
}
}
Successful Sync Response
{
"execution_id": "exec-abc123",
"status": "Completed",
"outputs": {
"request_id": "LR-8821",
"status": "PendingApproval",
"approver": "manager@company.com",
"submitted_at": "2024-06-15T10:00:00Z"
}
}