Human in the loop
Add human in the loop approvals to your AI agent with Pushary's ask_user MCP tool. Ask for a yes or no, a choice or text, and handle unanswered questions.
Human in the loop means your AI agent stops and asks you before it continues. With Pushary, the agent calls the ask_user MCP tool, you answer on your phone, and the agent reads your answer from the same call. This page covers an agent asking you, the workspace owner. If your product needs approval from your customers, use the Partner integration and its durable Decisions API.
Connect your agent and your phone first:
npx pushary@latest setupSetup installs the MCP connection and agent instructions, plus hooks where the agent supports them. MCP gives the agent tools it can call. Approval before other tool calls is enforced only through the agent's hook integration and your permission policies.
Ask a question and read the answer
ask_user waits by default. It can return the answer in the first call, with no second call needed.
{
"tool": "ask_user",
"arguments": {
"question": "Deploy this release to staging?",
"type": "confirm",
"agentName": "Codex - my-project",
"toolName": "Bash",
"toolTarget": "deploy staging"
}
}An answered question returns:
{
"correlationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"question": "Deploy this release to staging?",
"type": "confirm",
"status": "answered",
"answered": true,
"value": "yes"
}For a confirm question, proceed only on answered: true with value: "yes". answered: true with value: "no" is a refusal. An unanswered question never becomes an approval.
| Question type | You send | The answer |
|---|---|---|
confirm | A yes or no question | "yes" or "no" |
select | A question and 2 to 6 options | The chosen option string |
input | A question and an optional placeholder | The text the user typed |
Use context for background, agentName to name the project, and toolName and toolTarget for a tool approval. Ask only when you need the user's input. A notification on its own does not approve an action.
Handle a question nobody answered
Read handoffAction first when it is present. Otherwise follow nextAction. An unanswered result can mean a live timeout, a delivery mode handoff, or a final state.
- If the result has
nextAction: "wait_for_answer", keep polling with the samecorrelationIdandtimeoutMs: 55000. - Only for an explicit handoff, cancel the phone question before asking in the current chat.
- If cancellation returns
handoffAction: "stop", stop. Otherwise, if cancellation returns false, poll once for 1 second and honor any answer that won the race. - Ask in the current client only after the phone question is safely closed and no answer won the race.
{
"tool": "wait_for_answer",
"arguments": {
"correlationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"timeoutMs": 55000
}
}Only pending is a live unanswered state. cancelled, expired, missing and unavailable are not timeouts. Never turn a stop directive into another approval request.
While the agent waits, show the returned answerUrl so the user can open the question in the dashboard. For the full handoff contract, see When you don't answer.
Where you answer: delivery modes
| Mode | What happens |
|---|---|
When away (push_first) | Uses presence to decide whether to send the phone push, and waits for the push-first window. |
Always (push_only) | Sends to the phone and waits for the policy timeout. |
Alert only (notify_only) | Sends an alert only. The approval belongs in the current client. |
In agent (terminal_only) | Keeps the decision in the current client, with no phone push. |
In Claude Code, a command your own Claude Code allow rules already cover is left to Claude Code in every mode. The exceptions are a command that a Pushary rule or preset you chose names, and one that looks risky. Pushary records no decision for it.
Always read the returned directives. One call waits at most 55 seconds. Ordinary full-access MCP questions without a host deadline stay answerable until answered or cancelled, with one phone reminder after ten minutes when delivery settings allow it. Restricted connectors and native permission hooks retain their existing deadlines. The end of a wait is not consent.
Set wait: false only if your code will collect the answer later. The tools reference lists every parameter.
Cancel a question you no longer need
Cancel when the action is abandoned or replaced, or the user answers in chat:
{
"tool": "cancel_question",
"arguments": {
"correlationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"handoff": true
}
}Use handoff: true when you move the same question into the current client. Apply the race handling above before you act on a chat answer.
Agree on a run's scope
For a run that touches several files, propose_scope records the allowed paths, the off-limits paths and the definition of done under a session ID. Only ratified: true creates the contract on the server.
Scope narrows the file boundary. It does not approve risky actions inside it. Hook enforcement needs a supported agent with @pushary/agent-hooks 0.59.0 or later. Shell commands stay governed by your permission policy. See propose_scope.
Next steps
See what happens when you don't answer
The wait ladder for each approval mode, and how to tune the window.
Set your permission policies
Choose which tools ask you, how long they wait, and what happens next.
Read the tools reference
Every argument and return field for ask_user, wait_for_answer and cancel_question.