Ask User a Question
ask_userAsk the user a question as a push notification on their phone and block until they answer. Reach for this whenever you need the user's decision and they may be away from the current client: approving a risky or irreversible step (deleting files, force pushing, spending money, sending external messages), picking between implementation options, or supplying missing input. The user answers from the lock screen or a decision page; you do not need a separate wait_for_answer call because this tool waits by default. Three question types: "confirm" (yes/no), "select" (2 to 6 fixed choices), "input" (free text). A single call blocks for at most 55 seconds. If a live question times out, nextAction is "wait_for_answer": poll once with timeoutMs 55000. If that poll is also unanswered, cancel the phone question before asking in the current chat or client. If cancellation returns handoffAction "stop", stop. Otherwise, if cancellation returns false, poll once for 1 second and honor the answer that won the race. Cancelled, expired, and missing questions are reported as terminal states rather than as timeouts. Every response carries answerUrl, the signed-in dashboard page where this question is waiting. When you report that you are waiting, print that URL to the user so they can answer from a browser instead of hunting for it. Works from Claude Code, Codex, Cursor, Hermes, or any MCP client; no Claude subscription is required. SIDE EFFECT: sends a real push notification.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| env | No | Set to "test" from a test suite. The question is stored and returned as pending, and nothing is delivered to a phone, browser or Slack. The X-Pushary-Env: test header does the same for every call on the connection. | |
| tags | No | Deliver only to subscribers that have any of these tags. | |
| type | No | Question type: confirm renders yes/no buttons, select renders the options list, input renders a free-text field. | confirm |
| wait | No | true (default) blocks until the user answers or the timeout fires. Set false to return immediately with a pending correlationId and poll it yourself via wait_for_answer. | |
| action | No | The concrete operation about to happen, one line. Shown as the Action line. | |
| intent | No | The user's stated task (from their last prompt), one line. Shown as the Intent line so the user can see why the agent stopped. | |
| blocker | No | The single gating reason the agent stopped, one line. Shown as the Blocker line. | |
| context | No | One or two sentences about what the agent is working on, shown above the question so the user can decide without opening the terminal. | |
| options | No | The 2 to 6 choices for a select question. Required when type is "select", ignored otherwise. The answered value is the chosen option string. | |
| repoKey | No | Stable repository identity for the working directory, e.g. "github.com/acme/api". Lets an approval routing rule scoped to one repository avoid governing another. Optional; omit it and only workspace-wide routing rules apply. | |
| question | Yes | The question shown on the user's lock screen (max 500 chars). Phrase it so it is answerable at a glance; put background in context instead. | |
| toolName | No | The tool this approval is for (e.g. "Bash"), so the user can choose to always-allow it. | |
| agentName | No | Name of the agent asking, format "{Agent} - {project}" (e.g. "Claude Code - myproject"). Shown in the notification title so the user knows which session needs them. Falls back to the MCP client name if omitted. | |
| machineId | No | Stable machine id of the asking agent, so two machines never collapse into one session. | |
| questions | No | ONE question, in the richer Claude-compatible shape: a header, per-option descriptions, multiSelect, and an optional write-in. Exactly one keeps already-installed clients answerable; asking several means several calls. Runtime-populated; ordinary callers should omit it and use question/type/options. | |
| requestId | No | MACHINE-POPULATED. The CALLER's own identifier for one logical invocation, used only when the runtime supplies no toolUseId. Mint it once, outside your retry loop, and send the same value on every attempt, so three retries of one ask become one decision. Do NOT derive it from the question text or reuse it across two deliberate asks: both collapse a real second question into the first one's answer. If you are a model deciding to call this tool, omit this field. | |
| scopePath | No | Set ONLY when this approval exists because the path falls outside the scope the user ratified via propose_scope. Approving then widens the run scope to include this exact path, so the user is not asked again for the same area. | |
| sessionId | No | Opaque per-session id of the asking agent, so parallel sessions are attributed separately. | |
| timeoutMs | No | How long this call blocks, in milliseconds (max 55000). Defaults to the site policy timeout. The question stays open for 10 minutes regardless, so a timeout here is not a refusal; follow up with wait_for_answer. | |
| toolUseId | No | MACHINE-POPULATED. The agent RUNTIME's own identifier for the tool call this approval gates, forwarded verbatim by a hook that received it. Do NOT invent, guess, derive, or reuse a value: two different questions sent under the same id collapse into one, and the second one never reaches a human. If you are a model deciding to call this tool, omit this field. | |
| actionBody | No | The diff (Edit/Write) or full command (Bash/apply_patch), secret-redacted and size-capped. Rendered as a collapsible detail block; never used as the push body. | |
| toolTarget | No | Compact target of the tool call (e.g. the command head "git push" for Bash, or a file extension like ".ts" for Edit/Write). Used to mine policy suggestions. | |
| waitEndsAt | No | MACHINE-POPULATED. When the agent hook stops waiting live and hands control back to the terminal. The question may remain answerable after this time. Ordinary callers should omit it. | |
| callbackUrl | No | Webhook URL that receives a POST with the answer when the user responds, signed with the X-Pushary-Signature header. Useful when the agent process may exit before the answer arrives. | |
| externalIds | No | Deliver only to subscribers matching these external IDs. | |
| placeholder | No | Hint text shown inside the free-text field for input questions | |
| subscriberIds | No | Deliver only to these subscriber IDs. Omit all targeting fields to reach every connected device. |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| env | No | Echoed when the call was test traffic. | |
| held | No | Present when the question was stored but deliberately not delivered: test traffic, or a permission ask with no toolName and no sessionId. | |
| hint | No | What to do next, when there is a next step. | |
| mode | No | The site delivery mode that stopped this call from waiting. | |
| note | No | Free text the user added alongside their answer. | |
| type | Yes | The question type that was rendered. | |
| value | No | The user's answer: "yes" or "no" for confirm, the chosen option for select, the typed text for input. | |
| status | No | The question state. Only pending is a live unanswered wait; cancelled, expired, missing, and unavailable must not be described as timeouts. | |
| warning | No | Present only when no channel is connected, naming what the user has to connect. | |
| answered | No | True once the user responded. Absent on the wait:false path, where nothing was awaited. | |
| delivery | No | Per-channel reach for the push carrying this question. | |
| question | Yes | The question exactly as the user saw it. | |
| timedOut | No | True when the initial wait ended while the question was still live. Poll once with wait_for_answer, then follow handoffAction when present, otherwise nextAction. | |
| answerUrl | No | The signed-in dashboard page where this question is waiting. Print it when you tell the user you are waiting, so they can answer from a browser. | |
| noDevices | No | True when no phone, browser, or Slack channel could receive the question. Do not wait; follow handoffAction immediately. | |
| nextAction | No | Backward-compatible next step: poll once or ask in the current client. Follow handoffAction first when present. | |
| suppressed | No | True when the PHONE push was deliberately held because a terminal on this machine is active. It says nothing about the notch, the dashboard or Slack, which are unaffected and may still be showing this question. A caller with no screen of its own should treat it as the terminal's to answer; a caller that can render the question itself should keep waiting. | |
| waitEndsAt | No | When the agent hook stops waiting live. On an idempotent replay this is the original question's deadline, which the hook must reuse. | |
| correlationId | Yes | Id of the question that was created. Pass it to wait_for_answer to keep waiting, or to cancel_question to retract it. | |
| handoffAction | No | Race-safe directive for updated clients. Takes precedence over nextAction: cancel before asking in the current client, or stop the handoff. | |
| expiresInSeconds | No | How long the question stays answerable. |