Ensure methods work/are documented

This commit is contained in:
Jeremiah Lowin 2025-06-05 10:59:32 -04:00
commit f495de6f3a
6 changed files with 202 additions and 54 deletions

View file

@ -205,8 +205,8 @@ class TestToolDecorator:
mcp = FastMCP()
class MyClass:
@staticmethod
@mcp.tool
@staticmethod
def add(x: int, y: int) -> int:
return x + y
@ -223,6 +223,17 @@ class TestToolDecorator:
result = await mcp._mcp_call_tool("add", {"x": 1, "y": 2})
assert result[0].text == "3" # type: ignore[attr-defined]
async def test_tool_decorator_classmethod_error(self):
mcp = FastMCP()
with pytest.raises(ValueError, match="To decorate a classmethod"):
class MyClass:
@mcp.tool
@classmethod
def add(cls, y: int) -> None:
pass
async def test_tool_decorator_classmethod_async_function(self):
mcp = FastMCP()
@ -249,6 +260,20 @@ class TestToolDecorator:
result = await mcp._mcp_call_tool("add", {"x": 1, "y": 2})
assert result[0].text == "3" # type: ignore[attr-defined]
async def test_tool_decorator_staticmethod_order(self):
"""Test that the recommended decorator order works for static methods"""
mcp = FastMCP()
class MyClass:
@staticmethod
@mcp.tool
def add_v1(x: int, y: int) -> int:
return x + y
# Test that the recommended order works
result = await mcp._mcp_call_tool("add_v1", {"x": 1, "y": 2})
assert result[0].text == "3" # type: ignore[attr-defined]
async def test_tool_decorator_with_tags(self):
"""Test that the tool decorator properly sets tags."""
mcp = FastMCP()
@ -480,12 +505,23 @@ class TestResourceDecorator:
result = await client.read_resource("resource://data")
assert result[0].text == "Class prefix: Hello, world!" # type: ignore[attr-defined]
async def test_resource_decorator_classmethod_error(self):
mcp = FastMCP()
with pytest.raises(ValueError, match="To decorate a classmethod"):
class MyClass:
@mcp.resource("resource://data")
@classmethod
def get_data(cls) -> None:
pass
async def test_resource_decorator_staticmethod(self):
mcp = FastMCP()
class MyClass:
@staticmethod
@mcp.resource("resource://data")
@staticmethod
def get_data() -> str:
return "Static Hello, world!"
@ -504,6 +540,20 @@ class TestResourceDecorator:
result = await client.read_resource("resource://data")
assert result[0].text == "Async Hello, world!" # type: ignore[attr-defined]
async def test_resource_decorator_staticmethod_order(self):
"""Test that both decorator orders work for static methods"""
mcp = FastMCP()
class MyClass:
@mcp.resource("resource://data") # type: ignore[misc] # Type checker warns but runtime works
@staticmethod
def get_data() -> str:
return "Static Hello, world!"
async with Client(mcp) as client:
result = await client.read_resource("resource://data")
assert result[0].text == "Static Hello, world!" # type: ignore[attr-defined]
class TestTemplateDecorator:
async def test_template_decorator(self):
@ -609,8 +659,8 @@ class TestTemplateDecorator:
mcp = FastMCP()
class MyClass:
@staticmethod
@mcp.resource("resource://{name}/data")
@staticmethod
def get_data(name: str) -> str:
return f"Static Data for {name}"
@ -783,12 +833,23 @@ class TestPromptDecorator:
message = result.messages[0]
assert message.content.text == "Class prefix: Hello, world!" # type: ignore[attr-defined]
async def test_prompt_decorator_classmethod_error(self):
mcp = FastMCP()
with pytest.raises(ValueError, match="To decorate a classmethod"):
class MyClass:
@mcp.prompt
@classmethod
def test_prompt(cls) -> None:
pass
async def test_prompt_decorator_staticmethod(self):
mcp = FastMCP()
class MyClass:
@staticmethod
@mcp.prompt
@staticmethod
def test_prompt() -> str:
return "Static Hello, world!"
@ -881,6 +942,22 @@ class TestPromptDecorator:
def my_function() -> str:
return "Hello, world!"
async def test_prompt_decorator_staticmethod_order(self):
"""Test that both decorator orders work for static methods"""
mcp = FastMCP()
class MyClass:
@mcp.prompt # type: ignore[misc] # Type checker warns but runtime works
@staticmethod
def test_prompt() -> str:
return "Static Hello, world!"
async with Client(mcp) as client:
result = await client.get_prompt("test_prompt")
assert len(result.messages) == 1
message = result.messages[0]
assert message.content.text == "Static Hello, world!" # type: ignore[attr-defined]
class TestResourcePrefixHelpers:
@pytest.mark.parametrize(