Permission Scope

The access a task needs, scope by scope with its risk, granted or withheld by the user for a chosen duration.

Pattern

≈ 1,061 tokens per use (tool 625 · guidance 271 · typical payload 165)How token costs are estimated

Example: Permission Scope

Book the team offsite for next month and send everyone the invite.

Book the team offsite

To find a date, hold a venue and invite six people.

Scopes requested
Allow for

When to use

  • The agent needs several kinds of access before starting a task (read a calendar, send email, spend money) and the user hasn't granted them yet.
  • The user should decide scope by scope what the agent may touch, and for how long.

When not to use

  • The agent is about to take one specific action with known parameters. Use an Approval Request.
  • The user is choosing how independently the agent works in general. Use Autonomy Control.
  • Access is already granted. Don't ask again; act.

How the agent should use it

  • Ask only for what the task needs, lowest risk first, each label verb first ("Read your calendar").
  • Set risk honestly: low for reads, medium for acting in the user's name, high for spending, deleting or sharing outside.
  • Put caps in limit ("Up to $400"), from what the user said. Never invent an amount.
  • Mark a scope required only if the task can't run without it.
  • Offer durations that fit the work: once and session by default, always only for low-risk, recurring work.
  • After a grant, use only the scopes returned, for the duration returned. After a deny, don't retry; ask what the user wants instead.

Interaction

  • Each scope is a checkbox row. Low- and medium-risk scopes start ticked, high-risk ones start unticked (override with granted). Required scopes stay ticked.
  • Each row ends with its risk as a badge: Low blue, Medium orange, High red.
  • Ticking a high-risk scope opens a red warning note under the list (an icon, the scope's name, and one plain sentence on what it means for the chosen duration).
  • The duration is a radio group of pills; a one-line summary under the list recomposes as the selection changes.
  • Allow selected sends the ticked scope ids; it's off while nothing is ticked. Deny all sends a deny. Either one locks the card, and focus moves to the decision record.
  • While streaming, scopes arrive one by one and the controls are disabled. Changes are announced to screen readers.

Installation

npx shadcn@latest add https://your-site.com/r/permission-scope.json

Usage

app/api/chat/route.ts
import { tool } from "ai"
import { permissionScopeTool } from "@/components/genui/permission-scope.schema"

const tools = {
  [permissionScopeTool.name]: tool({
    description: permissionScopeTool.description,
    inputSchema: permissionScopeTool.inputSchema,
  }),
}
components/message.tsx
import { PermissionScope } from "@/components/genui/permission-scope"

function ToolPart({ part, addToolResult }) {
  if (part.type !== "tool-request_permissions") return null

  return (
    <PermissionScope
      {...part.input}
      status={part.state === "input-streaming" ? "streaming" : undefined}
      result={part.state === "output-available" ? part.output : undefined}
      onResult={(result) =>
        addToolResult({
          tool: "request_permissions",
          toolCallId: part.toolCallId,
          output: result,
        })
      }
    />
  )
}

The result tells the agent what it may use, and for how long:

{ action: "grant", scopes: ["calendar-read", "contacts", "email-send"], duration: "once" }
// or
{ action: "deny" }

Schema

PropTypeNotes
questionstring?Shown above the card
titlestringThe task, up to 60 characters
purposestring?One line on why, up to 140 characters
scopes{ id, label, detail?, risk, limit?, required?, granted? }[]1–8, lowest risk first. risk is low, medium or high. limit is data-bound
durations("once" | "session" | "always")[]Choices offered. Default ["once", "session"]
durationstring?Starting duration. Default the first offered

Host props: status (streaming, ready, granted, denied), onResult, result and className.

Source

On this page