Signal, query, or update a running workflow
workflow_interactSend signals, run queries, or apply updates to running workflows. Supports fire-and-forget events, synchronous state reads, and mid-execution modifications.
Instructions
Send a signal to or run a query against a running workflow execution.
action=signal: fire-and-forget event; the workflow reacts asynchronously.
name: signal name defined in the workflow.input: optional payload matching the signal's schema.
action=query: synchronous read of internal workflow state.
name: query handler name defined in the workflow.input: optional parameters for the query.Returns
query_name+resultinline.
action=update: synchronous request to modify workflow state mid-execution.
name: update handler name defined in the workflow.input: optional payload for the update.Returns
update_name+resultinline.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| action | Yes | Interaction type. | |
| executionId | Yes | Target execution ID. | |
| name | Yes | Signal or query handler name. | |
| input | No | Optional payload for the signal or query. |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| action | Yes | ||
| execution_id | Yes | ||
| message | No | Confirmation message for signal actions. | |
| query_name | No | ||
| update_name | No | ||
| result | No | Query or update result payload. |
Implementation Reference
- src/tools-workflows.ts:285-351 (handler)The handler function for the workflow_interact tool. It dispatches based on input.action: 'signal' (fire-and-forget via signalWorkflowExecution), 'query' (synchronous read via queryWorkflowExecution), or 'update' (modify state via updateWorkflowExecution). Returns structured content with appropriate fields for each action type.
async (input) => { try { if (input.action === "signal") { const res = await mistral.workflows.executions.signalWorkflowExecution({ executionId: input.executionId, signalInvocationBody: { name: input.name, input: input.input ?? null, }, }); const structured = { action: "signal" as const, execution_id: input.executionId, message: res.message, }; return { content: [toTextBlock(`Signal '${input.name}' sent to ${input.executionId}: ${res.message}`)], structuredContent: structured, }; } else if (input.action === "query") { const res = await mistral.workflows.executions.queryWorkflowExecution({ executionId: input.executionId, queryInvocationBody: { name: input.name, input: input.input ?? null, }, }); const structured = { action: "query" as const, execution_id: input.executionId, query_name: res.queryName, result: res.result, }; return { content: [toTextBlock(`Query '${res.queryName}' on ${input.executionId}: ${JSON.stringify(res.result)}`)], structuredContent: structured, }; } else { const res = await mistral.workflows.executions.updateWorkflowExecution({ executionId: input.executionId, updateInvocationBody: { name: input.name, input: input.input ?? null, }, }); const structured = { action: "update" as const, execution_id: input.executionId, update_name: res.updateName, result: res.result, }; return { content: [toTextBlock(`Update '${res.updateName}' on ${input.executionId}: ${JSON.stringify(res.result)}`)], structuredContent: structured, }; } } catch (err) { return errorResult("workflow_interact", err); } } ); - src/tools-workflows.ts:47-55 (schema)Output schema (WorkflowInteractOutputShape) for workflow_interact. Defines fields: action (enum signal/query/update), execution_id, message (optional, for signals), query_name/update_name (optional), and result (optional, for query/update payloads).
export const WorkflowInteractOutputShape = { action: z.enum(["signal", "query", "update"]), execution_id: z.string(), message: z.string().optional().describe("Confirmation message for signal actions."), query_name: z.string().optional(), update_name: z.string().optional(), result: z.unknown().optional().describe("Query or update result payload."), }; export const WorkflowInteractOutputSchema = z.object(WorkflowInteractOutputShape); - src/tools-workflows.ts:264-275 (schema)Input schema for workflow_interact. Defines required inputs: action (enum: signal/query/update), executionId (string), name (string), and optional input (record of strings to unknown).
inputSchema: { action: z.enum(["signal", "query", "update"]).describe("Interaction type."), executionId: z.string().min(1).describe("Target execution ID."), name: z .string() .min(1) .describe("Signal or query handler name."), input: z .record(z.string(), z.unknown()) .optional() .describe("Optional payload for the signal or query."), }, - src/tools-workflows.ts:242-352 (registration)Registration of the workflow_interact tool via server.registerTool(). Includes title, description, inputSchema, outputSchema (WorkflowInteractOutputShape), and annotations. Registered inside the registerWorkflowTools() function.
// ========== workflow_interact ========== server.registerTool( "workflow_interact", { title: "Signal, query, or update a running workflow", description: [ "Send a signal to or run a query against a running workflow execution.", "", "action=signal: fire-and-forget event; the workflow reacts asynchronously.", " - `name`: signal name defined in the workflow.", " - `input`: optional payload matching the signal's schema.", "", "action=query: synchronous read of internal workflow state.", " - `name`: query handler name defined in the workflow.", " - `input`: optional parameters for the query.", " - Returns `query_name` + `result` inline.", "", "action=update: synchronous request to modify workflow state mid-execution.", " - `name`: update handler name defined in the workflow.", " - `input`: optional payload for the update.", " - Returns `update_name` + `result` inline.", ].join("\n"), inputSchema: { action: z.enum(["signal", "query", "update"]).describe("Interaction type."), executionId: z.string().min(1).describe("Target execution ID."), name: z .string() .min(1) .describe("Signal or query handler name."), input: z .record(z.string(), z.unknown()) .optional() .describe("Optional payload for the signal or query."), }, outputSchema: WorkflowInteractOutputShape, annotations: { title: "Interact with running workflow (signal/query/update)", readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true, }, }, async (input) => { try { if (input.action === "signal") { const res = await mistral.workflows.executions.signalWorkflowExecution({ executionId: input.executionId, signalInvocationBody: { name: input.name, input: input.input ?? null, }, }); const structured = { action: "signal" as const, execution_id: input.executionId, message: res.message, }; return { content: [toTextBlock(`Signal '${input.name}' sent to ${input.executionId}: ${res.message}`)], structuredContent: structured, }; } else if (input.action === "query") { const res = await mistral.workflows.executions.queryWorkflowExecution({ executionId: input.executionId, queryInvocationBody: { name: input.name, input: input.input ?? null, }, }); const structured = { action: "query" as const, execution_id: input.executionId, query_name: res.queryName, result: res.result, }; return { content: [toTextBlock(`Query '${res.queryName}' on ${input.executionId}: ${JSON.stringify(res.result)}`)], structuredContent: structured, }; } else { const res = await mistral.workflows.executions.updateWorkflowExecution({ executionId: input.executionId, updateInvocationBody: { name: input.name, input: input.input ?? null, }, }); const structured = { action: "update" as const, execution_id: input.executionId, update_name: res.updateName, result: res.result, }; return { content: [toTextBlock(`Update '${res.updateName}' on ${input.executionId}: ${JSON.stringify(res.result)}`)], structuredContent: structured, }; } } catch (err) { return errorResult("workflow_interact", err); } } ); } - src/shared.ts:97-103 (helper)The errorResult helper function used by the workflow_interact handler to return error responses with isError: true flag and a formatted error message.
export function errorResult(tool: string, err: unknown) { const message = err instanceof Error ? err.message : String(err); return { content: [toTextBlock(`[mistral-mcp:${tool}] ${message}`)], isError: true as const, }; }