Skip to content

feat: v1/response接口无法传入mcp_servers的问题#1012

Open
Micro66 wants to merge 1 commit intowecode-ai:mainfrom
Micro66:human/yak-20260421-094518
Open

feat: v1/response接口无法传入mcp_servers的问题#1012
Micro66 wants to merge 1 commit intowecode-ai:mainfrom
Micro66:human/yak-20260421-094518

Conversation

@Micro66
Copy link
Copy Markdown
Contributor

@Micro66 Micro66 commented Apr 21, 2026

Summary by CodeRabbit

  • New Features
    • Users can now configure and provide their own MCP servers through tool settings. These servers are automatically detected and integrated into the system.
    • Tools are automatically enabled when MCP servers are configured.
    • User-provided server configurations take priority over system defaults.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 21, 2026

📝 Walkthrough

Walkthrough

This 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

Cohort / File(s) Summary
API Endpoint
backend/app/api/endpoints/openapi_responses.py
Parses user MCP servers from tool_settings["mcp_servers"], converts from dict to list format { "name": <name>, **config }, updates enable_tools logic to activate when MCP servers are present, and forwards servers to both non-streaming and streaming execution paths.
Chat Trigger Service
backend/app/services/chat/trigger/unified.py
Extends build_execution_request(...) signature with new optional mcp_servers parameter and forwards it to TaskRequestBuilder.build(...) as user_mcp_servers.
Execution Request Builder
backend/app/services/execution/request_builder.py
Updates TaskRequestBuilder.build(...) and _build_mcp_servers(...) to accept user-provided MCP servers, transforms headers to auth format, deduplicates by name, and enforces precedence order user > bot > system when conflicts occur.

Sequence Diagram

sequenceDiagram
    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
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 With whiskers twitching bright,
User servers now take flight,
Through trigger and builder they race,
Merging configs with grace,
MCP servers in their rightful place!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly addresses the core issue: enabling MCP servers to be passed through the v1/response endpoint. It clearly describes the primary change across all three modified files.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 normalizes mcp_servers to 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_servers

Then 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

📥 Commits

Reviewing files that changed from the base of the PR and between 5c62da3 and b5aea5f.

📒 Files selected for processing (3)
  • backend/app/api/endpoints/openapi_responses.py
  • backend/app/services/chat/trigger/unified.py
  • backend/app/services/execution/request_builder.py

Comment on lines +1635 to +1655
# 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)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant