Merge branch 'feature/mcp-ui' into bot_manager
This commit is contained in:
commit
333bef1289
@ -144,16 +144,13 @@ async def enhanced_generate_stream_response(
|
|||||||
elif isinstance(msg, ToolMessage) and msg.content:
|
elif isinstance(msg, ToolMessage) and msg.content:
|
||||||
message_tag = "TOOL_RESPONSE"
|
message_tag = "TOOL_RESPONSE"
|
||||||
waiting_for_answer_first_char = False
|
waiting_for_answer_first_char = False
|
||||||
# Always output UIResource, ask_user and render_ui responses even when tool_response is disabled
|
# Always output UIResource responses even when tool_response is disabled
|
||||||
is_ui_resource = (
|
is_ui_resource = (
|
||||||
msg.text
|
msg.text
|
||||||
and msg.text.lstrip().startswith('{"')
|
and msg.text.lstrip().startswith('{"')
|
||||||
and '"ui://' in msg.text
|
and '"ui://' in msg.text
|
||||||
and ('"text/html' in msg.text or '"text/uri-list' in msg.text)
|
|
||||||
)
|
)
|
||||||
is_ask_user = msg.name == 'ask_user'
|
if config.tool_response or is_ui_resource:
|
||||||
is_render_ui = msg.name == 'render_ui'
|
|
||||||
if config.tool_response or is_ui_resource or is_ask_user or is_render_ui:
|
|
||||||
new_content = f"[{message_tag}] {msg.name}\n{msg.text}\n"
|
new_content = f"[{message_tag}] {msg.name}\n{msg.text}\n"
|
||||||
|
|
||||||
# Collect full content
|
# Collect full content
|
||||||
|
|||||||
@ -8,6 +8,8 @@ import json
|
|||||||
import sys
|
import sys
|
||||||
from typing import Any, Dict
|
from typing import Any, Dict
|
||||||
|
|
||||||
|
from mcp_ui_server import create_ui_resource, UIMetadataKey
|
||||||
|
|
||||||
from mcp_common import (
|
from mcp_common import (
|
||||||
create_error_response,
|
create_error_response,
|
||||||
create_initialize_response,
|
create_initialize_response,
|
||||||
@ -18,40 +20,65 @@ from mcp_common import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
ASK_USER_RESPONSE = "Questions sent to user."
|
def _serialize_ui_resource(ui_resource) -> str:
|
||||||
|
"""Serialize a UIResource to JSON string."""
|
||||||
|
return json.dumps(ui_resource.model_dump(mode="json"), ensure_ascii=False)
|
||||||
|
|
||||||
|
|
||||||
def ask_user() -> Dict[str, Any]:
|
def ask_user() -> Dict[str, Any]:
|
||||||
"""Return a minimal fixed response for ask_user tool.
|
"""Return a UIResource response for ask_user tool.
|
||||||
|
|
||||||
The actual questions/options are already in the TOOL_CALL arguments,
|
The actual questions are in the TOOL_CALL arguments. The frontend
|
||||||
so the frontend parses them directly from there. This response only
|
detects ui_type from the UIResource metadata and extracts content
|
||||||
serves to acknowledge the tool call and minimize token usage in the
|
from the corresponding TOOL_CALL args.
|
||||||
subsequent LLM inference round.
|
|
||||||
"""
|
"""
|
||||||
return {
|
resource = create_ui_resource({
|
||||||
"content": [
|
"uri": "ui://mcp-ui/ask-user",
|
||||||
{"type": "text", "text": ASK_USER_RESPONSE}
|
"content": {"type": "rawHtml", "htmlString": "Questions sent to user."},
|
||||||
]
|
"encoding": "text",
|
||||||
}
|
"uiMetadata": {
|
||||||
|
"type": "ask_user",
|
||||||
|
"interactive": True,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return {"content": [{"type": "text", "text": _serialize_ui_resource(resource)}]}
|
||||||
|
|
||||||
|
|
||||||
RENDER_UI_RESPONSE = "UI rendered."
|
def render_ui(arguments: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
"""Return a UIResource response for render_ui tool.
|
||||||
|
|
||||||
|
The actual html_content/url is in the TOOL_CALL arguments. The frontend
|
||||||
def render_ui() -> Dict[str, Any]:
|
detects ui_type from the UIResource metadata and extracts content
|
||||||
"""Return a minimal fixed response for render_ui tool.
|
from the corresponding TOOL_CALL args.
|
||||||
|
|
||||||
The actual html_content/url is already in the TOOL_CALL arguments,
|
|
||||||
so the frontend parses them directly from there. This response only
|
|
||||||
serves to acknowledge the tool call and minimize token usage in the
|
|
||||||
subsequent LLM inference round.
|
|
||||||
"""
|
"""
|
||||||
return {
|
html_content = arguments.get("html_content", "")
|
||||||
"content": [
|
url = arguments.get("url", "")
|
||||||
{"type": "text", "text": RENDER_UI_RESPONSE}
|
width = arguments.get("width", "100%")
|
||||||
]
|
height = arguments.get("height", "auto")
|
||||||
}
|
|
||||||
|
if html_content:
|
||||||
|
resource = create_ui_resource({
|
||||||
|
"uri": "ui://mcp-ui/render-ui",
|
||||||
|
"content": {"type": "rawHtml", "htmlString": "UI rendered."},
|
||||||
|
"encoding": "text",
|
||||||
|
"uiMetadata": {
|
||||||
|
UIMetadataKey.PREFERRED_FRAME_SIZE: [width, height],
|
||||||
|
"type": "render_ui_html",
|
||||||
|
"interactive": False,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
resource = create_ui_resource({
|
||||||
|
"uri": "ui://mcp-ui/render-ui",
|
||||||
|
"content": {"type": "externalUrl", "iframeUrl": url},
|
||||||
|
"encoding": "text",
|
||||||
|
"uiMetadata": {
|
||||||
|
UIMetadataKey.PREFERRED_FRAME_SIZE: [width, height],
|
||||||
|
"type": "render_ui_url",
|
||||||
|
"interactive": False,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return {"content": [{"type": "text", "text": _serialize_ui_resource(resource)}]}
|
||||||
|
|
||||||
|
|
||||||
async def handle_request(request: Dict[str, Any]) -> Dict[str, Any]:
|
async def handle_request(request: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
@ -109,7 +136,7 @@ async def handle_request(request: Dict[str, Any]) -> Dict[str, Any]:
|
|||||||
request_id, -32602, "Missing required parameter: html_content or url"
|
request_id, -32602, "Missing required parameter: html_content or url"
|
||||||
)
|
)
|
||||||
|
|
||||||
result = render_ui()
|
result = render_ui(arguments)
|
||||||
return {"jsonrpc": "2.0", "id": request_id, "result": result}
|
return {"jsonrpc": "2.0", "id": request_id, "result": result}
|
||||||
|
|
||||||
elif tool_name == "ask_user":
|
elif tool_name == "ask_user":
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user