diff --git a/docs/servers/icons.mdx b/docs/servers/icons.mdx
index c9b558094..779c8da98 100644
--- a/docs/servers/icons.mdx
+++ b/docs/servers/icons.mdx
@@ -12,7 +12,7 @@ Icons provide visual representations for your MCP servers and components, helpin
## Icon Format
-Icons use the standard MCP Icon type from the MCP protocol specification. Each icon specifies a source URL or data URI, and optionally includes MIME type and size information.
+Icons use the standard MCP Icon type from the MCP protocol specification. Each icon specifies a source URL or data URI, and optionally includes MIME type, size, and theme information.
```python
from mcp.types import Icon
@@ -20,7 +20,8 @@ from mcp.types import Icon
icon = Icon(
src="https://example.com/icon.png",
mimeType="image/png",
- sizes=["48x48"]
+ sizes=["48x48"],
+ theme="light"
)
```
@@ -29,6 +30,7 @@ The fields serve different purposes:
- **src**: URL or data URI pointing to the icon image
- **mimeType** (optional): MIME type of the image (e.g., "image/png", "image/svg+xml")
- **sizes** (optional): Array of size descriptors (e.g., ["48x48"], ["any"])
+- **theme** (optional): `"light"` or `"dark"` — indicates whether the icon is designed for a light or dark background
## Server Icons
@@ -58,6 +60,36 @@ mcp = FastMCP(
Server icons appear in MCP client interfaces to help users identify your server among others they may have installed.
+## Theme Variants
+
+Use the `theme` field to provide separate icon variants for light and dark UI themes. Clients can select the appropriate icon based on their current appearance. The `IconTheme` type from FastMCP provides type safety for this field.
+
+```python
+from fastmcp import FastMCP
+from mcp.types import Icon
+
+mcp = FastMCP(
+ name="GitHubServer",
+ icons=[
+ Icon(
+ src="https://github.githubassets.com/favicons/favicon-dark.svg",
+ mimeType="image/svg+xml",
+ theme="dark",
+ ),
+ Icon(
+ src="https://github.githubassets.com/favicons/favicon-light.svg",
+ mimeType="image/svg+xml",
+ theme="light",
+ ),
+ ]
+)
+```
+
+
+For type checking, use `from fastmcp import IconTheme` to annotate theme variables:
+`theme: IconTheme = "light"`
+
+
## Component Icons
Icons can be added to individual tools, resources, resource templates, and prompts. This helps users visually distinguish between different component types and purposes.
diff --git a/docs/v2/servers/icons.mdx b/docs/v2/servers/icons.mdx
index a7d038f6a..9ea298b43 100644
--- a/docs/v2/servers/icons.mdx
+++ b/docs/v2/servers/icons.mdx
@@ -18,6 +18,7 @@ Icons use the standard MCP Icon type from the MCP protocol specification. Each i
- **src**: URL or data URI pointing to the icon image
- **mimeType** (optional): MIME type of the image (e.g., "image/png", "image/svg+xml")
- **sizes** (optional): Array of size descriptors (e.g., ["48x48"], ["any"])
+- **theme** (optional): `"light"` or `"dark"` — indicates whether the icon is designed for a light or dark background
```python
from mcp.types import Icon
@@ -25,7 +26,8 @@ from mcp.types import Icon
icon = Icon(
src="https://example.com/icon.png",
mimeType="image/png",
- sizes=["48x48"]
+ sizes=["48x48"],
+ theme="light"
)
```
@@ -57,6 +59,36 @@ mcp = FastMCP(
Server icons appear in MCP client interfaces to help users identify your server among others they may have installed.
+## Theme Variants
+
+Use the `theme` field to provide separate icon variants for light and dark UI themes. Clients can select the appropriate icon based on their current appearance. The `IconTheme` type from FastMCP provides type safety for this field.
+
+```python
+from fastmcp import FastMCP
+from mcp.types import Icon
+
+mcp = FastMCP(
+ name="GitHubServer",
+ icons=[
+ Icon(
+ src="https://github.githubassets.com/favicons/favicon-dark.svg",
+ mimeType="image/svg+xml",
+ theme="dark",
+ ),
+ Icon(
+ src="https://github.githubassets.com/favicons/favicon-light.svg",
+ mimeType="image/svg+xml",
+ theme="light",
+ ),
+ ]
+)
+```
+
+
+For type checking, use `from fastmcp import IconTheme` to annotate theme variables:
+`theme: IconTheme = "light"`
+
+
## Component Icons
Icons can be added to individual tools, resources, resource templates, and prompts:
diff --git a/src/fastmcp/__init__.py b/src/fastmcp/__init__.py
index 14c0abc5b..53ac8afe2 100644
--- a/src/fastmcp/__init__.py
+++ b/src/fastmcp/__init__.py
@@ -18,6 +18,7 @@
from fastmcp.client import Client
from . import client
+from .utilities.types import IconTheme
__version__ = _version("fastmcp")
@@ -31,5 +32,6 @@
"Client",
"Context",
"FastMCP",
+ "IconTheme",
"settings",
]
diff --git a/src/fastmcp/utilities/types.py b/src/fastmcp/utilities/types.py
index 91377df45..770b45d8c 100644
--- a/src/fastmcp/utilities/types.py
+++ b/src/fastmcp/utilities/types.py
@@ -11,6 +11,7 @@
from typing import (
Annotated,
Any,
+ Literal,
Protocol,
TypeAlias,
Union,
@@ -24,6 +25,14 @@
from pydantic import AnyUrl, BaseModel, ConfigDict, Field, TypeAdapter, UrlConstraints
from typing_extensions import TypeVar
+# Forward-compatible import: use IconTheme from mcp.types when available (v2+),
+# otherwise define locally for MCP SDK v1.x compatibility.
+# See https://modelcontextprotocol.io/specification/2025-11-25/schema#icon
+try:
+ from mcp.types import IconTheme # ty: ignore[unresolved-import]
+except ImportError:
+ IconTheme: TypeAlias = Literal["light", "dark"]
+
T = TypeVar("T", default=Any)
# sentinel values for optional arguments
diff --git a/tests/utilities/test_inspect.py b/tests/utilities/test_inspect.py
index 448e1eb03..538e32ea3 100644
--- a/tests/utilities/test_inspect.py
+++ b/tests/utilities/test_inspect.py
@@ -849,6 +849,85 @@ def data_uri_tool() -> str:
assert info.tools[0].icons[0]["src"] == data_uri
assert info.tools[0].icons[0]["mimeType"] == "image/png"
+ async def test_icon_theme_light_and_dark(self):
+ """Test that icon theme field is preserved for light/dark variants."""
+ from mcp.types import Icon
+
+ mcp = FastMCP(
+ "ThemeIconServer",
+ icons=[
+ Icon(
+ src="https://example.com/icon-dark.svg",
+ mimeType="image/svg+xml",
+ theme="dark", # ty: ignore[unknown-argument]
+ ),
+ Icon(
+ src="https://example.com/icon-light.svg",
+ mimeType="image/svg+xml",
+ theme="light", # ty: ignore[unknown-argument]
+ ),
+ ],
+ )
+
+ info = await inspect_fastmcp(mcp)
+
+ assert info.icons is not None
+ assert len(info.icons) == 2
+ assert info.icons[0]["theme"] == "dark"
+ assert info.icons[1]["theme"] == "light"
+
+ async def test_icon_theme_on_tool(self):
+ """Test that tool icons preserve the theme field."""
+ from mcp.types import Icon
+
+ mcp = FastMCP("ToolThemeServer")
+
+ @mcp.tool(
+ icons=[
+ Icon(
+ src="https://example.com/tool-dark.png",
+ mimeType="image/png",
+ theme="dark", # ty: ignore[unknown-argument]
+ ),
+ Icon(
+ src="https://example.com/tool-light.png",
+ mimeType="image/png",
+ theme="light", # ty: ignore[unknown-argument]
+ ),
+ ]
+ )
+ def themed_tool() -> str:
+ """A tool with light and dark theme icons."""
+ return "themed"
+
+ info = await inspect_fastmcp(mcp)
+
+ assert len(info.tools) == 1
+ assert info.tools[0].icons is not None
+ assert len(info.tools[0].icons) == 2
+ assert info.tools[0].icons[0]["theme"] == "dark"
+ assert info.tools[0].icons[1]["theme"] == "light"
+
+ async def test_icon_theme_type_import(self):
+ """Test that IconTheme can be imported from fastmcp."""
+ from fastmcp import IconTheme
+
+ # Verify that IconTheme accepts valid values
+ light: IconTheme = "light"
+ dark: IconTheme = "dark"
+ assert light == "light"
+ assert dark == "dark"
+
+ async def test_icon_without_theme(self):
+ """Test that icons without theme still work (theme defaults to absent)."""
+ from mcp.types import Icon
+
+ icon = Icon(src="https://example.com/icon.png")
+ dumped = icon.model_dump()
+
+ # theme should not appear unless explicitly set
+ assert "theme" not in dumped or dumped.get("theme") is None
+
async def test_icons_in_fastmcp_v1(self):
"""Test that icons are extracted from FastMCP 1.x servers."""
from mcp.types import Icon