mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-09 07:09:11 +02:00
Merge remote-tracking branch 'origin/main' into modern-merge
# Conflicts: # tests/client/test_roots.py
This commit is contained in:
commit
1593257f2a
5 changed files with 51 additions and 13 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}")
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ class AnthropicSamplingHandler:
|
|||
Example:
|
||||
```python
|
||||
from anthropic import AsyncAnthropic
|
||||
from fastmcp import FastMCP
|
||||
from fastmcp import Client
|
||||
from fastmcp.client.sampling.handlers.anthropic import AnthropicSamplingHandler
|
||||
|
||||
handler = AnthropicSamplingHandler(
|
||||
|
|
@ -83,7 +83,9 @@ class AnthropicSamplingHandler:
|
|||
client=AsyncAnthropic(),
|
||||
)
|
||||
|
||||
server = FastMCP(sampling_handler=handler)
|
||||
# Sampling is a server-initiated request, so it only exists on the
|
||||
# handshake era; pass `mode="legacy"` to answer one.
|
||||
client = Client(server_url, sampling_handler=handler, mode="legacy")
|
||||
```
|
||||
"""
|
||||
|
||||
|
|
|
|||
|
|
@ -60,18 +60,20 @@ class GoogleGenaiSamplingHandler:
|
|||
|
||||
Example:
|
||||
```python
|
||||
from google.genai import Client
|
||||
from fastmcp import FastMCP
|
||||
from google.genai import Client as GoogleGenaiClient
|
||||
from fastmcp import Client as FastMCPClient
|
||||
from fastmcp.client.sampling.handlers.google_genai import (
|
||||
GoogleGenaiSamplingHandler,
|
||||
)
|
||||
|
||||
handler = GoogleGenaiSamplingHandler(
|
||||
default_model="gemini-2.0-flash",
|
||||
client=Client(),
|
||||
client=GoogleGenaiClient(),
|
||||
)
|
||||
|
||||
server = FastMCP(sampling_handler=handler)
|
||||
# Sampling is a server-initiated request, so it only exists on the
|
||||
# handshake era; pass `mode="legacy"` to answer one.
|
||||
client = FastMCPClient(server_url, sampling_handler=handler, mode="legacy")
|
||||
```
|
||||
"""
|
||||
|
||||
|
|
|
|||
|
|
@ -1348,12 +1348,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 mcp_types import Root
|
||||
|
||||
|
|
@ -65,3 +67,40 @@ class TestClientRoots:
|
|||
|
||||
assert len(calls) == 1
|
||||
assert result.data == ["file://from/handler"]
|
||||
|
||||
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