mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-20 04:24:17 +02:00
Preserve deprecated initialization middleware
🤖 Generated with OpenAI Codex
This commit is contained in:
parent
3f58a13b6c
commit
eb52de3b38
2 changed files with 66 additions and 7 deletions
|
|
@ -1155,8 +1155,8 @@ class ProxyMetadataMiddleware(Middleware):
|
|||
return result.model_copy(update=self._updates(result, upstream))
|
||||
|
||||
|
||||
class ProxyInitializeMiddleware(ProxyMetadataMiddleware):
|
||||
"""Deprecated compatibility name for ``ProxyMetadataMiddleware``."""
|
||||
class ProxyInitializeMiddleware(Middleware):
|
||||
"""Deprecated middleware for forwarding instructions during initialization."""
|
||||
|
||||
def __init__(self, proxy: FastMCPProxy) -> None:
|
||||
warnings.warn(
|
||||
|
|
@ -1167,8 +1167,60 @@ class ProxyInitializeMiddleware(ProxyMetadataMiddleware):
|
|||
stacklevel=2,
|
||||
)
|
||||
self.proxy = proxy
|
||||
self.client_factory = proxy.client_factory
|
||||
self.identity: ProxyIdentity = "proxy"
|
||||
|
||||
async def on_initialize(
|
||||
self,
|
||||
context: MiddlewareContext[mcp_types.InitializeRequest],
|
||||
call_next: CallNext[
|
||||
mcp_types.InitializeRequest,
|
||||
mcp_types.InitializeResult | None,
|
||||
],
|
||||
) -> mcp_types.InitializeResult | None:
|
||||
client = await self.proxy._get_client()
|
||||
upstream_instructions: str | None = None
|
||||
try:
|
||||
if isinstance(client, ProxyClient):
|
||||
ctx = context.fastmcp_context
|
||||
if ctx is not None:
|
||||
client._proxy_rc_ref[0] = (
|
||||
ctx.request_context,
|
||||
ctx._fastmcp,
|
||||
)
|
||||
async with client:
|
||||
# Entering the context already ran connect-time negotiation.
|
||||
# `initialize()` returns the handshake result on a legacy backend,
|
||||
# but raises on a modern (server/discover) backend, which has no
|
||||
# InitializeResult. That mismatch only arises when an explicit
|
||||
# `mode=` pins the backend to a different era than this legacy
|
||||
# front (the era-mirroring default keeps the two in lockstep, so
|
||||
# a legacy front always reaches a legacy backend here). Skip the
|
||||
# handshake-only call when the backend negotiated the modern era.
|
||||
if client.protocol_version not in MODERN_PROTOCOL_VERSIONS:
|
||||
await client.initialize()
|
||||
# Capture the upstream's instructions while the session is
|
||||
# live; `initialize_result` clears once the context exits.
|
||||
init_result = client.initialize_result
|
||||
if init_result is not None:
|
||||
upstream_instructions = init_result.instructions
|
||||
except MCPError:
|
||||
raise
|
||||
except _PROXY_TRANSPORT_ERRORS as error:
|
||||
raise _proxy_upstream_error(error) from error
|
||||
|
||||
result = await call_next(context)
|
||||
|
||||
# Forward the upstream server's instructions unless the proxy defines its
|
||||
# own. `instructions` is part of the MCP InitializeResult and is meant to
|
||||
# steer the model, so a proxy that dropped it would silently degrade any
|
||||
# downstream consumer relying on upstream guidance.
|
||||
if (
|
||||
result is not None
|
||||
and self.proxy.instructions is None
|
||||
and upstream_instructions is not None
|
||||
):
|
||||
result.instructions = upstream_instructions
|
||||
|
||||
return result
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -387,9 +387,11 @@ def test_gateway_construction_does_not_create_backend_client():
|
|||
assert calls == 0
|
||||
|
||||
|
||||
def test_proxy_initialize_middleware_is_deprecated():
|
||||
async def test_proxy_initialize_middleware_preserves_legacy_behavior():
|
||||
upstream = FastMCP("upstream", instructions="legacy instructions")
|
||||
|
||||
def client_factory() -> ProxyClient:
|
||||
return ProxyClient(make_upstream())
|
||||
return ProxyClient(upstream)
|
||||
|
||||
proxy = FastMCPProxy(name="compatibility-proxy", client_factory=client_factory)
|
||||
|
||||
|
|
@ -399,8 +401,13 @@ def test_proxy_initialize_middleware_is_deprecated():
|
|||
):
|
||||
middleware = ProxyInitializeMiddleware(proxy)
|
||||
|
||||
proxy.middleware = [middleware]
|
||||
async with Client(proxy, mode="legacy") as client:
|
||||
assert client.instructions == "legacy instructions"
|
||||
async with Client(proxy, mode="auto") as client:
|
||||
assert client.instructions is None
|
||||
|
||||
assert middleware.proxy is proxy
|
||||
assert middleware.client_factory is client_factory
|
||||
|
||||
|
||||
async def test_fastmcp_proxy_uses_public_metadata_middleware():
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue