Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions python/packages/core/agent_framework/_skills.py
Original file line number Diff line number Diff line change
Expand Up @@ -4721,16 +4721,16 @@ async def _download(self, entry: _McpSkillIndexEntry) -> tuple[bytes, str | None

blob = _mcp_first_blob(result)
if blob is None:
logger.debug("Skipping skill '%s': archive resource returned no binary content", entry.name)
logger.warning("Skipping skill '%s': archive resource returned no binary content", entry.name)
return None

data, mime_type = blob
if not data:
logger.debug("Skipping skill '%s': archive resource returned empty content", entry.name)
logger.warning("Skipping skill '%s': archive resource returned empty content", entry.name)
return None

if len(data) > self._max_size_bytes:
logger.debug(
logger.warning(
"Skipping skill '%s': archive resource exceeds the maximum allowed size (%d bytes)",
entry.name,
self._max_size_bytes,
Expand All @@ -4749,7 +4749,9 @@ def _build_skill(self, entry: _McpSkillIndexEntry, data: bytes, mime_type: str |
"""
archive_format = _detect_archive_format(data, mime_type, entry.url)
if archive_format is _ArchiveFormat.UNKNOWN:
logger.debug("Skipping skill '%s': unsupported archive media type '%s'", entry.name, mime_type or "(none)")
logger.warning(
"Skipping skill '%s': unsupported archive media type '%s'", entry.name, mime_type or "(none)"
)
return None

try:
Expand All @@ -4766,13 +4768,13 @@ def _skill_from_files(self, entry: _McpSkillIndexEntry, files: dict[str, bytes])
"""Assemble an in-memory :class:`FileSkill` from the archive's extracted files."""
skill_md_key = self._find_skill_md(files)
if skill_md_key is None:
logger.debug("Skipping skill '%s': archive contains no SKILL.md", entry.name)
logger.warning("Skipping skill '%s': archive contains no SKILL.md", entry.name)
return None

try:
content = files[skill_md_key].decode("utf-8")
except UnicodeDecodeError:
logger.debug("Skipping skill '%s': SKILL.md is not valid UTF-8", entry.name)
logger.warning("Skipping skill '%s': SKILL.md is not valid UTF-8", entry.name)
return None

frontmatter = FileSkillsSource._extract_frontmatter( # pyright: ignore[reportPrivateUsage]
Expand All @@ -4782,7 +4784,7 @@ def _skill_from_files(self, entry: _McpSkillIndexEntry, files: dict[str, bytes])
return None

if frontmatter.name != entry.name:
logger.debug(
logger.warning(
"Skipping skill '%s': SKILL.md frontmatter name '%s' does not match the advertised entry name",
entry.name,
frontmatter.name,
Expand Down
17 changes: 17 additions & 0 deletions python/packages/core/tests/core/test_mcp_skills.py
Original file line number Diff line number Diff line change
Expand Up @@ -917,6 +917,23 @@ async def test_frontmatter_name_mismatch_is_skipped(self) -> None:
skills = await source.get_skills(_SOURCE_CTX)
assert skills == []

async def test_frontmatter_name_mismatch_is_logged_as_warning(self, caplog: pytest.LogCaptureFixture) -> None:
url = "skill://archives/packaged-skill.zip"
index = _make_archive_index("packaged-skill", url)
mismatched = ARCHIVE_SKILL_MD.replace("name: packaged-skill", "name: different-name")
archive = _make_zip({"SKILL.md": mismatched.encode()})
client = _archive_client(index, url, archive, "application/zip")

source = MCPSkillsSource(client=client)
with caplog.at_level("WARNING", logger="agent_framework._skills"):
skills = await source.get_skills(_SOURCE_CTX)

assert skills == []
assert any(
record.levelname == "WARNING" and "does not match the advertised entry name" in record.message
for record in caplog.records
)

@pytest.mark.asyncio
async def test_archive_without_skill_md_is_skipped(self) -> None:
url = "skill://archives/packaged-skill.zip"
Expand Down
Loading