Skip to content

Commit 8d17fd3

Browse files
authored
Merge pull request #137 from modelcontextprotocol/v1.2.x
fix: #129 resource template handling in FastMCP server
2 parents 0c00fdc + 4770bcd commit 8d17fd3

File tree

6 files changed

+98
-4
lines changed

6 files changed

+98
-4
lines changed
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
"""
2+
FastMCP Example showing parameter descriptions
3+
"""
4+
5+
from pydantic import Field
6+
7+
from mcp.server.fastmcp import FastMCP
8+
9+
# Create server
10+
mcp = FastMCP("Parameter Descriptions Server")
11+
12+
13+
@mcp.tool()
14+
def greet_user(
15+
name: str = Field(description="The name of the person to greet"),
16+
title: str = Field(description="Optional title like Mr/Ms/Dr", default=""),
17+
times: int = Field(description="Number of times to repeat the greeting", default=1),
18+
) -> str:
19+
"""Greet a user with optional title and repetition"""
20+
greeting = f"Hello {title + ' ' if title else ''}{name}!"
21+
return "\n".join([greeting] * times)

src/mcp/cli/cli.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66
import subprocess
77
import sys
88
from pathlib import Path
9+
from typing import Annotated
910

1011
try:
1112
import typer
12-
from typing_extensions import Annotated
1313
except ImportError:
1414
print("Error: typer is required. Install with 'pip install mcp[cli]'")
1515
sys.exit(1)

src/mcp/server/fastmcp/server.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,7 @@ def _setup_handlers(self) -> None:
133133
self._mcp_server.read_resource()(self.read_resource)
134134
self._mcp_server.list_prompts()(self.list_prompts)
135135
self._mcp_server.get_prompt()(self.get_prompt)
136-
# TODO: This has not been added to MCP yet, see https://github.com/jlowin/fastmcp/issues/10
137-
# self._mcp_server.list_resource_templates()(self.list_resource_templates)
136+
self._mcp_server.list_resource_templates()(self.list_resource_templates)
138137

139138
async def list_tools(self) -> list[MCPTool]:
140139
"""List all available tools."""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import pytest
2+
3+
from mcp import types
4+
from mcp.server.fastmcp import FastMCP
5+
6+
7+
@pytest.mark.anyio
8+
async def test_resource_templates():
9+
# Create an MCP server
10+
mcp = FastMCP("Demo")
11+
12+
# Add a dynamic greeting resource
13+
@mcp.resource("greeting://{name}")
14+
def get_greeting(name: str) -> str:
15+
"""Get a personalized greeting"""
16+
return f"Hello, {name}!"
17+
18+
@mcp.resource("users://{user_id}/profile")
19+
def get_user_profile(user_id: str) -> str:
20+
"""Dynamic user data"""
21+
return f"Profile data for user {user_id}"
22+
23+
# Get the list of resource templates using the underlying server
24+
# Note: list_resource_templates() returns a decorator that wraps the handler
25+
# The handler returns a ServerResult with a ListResourceTemplatesResult inside
26+
result = await mcp._mcp_server.request_handlers[types.ListResourceTemplatesRequest](
27+
types.ListResourceTemplatesRequest(
28+
method="resources/templates/list", params=None, cursor=None
29+
)
30+
)
31+
assert isinstance(result.root, types.ListResourceTemplatesResult)
32+
templates = result.root.resourceTemplates
33+
34+
# Verify we get both templates back
35+
assert len(templates) == 2
36+
37+
# Verify template details
38+
greeting_template = next(t for t in templates if t.name == "get_greeting")
39+
assert greeting_template.uriTemplate == "greeting://{name}"
40+
assert greeting_template.description == "Get a personalized greeting"
41+
42+
profile_template = next(t for t in templates if t.name == "get_user_profile")
43+
assert profile_template.uriTemplate == "users://{user_id}/profile"
44+
assert profile_template.description == "Dynamic user data"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
"""Test that parameter descriptions are properly exposed through list_tools"""
2+
3+
import pytest
4+
from pydantic import Field
5+
6+
from mcp.server.fastmcp import FastMCP
7+
8+
9+
@pytest.mark.asyncio
10+
async def test_parameter_descriptions():
11+
mcp = FastMCP("Test Server")
12+
13+
@mcp.tool()
14+
def greet(
15+
name: str = Field(description="The name to greet"),
16+
title: str = Field(description="Optional title", default=""),
17+
) -> str:
18+
"""A greeting tool"""
19+
return f"Hello {title} {name}"
20+
21+
tools = await mcp.list_tools()
22+
assert len(tools) == 1
23+
tool = tools[0]
24+
25+
# Check that parameter descriptions are present in the schema
26+
properties = tool.inputSchema["properties"]
27+
assert "name" in properties
28+
assert properties["name"]["description"] == "The name to greet"
29+
assert "title" in properties
30+
assert properties["title"]["description"] == "Optional title"

uv.lock

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)