Skip to content

Commit

Permalink
Merge pull request #137 from modelcontextprotocol/v1.2.x
Browse files Browse the repository at this point in the history
fix: #129 resource template handling in FastMCP server
  • Loading branch information
dsp-ant authored Jan 6, 2025
2 parents 0c00fdc + 4770bcd commit 8d17fd3
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 4 deletions.
21 changes: 21 additions & 0 deletions examples/fastmcp/parameter_descriptions.py
Original file line number Diff line number Diff line change
@@ -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)
2 changes: 1 addition & 1 deletion src/mcp/cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
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)
Expand Down
3 changes: 1 addition & 2 deletions src/mcp/server/fastmcp/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,7 @@ def _setup_handlers(self) -> None:
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."""
Expand Down
44 changes: 44 additions & 0 deletions tests/issues/test_129_resource_templates.py
Original file line number Diff line number Diff line change
@@ -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 changes: 30 additions & 0 deletions tests/server/fastmcp/test_parameter_descriptions.py
Original file line number Diff line number Diff line change
@@ -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 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 8d17fd3

Please sign in to comment.