mirror of
https://github.com/aljazceru/mcp-python-sdk.git
synced 2025-12-19 14:54:24 +01:00
fix: Update @mcp.resource to use function documentation as default descrip… (#489)
This commit is contained in:
@@ -11,7 +11,7 @@ import anyio.to_thread
|
||||
import httpx
|
||||
import pydantic
|
||||
import pydantic_core
|
||||
from pydantic import Field, ValidationInfo
|
||||
from pydantic import AnyUrl, Field, ValidationInfo, validate_call
|
||||
|
||||
from mcp.server.fastmcp.resources.base import Resource
|
||||
|
||||
@@ -68,6 +68,31 @@ class FunctionResource(Resource):
|
||||
except Exception as e:
|
||||
raise ValueError(f"Error reading resource {self.uri}: {e}")
|
||||
|
||||
@classmethod
|
||||
def from_function(
|
||||
cls,
|
||||
fn: Callable[..., Any],
|
||||
uri: str,
|
||||
name: str | None = None,
|
||||
description: str | None = None,
|
||||
mime_type: str | None = None,
|
||||
) -> "FunctionResource":
|
||||
"""Create a FunctionResource from a function."""
|
||||
func_name = name or fn.__name__
|
||||
if func_name == "<lambda>":
|
||||
raise ValueError("You must provide a name for lambda functions")
|
||||
|
||||
# ensure the arguments are properly cast
|
||||
fn = validate_call(fn)
|
||||
|
||||
return cls(
|
||||
uri=AnyUrl(uri),
|
||||
name=func_name,
|
||||
description=description or fn.__doc__ or "",
|
||||
mime_type=mime_type or "text/plain",
|
||||
fn=fn,
|
||||
)
|
||||
|
||||
|
||||
class FileResource(Resource):
|
||||
"""A resource that reads from a file.
|
||||
|
||||
@@ -148,9 +148,11 @@ class FastMCP:
|
||||
self._mcp_server = MCPServer(
|
||||
name=name or "FastMCP",
|
||||
instructions=instructions,
|
||||
lifespan=lifespan_wrapper(self, self.settings.lifespan)
|
||||
if self.settings.lifespan
|
||||
else default_lifespan,
|
||||
lifespan=(
|
||||
lifespan_wrapper(self, self.settings.lifespan)
|
||||
if self.settings.lifespan
|
||||
else default_lifespan
|
||||
),
|
||||
)
|
||||
self._tool_manager = ToolManager(
|
||||
warn_on_duplicate_tools=self.settings.warn_on_duplicate_tools
|
||||
@@ -465,16 +467,16 @@ class FastMCP:
|
||||
uri_template=uri,
|
||||
name=name,
|
||||
description=description,
|
||||
mime_type=mime_type or "text/plain",
|
||||
mime_type=mime_type,
|
||||
)
|
||||
else:
|
||||
# Register as regular resource
|
||||
resource = FunctionResource(
|
||||
uri=AnyUrl(uri),
|
||||
resource = FunctionResource.from_function(
|
||||
fn=fn,
|
||||
uri=uri,
|
||||
name=name,
|
||||
description=description,
|
||||
mime_type=mime_type or "text/plain",
|
||||
fn=fn,
|
||||
mime_type=mime_type,
|
||||
)
|
||||
self.add_resource(resource)
|
||||
return fn
|
||||
|
||||
@@ -136,3 +136,22 @@ class TestFunctionResource:
|
||||
content = await resource.read()
|
||||
assert content == "Hello, world!"
|
||||
assert resource.mime_type == "text/plain"
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_from_function(self):
|
||||
"""Test creating a FunctionResource from a function."""
|
||||
|
||||
async def get_data() -> str:
|
||||
"""get_data returns a string"""
|
||||
return "Hello, world!"
|
||||
|
||||
resource = FunctionResource.from_function(
|
||||
fn=get_data,
|
||||
uri="function://test",
|
||||
name="test",
|
||||
)
|
||||
|
||||
assert resource.description == "get_data returns a string"
|
||||
assert resource.mime_type == "text/plain"
|
||||
assert resource.name == "test"
|
||||
assert resource.uri == AnyUrl("function://test")
|
||||
|
||||
@@ -441,6 +441,24 @@ class TestServerResources:
|
||||
== base64.b64encode(b"Binary file data").decode()
|
||||
)
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_function_resource(self):
|
||||
mcp = FastMCP()
|
||||
|
||||
@mcp.resource("function://test", name="test_get_data")
|
||||
def get_data() -> str:
|
||||
"""get_data returns a string"""
|
||||
return "Hello, world!"
|
||||
|
||||
async with client_session(mcp._mcp_server) as client:
|
||||
resources = await client.list_resources()
|
||||
assert len(resources.resources) == 1
|
||||
resource = resources.resources[0]
|
||||
assert resource.description == "get_data returns a string"
|
||||
assert resource.uri == AnyUrl("function://test")
|
||||
assert resource.name == "test_get_data"
|
||||
assert resource.mimeType == "text/plain"
|
||||
|
||||
|
||||
class TestServerResourceTemplates:
|
||||
@pytest.mark.anyio
|
||||
|
||||
Reference in New Issue
Block a user