Skip to content

Add client utilities for downloading skills#2948

Merged
jlowin merged 7 commits intomainfrom
skills-client-utilities
Jan 20, 2026
Merged

Add client utilities for downloading skills#2948
jlowin merged 7 commits intomainfrom
skills-client-utilities

Conversation

@jlowin
Copy link
Copy Markdown
Member

@jlowin jlowin commented Jan 20, 2026

Adds client-side utilities for discovering and downloading skills from MCP servers that expose them via SkillsProvider.

from fastmcp import Client
from fastmcp.utilities.skills import list_skills, download_skill, sync_skills

async with Client("http://skills-server/mcp") as client:
    # See what's available
    skills = await list_skills(client)
    
    # Download one
    await download_skill(client, "pdf-processing", "~/.claude/skills")
    
    # Or sync everything
    await sync_skills(client, "~/.claude/skills")

The utilities work by reading the standard skill://{name}/_manifest resources that SkillsProvider exposes - no special protocol needed.

@marvin-context-protocol marvin-context-protocol Bot added enhancement Improvement to existing functionality. For issues and smaller PR improvements. client Related to the FastMCP client SDK or client-side functionality. labels Jan 20, 2026
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bb5af39b7b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/fastmcp/utilities/skills.py Outdated
Comment on lines +182 to +185
file_path = skill_dir / file_info.path

# Create parent directories if needed
file_path.parent.mkdir(parents=True, exist_ok=True)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Reject manifest paths that escape the target directory

The download loop trusts file_info.path from the server manifest and directly joins it with skill_dir. If a remote server returns paths like ../../.ssh/authorized_keys or absolute paths, this will write outside the intended skills folder (the subsequent mkdir/write_* calls will happily create or overwrite files). This is a security issue whenever these utilities are used against untrusted servers. Consider resolving the candidate path and enforcing is_relative_to(skill_dir) (and rejecting absolute paths) before creating directories or writing files.

Useful? React with 👍 / 👎.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 20, 2026

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds client-side skills utilities and examples for FastMCP. New module src/fastmcp/utilities/skills.py introduces dataclasses (SkillSummary, SkillFile, SkillManifest) and async functions (list_skills, get_skill_manifest, download_skill, sync_skills) to discover and download MCP skills. Adds example script examples/skills/download_skills.py demonstrating listing and downloading skills. Adds documentation in docs/servers/providers/skills.mdx with usage examples; the documentation block appears duplicated in the diff. No API signature removals or other public-entity deletions.

Possibly related PRs

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add client utilities for downloading skills' directly summarizes the main change: introducing new client-side utilities for skill discovery and download.
Description check ✅ Passed The PR description clearly explains the feature, provides code examples, and describes how the utilities work, though it doesn't include all checklist items from the template.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/fastmcp/utilities/skills.py (2)

176-179: Consider logging or warning when a file cannot be read.

Silently continuing when read_resource returns empty could hide server-side issues. While this keeps the function resilient, users may not realize files were skipped.

Optional: Add logging for skipped files
+import logging
+
+logger = logging.getLogger(__name__)
+
 # In download_skill function:
         if not result:
+            logger.warning(f"Could not read resource: {file_uri}")
             continue

231-239: Consider handling partial failures gracefully.

If download_skill raises ValueError (e.g., missing manifest), sync_skills aborts entirely. Depending on intended behavior, you might want to catch and log errors for individual skills while continuing with others.

Optional: Continue on individual skill failures
     for skill in skills:
         try:
             path = await download_skill(
                 client, skill.name, target_dir, overwrite=overwrite
             )
             downloaded.append(path)
         except FileExistsError:
             # Skip existing skills when not overwriting
             continue
+        except ValueError as e:
+            # Log and skip skills that fail to download
+            logger.warning(f"Failed to download skill '{skill.name}': {e}")
+            continue

Comment thread examples/skills/download_skills.py Outdated
Comment thread src/fastmcp/utilities/skills.py Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Comment thread src/fastmcp/utilities/skills.py Outdated
Comment thread src/fastmcp/utilities/skills.py
@jlowin jlowin merged commit ba18a71 into main Jan 20, 2026
11 checks passed
@jlowin jlowin deleted the skills-client-utilities branch January 20, 2026 01:10
gfortaine pushed a commit to gfortaine/fastmcp that referenced this pull request Jan 30, 2026
gfortaine pushed a commit to gfortaine/fastmcp that referenced this pull request Feb 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

client Related to the FastMCP client SDK or client-side functionality. enhancement Improvement to existing functionality. For issues and smaller PR improvements.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant