Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion backend/packages/harness/deerflow/config/extensions_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
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

Expand Down Expand Up @@ -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."""
Expand Down
35 changes: 35 additions & 0 deletions backend/tests/test_mcp_client_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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={
Expand Down
Loading