Skills are modular knowledge packages that teach AI how to perform multi-step tasks using tools. Unlike tools (which are individual actions), skills are recipes/playbooks that combine tools into coherent workflows.
Skills extend the MCP model by providing workflow guidance. They’re discovered via searchSkills and loaded via loadSkill tools that FrontMCP automatically registers.
Why Skills?
In the Model Context Protocol ecosystem, skills serve a distinct purpose from tools, resources, and prompts:
| Aspect | Skill | Tool | Resource | Prompt |
|---|
| Purpose | Multi-step workflows | Execute actions | Provide data | Provide templated instructions |
| Contains | Instructions + tool references | Execution logic | Read-only data | Message templates |
| Direction | Model loads on demand | Model triggers execution | Model pulls data | Model uses messages |
| Side effects | No (guidance only) | Yes (mutations, API calls) | No (read-only) | No (message generation) |
| Use case | Complex procedures, workflows | Actions, integrations | Context loading | Conversation templates |
Skills are ideal for:
- Complex workflows — multi-step procedures like PR reviews, deployments, migrations
- Domain expertise — codified knowledge from experts (security audits, code reviews)
- Orchestration guides — coordinating multiple tools in sequence
- Reusable playbooks — standardized procedures across teams
How Skills Work
Creating Skills
Class Style
Use class decorators for skills that need dependency injection or lifecycle hooks:
import { Skill, SkillContext } from '@frontmcp/sdk';
@Skill({
name: 'review-pr',
description: 'Review a GitHub pull request for code quality and issues',
instructions: `
## PR Review Process
1. First, fetch the PR details using github_get_pr
2. Review the changed files using github_get_pr_files
3. For each file, analyze for:
- Code quality issues
- Security vulnerabilities
- Performance concerns
4. Add review comments using github_add_review_comment
5. Submit the review using github_submit_review
`,
tools: [
{ name: 'github_get_pr', purpose: 'Fetch PR details', required: true },
{ name: 'github_get_pr_files', purpose: 'Get changed files' },
{ name: 'github_add_review_comment', purpose: 'Add inline comments' },
{ name: 'github_submit_review', purpose: 'Submit final review' },
],
tags: ['github', 'code-review', 'quality'],
})
class ReviewPRSkill extends SkillContext {}
Function Style
For simpler skills, use the functional builder:
import { skill } from '@frontmcp/sdk';
const DeployAppSkill = skill({
name: 'deploy-app',
description: 'Deploy application to production environment',
instructions: { file: './skills/deploy-app.md' },
tools: ['docker_build', 'docker_push', 'k8s_apply'],
tags: ['deployment', 'devops'],
});
Registering Skills
Add skills to your app via the skills array:
import { App } from '@frontmcp/sdk';
@App({
id: 'my-app',
name: 'My Application',
tools: [GitHubTool, DockerTool, K8sTool],
skills: [ReviewPRSkill, DeployAppSkill, RefactorSkill],
})
class MyApp {}
Skills are automatically:
- Indexed for search via
searchSkills
- Loadable via
loadSkill with tool availability info
@Skill({
name: string, // Required: kebab-case, max 64 chars
description: string, // Required: max 1024 chars, no XML tags
instructions: InstructionSource, // Required: detailed guidance (see below)
tools?: ToolRef[], // Optional: tool references with purpose
tags?: string[], // Optional: categorization tags
parameters?: Parameter[], // Optional: input parameters
examples?: Example[], // Optional: usage examples
priority?: number, // Optional: search ranking weight (default: 0)
hideFromDiscovery?: boolean, // Optional: hide from searchSkills (default: false)
visibility?: 'mcp' | 'http' | 'both', // Optional: discovery visibility (default: 'both')
toolValidation?: 'strict' | 'warn' | 'ignore', // Optional: tool ref validation (default: 'warn')
license?: string, // Optional: license name (e.g. 'MIT')
compatibility?: string, // Optional: environment requirements (max 500 chars)
specMetadata?: Record<string, string>, // Optional: arbitrary key-value metadata
allowedTools?: string, // Optional: space-delimited pre-approved tools
resources?: SkillResources, // Optional: bundled resource directories
})
Field descriptions:
| Field | Description |
|---|
name | Unique identifier, must be kebab-case (max 64 chars, no consecutive hyphens) |
description | Short text for discovery (max 1024 chars, no XML/HTML tags) |
instructions | Detailed step-by-step guidance (inline, file, or URL) |
tools | Tools this skill uses, with optional purpose descriptions |
tags | Categorization for filtering and organization |
parameters | Input values that customize skill behavior |
examples | Scenarios demonstrating when and how to use the skill |
priority | Higher values appear earlier in search results |
hideFromDiscovery | When true, skill is loadable but not listed in search |
visibility | Where this skill is discoverable: mcp, http, or both |
toolValidation | How to handle missing tool references: strict, warn, ignore |
license | License name or reference (per Agent Skills spec) |
compatibility | Environment requirements (max 500 chars, per Agent Skills spec) |
specMetadata | Arbitrary key-value metadata (maps to spec metadata field) |
allowedTools | Space-delimited pre-approved tools (maps to spec allowed-tools) |
resources | Bundled resource directories (scripts/, references/, assets/) |
Name Validation
Skill names follow strict kebab-case rules per the Anthropic Agent Skills specification:
- Lowercase letters, numbers, and hyphens only
- Must start and end with a letter or number
- No consecutive hyphens (
--)
- Maximum 64 characters
// Valid names
'review-pr' // ✓
'deploy-v2' // ✓
'a' // ✓
// Invalid names
'Review-PR' // ✗ uppercase
'review_pr' // ✗ underscore
'review--pr' // ✗ consecutive hyphens
'-review-pr' // ✗ starts with hyphen
Description Validation
- Maximum 1024 characters
- Must not contain XML/HTML tags (per Agent Skills spec)
Instruction Sources
Skills support three instruction sources:
Inline String (Recommended)
Best for dependency tracking and version control:
@Skill({
name: 'security-audit',
instructions: `
## Security Audit Process
1. Scan dependencies with security_scan_deps
2. Check for hardcoded secrets with secrets_scan
3. Analyze code for OWASP top 10 with code_analyze
4. Generate report with create_report
`,
})
File Path
Load from a markdown file at runtime:
@Skill({
name: 'migration-guide',
instructions: { file: './skills/database-migration.md' },
})
URL
Fetch from a remote source at load time:
@Skill({
name: 'external-workflow',
instructions: { url: 'https://docs.example.com/workflows/deploy.md' },
})
Inline instructions are recommended because they enable static analysis, type checking, and ensure the skill definition is complete and self-contained.
Reference tools with simple names or detailed objects:
Simple References
@Skill({
name: 'simple-workflow',
tools: ['github_create_pr', 'github_merge_pr', 'slack_notify'],
})
Detailed References
Include purpose and required flag for better LLM understanding:
@Skill({
name: 'detailed-workflow',
tools: [
{ name: 'github_create_pr', purpose: 'Create the pull request', required: true },
{ name: 'github_request_review', purpose: 'Request code review' },
{ name: 'github_merge_pr', purpose: 'Merge after approval', required: true },
{ name: 'slack_notify', purpose: 'Notify team of completion' },
],
})
| Property | Description |
|---|
name | The tool’s identifier |
purpose | Explains why/how the tool is used in this skill |
required | If true, skill may not work without this tool |
Parameters
Define input parameters that customize skill behavior:
@Skill({
name: 'review-pr',
parameters: [
{
name: 'pr_url',
description: 'URL of the pull request to review',
required: true,
type: 'string',
},
{
name: 'focus_areas',
description: 'Specific areas to focus on (security, performance, etc.)',
type: 'array',
},
{
name: 'strictness',
description: 'Review strictness level',
type: 'string',
default: 'normal',
},
],
})
| Property | Description |
|---|
name | Parameter identifier |
description | Human-readable description |
required | Whether the parameter must be provided |
type | Type hint: string, number, boolean, object, array |
default | Default value if not provided |
Examples
Provide usage examples to help LLMs understand when to use the skill:
@Skill({
name: 'review-pr',
examples: [
{
scenario: 'User asks to review a PR for security issues',
parameters: {
pr_url: 'https://github.com/org/repo/pull/123',
focus_areas: ['security', 'authentication'],
},
expectedOutcome: 'Detailed security-focused review with inline comments',
},
{
scenario: 'User wants a quick review of a small PR',
parameters: {
pr_url: 'https://github.com/org/repo/pull/456',
strictness: 'lenient',
},
expectedOutcome: 'High-level review focusing on major issues only',
},
],
})
Real-World Examples
PR Review Skill
@Skill({
name: 'review-pr',
description: 'Review a GitHub pull request for code quality, security, and best practices',
instructions: `
## PR Review Workflow
### Phase 1: Gather Context
1. Use github_get_pr to fetch PR metadata (title, description, author)
2. Use github_get_pr_files to get the list of changed files
3. For large PRs, prioritize: security-sensitive files, API changes, database migrations
### Phase 2: Analyze Changes
For each changed file:
- Check for security vulnerabilities (SQL injection, XSS, auth bypass)
- Review error handling and edge cases
- Verify test coverage for new functionality
- Look for performance issues (N+1 queries, memory leaks)
### Phase 3: Provide Feedback
1. Use github_add_review_comment for inline suggestions
2. Group related issues together
3. Distinguish between blocking issues and suggestions
### Phase 4: Submit Review
Use github_submit_review with:
- APPROVE: No blocking issues found
- REQUEST_CHANGES: Blocking issues that must be fixed
- COMMENT: Suggestions only, no blocking issues
`,
tools: [
{ name: 'github_get_pr', purpose: 'Fetch PR metadata and description', required: true },
{ name: 'github_get_pr_files', purpose: 'List all changed files', required: true },
{ name: 'github_add_review_comment', purpose: 'Add inline review comments' },
{ name: 'github_submit_review', purpose: 'Submit the final review verdict' },
],
tags: ['github', 'code-review', 'quality', 'security'],
parameters: [
{ name: 'pr_url', description: 'Pull request URL', required: true },
{ name: 'focus_areas', description: 'Areas to focus on', type: 'array' },
],
})
class ReviewPRSkill extends SkillContext {}
Deploy Application Skill
@Skill({
name: 'deploy-app',
description: 'Deploy application to Kubernetes production environment',
instructions: `
## Deployment Workflow
### Pre-flight Checks
1. Verify all tests pass with run_tests
2. Check for security vulnerabilities with security_scan
3. Ensure environment variables are configured
### Build Phase
1. Build Docker image with docker_build
2. Tag with version and commit SHA
3. Push to registry with docker_push
### Deploy Phase
1. Update Kubernetes manifests with k8s_update_manifests
2. Apply changes with k8s_apply
3. Wait for rollout with k8s_wait_rollout
### Post-deployment
1. Run smoke tests with run_smoke_tests
2. Notify team with slack_notify
3. If any step fails, trigger k8s_rollback
`,
tools: [
{ name: 'run_tests', purpose: 'Run test suite', required: true },
{ name: 'security_scan', purpose: 'Scan for vulnerabilities' },
{ name: 'docker_build', purpose: 'Build container image', required: true },
{ name: 'docker_push', purpose: 'Push to container registry', required: true },
{ name: 'k8s_apply', purpose: 'Apply Kubernetes manifests', required: true },
{ name: 'k8s_wait_rollout', purpose: 'Wait for deployment completion' },
{ name: 'k8s_rollback', purpose: 'Rollback on failure' },
{ name: 'slack_notify', purpose: 'Send deployment notifications' },
],
tags: ['deployment', 'kubernetes', 'devops', 'production'],
})
class DeployAppSkill extends SkillContext {}
Code Refactoring Skill
const RefactorSkill = skill({
name: 'refactor-code',
description: 'Safely refactor code with comprehensive testing',
instructions: `
## Refactoring Workflow
### Analysis
1. Read the target code with read_file
2. Identify code smells and improvement opportunities
3. Plan refactoring steps (small, testable changes)
### Refactoring Loop
For each planned change:
1. Make the change with edit_file
2. Run tests with run_tests
3. If tests fail, revert and try alternative approach
4. Commit working changes with git_commit
### Verification
1. Run full test suite
2. Check for regressions
3. Review final diff with git_diff
`,
tools: [
{ name: 'read_file', purpose: 'Read source code', required: true },
{ name: 'edit_file', purpose: 'Make code changes', required: true },
{ name: 'run_tests', purpose: 'Verify changes', required: true },
{ name: 'git_commit', purpose: 'Commit working changes' },
{ name: 'git_diff', purpose: 'Review final changes' },
],
tags: ['refactoring', 'code-quality', 'testing'],
});
MCP Protocol Integration
FrontMCP automatically registers two flows for skill discovery and loading:
| Flow | Description |
|---|
skills/search | Search for relevant skills by query, tags, or required tools |
skills/load | Load a skill’s full content including instructions and tool info |
searchSkills
Search for skills matching a query:
{
"method": "skills/search",
"params": {
"query": "review pull request",
"tags": ["github"],
"limit": 10,
"requireAllTools": false
}
}
Response:
{
"skills": [
{
"id": "review-pr",
"name": "review-pr",
"description": "Review a GitHub pull request...",
"score": 0.95,
"tags": ["github", "code-review"],
"tools": [
{ "name": "github_get_pr", "available": true },
{ "name": "github_submit_review", "available": true }
],
"source": "local"
}
],
"total": 5,
"hasMore": false
}
loadSkill
Load a skill’s full content:
{
"method": "skills/load",
"params": {
"skillId": "review-pr",
"format": "full"
}
}
Response:
{
"skill": {
"id": "review-pr",
"name": "review-pr",
"description": "Review a GitHub pull request...",
"instructions": "## PR Review Workflow\n\n### Phase 1...",
"tools": [
{ "name": "github_get_pr", "purpose": "Fetch PR metadata", "available": true },
{ "name": "github_submit_review", "purpose": "Submit review", "available": true }
],
"parameters": [
{ "name": "pr_url", "required": true }
]
},
"availableTools": ["github_get_pr", "github_submit_review"],
"missingTools": [],
"isComplete": true,
"formattedContent": "# Skill: review-pr\n\n## Instructions\n..."
}
When loading a skill, FrontMCP validates tool availability:
- Available tools: Tools registered in the current scope
- Missing tools: Referenced tools that don’t exist
- Hidden tools: Tools that exist but are hidden from discovery
Skills are still loadable even when some tools are missing:
{
"skill": { ... },
"availableTools": ["github_get_pr"],
"missingTools": ["github_submit_review"],
"isComplete": false,
"warning": "Skill 'review-pr' is missing 1 tool(s): github_submit_review. Some functionality may not work."
}
The LLM receives this information and can:
- Proceed with available tools only
- Inform the user about limitations
- Suggest alternative approaches
Setting required: true on a tool reference indicates the skill cannot function without it. Missing required tools are highlighted in the warning message.
Search Algorithm
Skills are searched using TF-IDF with weighted terms:
| Field | Weight |
|---|
| description | 3x |
| tags | 2x |
| tools | 1x |
| name | 1x |
Results are ranked by:
- Relevance score
- Priority field (higher = earlier)
- Tool availability (complete skills rank higher)
Agent Skills Specification Alignment
FrontMCP skills are aligned with the Anthropic Agent Skills specification. This means you can use standard SKILL.md files and skill directories with FrontMCP.
New Spec Fields
Fields from the Agent Skills spec are available as first-class properties:
@Skill({
name: 'deploy-app',
description: 'Deploy application to production',
instructions: '...',
tools: ['docker_build', 'k8s_apply'],
// Agent Skills spec fields
license: 'MIT',
compatibility: 'Requires Docker 24+ and kubectl',
specMetadata: {
author: 'platform-team',
version: '2.1.0',
category: 'devops',
},
allowedTools: 'Read Edit Bash(docker build) Bash(kubectl apply)',
resources: {
scripts: './skills/deploy-app/scripts',
references: './skills/deploy-app/references',
},
})
class DeployAppSkill extends SkillContext {}
SKILL.md Frontmatter
FrontMCP can parse SKILL.md files with YAML frontmatter, the standard format from the Agent Skills specification:
---
name: review-pr
description: Review a GitHub pull request
license: MIT
compatibility: Requires git CLI
tags:
- github
- code-review
metadata:
author: dev-team
version: "1.0"
allowed-tools: Read Edit Bash(git diff)
---
# PR Review Instructions
1. Fetch the PR details
2. Review each changed file
3. Add review comments
4. Submit the review
The frontmatter fields are mapped to FrontMCP metadata:
metadata → specMetadata
allowed-tools → allowedTools
- All other fields map directly
The markdown body becomes the instructions content.
When loading instructions from a file, FrontMCP automatically strips YAML frontmatter so you get clean instruction content.
Loading Skill Directories
Load complete skill directories following the Agent Skills spec structure:
skills/
review-pr/
SKILL.md # Required: frontmatter + instructions
scripts/ # Optional: automation scripts
references/ # Optional: reference documents
assets/ # Optional: images, diagrams, etc.
Using skillDir()
import { skillDir } from '@frontmcp/sdk';
// Load a skill directory
const reviewSkill = await skillDir('./skills/review-pr');
@App({
name: 'my-app',
skills: [reviewSkill],
})
class MyApp {}
Using loadSkillDirectory()
For more control:
import { loadSkillDirectory, scanSkillResources } from '@frontmcp/sdk';
// Load with full metadata
const record = await loadSkillDirectory('./skills/review-pr');
// record.kind === 'FILE'
// record.metadata contains parsed frontmatter
// record.metadata.resources contains detected directories
// Or just scan for resource directories
const { resources, hasSkillMd } = await scanSkillResources('./skills/review-pr');
The allowedTools field lists tools that are pre-approved for the skill. This is useful for skill sessions where tool access is restricted:
@Skill({
name: 'code-review',
description: 'Review code changes',
instructions: '...',
tools: ['github_get_pr', 'github_add_comment'],
allowedTools: 'Read Edit Bash(git diff) Bash(git log)',
})
Tools in allowedTools don’t require additional user confirmation during skill sessions.
Best Practices
Do:
- Write clear, step-by-step instructions that guide the LLM through the workflow
- Reference specific tools with purpose descriptions
- Include examples for common scenarios
- Use tags for categorization and discovery
- Keep skills focused on a single workflow or domain
Don’t:
- Create overly broad skills that try to do everything
- Skip tool purposes (hurts discoverability and understanding)
- Hardcode values that should be parameters
- Forget to list required tools
- Create skills for simple, single-tool operations (just use the tool directly)