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.
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.
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.
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.
const resumedStream = await myAgent.resumeStream(
{ name: "John Smith" },
{ runId: stream.runId },
);