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
This commit is contained in:
Ryker_Feng
2026-06-03 18:11:38 +08:00
committed by GitHub
parent 0ffa995fe9
commit 8fca56cf43
2 changed files with 54 additions and 1 deletions
@@ -5,7 +5,7 @@ import os
from pathlib import Path from pathlib import Path
from typing import Any, Literal 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 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") description: str = Field(default="", description="Human-readable description of what this MCP server provides")
model_config = ConfigDict(extra="allow") 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): class SkillStateConfig(BaseModel):
"""Configuration for a single skill's state.""" """Configuration for a single skill's state."""
+35
View File
@@ -83,6 +83,41 @@ def test_build_server_params_rejects_unsupported_transport():
build_server_params("bad-transport", config) 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(): def test_build_servers_config_returns_empty_when_no_enabled_servers():
extensions = ExtensionsConfig( extensions = ExtensionsConfig(
mcp_servers={ mcp_servers={