Skip to main content

Human in the Loop with Tools

Sometimes agents need human oversight before they can run potentially sensitive tools or long-running workflows. Mastra lets you pause tool execution to wait for approval, decline a tool call entirely, or suspend a tool mid-flight until the required data is available.

Tool approvals

Enforcing approval on every tool

Pass requireToolApproval: true when calling stream (or generate) to automatically pause execution whenever the agent tries to invoke a tool. The stream closes until you explicitly approve or decline the tool call using the original run’s runId.

Require approval before executing any tool
const stream = await myAgent.stream("Find the user with name - John Smith", {
requireToolApproval: true,
});

const approvedToolCallStream = await myAgent.approveToolCall({ runId: stream.runId });
const declinedToolCallStream = await myAgent.declineToolCall({ runId: stream.runId });

Requiring approval per tool

You can also enforce approval on specific tools. Set requireApproval: true when creating a tool so the agent pauses only when it tries to invoke that tool.

Mark a tool as approval-only
const findUserTool = createTool({
id: "Find user tool",
description: "Returns the name and email for a matching user",
inputSchema: z.object({
name: z.string(),
}),
execute: async (inputData) => {
return mockFindUser(inputData);
},
requireApproval: true,
});

When the agent reaches this tool, it emits a tool-call event you can approve or decline with the same approveToolCall or declineToolCall helpers.

Tool suspension

Sometimes a tool can’t finish immediately—maybe it’s waiting for external confirmation or extra input. You can suspend and later resume execution using workflow.suspend, resumeStream, and optional schemas that define what data is required at each step.

Suspend and resume a tool
const findUserTool = createTool({
id: "Find user tool",
description: "Returns the name and email for a matching user",
inputSchema: z.object({
name: z.string(),
}),
suspendSchema: z.object({
message: z.string(),
}),
resumeSchema: z.object({
name: z.string(),
}),
execute: async (inputData, { workflow }) => {
if (!workflow.resumeData) {
return workflow.suspend({ message: "Please provide the name of the user" });
}

return {
name: workflow?.resumeData?.name,
email: "test@test.com",
};
},
});

To continue execution, call resumeStream with the run’s runId and the resumeData that satisfies the resumeSchema. The workflow resumes from the suspension point and the tool completes with the new data.

Resume a suspended tool call
const resumedStream = await myAgent.resumeStream(
{ name: "John Smith" },
{ runId: stream.runId },
);