feat: v1/response接口无法传入mcp_servers的问题#1012
Conversation
📝 WalkthroughWalkthroughThis PR adds support for user-provided MCP (Model Context Protocol) servers to the chat execution flow. The API endpoint now parses user MCP servers from tool settings, converts them to list format, enables tools when servers are present, and passes them through to the execution request builder, which merges them with system/bot servers using user-defined precedence. Changes
Sequence DiagramsequenceDiagram
participant API as API Endpoint
participant Trigger as Chat Trigger Service
participant Builder as Request Builder
API->>API: Parse user MCP servers<br/>from tool_settings
API->>API: Convert dict to list format<br/>with name & config
API->>API: Enable tools if MCP<br/>servers non-empty
API->>Trigger: build_execution_request<br/>(mcp_servers=list)
Trigger->>Builder: TaskRequestBuilder.build<br/>(user_mcp_servers=list)
Builder->>Builder: Merge user + bot + system<br/>servers
Builder->>Builder: Transform headers→auth<br/>format
Builder->>Builder: Deduplicate by name<br/>apply precedence
Builder->>Builder: Return merged<br/>MCP servers
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
backend/app/api/endpoints/openapi_responses.py (1)
393-406: Centralize MCP server normalization and add strict validation.Both code paths duplicate this logic (lines 393-406 and 712-725) and silently skip malformed server configs. The upstream
parse_wegent_tools()already filters and normalizesmcp_serversto a dict-of-dicts structure, but the filtering logic repeats at the endpoint level with the same silent skip. If a user provides a non-dict server config, it is silently dropped at both layers without error, causing tools to be silently disabled.Either remove the redundant isinstance check at the endpoint level, or make
parse_wegent_tools()raise a 400 error for malformed input instead of filtering silently. The suggested helper function approach is reasonable to avoid drift between streaming and non-streaming paths.♻️ Suggested helper to avoid drift between streaming and non-streaming paths
+def _normalize_mcp_servers(raw_mcp_servers: Any) -> list[dict[str, Any]]: + """Normalize user-provided MCP servers from tool settings.""" + if not raw_mcp_servers: + return [] + + if not isinstance(raw_mcp_servers, dict): + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="tools.mcp_servers must be an object keyed by server name", + ) + + normalized_servers: list[dict[str, Any]] = [] + for name, config in raw_mcp_servers.items(): + if not isinstance(config, dict): + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail=f"tools.mcp_servers.{name} must be an object", + ) + normalized_servers.append({**config, "name": name}) + + return normalized_serversThen replace both duplicated blocks with:
- user_mcp_servers = tool_settings.get("mcp_servers", {}) - # Convert dict format to list format for build_execution_request - mcp_servers_list = [] - if user_mcp_servers: - for name, config in user_mcp_servers.items(): - if isinstance(config, dict): - mcp_servers_list.append({"name": name, **config}) + mcp_servers_list = _normalize_mcp_servers(tool_settings.get("mcp_servers"))Also applies to: 712-725
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@backend/app/api/endpoints/openapi_responses.py` around lines 393 - 406, Centralize MCP server normalization and strict validation by moving the dict-of-dicts enforcement into a single helper (e.g., normalize_mcp_servers) or into parse_wegent_tools so both code paths reuse it instead of duplicating the loop that builds mcp_servers_list from user_mcp_servers; update parse_wegent_tools (or the new helper) to raise a 400 HTTP error when any server config value is not a dict rather than silently dropping it, and replace the duplicated blocks that create mcp_servers_list and the isinstance checks with a call to that helper; ensure enable_tools logic continues to reference the normalized list (mcp_servers_list) and that error raising uses the same HTTPException pattern as other input validation in this module.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@backend/app/services/execution/request_builder.py`:
- Around line 1635-1655: The normalization currently forces server_entry["type"]
= server_config.get("type", "streamable-http") and overwrites/ignores
server_config["auth"]; update the user_mcp_servers loop (the code building
server_entry and appending to user_mcp_list) so that: 1) if server_config
contains "type" use it, else if it contains "transport" use that value as the
type fallback, else default to "streamable-http"; 2) preserve
server_config["auth"] if present (do not replace it), and only convert "headers"
into "auth" when "auth" is absent; keep the existing handling for "command",
"args", and "env" and continue appending server_entry to user_mcp_list.
---
Nitpick comments:
In `@backend/app/api/endpoints/openapi_responses.py`:
- Around line 393-406: Centralize MCP server normalization and strict validation
by moving the dict-of-dicts enforcement into a single helper (e.g.,
normalize_mcp_servers) or into parse_wegent_tools so both code paths reuse it
instead of duplicating the loop that builds mcp_servers_list from
user_mcp_servers; update parse_wegent_tools (or the new helper) to raise a 400
HTTP error when any server config value is not a dict rather than silently
dropping it, and replace the duplicated blocks that create mcp_servers_list and
the isinstance checks with a call to that helper; ensure enable_tools logic
continues to reference the normalized list (mcp_servers_list) and that error
raising uses the same HTTPException pattern as other input validation in this
module.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 6b40cbeb-108a-4327-adc4-3103e31d5fa7
📒 Files selected for processing (3)
backend/app/api/endpoints/openapi_responses.pybackend/app/services/chat/trigger/unified.pybackend/app/services/execution/request_builder.py
| # Process user-provided MCP servers from API request | ||
| user_mcp_list = [] | ||
| if user_mcp_servers: | ||
| for server_config in user_mcp_servers: | ||
| if isinstance(server_config, dict): | ||
| server_entry = { | ||
| "name": server_config.get("name", "user-server"), | ||
| "url": server_config.get("url", ""), | ||
| "type": server_config.get("type", "streamable-http"), | ||
| } | ||
| # Convert "headers" to "auth" for chat_shell compatibility | ||
| if "headers" in server_config: | ||
| server_entry["auth"] = server_config["headers"] | ||
| # Include stdio-specific fields (command, args, env) | ||
| if "command" in server_config: | ||
| server_entry["command"] = server_config["command"] | ||
| if "args" in server_config: | ||
| server_entry["args"] = server_config["args"] | ||
| if "env" in server_config: | ||
| server_entry["env"] = server_config["env"] | ||
| user_mcp_list.append(server_entry) |
There was a problem hiding this comment.
Preserve user MCP transport and auth fields during normalization.
The system MCP loader accepts transport as a fallback for type, but user-provided MCP servers ignore it and default to streamable-http. A user config using transport: "stdio" would be emitted as HTTP even though command/args are present. Also preserve already-normalized auth when callers provide it directly.
🐛 Proposed normalization fix
server_entry = {
"name": server_config.get("name", "user-server"),
"url": server_config.get("url", ""),
- "type": server_config.get("type", "streamable-http"),
+ "type": server_config.get(
+ "type",
+ server_config.get("transport", "streamable-http"),
+ ),
}
# Convert "headers" to "auth" for chat_shell compatibility
if "headers" in server_config:
server_entry["auth"] = server_config["headers"]
+ elif "auth" in server_config:
+ server_entry["auth"] = server_config["auth"]🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@backend/app/services/execution/request_builder.py` around lines 1635 - 1655,
The normalization currently forces server_entry["type"] =
server_config.get("type", "streamable-http") and overwrites/ignores
server_config["auth"]; update the user_mcp_servers loop (the code building
server_entry and appending to user_mcp_list) so that: 1) if server_config
contains "type" use it, else if it contains "transport" use that value as the
type fallback, else default to "streamable-http"; 2) preserve
server_config["auth"] if present (do not replace it), and only convert "headers"
into "auth" when "auth" is absent; keep the existing handling for "command",
"args", and "env" and continue appending server_entry to user_mcp_list.
Summary by CodeRabbit