Merge pull request #137 from modelcontextprotocol/v1.2.x
fix: #129 resource template handling in FastMCP server
This commit is contained in:
commit
8d17fd327b
6 changed files with 98 additions and 4 deletions
21
examples/fastmcp/parameter_descriptions.py
Normal file
21
examples/fastmcp/parameter_descriptions.py
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
"""
|
||||||
|
FastMCP Example showing parameter descriptions
|
||||||
|
"""
|
||||||
|
|
||||||
|
from pydantic import Field
|
||||||
|
|
||||||
|
from mcp.server.fastmcp import FastMCP
|
||||||
|
|
||||||
|
# Create server
|
||||||
|
mcp = FastMCP("Parameter Descriptions Server")
|
||||||
|
|
||||||
|
|
||||||
|
@mcp.tool()
|
||||||
|
def greet_user(
|
||||||
|
name: str = Field(description="The name of the person to greet"),
|
||||||
|
title: str = Field(description="Optional title like Mr/Ms/Dr", default=""),
|
||||||
|
times: int = Field(description="Number of times to repeat the greeting", default=1),
|
||||||
|
) -> str:
|
||||||
|
"""Greet a user with optional title and repetition"""
|
||||||
|
greeting = f"Hello {title + ' ' if title else ''}{name}!"
|
||||||
|
return "\n".join([greeting] * times)
|
||||||
|
|
@ -6,10 +6,10 @@ import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import Annotated
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import typer
|
import typer
|
||||||
from typing_extensions import Annotated
|
|
||||||
except ImportError:
|
except ImportError:
|
||||||
print("Error: typer is required. Install with 'pip install mcp[cli]'")
|
print("Error: typer is required. Install with 'pip install mcp[cli]'")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
|
||||||
|
|
@ -133,8 +133,7 @@ class FastMCP:
|
||||||
self._mcp_server.read_resource()(self.read_resource)
|
self._mcp_server.read_resource()(self.read_resource)
|
||||||
self._mcp_server.list_prompts()(self.list_prompts)
|
self._mcp_server.list_prompts()(self.list_prompts)
|
||||||
self._mcp_server.get_prompt()(self.get_prompt)
|
self._mcp_server.get_prompt()(self.get_prompt)
|
||||||
# TODO: This has not been added to MCP yet, see https://github.com/jlowin/fastmcp/issues/10
|
self._mcp_server.list_resource_templates()(self.list_resource_templates)
|
||||||
# self._mcp_server.list_resource_templates()(self.list_resource_templates)
|
|
||||||
|
|
||||||
async def list_tools(self) -> list[MCPTool]:
|
async def list_tools(self) -> list[MCPTool]:
|
||||||
"""List all available tools."""
|
"""List all available tools."""
|
||||||
|
|
|
||||||
44
tests/issues/test_129_resource_templates.py
Normal file
44
tests/issues/test_129_resource_templates.py
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from mcp import types
|
||||||
|
from mcp.server.fastmcp import FastMCP
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.anyio
|
||||||
|
async def test_resource_templates():
|
||||||
|
# Create an MCP server
|
||||||
|
mcp = FastMCP("Demo")
|
||||||
|
|
||||||
|
# Add a dynamic greeting resource
|
||||||
|
@mcp.resource("greeting://{name}")
|
||||||
|
def get_greeting(name: str) -> str:
|
||||||
|
"""Get a personalized greeting"""
|
||||||
|
return f"Hello, {name}!"
|
||||||
|
|
||||||
|
@mcp.resource("users://{user_id}/profile")
|
||||||
|
def get_user_profile(user_id: str) -> str:
|
||||||
|
"""Dynamic user data"""
|
||||||
|
return f"Profile data for user {user_id}"
|
||||||
|
|
||||||
|
# Get the list of resource templates using the underlying server
|
||||||
|
# Note: list_resource_templates() returns a decorator that wraps the handler
|
||||||
|
# The handler returns a ServerResult with a ListResourceTemplatesResult inside
|
||||||
|
result = await mcp._mcp_server.request_handlers[types.ListResourceTemplatesRequest](
|
||||||
|
types.ListResourceTemplatesRequest(
|
||||||
|
method="resources/templates/list", params=None, cursor=None
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assert isinstance(result.root, types.ListResourceTemplatesResult)
|
||||||
|
templates = result.root.resourceTemplates
|
||||||
|
|
||||||
|
# Verify we get both templates back
|
||||||
|
assert len(templates) == 2
|
||||||
|
|
||||||
|
# Verify template details
|
||||||
|
greeting_template = next(t for t in templates if t.name == "get_greeting")
|
||||||
|
assert greeting_template.uriTemplate == "greeting://{name}"
|
||||||
|
assert greeting_template.description == "Get a personalized greeting"
|
||||||
|
|
||||||
|
profile_template = next(t for t in templates if t.name == "get_user_profile")
|
||||||
|
assert profile_template.uriTemplate == "users://{user_id}/profile"
|
||||||
|
assert profile_template.description == "Dynamic user data"
|
||||||
30
tests/server/fastmcp/test_parameter_descriptions.py
Normal file
30
tests/server/fastmcp/test_parameter_descriptions.py
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
"""Test that parameter descriptions are properly exposed through list_tools"""
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from pydantic import Field
|
||||||
|
|
||||||
|
from mcp.server.fastmcp import FastMCP
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_parameter_descriptions():
|
||||||
|
mcp = FastMCP("Test Server")
|
||||||
|
|
||||||
|
@mcp.tool()
|
||||||
|
def greet(
|
||||||
|
name: str = Field(description="The name to greet"),
|
||||||
|
title: str = Field(description="Optional title", default=""),
|
||||||
|
) -> str:
|
||||||
|
"""A greeting tool"""
|
||||||
|
return f"Hello {title} {name}"
|
||||||
|
|
||||||
|
tools = await mcp.list_tools()
|
||||||
|
assert len(tools) == 1
|
||||||
|
tool = tools[0]
|
||||||
|
|
||||||
|
# Check that parameter descriptions are present in the schema
|
||||||
|
properties = tool.inputSchema["properties"]
|
||||||
|
assert "name" in properties
|
||||||
|
assert properties["name"]["description"] == "The name to greet"
|
||||||
|
assert "title" in properties
|
||||||
|
assert properties["title"]["description"] == "Optional title"
|
||||||
2
uv.lock
generated
2
uv.lock
generated
|
|
@ -191,7 +191,7 @@ wheels = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mcp"
|
name = "mcp"
|
||||||
version = "1.2.0.dev0"
|
version = "1.2.0"
|
||||||
source = { editable = "." }
|
source = { editable = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "anyio" },
|
{ name = "anyio" },
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue