From e227a28c02480ffa9a998b69417b7d6f9de0625e Mon Sep 17 00:00:00 2001 From: Jake Kaplan Date: Wed, 5 Aug 2026 20:39:40 -0400 Subject: [PATCH] Simplify proxy negotiation middleware API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with OpenAI Codex --- docs/servers/providers/proxy.mdx | 6 +++--- .../fastmcp/server/middleware/__init__.py | 16 ---------------- .../fastmcp/server/providers/proxy.py | 6 ++---- .../proxy/test_negotiation_metadata.py | 19 +++++++++---------- 4 files changed, 14 insertions(+), 33 deletions(-) diff --git a/docs/servers/providers/proxy.mdx b/docs/servers/providers/proxy.mdx index 836d653c5..144dacb37 100644 --- a/docs/servers/providers/proxy.mdx +++ b/docs/servers/providers/proxy.mdx @@ -390,13 +390,13 @@ Only reuse sessions when you know the backend is stateless (e.g. stateless HTTP) ### Forwarding Negotiation Metadata -Add `ProxyNegotiationMetadataMiddleware` when a gateway built with `ProxyProvider` should also expose backend instructions and namespaced `_meta`: +Add `ProxyNegotiationMiddleware` when a gateway built with `ProxyProvider` should also expose backend instructions and namespaced `_meta`: ```python from fastmcp import FastMCP from fastmcp.server.providers.proxy import ( ProxyClient, - ProxyNegotiationMetadataMiddleware, + ProxyNegotiationMiddleware, ProxyProvider, ) @@ -404,7 +404,7 @@ backend = ProxyProvider(lambda: ProxyClient("http://backend:8000/mcp", mode="aut gateway = FastMCP( "Controlled Gateway", providers=[backend], - middleware=[ProxyNegotiationMetadataMiddleware(backend)], + middleware=[ProxyNegotiationMiddleware(backend)], ) ``` diff --git a/fastmcp_slim/fastmcp/server/middleware/__init__.py b/fastmcp_slim/fastmcp/server/middleware/__init__.py index 48765f77a..8df6962bd 100644 --- a/fastmcp_slim/fastmcp/server/middleware/__init__.py +++ b/fastmcp_slim/fastmcp/server/middleware/__init__.py @@ -1,5 +1,3 @@ -from typing import TYPE_CHECKING - from .authorization import AuthMiddleware from .middleware import ( CallNext, @@ -8,24 +6,10 @@ from .middleware import ( ) from .ping import PingMiddleware -if TYPE_CHECKING: - from fastmcp.server.providers.proxy import ( - ProxyNegotiationMetadataMiddleware as ProxyNegotiationMetadataMiddleware, - ) - __all__ = [ "AuthMiddleware", "CallNext", "Middleware", "MiddlewareContext", "PingMiddleware", - "ProxyNegotiationMetadataMiddleware", ] - - -def __getattr__(name: str) -> object: - if name == "ProxyNegotiationMetadataMiddleware": - from fastmcp.server.providers.proxy import ProxyNegotiationMetadataMiddleware - - return ProxyNegotiationMetadataMiddleware - raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/fastmcp_slim/fastmcp/server/providers/proxy.py b/fastmcp_slim/fastmcp/server/providers/proxy.py index a428eb26a..db971fed3 100644 --- a/fastmcp_slim/fastmcp/server/providers/proxy.py +++ b/fastmcp_slim/fastmcp/server/providers/proxy.py @@ -1045,7 +1045,7 @@ class _NegotiationMetadata: ) -class ProxyNegotiationMetadataMiddleware(Middleware): +class ProxyNegotiationMiddleware(Middleware): """Forward optional negotiation metadata from a ``ProxyProvider`` backend. The frontend always owns protocol versions, capabilities, cache policy, and @@ -1344,9 +1344,7 @@ class FastMCPProxy(FastMCP): self.client_factory = client_factory provider = ProxyProvider(client_factory) self.add_provider(provider) - self.middleware.append( - ProxyNegotiationMetadataMiddleware(provider, identity=identity) - ) + self.middleware.append(ProxyNegotiationMiddleware(provider, identity=identity)) self._setup_proxy_ping_handler() async def _get_client(self) -> Client: diff --git a/tests/server/providers/proxy/test_negotiation_metadata.py b/tests/server/providers/proxy/test_negotiation_metadata.py index 98caeba68..5d928cfc2 100644 --- a/tests/server/providers/proxy/test_negotiation_metadata.py +++ b/tests/server/providers/proxy/test_negotiation_metadata.py @@ -11,13 +11,12 @@ from mcp_types.version import MODERN_PROTOCOL_VERSIONS from fastmcp import Client, FastMCP from fastmcp.client.transports import StreamableHttpTransport from fastmcp.server import create_proxy -from fastmcp.server.middleware import ( - CallNext, - Middleware, - MiddlewareContext, - ProxyNegotiationMetadataMiddleware, +from fastmcp.server.middleware import CallNext, Middleware, MiddlewareContext +from fastmcp.server.providers.proxy import ( + ProxyClient, + ProxyNegotiationMiddleware, + ProxyProvider, ) -from fastmcp.server.providers.proxy import ProxyClient, ProxyProvider from fastmcp.utilities.http import find_available_port ResultT = TypeVar("ResultT", bound=mcp_types.Result) @@ -132,7 +131,7 @@ def make_gateway( frontend_metadata: bool = False, ) -> FastMCP: provider = ProxyProvider(lambda: ProxyClient(upstream, mode=backend_mode)) - negotiation = ProxyNegotiationMetadataMiddleware(provider, identity=identity) + negotiation = ProxyNegotiationMiddleware(provider, identity=identity) middleware: list[Middleware] = [negotiation] if frontend_metadata: middleware.append(FrontendMetadataMiddleware()) @@ -235,7 +234,7 @@ async def test_unavailable_backend_does_not_fail_negotiation(frontend_mode: str) gateway = FastMCP( "available-gateway", providers=[provider], - middleware=[ProxyNegotiationMetadataMiddleware(provider)], + middleware=[ProxyNegotiationMiddleware(provider)], ) gateway.provider_error_strategy = "raise" @@ -258,7 +257,7 @@ def test_gateway_construction_does_not_create_backend_client(): FastMCP( "lazy-gateway", providers=[provider], - middleware=[ProxyNegotiationMetadataMiddleware(provider)], + middleware=[ProxyNegotiationMiddleware(provider)], ) assert calls == 0 @@ -268,7 +267,7 @@ async def test_fastmcp_proxy_uses_public_negotiation_middleware(): proxy = create_proxy(make_upstream(), name="convenience", identity="upstream") assert any( - isinstance(middleware, ProxyNegotiationMetadataMiddleware) + isinstance(middleware, ProxyNegotiationMiddleware) for middleware in proxy.middleware ) async with Client(proxy, mode="auto") as client: