From 8fca56cf434bf0a70cc6b195b5a6074c7ee22eb4 Mon Sep 17 00:00:00 2001 From: Ryker_Feng <90562015+18062706139fcz@users.noreply.github.com> Date: Wed, 3 Jun 2026 18:11:38 +0800 Subject: [PATCH] fix(mcp): accept transport field as alias for type (#3238) (#3243) The official MCP configuration schema uses `transport` to specify the transport mechanism (stdio/sse/http), but `McpServerConfig` only honored `type` and defaulted to `stdio`. Remote MCP servers configured with just `transport: sse` were therefore misidentified as stdio and failed with "with stdio transport requires 'command' field". Add a model validator that promotes `transport` to `type` when only `transport` is provided, while keeping `type` authoritative when both are set. This matches the MCP-spec field name without breaking existing configurations. Fixes #3238 --- .../deerflow/config/extensions_config.py | 20 ++++++++++- backend/tests/test_mcp_client_config.py | 35 +++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/backend/packages/harness/deerflow/config/extensions_config.py b/backend/packages/harness/deerflow/config/extensions_config.py index 425da12b8..18b8cd5e2 100644 --- a/backend/packages/harness/deerflow/config/extensions_config.py +++ b/backend/packages/harness/deerflow/config/extensions_config.py @@ -5,7 +5,7 @@ import os from pathlib import Path from typing import Any, Literal -from pydantic import BaseModel, ConfigDict, Field +from pydantic import BaseModel, ConfigDict, Field, model_validator from deerflow.config.runtime_paths import existing_project_file @@ -47,6 +47,24 @@ class McpServerConfig(BaseModel): description: str = Field(default="", description="Human-readable description of what this MCP server provides") model_config = ConfigDict(extra="allow") + @model_validator(mode="before") + @classmethod + def _accept_transport_alias(cls, data: Any) -> Any: + """Accept the MCP-spec ``transport`` field as an alias for ``type``. + + The official MCP configuration schema uses ``transport`` to indicate + the transport mechanism (``stdio``/``sse``/``http``). Earlier versions + of this project only honored ``type``, which caused remote SSE/HTTP + servers configured with just ``transport`` to be incorrectly treated as + ``stdio`` (the default). This validator normalizes the two so either + spelling works, with ``type`` taking precedence when both are provided. + """ + if isinstance(data, dict): + transport = data.get("transport") + if transport and not data.get("type"): + data = {**data, "type": transport} + return data + class SkillStateConfig(BaseModel): """Configuration for a single skill's state.""" diff --git a/backend/tests/test_mcp_client_config.py b/backend/tests/test_mcp_client_config.py index ca4d0de59..3216b59fa 100644 --- a/backend/tests/test_mcp_client_config.py +++ b/backend/tests/test_mcp_client_config.py @@ -83,6 +83,41 @@ def test_build_server_params_rejects_unsupported_transport(): build_server_params("bad-transport", config) +@pytest.mark.parametrize("transport", ["sse", "http"]) +def test_mcp_server_config_accepts_transport_alias(transport: str): + """The MCP-spec ``transport`` field should be accepted as an alias for ``type``. + + Regression test for https://github.com/bytedance/deer-flow/issues/3238 — a + remote MCP server configured with only ``transport: sse`` was previously + misidentified as ``stdio`` (the default for ``type``). + """ + config = McpServerConfig.model_validate( + { + "transport": transport, + "url": "https://example.com/mcp", + } + ) + + assert config.type == transport + + params = build_server_params("aliased-server", config) + assert params["transport"] == transport + assert params["url"] == "https://example.com/mcp" + + +def test_mcp_server_config_type_takes_precedence_over_transport(): + """When both ``type`` and ``transport`` are provided, ``type`` wins.""" + config = McpServerConfig.model_validate( + { + "type": "http", + "transport": "sse", + "url": "https://example.com/mcp", + } + ) + + assert config.type == "http" + + def test_build_servers_config_returns_empty_when_no_enabled_servers(): extensions = ExtensionsConfig( mcp_servers={