from pathlib import Path import pytest import yaml from fastmcp import Client from fastmcp import FastMCP from fastmcp.utilities.skills import get_skill_manifest from fastmcp.utilities.skills import list_skills from personal_mcp.skills import create_skills_provider pytestmark = pytest.mark.unit SKILLS_ROOT = Path(__file__).parents[2] / "docs" / "skills" class TestSkillsProvider: """Covers native FastMCP skill discovery and retrieval.""" @pytest.mark.asyncio async def test_discovers_authored_skills(self) -> None: """Ensures each authored skill is exposed with its description.""" expected_names = {path.parent.name for path in SKILLS_ROOT.glob("*/SKILL.md")} mcp = FastMCP("skills-test") mcp.add_provider(create_skills_provider()) async with Client(mcp) as client: skills = await list_skills(client) assert {skill.name for skill in skills} == expected_names assert all(skill.description for skill in skills) @pytest.mark.asyncio async def test_reads_manifest_and_supporting_file(self) -> None: """Ensures manifests disclose hashed files that remain directly readable.""" mcp = FastMCP("skills-test") mcp.add_provider(create_skills_provider()) async with Client(mcp) as client: manifest = await get_skill_manifest(client, "mcp-details") reference = next(file for file in manifest.files if file.path.startswith("references/")) contents = await client.read_resource(f"skill://mcp-details/{reference.path}") assert any(file.path == "SKILL.md" for file in manifest.files) assert all(file.hash.startswith("sha256:") for file in manifest.files) assert reference.size > 0 assert contents def test_frontmatter_names_match_directories(self) -> None: """Ensures provider identity and authored skill names remain aligned.""" for skill_file in SKILLS_ROOT.glob("*/SKILL.md"): raw = skill_file.read_text(encoding="utf-8") frontmatter = yaml.safe_load(raw.split("---", 2)[1]) assert set(frontmatter) == {"name", "description"} assert frontmatter["name"] == skill_file.parent.name assert frontmatter["description"]