Human-in-the-loop for the Claude Agent SDK
Human-in-the-loop for the Claude Agent SDK, in one config block.
One mcpServers entry gives your Claude Agent SDK agent an ask-a-human tool. Nothing else to install.
The integration
Add it to your Claude Agent SDK agent
Add the hosted Pushary MCP server to the mcpServers option of query() and allow its tools. The agent can then put a question on a phone with ask_user and hold for the reply with wait_for_answer, with nothing else to install. For agents you ship to your own users, the Partner plan adds enroll_end_user and create_decision as MCP tools, or call decisions.ask from @pushary/server inside a custom tool so each user answers about their own action.
import { query } from "@anthropic-ai/claude-agent-sdk"
for await (const message of query({
prompt: "Clean up stale feature flags. Ask before deleting anything.",
options: {
mcpServers: {
pushary: {
type: "http",
url: "https://pushary.com/api/mcp/mcp",
headers: { Authorization: `Bearer ${process.env.PUSHARY_API_KEY}` },
},
},
allowedTools: [
"mcp__pushary__ask_user",
"mcp__pushary__wait_for_answer",
"mcp__pushary__send_notification",
],
},
})) {
if (message.type === "result") console.log(message)
}Why you need it
Claude Agent SDK pauses the agent. Pushary reaches the human.
The Claude Agent SDK runs the loop, the tools, and the permissions, and it has no channel to you once you close the laptop. Its permission callbacks decide whether a tool may run, and they still have to answer that question from whatever is already in the process. The Pushary server adds ask-a-human and notify as MCP tools the model can call mid run, which turns the missing half into a configuration entry rather than code. The Python SDK works the same way through ClaudeAgentOptions(mcp_servers={...}).
What Claude Agent SDK already gives you
The canUseTool callback
The Claude Agent SDK lets you intercept a tool call before it runs. Supply a callback, and every tool the agent wants to use passes through your code first, where you return allow or deny and can rewrite the input on the way through. As an interception point it is exactly right, and it is where an approval belongs.
const result = query({
prompt,
options: {
canUseTool: async (toolName, input) => {
// your decision goes here
return { behavior: "allow", updatedInput: input }
},
},
})Where it stops
- The callback resolves in your process. It has no way to reach a person who is not at that machine.
- Whatever you return has to come back before the SDK gives up waiting, so a long human wait needs handling outside the callback.
- Allow and deny leave no trail. Nothing accumulates into a record you can query later.
Before you ship it
Things that bite on Claude Agent SDK
- Denying without a reason confuses the model
- A bare deny often makes the agent try a slightly different command to reach the same place. Return a reason with the denial so the model understands it was a policy decision rather than a malformed call, and stops rephrasing.
- Match on the input, not just the tool name
- Bash is one tool name covering git status and rm -rf. Gating on tool name alone either interrupts constantly or lets through the thing you cared about. The decision has to read the arguments.
- Subagents call tools too
- Work delegated to a subagent still runs tools. If your mental model is one callback for one linear session, the tool calls coming from delegated work can surprise you.
How it works
From ask to resume
Connect the phone once, then ask a human from the tool or step that guards the action. The waiting, the delivery, and the record are handled for you.
- 01
Add the mcpServers entry
Put the hosted Pushary server in the mcpServers option of query() with your API key as a Bearer token.
- 02
Allow the tools
Add mcp__pushary__ask_user, mcp__pushary__wait_for_answer, and mcp__pushary__send_notification to allowedTools.
- 03
Tell the agent when to ask
In the prompt or system prompt, say which actions need a human first. The model calls ask_user at that step and holds for the reply.
- 04
Reach your own users when you ship
When the agent serves your own product's users, allow the Partner-plan tools (mcp__pushary__enroll_end_user, mcp__pushary__create_decision, mcp__pushary__get_decision) so the decision goes durably to the right person, or call decisions.ask from @pushary/server inside a custom tool.
A decision is a yes or no confirm, a multiple-choice select, or a free-text input. Every one is stored with its outcome and kept in the audit log.
The Partner plan
Your agent asks your users, not just you
Ship the agent to real users and let each of them approve their own decisions from their phone, under your brand.
Your users never see a Pushary login or bill.
Your users answer on their own phones
The externalId is your own id for the end-user. Connect their phone once with a keyless one-tap link that opens the Pushary app, and every approval lands with the right person. No account and no key on their side.
Durable, fail-closed waits
The agent stays open on a durable wait backed by a decisions ledger. If nobody answers in the window you set, the decision comes back denied, so a timeout or a restart never turns into an unapproved action.
Phone-first delivery
Pushary reaches the person on the device they carry: the app first, because it is the only channel that puts Approve and Deny on a lock screen, then PWA push notifications, then Slack if you route it there. You do not build a delivery pipeline.
Signed webhooks and an audit trail
Every decision is written to a durable ledger you can query and export, and resolved decisions post a signed webhook to your callback URL. You have the record of who approved what and when.
FAQ
Questions, answered
Do I need a Pushary npm package for the Claude Agent SDK?
Which tool names do I put in allowedTools?
Does this work in the Python Claude Agent SDK?
Who answers the question?
What if nobody replies before the window closes?
Let your Claude Agent SDK agent ask a human.
Connect a user with one tap, ask a human with one call, and act on a fail-closed decision. Every decision is saved to a durable ledger you can audit.
Partner plan, $99 a month. Your users never see a Pushary login or bill.