mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-23 22:14:18 +02:00
fix: accept callable roots handlers (#4639)
* Accept callable roots handlers 🤖 Generated with OpenAI Codex * fix: cover callable object roots handlers 🤖 Generated with OpenAI Codex --------- Co-authored-by: Shuying <zsy@u.northwestern.edu>
This commit is contained in:
parent
b07f9ce9ae
commit
2a93404e8c
3 changed files with 41 additions and 7 deletions
|
|
@ -37,7 +37,7 @@ def create_roots_callback(
|
|||
if isinstance(handler, list):
|
||||
# TODO(ty): remove when ty supports isinstance union narrowing
|
||||
return _create_roots_callback_from_roots(handler) # type: ignore[arg-type] # ty:ignore[invalid-argument-type]
|
||||
elif inspect.isfunction(handler):
|
||||
elif callable(handler):
|
||||
return _create_roots_callback_from_fn(handler)
|
||||
else:
|
||||
raise ValueError(f"Invalid roots handler: {handler}")
|
||||
|
|
|
|||
|
|
@ -1327,12 +1327,7 @@ def _restore_request_context(
|
|||
|
||||
|
||||
def _make_restoring_handler(handler: Callable, rc_ref: list[Any]) -> Callable:
|
||||
"""Wrap a proxy handler to restore request_ctx before delegating.
|
||||
|
||||
The wrapper is a plain ``async def`` so it passes
|
||||
``inspect.isfunction()`` checks in handler registration paths
|
||||
(e.g., ``create_roots_callback``).
|
||||
"""
|
||||
"""Wrap a proxy handler to restore request_ctx before delegating."""
|
||||
|
||||
async def wrapper(*args: Any, **kwargs: Any) -> Any:
|
||||
_restore_request_context(rc_ref)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import functools
|
||||
|
||||
import pytest
|
||||
|
||||
from fastmcp import Client, Context, FastMCP
|
||||
|
|
@ -43,3 +45,40 @@ class TestClientRoots:
|
|||
"file://x/y/z",
|
||||
"file://x/y/z",
|
||||
]
|
||||
|
||||
async def test_bound_method_roots_handler(self, fastmcp_server: FastMCP):
|
||||
class RootsProvider:
|
||||
async def get_roots(self, _context: object) -> list[str]:
|
||||
return ["file:///bound-method"]
|
||||
|
||||
provider = RootsProvider()
|
||||
|
||||
async with Client(
|
||||
fastmcp_server, mode="legacy", roots=provider.get_roots
|
||||
) as client:
|
||||
result = await client.call_tool("list_roots", {})
|
||||
|
||||
assert result.data == ["file:///bound-method"]
|
||||
|
||||
async def test_partial_roots_handler(self, fastmcp_server: FastMCP):
|
||||
async def get_roots(prefix: str, _context: object) -> list[str]:
|
||||
return [f"file:///{prefix}"]
|
||||
|
||||
handler = functools.partial(get_roots, "partial")
|
||||
|
||||
async with Client(fastmcp_server, mode="legacy", roots=handler) as client:
|
||||
result = await client.call_tool("list_roots", {})
|
||||
|
||||
assert result.data == ["file:///partial"]
|
||||
|
||||
async def test_callable_object_roots_handler(self, fastmcp_server: FastMCP):
|
||||
class RootsProvider:
|
||||
async def __call__(self, _context: object) -> list[str]:
|
||||
return ["file:///callable-object"]
|
||||
|
||||
async with Client(
|
||||
fastmcp_server, mode="legacy", roots=RootsProvider()
|
||||
) as client:
|
||||
result = await client.call_tool("list_roots", {})
|
||||
|
||||
assert result.data == ["file:///callable-object"]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue