From 6ad4bb3fd839daecb7de9ba1df6611bd47ebf888 Mon Sep 17 00:00:00 2001 From: Peter Alexander Date: Tue, 9 Sep 2025 19:47:18 +0100 Subject: [PATCH 01/11] Implementation of SEP-973 --- examples/fastmcp/icons_demo.py | 59 ++++++++++++++++++ examples/fastmcp/mcp.png | Bin 0 -> 62915 bytes src/mcp/server/fastmcp/prompts/base.py | 5 +- src/mcp/server/fastmcp/resources/base.py | 3 + src/mcp/server/fastmcp/resources/templates.py | 1 + src/mcp/server/fastmcp/resources/types.py | 3 + src/mcp/server/fastmcp/server.py | 24 ++++++- src/mcp/server/fastmcp/tools/base.py | 5 +- src/mcp/server/fastmcp/tools/tool_manager.py | 4 +- src/mcp/server/lowlevel/server.py | 6 ++ src/mcp/server/models.py | 3 + src/mcp/server/session.py | 2 + src/mcp/types.py | 28 +++++++++ 13 files changed, 137 insertions(+), 6 deletions(-) create mode 100644 examples/fastmcp/icons_demo.py create mode 100644 examples/fastmcp/mcp.png diff --git a/examples/fastmcp/icons_demo.py b/examples/fastmcp/icons_demo.py new file mode 100644 index 000000000..183dbb70d --- /dev/null +++ b/examples/fastmcp/icons_demo.py @@ -0,0 +1,59 @@ +""" +FastMCP Icons Demo Server + +Demonstrates using icons with tools, resources, prompts, and implementation. +""" + +import base64 +from pathlib import Path + +from mcp.server.fastmcp import FastMCP +from mcp.types import Icon + +# Load the icon file and convert to data URI +icon_path = Path(__file__).parent / "mcp.png" +with open(icon_path, "rb") as f: + icon_data = base64.b64encode(f.read()).decode("utf-8") + icon_data_uri = f"data:image/png;base64,{icon_data}" + +icon_data = Icon(src=icon_data_uri, mimeType="image/png", sizes="32x32") + +# Create server with icons in implementation +mcp = FastMCP( + "Icons Demo Server", website_url="https://https://github.com/modelcontextprotocol/python-sdk", icons=[icon_data] +) + + +@mcp.tool(icons=[icon_data]) +def demo_tool(message: str) -> str: + """A demo tool with an icon.""" + return message + + +@mcp.resource("demo://readme", icons=[icon_data]) +def readme_resource() -> str: + """A demo resource with an icon""" + return "This resource has an icon" + + +@mcp.prompt("prompt_with_icon", icons=[icon_data]) +def prompt_with_icon(text: str) -> str: + """A demo prompt with an icon""" + return text + + +@mcp.tool( + icons=[ + Icon(src=icon_data_uri, mimeType="image/png", sizes="16x16"), + Icon(src=icon_data_uri, mimeType="image/png", sizes="32x32"), + Icon(src=icon_data_uri, mimeType="image/png", sizes="64x64"), + ] +) +def multi_icon_tool(action: str) -> str: + """A tool demonstrating multiple icons.""" + return "multi_icon_tool" + + +if __name__ == "__main__": + # Run the server + mcp.run() diff --git a/examples/fastmcp/mcp.png b/examples/fastmcp/mcp.png new file mode 100644 index 0000000000000000000000000000000000000000..3bfd58ef1a270dea7f5be4c59ce6ee0ad98f82d0 GIT binary patch literal 62915 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
w4gLiSw?8H2=9@Q0q)c)*3QqSVaW3#pOsaU| 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

cLr6r`BI4ieU67mk|hui=31L?w7-YJhqI*0}mC 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 "Prompt": """Create a Prompt from a function. @@ -109,6 +111,7 @@ def from_function( description=description or fn.__doc__ or "", arguments=arguments, fn=fn, + icons=icons, ) async def render(self, arguments: dict[str, Any] | None = None) -> list[Message]: diff --git a/src/mcp/server/fastmcp/resources/base.py b/src/mcp/server/fastmcp/resources/base.py index f57631cc1..0bef1a266 100644 --- a/src/mcp/server/fastmcp/resources/base.py +++ b/src/mcp/server/fastmcp/resources/base.py @@ -13,6 +13,8 @@ field_validator, ) +from mcp.types import Icon + class Resource(BaseModel, abc.ABC): """Base class for all resources.""" @@ -28,6 +30,7 @@ class Resource(BaseModel, abc.ABC): description="MIME type of the resource content", pattern=r"^[a-zA-Z0-9]+/[a-zA-Z0-9\-+.]+$", ) + icons: list[Icon] | None = Field(default=None, description="Optional list of icons for this resource") @field_validator("name", mode="before") @classmethod diff --git a/src/mcp/server/fastmcp/resources/templates.py b/src/mcp/server/fastmcp/resources/templates.py index b1c7b2711..642f93d04 100644 --- a/src/mcp/server/fastmcp/resources/templates.py +++ b/src/mcp/server/fastmcp/resources/templates.py @@ -78,6 +78,7 @@ async def create_resource(self, uri: str, params: dict[str, Any]) -> Resource: description=self.description, mime_type=self.mime_type, fn=lambda: result, # Capture result in closure + icons=None, # Resource templates don't support icons ) except Exception as e: raise ValueError(f"Error creating resource from template: {e}") diff --git a/src/mcp/server/fastmcp/resources/types.py b/src/mcp/server/fastmcp/resources/types.py index f2a330706..c578e23de 100644 --- a/src/mcp/server/fastmcp/resources/types.py +++ b/src/mcp/server/fastmcp/resources/types.py @@ -14,6 +14,7 @@ from pydantic import AnyUrl, Field, ValidationInfo, validate_call from mcp.server.fastmcp.resources.base import Resource +from mcp.types import Icon class TextResource(Resource): @@ -80,6 +81,7 @@ def from_function( title: str | None = None, description: str | None = None, mime_type: str | None = None, + icons: list[Icon] | None = None, ) -> "FunctionResource": """Create a FunctionResource from a function.""" func_name = name or fn.__name__ @@ -96,6 +98,7 @@ def from_function( description=description or fn.__doc__ or "", mime_type=mime_type or "text/plain", fn=fn, + icons=icons, ) diff --git a/src/mcp/server/fastmcp/server.py b/src/mcp/server/fastmcp/server.py index 8724deca7..c78921cbd 100644 --- a/src/mcp/server/fastmcp/server.py +++ b/src/mcp/server/fastmcp/server.py @@ -42,7 +42,7 @@ from mcp.server.streamable_http_manager import StreamableHTTPSessionManager from mcp.server.transport_security import TransportSecuritySettings from mcp.shared.context import LifespanContextT, RequestContext, RequestT -from mcp.types import AnyFunction, ContentBlock, GetPromptResult, ToolAnnotations +from mcp.types import AnyFunction, ContentBlock, GetPromptResult, Icon, ToolAnnotations from mcp.types import Prompt as MCPPrompt from mcp.types import PromptArgument as MCPPromptArgument from mcp.types import Resource as MCPResource @@ -123,6 +123,8 @@ def __init__( self, name: str | None = None, instructions: str | None = None, + website_url: str | None = None, + icons: list[Icon] | None = None, auth_server_provider: OAuthAuthorizationServerProvider[Any, Any, Any] | None = None, token_verifier: TokenVerifier | None = None, event_store: EventStore | None = None, @@ -169,6 +171,8 @@ def __init__( self._mcp_server = MCPServer( name=name or "FastMCP", instructions=instructions, + website_url=website_url, + icons=icons, # TODO(Marcelo): It seems there's a type mismatch between the lifespan type from an FastMCP and Server. # We need to create a Lifespan type that is a generic on the server type, like Starlette does. lifespan=(lifespan_wrapper(self, self.settings.lifespan) if self.settings.lifespan else default_lifespan), # type: ignore @@ -276,6 +280,7 @@ async def list_tools(self) -> list[MCPTool]: inputSchema=info.parameters, outputSchema=info.output_schema, annotations=info.annotations, + icons=info.icons, ) for info in tools ] @@ -307,6 +312,7 @@ async def list_resources(self) -> list[MCPResource]: title=resource.title, description=resource.description, mimeType=resource.mime_type, + icons=resource.icons, ) for resource in resources ] @@ -344,6 +350,7 @@ def add_tool( title: str | None = None, description: str | None = None, annotations: ToolAnnotations | None = None, + icons: list[Icon] | None = None, structured_output: bool | None = None, ) -> None: """Add a tool to the server. @@ -368,6 +375,7 @@ def add_tool( title=title, description=description, annotations=annotations, + icons=icons, structured_output=structured_output, ) @@ -377,6 +385,7 @@ def tool( title: str | None = None, description: str | None = None, annotations: ToolAnnotations | None = None, + icons: list[Icon] | None = None, structured_output: bool | None = None, ) -> Callable[[AnyFunction], AnyFunction]: """Decorator to register a tool. @@ -423,6 +432,7 @@ def decorator(fn: AnyFunction) -> AnyFunction: title=title, description=description, annotations=annotations, + icons=icons, structured_output=structured_output, ) return fn @@ -463,6 +473,7 @@ def resource( title: str | None = None, description: str | None = None, mime_type: str | None = None, + icons: list[Icon] | None = None, ) -> Callable[[AnyFunction], AnyFunction]: """Decorator to register a function as a resource. @@ -531,6 +542,7 @@ def decorator(fn: AnyFunction) -> AnyFunction: title=title, description=description, mime_type=mime_type, + # Note: Resource templates don't support icons ) else: # Register as regular resource @@ -541,6 +553,7 @@ def decorator(fn: AnyFunction) -> AnyFunction: title=title, description=description, mime_type=mime_type, + icons=icons, ) self.add_resource(resource) return fn @@ -556,7 +569,11 @@ def add_prompt(self, prompt: Prompt) -> None: self._prompt_manager.add_prompt(prompt) def prompt( - self, name: str | None = None, title: str | None = None, description: str | None = None + self, + name: str | None = None, + title: str | None = None, + description: str | None = None, + icons: list[Icon] | None = None, ) -> Callable[[AnyFunction], AnyFunction]: """Decorator to register a prompt. @@ -600,7 +617,7 @@ async def analyze_file(path: str) -> list[Message]: ) def decorator(func: AnyFunction) -> AnyFunction: - prompt = Prompt.from_function(func, name=name, title=title, description=description) + prompt = Prompt.from_function(func, name=name, title=title, description=description, icons=icons) self.add_prompt(prompt) return func @@ -971,6 +988,7 @@ async def list_prompts(self) -> list[MCPPrompt]: ) for arg in (prompt.arguments or []) ], + icons=prompt.icons, ) for prompt in prompts ] diff --git a/src/mcp/server/fastmcp/tools/base.py b/src/mcp/server/fastmcp/tools/base.py index f50126081..a44a50908 100644 --- a/src/mcp/server/fastmcp/tools/base.py +++ b/src/mcp/server/fastmcp/tools/base.py @@ -10,7 +10,7 @@ from mcp.server.fastmcp.exceptions import ToolError from mcp.server.fastmcp.utilities.func_metadata import FuncMetadata, func_metadata -from mcp.types import ToolAnnotations +from mcp.types import Icon, ToolAnnotations if TYPE_CHECKING: from mcp.server.fastmcp.server import Context @@ -32,6 +32,7 @@ class Tool(BaseModel): is_async: bool = Field(description="Whether the tool is async") context_kwarg: str | None = Field(None, description="Name of the kwarg that should receive context") annotations: ToolAnnotations | None = Field(None, description="Optional annotations for the tool") + icons: list[Icon] | None = Field(default=None, description="Optional list of icons for this tool") @cached_property def output_schema(self) -> dict[str, Any] | None: @@ -46,6 +47,7 @@ def from_function( description: str | None = None, context_kwarg: str | None = None, annotations: ToolAnnotations | None = None, + icons: list[Icon] | None = None, structured_output: bool | None = None, ) -> Tool: """Create a Tool from a function.""" @@ -85,6 +87,7 @@ def from_function( is_async=is_async, context_kwarg=context_kwarg, annotations=annotations, + icons=icons, ) async def run( diff --git a/src/mcp/server/fastmcp/tools/tool_manager.py b/src/mcp/server/fastmcp/tools/tool_manager.py index bfa8b2382..443196d0d 100644 --- a/src/mcp/server/fastmcp/tools/tool_manager.py +++ b/src/mcp/server/fastmcp/tools/tool_manager.py @@ -7,7 +7,7 @@ from mcp.server.fastmcp.tools.base import Tool from mcp.server.fastmcp.utilities.logging import get_logger from mcp.shared.context import LifespanContextT, RequestT -from mcp.types import ToolAnnotations +from mcp.types import Icon, ToolAnnotations if TYPE_CHECKING: from mcp.server.fastmcp.server import Context @@ -49,6 +49,7 @@ def add_tool( title: str | None = None, description: str | None = None, annotations: ToolAnnotations | None = None, + icons: list[Icon] | None = None, structured_output: bool | None = None, ) -> Tool: """Add a tool to the server.""" @@ -58,6 +59,7 @@ def add_tool( title=title, description=description, annotations=annotations, + icons=icons, structured_output=structured_output, ) existing = self._tools.get(tool.name) diff --git a/src/mcp/server/lowlevel/server.py b/src/mcp/server/lowlevel/server.py index 3076e283e..e4a6fc2ee 100644 --- a/src/mcp/server/lowlevel/server.py +++ b/src/mcp/server/lowlevel/server.py @@ -135,6 +135,8 @@ def __init__( name: str, version: str | None = None, instructions: str | None = None, + website_url: str | None = None, + icons: list[types.Icon] | None = None, lifespan: Callable[ [Server[LifespanResultT, RequestT]], AbstractAsyncContextManager[LifespanResultT], @@ -143,6 +145,8 @@ def __init__( self.name = name self.version = version self.instructions = instructions + self.website_url = website_url + self.icons = icons self.lifespan = lifespan self.request_handlers: dict[type, Callable[..., Awaitable[types.ServerResult]]] = { types.PingRequest: _ping_handler, @@ -176,6 +180,8 @@ def pkg_version(package: str) -> str: experimental_capabilities or {}, ), instructions=self.instructions, + website_url=self.website_url, + icons=self.icons, ) def get_capabilities( diff --git a/src/mcp/server/models.py b/src/mcp/server/models.py index 3b5abba78..ddf716cb9 100644 --- a/src/mcp/server/models.py +++ b/src/mcp/server/models.py @@ -6,6 +6,7 @@ from pydantic import BaseModel from mcp.types import ( + Icon, ServerCapabilities, ) @@ -15,3 +16,5 @@ class InitializationOptions(BaseModel): server_version: str capabilities: ServerCapabilities instructions: str | None = None + website_url: str | None = None + icons: list[Icon] | None = None diff --git a/src/mcp/server/session.py b/src/mcp/server/session.py index 7b3680f7c..d00277f11 100644 --- a/src/mcp/server/session.py +++ b/src/mcp/server/session.py @@ -156,6 +156,8 @@ async def _received_request(self, responder: RequestResponder[types.ClientReques serverInfo=types.Implementation( name=self._init_options.server_name, version=self._init_options.server_version, + websiteUrl=self._init_options.website_url, + icons=self._init_options.icons, ), instructions=self._init_options.instructions, ) diff --git a/src/mcp/types.py b/src/mcp/types.py index 62feda87a..fa99f748b 100644 --- a/src/mcp/types.py +++ b/src/mcp/types.py @@ -213,10 +213,32 @@ class BaseMetadata(BaseModel): """ +class Icon(BaseModel): + """An icon for display in user interfaces.""" + + src: str + """URL or data URI for the icon.""" + + mimeType: str | None = None + """Optional MIME type for the icon.""" + + sizes: str | None = None + """Optional string specifying icon dimensions (e.g., "48x48 96x96").""" + + model_config = ConfigDict(extra="allow") + + class Implementation(BaseMetadata): """Describes the name and version of an MCP implementation.""" version: str + + websiteUrl: str | None = None + """An optional URL of the website for this implementation.""" + + icons: list[Icon] | None = None + """An optional list of icons for this implementation.""" + model_config = ConfigDict(extra="allow") @@ -422,6 +444,8 @@ class Resource(BaseMetadata): This can be used by Hosts to display file sizes and estimate context window usage. """ + icons: list[Icon] | None = None + """An optional list of icons for this resource.""" annotations: Annotations | None = None meta: dict[str, Any] | None = Field(alias="_meta", default=None) """ @@ -628,6 +652,8 @@ class Prompt(BaseMetadata): """An optional description of what this prompt provides.""" arguments: list[PromptArgument] | None = None """A list of arguments to use for templating the prompt.""" + icons: list[Icon] | None = None + """An optional list of icons for this prompt.""" meta: dict[str, Any] | None = Field(alias="_meta", default=None) """ See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) @@ -852,6 +878,8 @@ class Tool(BaseMetadata): An optional JSON Schema object defining the structure of the tool's output returned in the structuredContent field of a CallToolResult. """ + icons: list[Icon] | None = None + """An optional list of icons for this tool.""" annotations: ToolAnnotations | None = None """Optional additional tool information.""" meta: dict[str, Any] | None = Field(alias="_meta", default=None) From b999b4e460cafd024aeed1697403893437e48f27 Mon Sep 17 00:00:00 2001 From: Peter Alexander Date: Tue, 9 Sep 2025 21:40:03 +0100 Subject: [PATCH 02/11] Resize icon to 64x64 and update demo sizes --- examples/fastmcp/icons_demo.py | 2 +- examples/fastmcp/mcp.png | Bin 62915 -> 2586 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/fastmcp/icons_demo.py b/examples/fastmcp/icons_demo.py index 183dbb70d..105d2e3c6 100644 --- a/examples/fastmcp/icons_demo.py +++ b/examples/fastmcp/icons_demo.py @@ -16,7 +16,7 @@ icon_data = base64.b64encode(f.read()).decode("utf-8") icon_data_uri = f"data:image/png;base64,{icon_data}" -icon_data = Icon(src=icon_data_uri, mimeType="image/png", sizes="32x32") +icon_data = Icon(src=icon_data_uri, mimeType="image/png", sizes="64x64") # Create server with icons in implementation mcp = FastMCP( diff --git a/examples/fastmcp/mcp.png b/examples/fastmcp/mcp.png index 3bfd58ef1a270dea7f5be4c59ce6ee0ad98f82d0..8e08571d326bba9110ea3ced50e6769d0e100b21 100644 GIT binary patch literal 2586 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


w4gLiSw?8H2=9@Q0q)c)*3QqSVaW3#pOsaU| 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

cLr6r`BI4ieU67mk|hui=31L?w7-YJhqI*0}mC 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 Date: Tue, 9 Sep 2025 21:52:59 +0100 Subject: [PATCH 03/11] Add public properties for website_url and icons, add tests --- src/mcp/server/fastmcp/server.py | 8 ++ tests/issues/test_1338_icons_and_metadata.py | 130 +++++++++++++++++++ 2 files changed, 138 insertions(+) create mode 100644 tests/issues/test_1338_icons_and_metadata.py diff --git a/src/mcp/server/fastmcp/server.py b/src/mcp/server/fastmcp/server.py index c78921cbd..d33cf83dd 100644 --- a/src/mcp/server/fastmcp/server.py +++ b/src/mcp/server/fastmcp/server.py @@ -214,6 +214,14 @@ def name(self) -> str: def instructions(self) -> str | None: return self._mcp_server.instructions + @property + def website_url(self) -> str | None: + return self._mcp_server.website_url + + @property + def icons(self) -> list[Icon] | None: + return self._mcp_server.icons + @property def session_manager(self) -> StreamableHTTPSessionManager: """Get the StreamableHTTP session manager. diff --git a/tests/issues/test_1338_icons_and_metadata.py b/tests/issues/test_1338_icons_and_metadata.py new file mode 100644 index 000000000..a95371636 --- /dev/null +++ b/tests/issues/test_1338_icons_and_metadata.py @@ -0,0 +1,130 @@ +"""Test icon and metadata support (SEP-973).""" + +import pytest + +from mcp.server.fastmcp import FastMCP +from mcp.types import Icon + +pytestmark = pytest.mark.anyio + + +async def test_icons_and_website_url(): + """Test that icons and websiteUrl are properly returned in API calls.""" + + # Create test icon + test_icon = Icon( + src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==", + mimeType="image/png", + sizes="1x1" + ) + + # Create server with website URL and icon + mcp = FastMCP( + "TestServer", + website_url="https://example.com", + icons=[test_icon] + ) + + # Create tool with icon + @mcp.tool(icons=[test_icon]) + def test_tool(message: str) -> str: + """A test tool with an icon.""" + return message + + # Create resource with icon + @mcp.resource("test://resource", icons=[test_icon]) + def test_resource() -> str: + """A test resource with an icon.""" + return "test content" + + # Create prompt with icon + @mcp.prompt("test_prompt", icons=[test_icon]) + def test_prompt(text: str) -> str: + """A test prompt with an icon.""" + return text + + # Test server metadata includes websiteUrl and icons + assert mcp.name == "TestServer" + assert mcp.website_url == "https://example.com" + assert mcp.icons is not None + assert len(mcp.icons) == 1 + assert mcp.icons[0].src == test_icon.src + assert mcp.icons[0].mimeType == test_icon.mimeType + assert mcp.icons[0].sizes == test_icon.sizes + + # Test tool includes icon + tools = await mcp.list_tools() + assert len(tools) == 1 + tool = tools[0] + assert tool.name == "test_tool" + assert tool.icons is not None + assert len(tool.icons) == 1 + assert tool.icons[0].src == test_icon.src + + # Test resource includes icon + resources = await mcp.list_resources() + assert len(resources) == 1 + resource = resources[0] + assert str(resource.uri) == "test://resource" + assert resource.icons is not None + assert len(resource.icons) == 1 + assert resource.icons[0].src == test_icon.src + + # Test prompt includes icon + prompts = await mcp.list_prompts() + assert len(prompts) == 1 + prompt = prompts[0] + assert prompt.name == "test_prompt" + assert prompt.icons is not None + assert len(prompt.icons) == 1 + assert prompt.icons[0].src == test_icon.src + + +async def test_multiple_icons(): + """Test that multiple icons can be added to tools, resources, and prompts.""" + + # Create multiple test icons + icon1 = Icon(src="data:image/png;base64,icon1", mimeType="image/png", sizes="16x16") + icon2 = Icon(src="data:image/png;base64,icon2", mimeType="image/png", sizes="32x32") + icon3 = Icon(src="data:image/png;base64,icon3", mimeType="image/png", sizes="64x64") + + mcp = FastMCP("MultiIconServer") + + # Create tool with multiple icons + @mcp.tool(icons=[icon1, icon2, icon3]) + def multi_icon_tool() -> str: + """A tool with multiple icons.""" + return "success" + + # Test tool has all icons + tools = await mcp.list_tools() + assert len(tools) == 1 + tool = tools[0] + assert tool.icons is not None + assert len(tool.icons) == 3 + assert tool.icons[0].sizes == "16x16" + assert tool.icons[1].sizes == "32x32" + assert tool.icons[2].sizes == "64x64" + + +async def test_no_icons_or_website(): + """Test that server works without icons or websiteUrl.""" + + mcp = FastMCP("BasicServer") + + @mcp.tool() + def basic_tool() -> str: + """A basic tool without icons.""" + return "success" + + # Test server metadata has no websiteUrl or icons + assert mcp.name == "BasicServer" + assert mcp.website_url is None + assert mcp.icons is None + + # Test tool has no icons + tools = await mcp.list_tools() + assert len(tools) == 1 + tool = tools[0] + assert tool.name == "basic_tool" + assert tool.icons is None \ No newline at end of file From 883d2b4083b78a1f19792720ba1a52a46665462a Mon Sep 17 00:00:00 2001 From: Peter Alexander Date: Tue, 9 Sep 2025 21:54:08 +0100 Subject: [PATCH 04/11] Fix double https:// typo in icons demo website URL --- examples/fastmcp/icons_demo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/fastmcp/icons_demo.py b/examples/fastmcp/icons_demo.py index 105d2e3c6..e7741dfe5 100644 --- a/examples/fastmcp/icons_demo.py +++ b/examples/fastmcp/icons_demo.py @@ -20,7 +20,7 @@ # Create server with icons in implementation mcp = FastMCP( - "Icons Demo Server", website_url="https://https://github.com/modelcontextprotocol/python-sdk", icons=[icon_data] + "Icons Demo Server", website_url="https://github.com/modelcontextprotocol/python-sdk", icons=[icon_data] ) From d028efbe75d025c5d55b6b56a9f84d97586caacf Mon Sep 17 00:00:00 2001 From: Peter Alexander Date: Tue, 9 Sep 2025 21:59:36 +0100 Subject: [PATCH 05/11] Add icon documentation to README --- README.md | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/README.md b/README.md index d2fb9194a..821c868d2 100644 --- a/README.md +++ b/README.md @@ -515,6 +515,42 @@ def debug_error(error: str) -> list[base.Message]: _Full example: [examples/snippets/servers/basic_prompt.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/basic_prompt.py)_ +### Icons + +MCP servers can provide icons for UI display. Icons can be added to the server implementation, tools, resources, and prompts: + +```python +from mcp.server.fastmcp import FastMCP +from mcp.types import Icon + +# Create an icon from a file path or URL +icon = Icon( + src="icon.png", + mimeType="image/png", + sizes="64x64" +) + +# Add icons to server +mcp = FastMCP( + "My Server", + website_url="https://example.com", + icons=[icon] +) + +# Add icons to tools, resources, and prompts +@mcp.tool(icons=[icon]) +def my_tool(): + """Tool with an icon.""" + return "result" + +@mcp.resource("demo://resource", icons=[icon]) +def my_resource(): + """Resource with an icon.""" + return "content" +``` + +_Full example: [examples/fastmcp/icons_demo.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/fastmcp/icons_demo.py)_ + ### Images FastMCP provides an `Image` class that automatically handles image data: @@ -895,6 +931,8 @@ The FastMCP server instance accessible via `ctx.fastmcp` provides access to serv - `ctx.fastmcp.name` - The server's name as defined during initialization - `ctx.fastmcp.instructions` - Server instructions/description provided to clients +- `ctx.fastmcp.website_url` - Optional website URL for the server +- `ctx.fastmcp.icons` - Optional list of icons for UI display - `ctx.fastmcp.settings` - Complete server configuration object containing: - `debug` - Debug mode flag - `log_level` - Current logging level From 7bd4334f9ec314fa2b69943f4110ae3b89af0df8 Mon Sep 17 00:00:00 2001 From: Peter Alexander Date: Tue, 9 Sep 2025 22:07:41 +0100 Subject: [PATCH 06/11] Export Icon from FastMCP module for simpler imports --- README.md | 3 +-- examples/fastmcp/icons_demo.py | 3 +-- src/mcp/server/fastmcp/__init__.py | 4 +++- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 821c868d2..f54fac587 100644 --- a/README.md +++ b/README.md @@ -520,8 +520,7 @@ _Full example: [examples/snippets/servers/basic_prompt.py](https://github.com/mo MCP servers can provide icons for UI display. Icons can be added to the server implementation, tools, resources, and prompts: ```python -from mcp.server.fastmcp import FastMCP -from mcp.types import Icon +from mcp.server.fastmcp import FastMCP, Icon # Create an icon from a file path or URL icon = Icon( diff --git a/examples/fastmcp/icons_demo.py b/examples/fastmcp/icons_demo.py index e7741dfe5..d0a6c2e8b 100644 --- a/examples/fastmcp/icons_demo.py +++ b/examples/fastmcp/icons_demo.py @@ -7,8 +7,7 @@ import base64 from pathlib import Path -from mcp.server.fastmcp import FastMCP -from mcp.types import Icon +from mcp.server.fastmcp import FastMCP, Icon # Load the icon file and convert to data URI icon_path = Path(__file__).parent / "mcp.png" diff --git a/src/mcp/server/fastmcp/__init__.py b/src/mcp/server/fastmcp/__init__.py index f8f9c1c4c..a89902cfd 100644 --- a/src/mcp/server/fastmcp/__init__.py +++ b/src/mcp/server/fastmcp/__init__.py @@ -2,8 +2,10 @@ from importlib.metadata import version +from mcp.types import Icon + from .server import Context, FastMCP from .utilities.types import Audio, Image __version__ = version("mcp") -__all__ = ["FastMCP", "Context", "Image", "Audio"] +__all__ = ["FastMCP", "Context", "Image", "Audio", "Icon"] From 22a906745772b5991c06bfed75bdebdc24c5ef56 Mon Sep 17 00:00:00 2001 From: Peter Alexander Date: Tue, 9 Sep 2025 22:26:16 +0100 Subject: [PATCH 07/11] Add missing newline at end of test file --- tests/issues/test_1338_icons_and_metadata.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/issues/test_1338_icons_and_metadata.py b/tests/issues/test_1338_icons_and_metadata.py index a95371636..f8920542f 100644 --- a/tests/issues/test_1338_icons_and_metadata.py +++ b/tests/issues/test_1338_icons_and_metadata.py @@ -127,4 +127,4 @@ def basic_tool() -> str: assert len(tools) == 1 tool = tools[0] assert tool.name == "basic_tool" - assert tool.icons is None \ No newline at end of file + assert tool.icons is None From 10515fb7f7dc6f64df3e9cef9bd7cf5b47235056 Mon Sep 17 00:00:00 2001 From: Peter Alexander Date: Tue, 9 Sep 2025 22:41:36 +0100 Subject: [PATCH 08/11] Fix pre-commit formatting issues --- examples/fastmcp/icons_demo.py | 4 +- src/mcp/server/fastmcp/server.py | 2 +- tests/issues/test_1338_icons_and_metadata.py | 42 +++++++++----------- 3 files changed, 21 insertions(+), 27 deletions(-) diff --git a/examples/fastmcp/icons_demo.py b/examples/fastmcp/icons_demo.py index d0a6c2e8b..855d7c25f 100644 --- a/examples/fastmcp/icons_demo.py +++ b/examples/fastmcp/icons_demo.py @@ -18,9 +18,7 @@ icon_data = Icon(src=icon_data_uri, mimeType="image/png", sizes="64x64") # Create server with icons in implementation -mcp = FastMCP( - "Icons Demo Server", website_url="https://github.com/modelcontextprotocol/python-sdk", icons=[icon_data] -) +mcp = FastMCP("Icons Demo Server", website_url="https://github.com/modelcontextprotocol/python-sdk", icons=[icon_data]) @mcp.tool(icons=[icon_data]) diff --git a/src/mcp/server/fastmcp/server.py b/src/mcp/server/fastmcp/server.py index d33cf83dd..ee9db3f77 100644 --- a/src/mcp/server/fastmcp/server.py +++ b/src/mcp/server/fastmcp/server.py @@ -119,7 +119,7 @@ async def wrap(_: MCPServer[LifespanResultT, Request]) -> AsyncIterator[Lifespan class FastMCP(Generic[LifespanResultT]): - def __init__( + def __init__( # noqa: PLR0913 self, name: str | None = None, instructions: str | None = None, diff --git a/tests/issues/test_1338_icons_and_metadata.py b/tests/issues/test_1338_icons_and_metadata.py index f8920542f..ad39d7f01 100644 --- a/tests/issues/test_1338_icons_and_metadata.py +++ b/tests/issues/test_1338_icons_and_metadata.py @@ -10,39 +10,35 @@ async def test_icons_and_website_url(): """Test that icons and websiteUrl are properly returned in API calls.""" - + # Create test icon test_icon = Icon( src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==", mimeType="image/png", - sizes="1x1" + sizes="1x1", ) - + # Create server with website URL and icon - mcp = FastMCP( - "TestServer", - website_url="https://example.com", - icons=[test_icon] - ) - + mcp = FastMCP("TestServer", website_url="https://example.com", icons=[test_icon]) + # Create tool with icon @mcp.tool(icons=[test_icon]) def test_tool(message: str) -> str: """A test tool with an icon.""" return message - + # Create resource with icon @mcp.resource("test://resource", icons=[test_icon]) def test_resource() -> str: """A test resource with an icon.""" return "test content" - + # Create prompt with icon @mcp.prompt("test_prompt", icons=[test_icon]) def test_prompt(text: str) -> str: """A test prompt with an icon.""" return text - + # Test server metadata includes websiteUrl and icons assert mcp.name == "TestServer" assert mcp.website_url == "https://example.com" @@ -51,7 +47,7 @@ def test_prompt(text: str) -> str: assert mcp.icons[0].src == test_icon.src assert mcp.icons[0].mimeType == test_icon.mimeType assert mcp.icons[0].sizes == test_icon.sizes - + # Test tool includes icon tools = await mcp.list_tools() assert len(tools) == 1 @@ -60,7 +56,7 @@ def test_prompt(text: str) -> str: assert tool.icons is not None assert len(tool.icons) == 1 assert tool.icons[0].src == test_icon.src - + # Test resource includes icon resources = await mcp.list_resources() assert len(resources) == 1 @@ -69,7 +65,7 @@ def test_prompt(text: str) -> str: assert resource.icons is not None assert len(resource.icons) == 1 assert resource.icons[0].src == test_icon.src - + # Test prompt includes icon prompts = await mcp.list_prompts() assert len(prompts) == 1 @@ -82,20 +78,20 @@ def test_prompt(text: str) -> str: async def test_multiple_icons(): """Test that multiple icons can be added to tools, resources, and prompts.""" - + # Create multiple test icons icon1 = Icon(src="data:image/png;base64,icon1", mimeType="image/png", sizes="16x16") icon2 = Icon(src="data:image/png;base64,icon2", mimeType="image/png", sizes="32x32") icon3 = Icon(src="data:image/png;base64,icon3", mimeType="image/png", sizes="64x64") - + mcp = FastMCP("MultiIconServer") - + # Create tool with multiple icons @mcp.tool(icons=[icon1, icon2, icon3]) def multi_icon_tool() -> str: """A tool with multiple icons.""" return "success" - + # Test tool has all icons tools = await mcp.list_tools() assert len(tools) == 1 @@ -109,19 +105,19 @@ def multi_icon_tool() -> str: async def test_no_icons_or_website(): """Test that server works without icons or websiteUrl.""" - + mcp = FastMCP("BasicServer") - + @mcp.tool() def basic_tool() -> str: """A basic tool without icons.""" return "success" - + # Test server metadata has no websiteUrl or icons assert mcp.name == "BasicServer" assert mcp.website_url is None assert mcp.icons is None - + # Test tool has no icons tools = await mcp.list_tools() assert len(tools) == 1 From 32624d953d8af266ce42a2058962798c1bdfe7a0 Mon Sep 17 00:00:00 2001 From: Peter Alexander Date: Wed, 10 Sep 2025 15:28:01 +0100 Subject: [PATCH 09/11] icon path parsing suggestion --- examples/fastmcp/icons_demo.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/examples/fastmcp/icons_demo.py b/examples/fastmcp/icons_demo.py index 855d7c25f..5a3646b7b 100644 --- a/examples/fastmcp/icons_demo.py +++ b/examples/fastmcp/icons_demo.py @@ -10,10 +10,9 @@ from mcp.server.fastmcp import FastMCP, Icon # Load the icon file and convert to data URI -icon_path = Path(__file__).parent / "mcp.png" -with open(icon_path, "rb") as f: - icon_data = base64.b64encode(f.read()).decode("utf-8") - icon_data_uri = f"data:image/png;base64,{icon_data}" +icon_path = Path(__file__).parent / "mcp.png" +icon_data = base64.standard_b64encode(icon_path.read_bytes()).decode() +icon_data_uri = f"data:image/png;base64,{icon_data}" icon_data = Icon(src=icon_data_uri, mimeType="image/png", sizes="64x64") From 8353be8e677094da443230e457f248fc1c1ef6bb Mon Sep 17 00:00:00 2001 From: Peter Alexander Date: Wed, 10 Sep 2025 15:28:44 +0100 Subject: [PATCH 10/11] fix arg ordering --- src/mcp/server/fastmcp/resources/templates.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mcp/server/fastmcp/resources/templates.py b/src/mcp/server/fastmcp/resources/templates.py index 642f93d04..8338dca81 100644 --- a/src/mcp/server/fastmcp/resources/templates.py +++ b/src/mcp/server/fastmcp/resources/templates.py @@ -77,8 +77,8 @@ async def create_resource(self, uri: str, params: dict[str, Any]) -> Resource: title=self.title, description=self.description, mime_type=self.mime_type, - fn=lambda: result, # Capture result in closure icons=None, # Resource templates don't support icons + fn=lambda: result, # Capture result in closure ) except Exception as e: raise ValueError(f"Error creating resource from template: {e}") From 09726740c556641d8bc5e23b8c61324f713c3f38 Mon Sep 17 00:00:00 2001 From: Peter Alexander Date: Wed, 10 Sep 2025 16:05:33 +0100 Subject: [PATCH 11/11] Pre-commit --- examples/fastmcp/icons_demo.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/fastmcp/icons_demo.py b/examples/fastmcp/icons_demo.py index 5a3646b7b..7467e448a 100644 --- a/examples/fastmcp/icons_demo.py +++ b/examples/fastmcp/icons_demo.py @@ -10,8 +10,8 @@ from mcp.server.fastmcp import FastMCP, Icon # Load the icon file and convert to data URI -icon_path = Path(__file__).parent / "mcp.png" -icon_data = base64.standard_b64encode(icon_path.read_bytes()).decode() +icon_path = Path(__file__).parent / "mcp.png" +icon_data = base64.standard_b64encode(icon_path.read_bytes()).decode() icon_data_uri = f"data:image/png;base64,{icon_data}" icon_data = Icon(src=icon_data_uri, mimeType="image/png", sizes="64x64")