Safety: stage → confirm

Worker-facing tools use a two-call gate so a human approves exactly what sends, to whom.

Worker-facing tools — Alerts, Messages, and Scheduled Reminders — reach real people's phones. The stage → confirm model makes sure a human sees exactly what will go out, and to whom, before anything is dispatched.

The two-call flow

stage_alert(text_en: "…")
  → { confirmation_token, expires_at, preview: { recipient_count, rate_limit, … } }

    ← human reviews the preview

send_alert(confirmation_token)
  → { alert_id, recipient_count }

The same pattern applies to Messages (stage_message / send_message) and Scheduled Reminders (stage_scheduled_message / schedule_message). Always show the preview to the user before calling send_* / schedule_*.

Why two calls

  1. Content can't change after approval. stage_* stores the full payload server-side; send_* takes only the token — it can't supply or alter text or targeting. What the human approved is what sends.
  2. Replay protection. The token is single-use. A retry with the same token is refused, so a looping workflow can't double-send.
  3. The recipient list stays live. The preview is a snapshot for review; recipients are re-resolved at send time from the stored filter. Workers who joined are included; those who left are excluded.

The confirmation token

  • A UUID stored server-side (mcp_pending_actions).
  • Single-use — the first matching send_* consumes it.
  • 15-minute TTL — if approval takes longer, call stage_* again for a fresh token and current preview.
  • Agent-scoped — a token from one agent can't be consumed by another.

Alert rate limit

Alerts are capped at one per hour per agent, shared across MCP, the REST API, and the email Virtual Operations Agent. If an Alert went out in the last hour, stage_alert still returns a token but its preview shows rate_limit.allowed: false with a retry window; send_alert is then refused with rate_limited until the window clears. Messages and Scheduled Reminders have no hourly cap.

Errors

Worker-facing errors surface as MCP tool errors (isError: true), not JSON ok objects:

ErrorMeaning
invalid_or_expired_tokenToken not found, expired (>15 min), already used, or from a different agent
rate_limitedThe Alert hourly cap is active
no_recipientsNo workers matched the filter at stage time — refine and re-stage
bot_not_activeThe agent is deactivated; worker-facing tools are refused
ownership_mismatchThe token's Admin doesn't own the worker question being replied to
status_mismatch: <status>The worker question isn't awaiting a reply (e.g. already replied)
not_foundThe user_admin_message_id doesn't exist on this agent

reply_to_user_message is direct

Replying to a worker question doesn't use stage → confirm. It takes the user_admin_message_id (from list_pending_user_questions) and sends directly — but the server still re-checks that the replying Admin owns the question and that it's still awaiting a reply before dispatching.

Related


Did this page help you?