79 lines
3.4 KiB
Python
79 lines
3.4 KiB
Python
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
|