from __future__ import annotations import json import pytest pytestmark = pytest.mark.smoke class TestMcpSkillsSurface: """Covers native skill resources over the HTTP MCP surface.""" class TestCompatibilityTools: """Covers metadata for tool-only MCP clients.""" @pytest.mark.asyncio async def test_exposes_safe_human_readable_tools(self, mcp_session_factory) -> None: """Ensures clients receive display titles and complete safety hints.""" async with mcp_session_factory() as mcp_session: result = await mcp_session.list_tools() tools = {tool.name: tool for tool in result.tools} assert tools["search_skills"].title == "Search Skills" assert tools["list_resources"].title == "List Resources" assert tools["read_resource"].title == "Read Resource" assert all(tool.annotations is not None for tool in tools.values()) assert all(tool.annotations.read_only_hint for tool in tools.values() if tool.annotations is not None) assert all(tool.annotations.idempotent_hint for tool in tools.values() if tool.annotations is not None) assert all( tool.annotations.open_world_hint is False for tool in tools.values() if tool.annotations is not None ) @pytest.mark.asyncio async def test_searches_skill_metadata_without_loading_content(self, mcp_session_factory) -> None: """Ensures search returns bounded canonical skill pointers ranked by metadata.""" async with mcp_session_factory() as mcp_session: result = await mcp_session.call_tool( "search_skills", {"query": "FastMCP protocol", "limit": 1}, ) assert result.is_error is False assert result.structured_content == { "results": [ { "name": "mcp-details", "description": ( "Reference hub for MCP and FastMCP source documentation links. Use when you need " "authoritative protocol, SDK, transport, and deployment docs without loading broad " "implementation guidance." ), "uri": "skill://mcp-details/SKILL.md", } ] } class TestResources: """Covers native skill resources, manifests, and file templates.""" @pytest.mark.asyncio async def test_lists_main_file_and_manifest(self, mcp_session_factory) -> None: """Ensures resources/list exposes native skill entry points.""" async with mcp_session_factory() as mcp_session: result = await mcp_session.list_resources() resource_uris = {str(resource.uri) for resource in result.resources} assert "skill://mcp-details/SKILL.md" in resource_uris assert "skill://mcp-details/_manifest" in resource_uris @pytest.mark.asyncio async def test_lists_resource_templates(self, mcp_session_factory) -> None: """Ensures supporting files use per-skill wildcard templates.""" async with mcp_session_factory() as mcp_session: result = await mcp_session.list_resource_templates() template_uris = {template.uri_template for template in result.resource_templates} assert "skill://mcp-details/{path*}" in template_uris @pytest.mark.asyncio async def test_reads_manifest_and_supporting_file(self, mcp_session_factory) -> None: """Ensures manifest paths resolve through the supporting-file template.""" async with mcp_session_factory() as mcp_session: manifest_result = await mcp_session.read_resource("skill://mcp-details/_manifest") manifest = json.loads(manifest_result.contents[0].text) reference = next(file["path"] for file in manifest["files"] if file["path"].startswith("references/")) reference_result = await mcp_session.read_resource(f"skill://mcp-details/{reference}") assert manifest["skill"] == "mcp-details" assert reference_result.contents