"""Integration tests for title field functionality.""" import pytest from mcp_types import Prompt, Resource, ResourceTemplate, Tool, ToolAnnotations from mcp import Client from mcp.server.mcpserver import MCPServer from mcp.server.mcpserver.resources import FunctionResource from mcp.shared.metadata_utils import get_display_name @pytest.mark.anyio async def test_server_name_title_description_version(): """Test that server title and description are set and retrievable correctly.""" mcp = MCPServer( name="TestServer", title="Test Server Title", description="This is a test server description.", version="1.0", ) assert mcp.title == "Test Server Title" assert mcp.description == "This is a test server description." assert mcp.version == "1.0" # Start server and connect client async with Client(mcp) as client: assert client.server_info.name == "TestServer" assert client.server_info.title == "Test Server Title" assert client.server_info.description == "This is a test server description." assert client.server_info.version == "1.0" @pytest.mark.anyio async def test_tool_title_precedence(): """Test that tool title precedence works correctly: title > annotations.title > name.""" # Create server with various tool configurations mcp = MCPServer(name="TitleTestServer") # Tool with only name @mcp.tool(description="Basic tool") def basic_tool(message: str) -> str: # pragma: no cover return message # Tool with title @mcp.tool(description="Tool with title", title="User-Friendly Tool") def tool_with_title(message: str) -> str: # pragma: no cover return message # Tool with annotations.title (when title is not supported on decorator) # We'll need to add this manually after registration @mcp.tool(description="Tool with annotations") def tool_with_annotations(message: str) -> str: # pragma: no cover return message # Tool with both title and annotations.title @mcp.tool(description="Tool with both", title="Primary Title") def tool_with_both(message: str) -> str: # pragma: no cover return message # Start server and connect client async with Client(mcp) as client: # List tools tools_result = await client.list_tools() tools = {tool.name: tool for tool in tools_result.tools} # Verify basic tool uses name assert "basic_tool" in tools basic = tools["basic_tool"] # Since we haven't implemented get_display_name yet, we'll check the raw fields assert basic.title is None assert basic.name == "basic_tool" # Verify tool with title assert "tool_with_title" in tools titled = tools["tool_with_title"] assert titled.title == "User-Friendly Tool" # For now, we'll skip the annotations.title test as it requires modifying # the tool after registration, which we'll implement later # Verify tool with both uses title over annotations.title assert "tool_with_both" in tools both = tools["tool_with_both"] assert both.title == "Primary Title" @pytest.mark.anyio async def test_prompt_title(): """Test that prompt titles work correctly.""" mcp = MCPServer(name="PromptTitleServer") # Prompt with only name @mcp.prompt(description="Basic prompt") def basic_prompt(topic: str) -> str: # pragma: no cover return f"Tell me about {topic}" # Prompt with title @mcp.prompt(description="Titled prompt", title="Ask About Topic") def titled_prompt(topic: str) -> str: # pragma: no cover return f"Tell me about {topic}" # Start server and connect client async with Client(mcp) as client: # List prompts prompts_result = await client.list_prompts() prompts = {prompt.name: prompt for prompt in prompts_result.prompts} # Verify basic prompt uses name assert "basic_prompt" in prompts basic = prompts["basic_prompt"] assert basic.title is None assert basic.name == "basic_prompt" # Verify prompt with title assert "titled_prompt" in prompts titled = prompts["titled_prompt"] assert titled.title == "Ask About Topic" @pytest.mark.anyio async def test_resource_title(): """Test that resource titles work correctly.""" mcp = MCPServer(name="ResourceTitleServer") # Static resource without title def get_basic_data() -> str: # pragma: no cover return "Basic data" basic_resource = FunctionResource( uri="resource://basic", name="basic_resource", description="Basic resource", fn=get_basic_data, ) mcp.add_resource(basic_resource) # Static resource with title def get_titled_data() -> str: # pragma: no cover return "Titled data" titled_resource = FunctionResource( uri="resource://titled", name="titled_resource", title="User-Friendly Resource", description="Resource with title", fn=get_titled_data, ) mcp.add_resource(titled_resource) # Dynamic resource without title @mcp.resource("resource://dynamic/{id}") def dynamic_resource(id: str) -> str: # pragma: no cover return f"Data for {id}" # Dynamic resource with title (when supported) @mcp.resource("resource://titled-dynamic/{id}", title="Dynamic Data") def titled_dynamic_resource(id: str) -> str: # pragma: no cover return f"Data for {id}" # Start server and connect client async with Client(mcp) as client: # List resources resources_result = await client.list_resources() resources = {str(res.uri): res for res in resources_result.resources} # Verify basic resource uses name assert "resource://basic" in resources basic = resources["resource://basic"] assert basic.title is None assert basic.name == "basic_resource" # Verify resource with title assert "resource://titled" in resources titled = resources["resource://titled"] assert titled.title == "User-Friendly Resource" # List resource templates templates_result = await client.list_resource_templates() templates = {tpl.uri_template: tpl for tpl in templates_result.resource_templates} # Verify dynamic resource template assert "resource://dynamic/{id}" in templates dynamic = templates["resource://dynamic/{id}"] assert dynamic.title is None assert dynamic.name == "dynamic_resource" # Verify titled dynamic resource template (when supported) if "resource://titled-dynamic/{id}" in templates: # pragma: no branch titled_dynamic = templates["resource://titled-dynamic/{id}"] assert titled_dynamic.title == "Dynamic Data" @pytest.mark.anyio async def test_get_display_name_utility(): """Test the get_display_name utility function.""" # Test tool precedence: title > annotations.title > name tool_name_only = Tool(name="test_tool", input_schema={}) assert get_display_name(tool_name_only) == "test_tool" tool_with_title = Tool(name="test_tool", title="Test Tool", input_schema={}) assert get_display_name(tool_with_title) == "Test Tool" tool_with_annotations = Tool(name="test_tool", input_schema={}, annotations=ToolAnnotations(title="Annotated Tool")) assert get_display_name(tool_with_annotations) == "Annotated Tool" tool_with_both = Tool( name="test_tool", title="Primary Title", input_schema={}, annotations=ToolAnnotations(title="Secondary Title") ) assert get_display_name(tool_with_both) == "Primary Title" # Test other types: title > name resource = Resource(uri="file://test", name="test_res") assert get_display_name(resource) == "test_res" resource_with_title = Resource(uri="file://test", name="test_res", title="Test Resource") assert get_display_name(resource_with_title) == "Test Resource" prompt = Prompt(name="test_prompt") assert get_display_name(prompt) == "test_prompt" prompt_with_title = Prompt(name="test_prompt", title="Test Prompt") assert get_display_name(prompt_with_title) == "Test Prompt" template = ResourceTemplate(uri_template="file://{id}", name="test_template") assert get_display_name(template) == "test_template" template_with_title = ResourceTemplate(uri_template="file://{id}", name="test_template", title="Test Template") assert get_display_name(template_with_title) == "Test Template"