Skip to main content

Tool approvals and permissions

The Harness provides a permission system that controls which tools require user approval before execution. You can configure policies at the category level or per-tool, and grant session-wide exceptions for trusted tools. This gives agents with access to destructive or sensitive tools — file writes, command execution, API calls — a human-in-the-loop checkpoint before those tools run.

Permission policies
Direct link to Permission policies

Three policies control tool behavior:

  • allow: The tool runs without prompting
  • ask: The tool pauses and emits a tool_approval_required event; the user must approve or decline
  • deny: The tool is blocked from execution

Setting policies
Direct link to Setting policies

Set policies per-category or per-tool. Per-tool policies take precedence over category policies:

// Category-level: all execute tools require approval
harness.setPermissionForCategory({ category: 'execute', policy: 'ask' })

// Tool-level: this specific tool is always blocked
harness.setPermissionForTool({ toolName: 'dangerous_tool', policy: 'deny' })

Tool categories
Direct link to Tool categories

The toolCategoryResolver maps tool names to categories. Pass it to the Harness constructor:

src/mastra/harness.ts
const harness = new Harness({
id: 'my-agent',
toolCategoryResolver: toolName => {
if (toolName.includes('write') || toolName.includes('delete')) return 'edit'
if (toolName.includes('execute')) return 'execute'
return 'read'
},
})

Built-in categories are read, edit, execute, mcp, and other.

Responding to approval requests
Direct link to Responding to approval requests

When a tool's policy is ask, the Harness emits a tool_approval_required event. Your UI should display a prompt and call session.respondToToolApproval():

harness.subscribe(event => {
if (event.type === 'tool_approval_required') {
// Show approval UI...
harness.session.respondToToolApproval({ decision: 'approve' })
}
})

The decision field accepts 'approve', 'decline', or 'always_allow_category'. When always_allow_category is used, the tool's category is granted for the rest of the session. Future tools in the same category are auto-approved.

harness.session.respondToToolApproval({ decision: 'always_allow_category' })

Session grants
Direct link to Session grants

The Harness owns permission policy (which categories require approval); the Session owns the grants a user makes during a conversation. Grant a category or tool for the rest of the session so it runs without further prompting:

// Grant all edit tools for this session
harness.session.grantCategory('edit')

// Grant a specific tool
harness.session.grantTool('mastra_workspace_execute_command')

// Check current grants
const grants = harness.session.getGrants()
// { categories: ['edit'], tools: ['mastra_workspace_execute_command'] }

Tool suspensions
Direct link to Tool suspensions

Interactive built-in tools (ask_user, submit_plan) use the native tool-suspension primitive instead of the approval flow. They emit a tool_suspended event with toolCallId, toolName, and suspendPayload. Resume with respondToToolSuspension():

harness.subscribe(event => {
if (event.type === 'tool_suspended' && event.toolName === 'ask_user') {
const { question } = event.suspendPayload as { question: string }
// Show question to user, then resume:
harness.respondToToolSuspension({
toolCallId: event.toolCallId,
resumeData: 'User response here',
})
}
})

Plan approval
Direct link to Plan approval

The submit_plan tool suspends via the same mechanism. Resume with an action field:

// Approve the plan
harness.respondToToolSuspension({
toolCallId: event.toolCallId,
resumeData: { action: 'approved' },
})

// Reject with feedback
harness.respondToToolSuspension({
toolCallId: event.toolCallId,
resumeData: { action: 'rejected', feedback: 'Needs more detail' },
})

Built-in tools
Direct link to Built-in tools

The Harness provides these built-in tools to agents in every mode:

ToolDescription
ask_userAsk the user a question (free text, single-select, or multi-select)
submit_planSubmit a plan for user review and approval
task_writeCreate or replace a structured task list
task_updateUpdate one tracked task by ID
task_completeMark one tracked task completed
task_checkCheck task list completion status
subagentSpawn a focused subagent (requires subagents config)

Disable specific built-in tools with disableBuiltinTools:

const harness = new Harness({
id: 'no-plans',
disableBuiltinTools: ['submit_plan'],
})