mirror of
https://github.com/aljazceru/mcp-python-sdk.git
synced 2025-12-19 14:54:24 +01:00
add support for async resources
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
"""Concrete resource implementations."""
|
||||
|
||||
import inspect
|
||||
import json
|
||||
from collections.abc import Callable
|
||||
from pathlib import Path
|
||||
@@ -53,7 +54,9 @@ class FunctionResource(Resource):
|
||||
async def read(self) -> str | bytes:
|
||||
"""Read the resource by calling the wrapped function."""
|
||||
try:
|
||||
result = self.fn()
|
||||
result = (
|
||||
await self.fn() if inspect.iscoroutinefunction(self.fn) else self.fn()
|
||||
)
|
||||
if isinstance(result, Resource):
|
||||
return await result.read()
|
||||
if isinstance(result, bytes):
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
"""FastMCP - A more ergonomic interface for MCP servers."""
|
||||
|
||||
import functools
|
||||
import inspect
|
||||
import json
|
||||
import re
|
||||
@@ -305,9 +304,19 @@ class FastMCP:
|
||||
def get_data() -> str:
|
||||
return "Hello, world!"
|
||||
|
||||
@server.resource("resource://my-resource")
|
||||
async get_data() -> str:
|
||||
data = await fetch_data()
|
||||
return f"Hello, world! {data}"
|
||||
|
||||
@server.resource("resource://{city}/weather")
|
||||
def get_weather(city: str) -> str:
|
||||
return f"Weather for {city}"
|
||||
|
||||
@server.resource("resource://{city}/weather")
|
||||
async def get_weather(city: str) -> str:
|
||||
data = await fetch_weather(city)
|
||||
return f"Weather for {city}: {data}"
|
||||
"""
|
||||
# Check if user passed function directly instead of calling decorator
|
||||
if callable(uri):
|
||||
@@ -317,10 +326,6 @@ class FastMCP:
|
||||
)
|
||||
|
||||
def decorator(fn: Callable) -> Callable:
|
||||
@functools.wraps(fn)
|
||||
def wrapper(*args: Any, **kwargs: Any) -> Any:
|
||||
return fn(*args, **kwargs)
|
||||
|
||||
# Check if this should be a template
|
||||
has_uri_params = "{" in uri and "}" in uri
|
||||
has_func_params = bool(inspect.signature(fn).parameters)
|
||||
@@ -338,7 +343,7 @@ class FastMCP:
|
||||
|
||||
# Register as template
|
||||
self._resource_manager.add_template(
|
||||
wrapper,
|
||||
fn=fn,
|
||||
uri_template=uri,
|
||||
name=name,
|
||||
description=description,
|
||||
@@ -351,10 +356,10 @@ class FastMCP:
|
||||
name=name,
|
||||
description=description,
|
||||
mime_type=mime_type or "text/plain",
|
||||
fn=wrapper,
|
||||
fn=fn,
|
||||
)
|
||||
self.add_resource(resource)
|
||||
return wrapper
|
||||
return fn
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
@@ -120,3 +120,19 @@ class TestFunctionResource:
|
||||
)
|
||||
content = await resource.read()
|
||||
assert isinstance(content, str)
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_async_read_text(self):
|
||||
"""Test reading text from async FunctionResource."""
|
||||
|
||||
async def get_data() -> str:
|
||||
return "Hello, world!"
|
||||
|
||||
resource = FunctionResource(
|
||||
uri=AnyUrl("function://test"),
|
||||
name="test",
|
||||
fn=get_data,
|
||||
)
|
||||
content = await resource.read()
|
||||
assert content == "Hello, world!"
|
||||
assert resource.mime_type == "text/plain"
|
||||
|
||||
Reference in New Issue
Block a user