Simplify proxy negotiation middleware API

🤖 Generated with OpenAI Codex
This commit is contained in:
Jake Kaplan 2026-08-05 20:39:40 -04:00
commit e227a28c02
4 changed files with 14 additions and 33 deletions

View file

@ -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)],
)
```

View file

@ -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}")

View file

@ -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:

View file

@ -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: