feat: add structured questions (AskUserQuestion) to assistant chat

Add interactive multiple-choice question support to the project assistant,
allowing it to present clickable options when clarification is needed.

Backend changes:
- Add ask_user MCP tool to feature_mcp.py with input validation
- Add mcp__features__ask_user to assistant allowed tools list
- Intercept ask_user tool calls in _query_claude() to yield question messages
- Add answer WebSocket message handler in assistant_chat router
- Document ask_user tool in assistant system prompt

Frontend changes:
- Add AssistantChatQuestionMessage type and update server message union
- Add currentQuestions state and sendAnswer() to useAssistantChat hook
- Handle question WebSocket messages by attaching to last assistant message
- Render QuestionOptions component between messages and input area
- Disable text input while structured questions are active

Flow: Claude calls ask_user → backend intercepts → WebSocket question message →
frontend renders QuestionOptions → user clicks options → answer sent back →
Claude receives formatted answer and continues conversation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Auto
2026-02-06 15:26:36 +02:00
parent 46ac373748
commit 71f17c73c2
7 changed files with 171 additions and 6 deletions

View File

@@ -984,5 +984,35 @@ def feature_set_dependencies(
return json.dumps({"error": f"Failed to set dependencies: {str(e)}"})
@mcp.tool()
def ask_user(
questions: Annotated[list[dict], Field(description="List of questions to ask, each with question, header, options (list of {label, description}), and multiSelect (bool)")]
) -> str:
"""Ask the user structured questions with selectable options.
Use this when you need clarification or want to offer choices to the user.
Each question has a short header, the question text, and 2-4 clickable options.
The user's selections will be returned as your next message.
Args:
questions: List of questions, each with:
- question (str): The question to ask
- header (str): Short label (max 12 chars)
- options (list): Each with label (str) and description (str)
- multiSelect (bool): Allow multiple selections (default false)
Returns:
Acknowledgment that questions were presented to the user
"""
# Validate input
for i, q in enumerate(questions):
if not all(key in q for key in ["question", "header", "options"]):
return json.dumps({"error": f"Question at index {i} missing required fields"})
if len(q["options"]) < 2 or len(q["options"]) > 4:
return json.dumps({"error": f"Question at index {i} must have 2-4 options"})
return "Questions presented to the user. Their response will arrive as your next message."
if __name__ == "__main__":
mcp.run()