from __future__ import annotations import json import pytest pytestmark = pytest.mark.smoke RETIRED_TOOL_NAMES = { "search_patterns", "get_pattern_by_id", "get_skill_document_by_id", } class TestMcpSkillsSurface: """Covers native skill resources over the HTTP MCP surface.""" class TestTools: """Covers generic resource fallback tools for native skills.""" @pytest.mark.asyncio async def test_lists_resource_fallback_tools(self, mcp_session_factory) -> None: """Ensures generic resource tools replace skill-specific catalog tools.""" async with mcp_session_factory() as mcp_session: result = await mcp_session.list_tools() tool_names = {tool.name for tool in result.tools} assert {"list_resources", "read_resource"}.issubset(tool_names) assert RETIRED_TOOL_NAMES.isdisjoint(tool_names) @pytest.mark.asyncio async def test_reads_skill_through_fallback_tool(self, mcp_session_factory) -> None: """Ensures tool-only clients can read a native skill resource.""" async with mcp_session_factory() as mcp_session: result = await mcp_session.call_tool( "read_resource", {"uri": "skill://mcp-details/SKILL.md"}, ) assert result.isError is False assert result.content 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 assert "resource://catalog/skills_index" not 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.uriTemplate for template in result.resourceTemplates} assert "skill://mcp-details/{path*}" in template_uris assert "resource://skills/{skill_id}/document" not 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