Merge branch 'feature/mcp-ui' into bot_manager

This commit is contained in:
朱潮 2026-05-15 19:25:56 +08:00
commit 6d7bcd62cd
3 changed files with 60 additions and 4 deletions

View File

@ -140,12 +140,14 @@ async def enhanced_generate_stream_response(
elif isinstance(msg, ToolMessage) and msg.content:
message_tag = "TOOL_RESPONSE"
waiting_for_answer_first_char = False
# Always output mcp-ui UIResource responses even when tool_response is disabled
# Always output UIResource and ask_user responses even when tool_response is disabled
is_ui_resource = (
msg.text
and msg.text.lstrip().startswith('{"type"')
and '"ui://' in msg.text
and '"text/html' in msg.text
and msg.text.lstrip().startswith('{"')
and (
('"ui://' in msg.text and '"text/html' in msg.text)
or '"__ask_user__"' in msg.text
)
)
if config.tool_response or is_ui_resource:
new_content = f"[{message_tag}] {msg.name}\n{msg.text}\n"

View File

@ -26,5 +26,26 @@
},
"required": ["title", "html_content"]
}
},
{
"name": "ask_user",
"description": "Ask the user a question and present options for them to choose from. Use this tool when you need user input to proceed, such as clarifying requirements, choosing between alternatives, or confirming an action. The question will be displayed at the end of your response with clickable option buttons.",
"inputSchema": {
"type": "object",
"properties": {
"question": {
"type": "string",
"description": "The question to ask the user"
},
"options": {
"type": "array",
"items": {
"type": "string"
},
"description": "List of options for the user to choose from. If empty, a free text input will be shown instead."
}
},
"required": ["question"]
}
}
]

View File

@ -21,6 +21,27 @@ from mcp_common import (
)
ASK_USER_MARKER = "__ask_user__"
def ask_user(question: str, options: list = None) -> Dict[str, Any]:
"""Create an ask_user response.
Returns a JSON structure with a marker so the backend can detect it
and emit it as a special delta.ask_user event at the end of the stream.
"""
payload = {
"__type__": ASK_USER_MARKER,
"question": question,
"options": options or [],
}
return {
"content": [
{"type": "text", "text": json.dumps(payload, ensure_ascii=False)}
]
}
def render_ui(
title: str, html_content: str, width: str = "100%", height: str = "400px"
) -> Dict[str, Any]:
@ -115,6 +136,18 @@ async def handle_request(request: Dict[str, Any]) -> Dict[str, Any]:
result = render_ui(title, html_content, width, height)
return {"jsonrpc": "2.0", "id": request_id, "result": result}
elif tool_name == "ask_user":
question = arguments.get("question", "")
options = arguments.get("options", [])
if not question:
return create_error_response(
request_id, -32602, "Missing required parameter: question"
)
result = ask_user(question, options)
return {"jsonrpc": "2.0", "id": request_id, "result": result}
else:
return create_error_response(
request_id, -32601, f"Unknown tool: {tool_name}"