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 sys
|
||||
from pathlib import Path
|
||||
from typing import Annotated
|
||||
|
||||
try:
|
||||
import typer
|
||||
from typing_extensions import Annotated
|
||||
except ImportError:
|
||||
print("Error: typer is required. Install with 'pip install mcp[cli]'")
|
||||
sys.exit(1)
|
||||
|
|
|
|||
|
|
@ -133,8 +133,7 @@ class FastMCP:
|
|||
self._mcp_server.read_resource()(self.read_resource)
|
||||
self._mcp_server.list_prompts()(self.list_prompts)
|
||||
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]:
|
||||
"""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]]
|
||||
name = "mcp"
|
||||
version = "1.2.0.dev0"
|
||||
version = "1.2.0"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "anyio" },
|
||||
|
|
|
|||
Loading…
Reference in a new issue