Mark transforming single-server config wrapper legacy-only

This commit is contained in:
Jeremiah Lowin 2026-07-20 16:34:42 -04:00
commit 52b37b9a0c
No known key found for this signature in database
2 changed files with 27 additions and 1 deletions

View file

@ -164,7 +164,16 @@ class _TransformingMCPServerMixin(BaseModel):
)
) from exc
return FastMCPTransport(mcp=self._to_server_and_underlying_transport()[0])
transport = FastMCPTransport(mcp=self._to_server_and_underlying_transport()[0])
# The wrapped proxy talks to its upstream over the legacy era (it pins
# the backend client to `mode="legacy"` to forward the initialize
# handshake and server-initiated features). Mark the wrapper legacy-only
# so a default `Client(config)` on `mode="auto"` negotiates legacy with
# it too, keeping both legs on the same era — otherwise a modern
# frontend would receive a forwarded server-initiated request that the
# modern era has no back-channel for.
transport.legacy_only = True
return transport
class StdioMCPServer(BaseModel):

View file

@ -140,6 +140,23 @@ class TestConfigTransportLegacyOnly:
transport = MCPConfigTransport(config)
assert transport.legacy_only is True
def test_transforming_single_server_wrapper_is_legacy_only(self):
"""A single-server config that uses tool transforms or tag filters wraps
a legacy-pinned proxy; the wrapper transport must advertise legacy-only
so a default `mode="auto"` frontend negotiates the same era as the
backend rather than negotiating modern against a legacy upstream."""
config = {
"mcpServers": {
"a": {
"url": "https://a.example.com/mcp",
"include_tags": ["public"],
},
},
}
mcp_config = MCPConfig.from_dict(config)
transport = mcp_config.mcpServers["a"].to_transport()
assert transport.legacy_only is True
def test_parse_single_stdio_config():
config = {