mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-09 07:09:11 +02:00
* Forward proxy negotiation metadata 🤖 Generated with OpenAI Codex * Limit forwarded proxy metadata 🤖 Generated with OpenAI Codex * Tighten negotiation metadata forwarding 🤖 Generated with OpenAI Codex * Tighten proxy metadata docs 🤖 Generated with OpenAI Codex * Keep proxy metadata middleware with provider 🤖 Generated with OpenAI Codex * Simplify proxy negotiation middleware API 🤖 Generated with OpenAI Codex * Name proxy metadata middleware directly 🤖 Generated with OpenAI Codex * Preserve discovery middleware contracts 🤖 Generated with OpenAI Codex * Clarify proxy metadata ownership 🤖 Generated with OpenAI Codex * Align proxy metadata wording 🤖 Generated with OpenAI Codex * Call forwarded values server metadata 🤖 Generated with OpenAI Codex * Harden proxy metadata reads 🤖 Generated with OpenAI Codex * Expose configured discovery result 🤖 Generated with OpenAI Codex * Preserve proxy discovery compatibility 🤖 Generated with OpenAI Codex * Preserve deprecated initialization middleware 🤖 Generated with OpenAI Codex * Harden proxy metadata boundaries 🤖 Generated with OpenAI Codex * Restore deprecated middleware location 🤖 Generated with OpenAI Codex * Simplify proxy metadata client lifecycle 🤖 Generated with OpenAI Codex * Clarify proxy metadata lifecycle 🤖 Generated with OpenAI Codex * Preserve proxy factory errors 🤖 Generated with OpenAI Codex * Detach forwarded proxy metadata 🤖 Generated with OpenAI Codex * Simplify proxy metadata implementation 🤖 Generated with OpenAI Codex * Distinguish proxy metadata failures 🤖 Generated with OpenAI Codex * Narrow proxy metadata validation fallback 🤖 Generated with OpenAI Codex * Retrigger CI 🤖 Generated with OpenAI Codex
108 lines
3.7 KiB
Python
108 lines
3.7 KiB
Python
"""Tests for typed middleware support during modern discovery."""
|
|
|
|
from typing import Any
|
|
|
|
import mcp_types
|
|
from mcp_types.version import LATEST_MODERN_VERSION
|
|
|
|
from fastmcp import Client, FastMCP
|
|
from fastmcp.server.middleware import CallNext, Middleware, MiddlewareContext
|
|
|
|
|
|
async def test_on_discover_receives_and_transforms_typed_result():
|
|
class DiscoveryMiddleware(Middleware):
|
|
def __init__(self) -> None:
|
|
self.request: mcp_types.DiscoverRequest | None = None
|
|
self.result: mcp_types.DiscoverResult | None = None
|
|
|
|
async def on_discover(
|
|
self,
|
|
context: MiddlewareContext[mcp_types.DiscoverRequest],
|
|
call_next: CallNext[
|
|
mcp_types.DiscoverRequest,
|
|
mcp_types.DiscoverResult | dict[str, Any],
|
|
],
|
|
) -> mcp_types.DiscoverResult | dict[str, Any]:
|
|
self.request = context.message
|
|
result = await call_next(context)
|
|
assert isinstance(result, mcp_types.DiscoverResult)
|
|
self.result = result
|
|
return result.model_copy(update={"instructions": "discovered"})
|
|
|
|
middleware = DiscoveryMiddleware()
|
|
server = FastMCP("typed-discovery", middleware=[middleware])
|
|
|
|
async with Client(server, mode="auto") as client:
|
|
assert client.instructions == "discovered"
|
|
|
|
assert isinstance(middleware.request, mcp_types.DiscoverRequest)
|
|
assert isinstance(middleware.result, mcp_types.DiscoverResult)
|
|
|
|
|
|
async def test_on_discover_forwards_modified_params():
|
|
modified = False
|
|
server = FastMCP("modified-discovery")
|
|
default_handler = server._mcp_server._handle_discover
|
|
|
|
async def capture_params(ctx, params):
|
|
nonlocal modified
|
|
assert params is not None
|
|
assert params.meta is not None
|
|
modified = params.meta["com.example/modified"] is True
|
|
return await default_handler(ctx, params)
|
|
|
|
server._mcp_server.add_request_handler(
|
|
"server/discover", mcp_types.RequestParams, capture_params
|
|
)
|
|
|
|
class ModifyParams(Middleware):
|
|
async def on_discover(self, context, call_next):
|
|
assert context.message.params is not None
|
|
assert context.message.params.meta is not None
|
|
context.message.params = mcp_types.RequestParams(
|
|
meta={
|
|
**context.message.params.meta,
|
|
"com.example/modified": True,
|
|
}
|
|
)
|
|
return await call_next(context)
|
|
|
|
server.add_middleware(ModifyParams())
|
|
|
|
async with Client(server, mode="auto"):
|
|
pass
|
|
|
|
assert modified
|
|
|
|
|
|
async def test_on_discover_preserves_extension_owned_result():
|
|
extension_result = {
|
|
"resultType": "com.example/custom",
|
|
"payload": {"enabled": True},
|
|
}
|
|
|
|
async def custom_discover(_ctx, _params):
|
|
return extension_result
|
|
|
|
class ObserveExtension(Middleware):
|
|
def __init__(self) -> None:
|
|
self.result: mcp_types.DiscoverResult | dict[str, Any] | None = None
|
|
|
|
async def on_discover(self, context, call_next):
|
|
self.result = await call_next(context)
|
|
return self.result
|
|
|
|
middleware = ObserveExtension()
|
|
server = FastMCP("extension-discovery", middleware=[middleware])
|
|
server._mcp_server.add_request_handler(
|
|
"server/discover", mcp_types.RequestParams, custom_discover
|
|
)
|
|
|
|
async with Client(server, mode=LATEST_MODERN_VERSION) as client:
|
|
result = await client.session.send_discover(LATEST_MODERN_VERSION)
|
|
|
|
assert isinstance(result, dict)
|
|
assert result["resultType"] == "com.example/custom"
|
|
assert result["payload"] == {"enabled": True}
|
|
assert isinstance(middleware.result, dict)
|
|
assert middleware.result["payload"] == {"enabled": True}
|