chore: import upstream snapshot with attribution
Deploy Docs / deploy-docs (push) Failing after 1s
Conformance Tests / client-conformance (push) Failing after 3s
Conformance Tests / server-conformance (push) Failing after 1s
GitHub Actions Security Analysis / zizmor (push) Failing after 1s
CI / checks (push) Failing after 59m20s
CI / all-green (push) Has been cancelled
Deploy Docs / deploy-docs (push) Failing after 1s
Conformance Tests / client-conformance (push) Failing after 3s
Conformance Tests / server-conformance (push) Failing after 1s
GitHub Actions Security Analysis / zizmor (push) Failing after 1s
CI / checks (push) Failing after 59m20s
CI / all-green (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,230 @@
|
||||
"""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"
|
||||
Reference in New Issue
Block a user