Skip to content

Commit 07558fc

Browse files
committed
Add icons support for ResourceTemplate
This change adds icons metadata support to ResourceTemplate, bringing it in line with Resource, Tool, and Prompt types. The MCP specification was updated to include icons for ResourceTemplate, and this implements that feature in the Python SDK. Changes: - Add icons field to ResourceTemplate type definition - Update ResourceTemplate class to store and pass icons - Update resource_manager to accept icons parameter - Update FastMCP server to expose icons in list_resource_templates - Add test coverage for resource template icons All primitives (tools, resources, prompts, resource templates) now consistently support icons metadata.
1 parent 80c0d23 commit 07558fc

File tree

5 files changed

+27
-2
lines changed

5 files changed

+27
-2
lines changed

src/mcp/server/fastmcp/resources/resource_manager.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ def add_template(
6161
title: str | None = None,
6262
description: str | None = None,
6363
mime_type: str | None = None,
64+
icons: list[Any] | None = None,
6465
) -> ResourceTemplate:
6566
"""Add a template from a function."""
6667
template = ResourceTemplate.from_function(
@@ -70,6 +71,7 @@ def add_template(
7071
title=title,
7172
description=description,
7273
mime_type=mime_type,
74+
icons=icons,
7375
)
7476
self._templates[template.uri_template] = template
7577
return template

src/mcp/server/fastmcp/resources/templates.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from mcp.server.fastmcp.resources.types import FunctionResource, Resource
1313
from mcp.server.fastmcp.utilities.context_injection import find_context_parameter, inject_context
1414
from mcp.server.fastmcp.utilities.func_metadata import func_metadata
15+
from mcp.types import Icon
1516

1617
if TYPE_CHECKING:
1718
from mcp.server.fastmcp.server import Context
@@ -27,6 +28,7 @@ class ResourceTemplate(BaseModel):
2728
title: str | None = Field(description="Human-readable title of the resource", default=None)
2829
description: str | None = Field(description="Description of what the resource does")
2930
mime_type: str = Field(default="text/plain", description="MIME type of the resource content")
31+
icons: list[Icon] | None = Field(default=None, description="Optional list of icons for the resource template")
3032
fn: Callable[..., Any] = Field(exclude=True)
3133
parameters: dict[str, Any] = Field(description="JSON schema for function parameters")
3234
context_kwarg: str | None = Field(None, description="Name of the kwarg that should receive context")
@@ -40,6 +42,7 @@ def from_function(
4042
title: str | None = None,
4143
description: str | None = None,
4244
mime_type: str | None = None,
45+
icons: list[Icon] | None = None,
4346
context_kwarg: str | None = None,
4447
) -> ResourceTemplate:
4548
"""Create a template from a function."""
@@ -67,6 +70,7 @@ def from_function(
6770
title=title,
6871
description=description or fn.__doc__ or "",
6972
mime_type=mime_type or "text/plain",
73+
icons=icons,
7074
fn=fn,
7175
parameters=parameters,
7276
context_kwarg=context_kwarg,
@@ -103,7 +107,7 @@ async def create_resource(
103107
title=self.title,
104108
description=self.description,
105109
mime_type=self.mime_type,
106-
icons=None, # Resource templates don't support icons
110+
icons=self.icons,
107111
fn=lambda: result, # Capture result in closure
108112
)
109113
except Exception as e:

src/mcp/server/fastmcp/server.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,7 @@ async def list_resource_templates(self) -> list[MCPResourceTemplate]:
335335
title=template.title,
336336
description=template.description,
337337
mimeType=template.mime_type,
338+
icons=template.icons,
338339
)
339340
for template in templates
340341
]
@@ -559,7 +560,7 @@ def decorator(fn: AnyFunction) -> AnyFunction:
559560
title=title,
560561
description=description,
561562
mime_type=mime_type,
562-
# Note: Resource templates don't support icons
563+
icons=icons,
563564
)
564565
else:
565566
# Register as regular resource

src/mcp/types.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,8 @@ class ResourceTemplate(BaseMetadata):
470470
The MIME type for all resources that match this template. This should only be
471471
included if all resources matching this template have the same type.
472472
"""
473+
icons: list[Icon] | None = None
474+
"""An optional list of icons for this resource template."""
473475
annotations: Annotations | None = None
474476
meta: dict[str, Any] | None = Field(alias="_meta", default=None)
475477
"""

tests/issues/test_1338_icons_and_metadata.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ def test_prompt(text: str) -> str:
3939
"""A test prompt with an icon."""
4040
return text
4141

42+
# Create resource template with icon
43+
@mcp.resource("test://weather/{city}", icons=[test_icon])
44+
def test_resource_template(city: str) -> str:
45+
"""Get weather for a city."""
46+
return f"Weather for {city}"
47+
4248
# Test server metadata includes websiteUrl and icons
4349
assert mcp.name == "TestServer"
4450
assert mcp.website_url == "https://example.com"
@@ -75,6 +81,16 @@ def test_prompt(text: str) -> str:
7581
assert len(prompt.icons) == 1
7682
assert prompt.icons[0].src == test_icon.src
7783

84+
# Test resource template includes icon
85+
templates = await mcp.list_resource_templates()
86+
assert len(templates) == 1
87+
template = templates[0]
88+
assert template.name == "test_resource_template"
89+
assert template.uriTemplate == "test://weather/{city}"
90+
assert template.icons is not None
91+
assert len(template.icons) == 1
92+
assert template.icons[0].src == test_icon.src
93+
7894

7995
async def test_multiple_icons():
8096
"""Test that multiple icons can be added to tools, resources, and prompts."""

0 commit comments

Comments
 (0)