|
12 | 12 | from fastmcp import FastMCP |
13 | 13 | from fastmcp.server.middleware import MiddlewareContext |
14 | 14 | from mcp.types import ( |
| 15 | + GetPromptRequestParams, |
15 | 16 | ListPromptsRequest, |
16 | 17 | ListResourcesRequest, |
17 | 18 | ListResourceTemplatesRequest, |
18 | 19 | ListToolsRequest, |
| 20 | + ReadResourceRequestParams, |
19 | 21 | ) |
| 22 | +from pydantic.networks import AnyUrl |
20 | 23 |
|
21 | 24 | from cyt_mcp.aggregator import build_aggregator |
22 | 25 | from cyt_mcp.config import AggregatorConfig, sample_aggregator_config |
@@ -67,6 +70,8 @@ class OfferingsPassthroughScenario: |
67 | 70 | expected_template_names: tuple[str, ...] = () |
68 | 71 | cached_tool_name: str | None = None |
69 | 72 | expects_call_next: bool | None = None |
| 73 | + expected_resource_body: str | None = None |
| 74 | + read_resource_uri: str | None = None |
70 | 75 |
|
71 | 76 |
|
72 | 77 | def _load_json(path: Path) -> dict[str, Any]: |
@@ -161,6 +166,14 @@ def load_offerings_passthrough_scenarios( |
161 | 166 | expects_call_next=( |
162 | 167 | bool(item["expects_call_next"]) if "expects_call_next" in item else None |
163 | 168 | ), |
| 169 | + expected_resource_body=( |
| 170 | + str(item["expected_resource_body"]) |
| 171 | + if item.get("expected_resource_body") |
| 172 | + else None |
| 173 | + ), |
| 174 | + read_resource_uri=( |
| 175 | + str(item["read_resource_uri"]) if item.get("read_resource_uri") else None |
| 176 | + ), |
164 | 177 | ), |
165 | 178 | ) |
166 | 179 | return tuple(scenarios) |
@@ -295,3 +308,47 @@ async def _call_next(_ctx: MiddlewareContext[Any]) -> Sequence[Any]: |
295 | 308 |
|
296 | 309 | result = await handler(context, _call_next) |
297 | 310 | return cast(Sequence[Any], result) |
| 311 | + |
| 312 | + |
| 313 | +def make_read_resource_context(uri: str) -> MiddlewareContext[Any]: |
| 314 | + context = MagicMock() |
| 315 | + context.method = "resources/read" |
| 316 | + context.message = ReadResourceRequestParams(uri=AnyUrl(uri)) |
| 317 | + context.fastmcp_context = None |
| 318 | + return context |
| 319 | + |
| 320 | + |
| 321 | +def make_get_prompt_context(name: str) -> MiddlewareContext[Any]: |
| 322 | + context = MagicMock() |
| 323 | + context.method = "prompts/get" |
| 324 | + context.message = GetPromptRequestParams(name=name) |
| 325 | + context.fastmcp_context = None |
| 326 | + return context |
| 327 | + |
| 328 | + |
| 329 | +async def read_resource_via_middleware( |
| 330 | + middleware: SessionWorkspaceMiddleware, |
| 331 | + server: FastMCP, |
| 332 | + *, |
| 333 | + uri: str, |
| 334 | +) -> Any: |
| 335 | + context = make_read_resource_context(uri) |
| 336 | + |
| 337 | + async def _call_next(_ctx: MiddlewareContext[Any]) -> Any: |
| 338 | + return await server.read_resource(uri) |
| 339 | + |
| 340 | + return await middleware.on_read_resource(context, _call_next) |
| 341 | + |
| 342 | + |
| 343 | +async def get_prompt_via_middleware( |
| 344 | + middleware: SessionWorkspaceMiddleware, |
| 345 | + server: FastMCP, |
| 346 | + *, |
| 347 | + name: str, |
| 348 | +) -> Any: |
| 349 | + context = make_get_prompt_context(name) |
| 350 | + |
| 351 | + async def _call_next(_ctx: MiddlewareContext[Any]) -> Any: |
| 352 | + return await server._get_prompt(name) |
| 353 | + |
| 354 | + return await middleware.on_get_prompt(context, _call_next) |
0 commit comments