Files
wehub-resource-sync 60e0ffc959
Upgrade checks / Notify on failure (push) Has been cancelled
Upgrade checks / Close issue on success (push) Has been cancelled
Schema Crash Test / Real-world schema crash test (232K schemas) (push) Has been cancelled
Run static analysis / static_analysis (push) Has been cancelled
Tests / Tests: Python 3.10 on ubuntu-latest (push) Has been cancelled
Tests / Tests: Python 3.13 on ubuntu-latest (push) Has been cancelled
Tests / Tests: Python 3.10 on windows-latest (push) Has been cancelled
Tests / Tests with lowest-direct dependencies (push) Has been cancelled
Tests / MCP conformance tests (push) Has been cancelled
Tests / Integration tests (push) Has been cancelled
Tests / Package install smoke (push) Has been cancelled
Upgrade checks / Static analysis (push) Has been cancelled
Upgrade checks / Tests: Python 3.10 on ubuntu-latest (push) Has been cancelled
Upgrade checks / Tests: Python 3.13 on ubuntu-latest (push) Has been cancelled
Upgrade checks / Tests: Python 3.10 on windows-latest (push) Has been cancelled
Upgrade checks / Integration tests (push) Has been cancelled
Update MCPServerConfig Schema / update-config-schema (push) Has been cancelled
Update SDK Documentation / update-sdk-docs (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:39:59 +08:00

343 lines
11 KiB
Python

"""Tests for icon support across all MCP object types."""
from mcp_types import Icon
from fastmcp import Client, FastMCP
from fastmcp.prompts import Message, Prompt
from fastmcp.resources import Resource
from fastmcp.resources.template import ResourceTemplate
from fastmcp.tools import Tool
class TestServerIcons:
"""Test icon support at the server/implementation level."""
async def test_server_with_icons_and_website_url(self):
"""Test that server accepts icons and websiteUrl in constructor."""
icons = [
Icon(
src="https://example.com/icon.png",
mime_type="image/png",
sizes=["48x48"],
),
Icon(
src="data:image/svg+xml;base64,PHN2Zz48L3N2Zz4=",
mime_type="image/svg+xml",
sizes=["any"],
),
]
mcp = FastMCP(
name="TestServer",
version="1.0.0",
website_url="https://example.com",
icons=icons,
)
# Verify that icons and website_url are passed to the underlying server
async with Client(mcp) as client:
server_info = client.initialize_result.server_info
assert server_info.website_url == "https://example.com"
assert server_info.icons == icons
async def test_server_without_icons_and_website_url(self):
"""Test that server works without icons and websiteUrl."""
mcp = FastMCP(name="TestServer")
async with Client(mcp) as client:
server_info = client.initialize_result.server_info
assert server_info.website_url is None
assert server_info.icons is None
class TestToolIcons:
"""Test icon support for tools."""
async def test_tool_with_icons(self):
"""Test that tools can have icons."""
mcp = FastMCP("TestServer")
icons = [
Icon(src="https://example.com/tool-icon.png", mime_type="image/png"),
]
@mcp.tool(icons=icons)
def my_tool(name: str) -> str:
"""A tool with an icon."""
return f"Hello, {name}!"
async with Client(mcp) as client:
tools = await client.list_tools()
assert len(tools) == 1
tool = tools[0]
assert tool.icons == icons
async def test_tool_from_function_with_icons(self):
"""Test creating a tool from a function with icons."""
icons = [Icon(src="https://example.com/icon.png")]
def my_function(x: int) -> int:
"""A function."""
return x * 2
tool = Tool.from_function(my_function, icons=icons)
assert tool.icons == icons
# Verify it converts to MCP tool correctly
mcp_tool = tool.to_mcp_tool()
assert mcp_tool.icons == icons
async def test_tool_without_icons(self):
"""Test that tools work without icons."""
mcp = FastMCP("TestServer")
@mcp.tool
def my_tool(name: str) -> str:
"""A tool without an icon."""
return f"Hello, {name}!"
async with Client(mcp) as client:
tools = await client.list_tools()
assert len(tools) == 1
tool = tools[0]
assert tool.icons is None
class TestResourceIcons:
"""Test icon support for resources."""
async def test_resource_with_icons(self):
"""Test that resources can have icons."""
mcp = FastMCP("TestServer")
icons = [Icon(src="https://example.com/resource-icon.png")]
@mcp.resource("test://resource", icons=icons)
def my_resource() -> str:
"""A resource with an icon."""
return "Resource content"
async with Client(mcp) as client:
resources = await client.list_resources()
assert len(resources) == 1
resource = resources[0]
assert resource.icons == icons
async def test_resource_from_function_with_icons(self):
"""Test creating a resource from a function with icons."""
icons = [Icon(src="https://example.com/icon.png")]
def my_function() -> str:
"""A function."""
return "content"
resource = Resource.from_function(
my_function,
uri="test://resource",
icons=icons,
)
assert resource.icons == icons
# Verify it converts to MCP resource correctly
mcp_resource = resource.to_mcp_resource()
assert mcp_resource.icons == icons
async def test_resource_without_icons(self):
"""Test that resources work without icons."""
mcp = FastMCP("TestServer")
@mcp.resource("test://resource")
def my_resource() -> str:
"""A resource without an icon."""
return "Resource content"
async with Client(mcp) as client:
resources = await client.list_resources()
assert len(resources) == 1
resource = resources[0]
assert resource.icons is None
class TestResourceTemplateIcons:
"""Test icon support for resource templates."""
async def test_resource_template_with_icons(self):
"""Test that resource templates can have icons."""
mcp = FastMCP("TestServer")
icons = [Icon(src="https://example.com/template-icon.png")]
@mcp.resource("test://resource/{id}", icons=icons)
def my_template(id: str) -> str:
"""A resource template with an icon."""
return f"Resource {id}"
async with Client(mcp) as client:
templates = await client.list_resource_templates()
assert len(templates) == 1
template = templates[0]
assert template.icons == icons
async def test_resource_template_from_function_with_icons(self):
"""Test creating a resource template from a function with icons."""
icons = [Icon(src="https://example.com/icon.png")]
def my_function(id: str) -> str:
"""A function."""
return f"content-{id}"
template = ResourceTemplate.from_function(
my_function,
uri_template="test://resource/{id}",
icons=icons,
)
assert template.icons == icons
# Verify it converts to MCP template correctly
mcp_template = template.to_mcp_template()
assert mcp_template.icons == icons
async def test_resource_template_without_icons(self):
"""Test that resource templates work without icons."""
mcp = FastMCP("TestServer")
@mcp.resource("test://resource/{id}")
def my_template(id: str) -> str:
"""A resource template without an icon."""
return f"Resource {id}"
async with Client(mcp) as client:
templates = await client.list_resource_templates()
assert len(templates) == 1
template = templates[0]
assert template.icons is None
class TestPromptIcons:
"""Test icon support for prompts."""
async def test_prompt_with_icons(self):
"""Test that prompts can have icons."""
mcp = FastMCP("TestServer")
icons = [Icon(src="https://example.com/prompt-icon.png")]
@mcp.prompt(icons=icons)
def my_prompt(name: str):
"""A prompt with an icon."""
return Message(f"Hello, {name}!")
async with Client(mcp) as client:
prompts = await client.list_prompts()
assert len(prompts) == 1
prompt = prompts[0]
assert prompt.icons == icons
async def test_prompt_from_function_with_icons(self):
"""Test creating a prompt from a function with icons."""
icons = [Icon(src="https://example.com/icon.png")]
def my_function(topic: str):
"""A function."""
return Message(f"Tell me about {topic}")
prompt = Prompt.from_function(my_function, icons=icons)
assert prompt.icons == icons
# Verify it converts to MCP prompt correctly
mcp_prompt = prompt.to_mcp_prompt()
assert mcp_prompt.icons == icons
async def test_prompt_without_icons(self):
"""Test that prompts work without icons."""
mcp = FastMCP("TestServer")
@mcp.prompt
def my_prompt(name: str):
"""A prompt without an icon."""
return Message(f"Hello, {name}!")
async with Client(mcp) as client:
prompts = await client.list_prompts()
assert len(prompts) == 1
prompt = prompts[0]
assert prompt.icons is None
class TestIconTypes:
"""Test different types of icon data."""
async def test_multiple_icon_sizes(self):
"""Test that multiple icon sizes can be specified."""
icons = [
Icon(
src="https://example.com/icon-48.png",
mime_type="image/png",
sizes=["48x48"],
),
Icon(
src="https://example.com/icon-96.png",
mime_type="image/png",
sizes=["96x96"],
),
Icon(
src="https://example.com/icon.svg",
mime_type="image/svg+xml",
sizes=["any"],
),
]
mcp = FastMCP("TestServer", icons=icons)
async with Client(mcp) as client:
server_info = client.initialize_result.server_info
assert len(server_info.icons) == 3
assert server_info.icons == icons
async def test_data_uri_icon(self):
"""Test using data URIs for icons."""
# Simple SVG data URI
data_uri = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCI+PHBhdGggZD0iTTEyIDJDNi40OCAyIDIgNi40OCAyIDEyczQuNDggMTAgMTAgMTAgMTAtNC40OCAxMC0xMFMxNy41MiAyIDEyIDJ6Ii8+PC9zdmc+"
icons = [Icon(src=data_uri, mime_type="image/svg+xml")]
mcp = FastMCP("TestServer")
@mcp.tool(icons=icons)
def my_tool() -> str:
"""A tool with a data URI icon."""
return "result"
async with Client(mcp) as client:
tools = await client.list_tools()
assert tools[0].icons[0].src == data_uri
async def test_icon_without_optional_fields(self):
"""Test that icons work with only the src field."""
icons = [Icon(src="https://example.com/icon.png")]
mcp = FastMCP("TestServer", icons=icons)
async with Client(mcp) as client:
server_info = client.initialize_result.server_info
assert server_info.icons[0].src == "https://example.com/icon.png"
assert server_info.icons[0].mime_type is None
assert server_info.icons[0].sizes is None
class TestIconImport:
"""Test that Icon must be imported from mcp_types."""
def test_icon_import(self):
"""Test that Icon must be imported from mcp.types, not fastmcp."""
# Icon should NOT be available from fastmcp
import fastmcp
assert not hasattr(fastmcp, "Icon")
# Icon should be imported from mcp.types
from mcp_types import Icon as MCPIcon
icon = MCPIcon(src="https://example.com/icon.png")
assert icon.src == "https://example.com/icon.png"