diff --git a/fastmcp_slim/fastmcp/server/providers/skills/_common.py b/fastmcp_slim/fastmcp/server/providers/skills/_common.py index 14bf4a5d6..c752d4791 100644 --- a/fastmcp_slim/fastmcp/server/providers/skills/_common.py +++ b/fastmcp_slim/fastmcp/server/providers/skills/_common.py @@ -104,7 +104,13 @@ def parse_frontmatter(content: str) -> tuple[dict[str, Any], str]: # YAML may type `description: true` / dates as non-str, but SkillInfo.description # is a str field that callers truthiness-check. if "description" in parsed and parsed["description"] is not None: - parsed["description"] = str(parsed["description"]) + if not isinstance(parsed["description"], str): + # YAML 1.1 coerces bare words like `yes` to non-str; recover the + # original text via the line-based parser instead. + raw = _parse_frontmatter_line_based(frontmatter_text).get("description") + parsed["description"] = ( + raw if isinstance(raw, str) else str(parsed["description"]) + ) return parsed, remaining diff --git a/tests/server/providers/test_skills_provider.py b/tests/server/providers/test_skills_provider.py index e38ab0872..6310e6609 100644 --- a/tests/server/providers/test_skills_provider.py +++ b/tests/server/providers/test_skills_provider.py @@ -152,7 +152,7 @@ Body assert frontmatter["enabled"] is True assert isinstance(frontmatter["enabled"], bool) - def test_frontmatter_description_bool_coerced_to_str(self): + def test_frontmatter_description_bool_recovers_original_text(self): content = """--- description: true --- @@ -160,9 +160,41 @@ description: true Body """ frontmatter, body = parse_frontmatter(content) - assert frontmatter["description"] == "True" + assert frontmatter["description"] == "true" assert isinstance(frontmatter["description"], str) + def test_frontmatter_description_yes_not_coerced_to_true(self): + # FastMCP #4416 regression: `yes` bare word previously coerced to True. + content = """--- +description: yes +--- + +Body +""" + frontmatter, body = parse_frontmatter(content) + assert frontmatter["description"] == "yes" + + def test_frontmatter_description_no_not_coerced_to_false(self): + content = """--- +description: no +--- + +Body +""" + frontmatter, body = parse_frontmatter(content) + assert frontmatter["description"] == "no" + + def test_frontmatter_description_numeric_scalar_preserves_original_text(self): + # YAML resolves `1.10` to float 1.1, dropping the trailing zero the author wrote. + content = """--- +description: 1.10 +--- + +Body +""" + frontmatter, body = parse_frontmatter(content) + assert frontmatter["description"] == "1.10" + class TestSkillProvider: """Tests for SkillProvider - single skill folder."""