How to Build a Claude Ask User Question Tool
What "Claude Ask User Question Tool" Actually Means
If you're building an agent with Claude and it needs to stop mid-task to ask the human a clarifying question, you're looking for a pattern, not a built-in feature. Claude doesn't have a native "ask_user" button you flip on. Instead, you define a custom tool — commonly named ask_user_question — that Claude calls whenever it needs input it doesn't have, and your application code pauses the agent loop, shows the question to the user, and feeds the answer back as a tool result.
This is one of the most requested patterns for agentic workflows: a coding assistant that isn't sure which file to modify, a form-filling bot that hits an ambiguous field, a support agent that needs the customer's account ID. The rest of this article shows exactly how to implement it, including the tool schema, the loop logic, and the pitfalls people hit in production.
Why You Need a Custom Tool for This
Claude's tool use system lets you register any function-like capability with a JSON schema, and Claude decides when to call it based on the conversation. There's no reserved "ask the user" tool because Anthropic can't know how you want that question surfaced — Slack message, web modal, CLI prompt, SMS. So you define it yourself, exactly like you'd define get_weather or create_ticket.
The core idea: give Claude a tool whose entire purpose is to stop and request clarification, then treat its invocation as a signal to your application layer rather than something to auto-answer.
Defining the Tool Schema
Here's a minimal schema that works well in practice:
{
"name": "ask_user_question",
"description": "Ask the human user a clarifying question when the task cannot proceed without more information. Use only when necessary — do not ask about things you can reasonably infer.",
"input_schema": {
"type": "object",
"properties": {
"question": {
"type": "string",
"description": "The question to show the user, phrased clearly and concisely."
},
"options": {
"type": "array",
"items": { "type": "string" },
"description": "Optional list of suggested answers, if the question is multiple choice."
},
"urgency": {
"type": "string",
"enum": ["blocking", "optional"],
"description": "Whether the task cannot continue without an answer."
}
},
"required": ["question"]
}
}
The description field is doing real work here. Claude decides when to call a tool largely based on how you describe it, so be explicit about the boundary: ask only when genuinely blocked, not for every minor ambiguity. Without that guardrail, agents tend to over-ask and interrupt flows unnecessarily.
The Request/Pause/Resume Loop
The mechanics are the same as any tool use workflow: Claude returns a tool_use content block, you execute the tool (in this case, "execute" means show the user a prompt and wait), and you send the answer back as a tool_result in the next message.
const response = await fetch("https://api.subtoapi.app/v1/messages", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.SUBTOAPI_KEY}`,
"content-type": "application/json"
},
body: JSON.stringify({
model: "claude-sonnet-4-5",
max_tokens: 1024,
tools: [askUserQuestionTool],
messages: [
{ role: "user", content: "Rename my project files to match the new naming convention." }
]
})
});
const data = await response.json();
const toolUse = data.content.find(b => b.type === "tool_use");
if (toolUse?.name === "ask_user_question") {
// Pause the loop here — show toolUse.input.question to a real human
const userAnswer = await showQuestionToHuman(toolUse.input);
// Continue the conversation with the answer
const followUp = await fetch("https://api.subtoapi.app/v1/messages", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.SUBTOAPI_KEY}`,
"content-type": "application/json"
},
body: JSON.stringify({
model: "claude-sonnet-4-5",
max_tokens: 1024,
tools: [askUserQuestionTool],
messages: [
{ role: "user", content: "Rename my project files to match the new naming convention." },
{ role: "assistant", content: data.content },
{
role: "user",
content: [{
type: "tool_result",
tool_use_id: toolUse.id,
content: userAnswer
}]
}
]
})
});
}
The important detail: your application is the one deciding what "asking the user" means. It could be a synchronous CLI prompt, an async Slack message with a webhook that resumes the conversation hours later, or a form field in a web UI. Claude just knows it called a tool and is waiting for a result — the conversation state doesn't expire while you wait.
Handling Multi-Turn Clarification
Sometimes one question isn't enough. Claude might ask a follow-up after seeing the first answer, especially with options provided but no clear match. Keep looping on the same pattern: check if the next response contains another ask_user_question tool_use block before assuming the task is done. A simple state machine works fine:
while (true) {
const toolUse = latestResponse.content.find(b => b.type === "tool_use");
if (!toolUse) break; // Claude finished without needing more input
if (toolUse.name === "ask_user_question") {
const answer = await promptUser(toolUse.input.question);
latestResponse = await sendToolResult(toolUse.id, answer);
} else {
// handle other tools
}
}
Cap the number of clarification rounds (three or four is reasonable) so a poorly-scoped task doesn't spiral into an endless interrogation.
Streaming Considerations
If your UI shows Claude "thinking" in real time, you still want the question to appear as soon as it's ready rather than waiting for the full response to buffer. Server-sent events via streaming let you detect the tool_use block as it completes and surface the question immediately, which matters a lot for perceived responsiveness in chat-style interfaces.
Where SubToAPI Fits
If you're already building this pattern against the Claude API, SubToAPI gives you an HTTPS endpoint (sub_live_... keys) that supports tool use, streaming, and usage metadata without managing separate provider credentials for each team member. It's useful once you have multiple developers or services calling the same agent loop and want centralized keys and usage visibility rather than sharing one raw API key. Check the quickstart and pricing if that's relevant to your setup.
questions
Does Claude have a built-in "ask user" feature? No. There's no reserved tool name for this. You define a custom tool (commonly ask_user_question) with a JSON schema, and Claude calls it like any other tool when it needs clarification.
How do I stop Claude from asking too many questions? Write a tight tool description that instructs Claude to ask only when genuinely blocked, cap the number of clarification rounds in your loop, and prefer giving Claude enough context upfront to reduce ambiguity.
Can the conversation pause indefinitely while waiting for a user answer? Yes. Claude's tool_use/tool_result pattern is stateless between calls — you can wait seconds or hours before sending the tool_result back, as long as you preserve the full message history.