forked from phil65/agentpool
-
Notifications
You must be signed in to change notification settings - Fork 9
Feature/yuchen.liu/mcp skills resources provider #14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Leoyzen
merged 29 commits into
develop/agentic
from
feature/yuchen.liu/mcp-skills-resources-provider
Apr 13, 2026
Merged
Changes from all commits
Commits
Show all changes
29 commits
Select commit
Hold shift + click to select a range
67c78d6
rfc: add mcp skills resources provider rfc
Leoyzen 3cc7454
fix(opencode): remove skill: prefix for OpenCode protocol compatibility
Leoyzen f2c1c4b
feat(opencode): add source and template fields to Command model
Leoyzen 5e268e0
fix(opencode): get skills from skill_provider when skill_bridge unava…
Leoyzen 5f45940
debug: add logging to list_commands for skill_provider debugging
Leoyzen 37f8f7e
feat(skills): implement RFC-0020 MCP Skills Resources Provider Protocol
Leoyzen 038c7de
fix(skills): load_skill checks skill_provider for MCP-based skills
Leoyzen facd191
fix(skills): list_skills includes MCP-based skills + add tests
Leoyzen 8709bc8
test(skills): fix test skill names to use hyphens
Leoyzen 966f77a
fix(skills): allow underscores in skill names for consistency
Leoyzen aabc34f
test(skills): add test for underscore skill names
Leoyzen d52c53f
fix(skills): address Gemini review - pool param and skill_resolver
Leoyzen d202bed
fix(skills): address Gemini review - local skills priority
Leoyzen a501ca0
fix(local): emit skills_changed signal on add/remove
Leoyzen 6c34f2c
fix(mcp): lazy-load skill instructions during discovery
Leoyzen 51f766c
fix(skills): load virtual skill instructions from provider
Leoyzen 0cb320d
fix(mcp): add arguments parameter to get_skill_instructions
Leoyzen befe1a8
fix(providers): update get_skill_instructions signature with arguments
Leoyzen eb24936
test(skills): update mocks for skill_resolver and skill_provider
Leoyzen b2031ee
fix: address code review comments from PR #14
Leoyzen d963091
fix: handle virtual skills in load_instructions callers
Leoyzen 3dbbd77
fix: include skill URI in metadata injection
Leoyzen f7a0c3d
fix: bare skill name resolution and virtual path detection
Leoyzen cd59d04
fix: security - prevent absolute path traversal in skill references
Leoyzen cc92f69
fix: address code review comments - batch 1
Leoyzen c66282f
fix: address code review comments - batch 2
Leoyzen 834e9c4
fix: parse URI before decoding to handle URL-encoded characters corre…
Leoyzen 0a8b101
fix: remaining code review comments
Leoyzen b127f15
chore: move rfc to complete.
Leoyzen File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,358 @@ | ||
| --- | ||
| title: Skill URI Usage | ||
| description: Using skill:// URIs to load skills and reference content | ||
| icon: material/link-variant | ||
| order: 11 | ||
| --- | ||
|
|
||
| # Skill URI Usage | ||
|
|
||
| AgentPool supports a unified `skill://` URI scheme for accessing skills from both local filesystem and MCP servers. This enables consistent skill loading regardless of where skills are stored. | ||
|
|
||
| ## Overview | ||
|
|
||
| The skill URI system provides: | ||
|
|
||
| - **Unified access**: Load skills from local directories or MCP servers using the same interface | ||
| - **Reference content**: Access supporting files bundled with skills | ||
| - **Argument substitution**: Pass arguments to skills with bash-style variables | ||
| - **Provider priority**: Automatic resolution when multiple sources have the same skill name | ||
|
|
||
| ## URI Format | ||
|
|
||
| ``` | ||
| skill://{provider}/{skill-name}/{reference-path} | ||
| ``` | ||
|
|
||
| | Component | Description | Example | | ||
| |-----------|-------------|---------| | ||
| | `provider` | Skill source identifier | `local`, `github-copilot` | | ||
| | `skill-name` | Name of the skill | `python-expert` | | ||
| | `reference-path` | Optional path to supporting files | `references/style-guide.md` | | ||
|
|
||
| ## Loading Skills | ||
|
|
||
| ### By Short Name (Auto-Routing) | ||
|
|
||
| When you use a bare skill name, AgentPool searches all providers in priority order: | ||
|
|
||
| ```python | ||
| from agentpool import AgentPool | ||
|
|
||
| async with AgentPool("config.yml") as pool: | ||
| agent = pool.get_agent("assistant") | ||
| # Agent can use: load_skill(ctx, "python-expert") | ||
| # Automatically finds skill across all providers | ||
| ``` | ||
|
|
||
| **Priority order**: Local skills first, then MCP providers in registration order. | ||
|
|
||
| ### By Full URI (Explicit Provider) | ||
|
|
||
| For precise control over which provider to use: | ||
|
|
||
| ```python | ||
| # Local filesystem skill | ||
| await load_skill(ctx, "skill://local/python-expert") | ||
|
|
||
| # MCP server skill | ||
| await load_skill(ctx, "skill://github-copilot/code-review") | ||
| ``` | ||
|
|
||
| ### Loading Reference Content | ||
|
|
||
| Skills can bundle supporting files in a `references/` directory: | ||
|
|
||
| ```python | ||
| # Load a reference file from a local skill | ||
| await load_skill(ctx, "skill://local/python-expert/references/pep8-guide.md") | ||
|
|
||
| # Load from MCP resource-based skill | ||
| await load_skill(ctx, "skill://skills-server/pdf-processing/examples/sample.pdf") | ||
| ``` | ||
|
|
||
| ## URI Examples | ||
|
|
||
| ### Local Filesystem Skills | ||
|
|
||
| ``` | ||
| skill://local/python-expert # Main skill | ||
| skill://local/python-expert/SKILL.md # Explicit main file | ||
| skill://local/python-expert/references/style-guide.md # Reference file | ||
| skill://local/my%20skill # URL-encoded name | ||
| ``` | ||
|
|
||
| ### MCP Prompt-Based Skills | ||
|
|
||
| ``` | ||
| skill://github-copilot/code-review # Prompt exposed as skill | ||
| skill://my-mcp/custom-prompt # Custom MCP server prompt | ||
| ``` | ||
|
|
||
| ### MCP Resource-Based Skills (FastMCP Skills Provider) | ||
|
|
||
| ``` | ||
| skill://skills-server/pdf-processing # Short form | ||
| skill://skills-server/pdf-processing/SKILL.md # Main skill file | ||
| skill://skills-server/pdf-processing/_manifest # JSON manifest | ||
| skill://skills-server/pdf-processing/examples/doc.pdf # Reference | ||
| ``` | ||
|
|
||
| ## Argument Substitution | ||
|
|
||
| Skills support bash-style variable substitution when arguments are provided: | ||
|
|
||
| | Variable | Description | Example | | ||
| |----------|-------------|---------| | ||
| | `$1`, `$2`, ... | Positional arguments | `$1` becomes first argument | | ||
| | `$@` | All arguments | All arguments as single string | | ||
| | `$ARGUMENTS` | All arguments | Alias for `$@` | | ||
|
|
||
| ### Example Skill with Arguments | ||
|
|
||
| ```markdown | ||
| # Skill: greeting | ||
|
|
||
| Generate a personalized greeting. | ||
|
|
||
| ## Instructions | ||
|
|
||
| Create a greeting for $1 from $2. | ||
| Use a $3 tone. | ||
|
|
||
| ## Allowed Tools | ||
|
|
||
| generate_text | ||
| ``` | ||
|
|
||
| ### Using Arguments | ||
|
|
||
| ```python | ||
| # Arguments are passed as a string | ||
| await load_skill(ctx, "greeting", "Alice Company formal") | ||
|
|
||
| # Result substitutes: | ||
| # $1 → "Alice" | ||
| # $2 → "Company" | ||
| # $3 → "formal" | ||
| ``` | ||
|
|
||
| !!! note "Arguments containing spaces" | ||
| Arguments are split by whitespace, which means values containing spaces | ||
| (like `"Alice Smith"`) will be treated as separate arguments. For example, | ||
| the string `"Alice Smith formal"` becomes three arguments: `"Alice"`, | ||
| `"Smith"`, and `"formal"`. | ||
|
|
||
| To pass multi-word values as a single argument, use underscores or hyphens | ||
| (e.g., `Alice-Smith`), or structure your skill to accept multiple arguments | ||
| that are joined in the template. | ||
|
|
||
| ## Creating Skills with References | ||
|
|
||
| To create a skill with supporting files: | ||
|
|
||
| 1. Create the skill directory: | ||
| ``` | ||
| ~/.claude/skills/my-skill/ | ||
| ``` | ||
|
|
||
| 2. Add the main `SKILL.md`: | ||
| ```markdown | ||
| # Skill: my-skill | ||
|
|
||
| Description of what this skill does. | ||
|
|
||
| ## License | ||
| MIT | ||
|
|
||
| ## Compatibility | ||
| 1.0.0 | ||
|
|
||
| ## Allowed Tools | ||
| bash, read, grep | ||
|
|
||
| ## Instructions | ||
| Detailed instructions here... | ||
| ``` | ||
|
|
||
| 3. Create a `references/` subdirectory: | ||
| ``` | ||
| ~/.claude/skills/my-skill/references/ | ||
| ``` | ||
|
|
||
| 4. Add reference files: | ||
| ``` | ||
| ~/.claude/skills/my-skill/references/guide.md | ||
| ~/.claude/skills/my-skill/references/examples/ | ||
| ~/.claude/skills/my-skill/references/templates/ | ||
| ``` | ||
|
|
||
| 5. Access via URI: | ||
| ```python | ||
| await load_skill(ctx, "skill://local/my-skill/references/guide.md") | ||
| ``` | ||
|
|
||
| ## MCP Skills Provider Protocol | ||
|
|
||
| AgentPool supports the [FastMCP Skills Provider protocol](https://gofastmcp.com/servers/providers/skills), allowing MCP servers to expose skills as resources. | ||
|
|
||
| ### Resource Patterns | ||
|
|
||
| When an MCP server implements the Skills Provider protocol: | ||
|
|
||
| | URI Pattern | Purpose | | ||
| |-------------|---------| | ||
| | `skill://{server}/{skill}` | Short form (resolves to main skill) | | ||
| | `skill://{server}/{skill}/SKILL.md` | Main instruction file | | ||
| | `skill://{server}/{skill}/_manifest` | JSON manifest with file list | | ||
| | `skill://{server}/{skill}/{file}` | Supporting/reference files | | ||
|
|
||
| ### Configuration Example | ||
|
|
||
| ```yaml | ||
| mcp_servers: | ||
| - "uvx mcp-server-with-skills" | ||
|
|
||
| agents: | ||
| assistant: | ||
| model: openai:gpt-4o | ||
| tools: | ||
| - type: skills | ||
| # Can now load skills from MCP server: | ||
| # skill://mcp-server-with-skills/pdf-processing | ||
| ``` | ||
|
|
||
| ## Security Considerations | ||
|
|
||
| The skill URI system includes several security protections: | ||
|
|
||
| ### Path Traversal Protection | ||
|
|
||
| - `..` components in paths are rejected | ||
| - Paths are resolved and verified to be within allowed directories | ||
| - Symlinks are resolved before validation | ||
|
|
||
| ### Provider Name Validation | ||
|
|
||
| - Must start with alphanumeric character | ||
| - Can contain alphanumeric, hyphen, and underscore | ||
| - Maximum 63 characters | ||
|
|
||
| ### Null Byte Protection | ||
|
|
||
| - Null bytes (`\x00`) in paths are rejected | ||
|
|
||
| ## Provider Priority and Collision Resolution | ||
|
|
||
| When multiple providers have skills with the same name: | ||
|
|
||
| 1. **Local provider** always has highest priority | ||
| 2. **MCP providers** are checked in registration order | ||
| 3. Collisions are logged with the selected provider noted | ||
|
|
||
| ### Example | ||
|
|
||
| ``` | ||
| Local provider: python-expert, refactoring | ||
| MCP provider A: code-review, python-expert | ||
| MCP provider B: documentation, code-review | ||
|
|
||
| Resolution: | ||
| - python-expert → local (priority) | ||
| - refactoring → local | ||
| - code-review → MCP provider A (first registered) | ||
| - documentation → MCP provider B | ||
| ``` | ||
|
|
||
| ## Listing Available Skills | ||
|
|
||
| Use the `list_skills` tool to see all available skills: | ||
|
|
||
| ```python | ||
| result = await list_skills(ctx) | ||
| print(result) | ||
| ``` | ||
|
|
||
| Output format: | ||
| ``` | ||
| Available skills: | ||
|
|
||
| ## local (3 skills) | ||
| - **python-expert**: Expert Python development techniques | ||
| URI: `skill://local/python-expert` | ||
| - **refactoring**: Safe code refactoring patterns | ||
| URI: `skill://local/refactoring` | ||
|
|
||
| ## github-copilot (2 skills) | ||
| - **code-review**: Automated code review | ||
| URI: `skill://github-copilot/code-review` | ||
| ``` | ||
|
|
||
| ## Configuration Reference | ||
|
|
||
| Enable skill loading in your agent configuration: | ||
|
|
||
| ```yaml | ||
| agents: | ||
| my_agent: | ||
| model: openai:gpt-4o | ||
| tools: | ||
| - type: skills | ||
| # Optional: limit number of skills shown in listings | ||
| max_skills: 20 | ||
| ``` | ||
|
|
||
| See [Skills Configuration](./skills.md) for detailed configuration options. | ||
|
|
||
| ## Migration Guide | ||
|
|
||
| ### From Bare Skill Names | ||
|
|
||
| Existing code using bare skill names continues to work: | ||
|
|
||
| ```python | ||
| # Before (still works) | ||
| await load_skill(ctx, "python-expert") | ||
|
|
||
| # After (new option) | ||
| await load_skill(ctx, "skill://local/python-expert") | ||
| ``` | ||
|
|
||
| ### New Capabilities | ||
|
|
||
| New features available with RFC-0020: | ||
|
|
||
| 1. **Explicit provider selection**: Use full URIs when multiple providers have the same skill | ||
| 2. **Reference content**: Access supporting files via URI paths | ||
| 3. **MCP skills**: Load skills from MCP servers using the same interface | ||
| 4. **Argument substitution**: Pass dynamic arguments to skills | ||
|
|
||
| ## Troubleshooting | ||
|
|
||
| ### Skill Not Found | ||
|
|
||
| ``` | ||
| Skill not found: 'my-skill'. Available: python-expert, refactoring | ||
| ``` | ||
|
|
||
| - Check skill name spelling | ||
| - Verify skill exists with `list_skills` | ||
| - Check provider name if using full URI | ||
|
|
||
| ### Reference Not Found | ||
|
|
||
| ``` | ||
| Reference not found: 'guide.md' in skill 'my-skill' | ||
| ``` | ||
|
|
||
| - Verify reference file exists in skill's `references/` directory | ||
| - Check for typos in the reference path | ||
|
|
||
| ### Security Error | ||
|
|
||
| ``` | ||
| Security error: Path traversal detected in URI | ||
| ``` | ||
|
|
||
| - Remove `..` components from paths | ||
| - Ensure path does not escape the skill directory | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The documentation on argument substitution should clarify how arguments containing spaces are handled. The current implementation splits arguments by spaces, which means a value like
"Alice Smith"would be treated as two separate arguments,"Alice"and"Smith".Please add a note explaining this behavior and suggest how to handle multi-word arguments, for example by using quotes if the parser supports it, or by noting it as a current limitation.