Confirmations

confirm(), MRTR input_required, ctx.ask, and execute after accept.

Destructive or sensitive tools declare confirmation outside execute.

export default defineTool({
  description: "Delete rows matching a query",
  input: z.object({ query: z.string() }),
  async confirm({ query }) {
    const plan = planDelete(query);
    return {
      message: `Delete ${plan.rows} rows matching ${query}?`,
      preview: plan,
    };
  },
  async execute({ query }) {
    const plan = planDelete(query);
    return result(plan, `Deleted ${plan.rows} rows.`);
  },
});

MRTR

confirm maps to resultType: "input_required" (MRTR).

  • Text-only hosts see message
  • UI hosts may render preview from the MRTR structuredContent
  • execute runs once, after accept
  • The mutation does not run on the first call and does not replay

ctx.ask

For mid-flight fields, use a continuation inside execute:

async execute(input, ctx) {
  const { note } = await ctx.ask(z.object({
    note: z.string().describe("Why are you deleting these rows?"),
  }));
  return applyDelete(input.query, note);
}

ctx.ask re-enters execute on a new request with inputResponses. Do not mutate before ask.

Prefer confirm for yes/no gates. Raw ctx.inputRequired remains an escape hatch.

Never View-only

A View must not be the only confirm path for a side effect. MRTR works in terminal hosts. Views can show previews, not replace the gate.