In Part 1, we explored what Claude Agent Skills are and how they work architecturally. Now it is time to get hands-on. This tutorial will walk you through creating your first custom Agent Skill from scratch, complete with working examples you can adapt for your own use cases. By the end of this guide, you will have built, tested, and deployed a functional skill across multiple platforms.
Prerequisites and Setup
Before we begin, ensure you have the following:
- An Anthropic account with access to Claude (Pro, Max, Team, or Enterprise plan)
- Code execution feature enabled in your Claude settings
- A text editor or IDE for creating skill files
- Basic familiarity with YAML and Markdown syntax
- For API integration: Node.js, Python, or .NET SDK installed
The minimal skill requires just one file (SKILL.md), making this accessible even for non-programmers. However, we will also explore more advanced patterns with executable scripts and reference files.
Planning Your First Skill
Strong skills address concrete needs with measurable outcomes. Before writing any code, clarify what problem your skill solves. For this tutorial, we will create a “Code Review Assistant” skill that helps developers perform consistent, thorough code reviews following best practices.
Skill Design Checklist
When planning your skill, answer these questions:
- What specific problem does this skill solve?
- What are the inputs and expected outputs?
- When should Claude automatically load this skill?
- What are the edge cases and limitations?
- What examples will help Claude understand the task?
For our Code Review Assistant skill, the answers are clear. It solves the problem of inconsistent code reviews. Inputs are code files or snippets, outputs are structured review comments. Claude should load this skill when users mention “code review,” “review this code,” or similar phrases. Edge cases include non-code files, incomplete code, or code in unfamiliar languages.
Creating the Basic Skill Structure
Every Agent Skill starts with a directory containing at minimum a SKILL.md file. Let us create our first skill step by step.
Step 1: Create the Skill Directory
Create a directory named “code-review-assistant” with the following structure:
code-review-assistant/
└── SKILL.mdStep 2: Write the YAML Frontmatter
Open SKILL.md and start with the required YAML frontmatter. This metadata tells Claude when and how to use your skill:
---
name: code-review-assistant
description: Perform thorough code reviews following industry best practices. Use when the user asks to review code, check code quality, or identify potential issues in code snippets or files.
version: "1.0.0"
license: Apache-2.0
---The description field is critical. It should include both what the skill does and specific triggers for when Claude should use it. According to Anthropic’s best practices, always write descriptions in third person and keep them under 1024 characters.
Step 3: Write the Skill Instructions
After the frontmatter, add the detailed instructions in Markdown. This is where you teach Claude exactly how to perform code reviews:
# Code Review Assistant
## Purpose
This skill helps you perform comprehensive code reviews by checking for common issues, best practices, security vulnerabilities, and maintainability concerns.
## Review Checklist
### Code Quality
- Check for proper variable and function naming
- Verify code follows consistent style guidelines
- Identify overly complex functions that should be refactored
- Look for code duplication that could be extracted
- Assess readability and documentation quality
### Security
- Check for SQL injection vulnerabilities
- Look for cross-site scripting (XSS) risks
- Identify hardcoded credentials or secrets
- Verify proper input validation and sanitization
- Check for insecure dependencies
### Performance
- Identify inefficient algorithms or data structures
- Look for unnecessary database queries or API calls
- Check for memory leaks or resource management issues
- Assess whether caching could improve performance
### Maintainability
- Verify proper error handling exists
- Check for adequate test coverage
- Identify tightly coupled code that should be modular
- Look for missing documentation or comments
- Assess whether the code follows SOLID principles
## Output Format
Structure your review as follows:
1. **Summary**: Brief overview of the code's purpose and overall quality
2. **Critical Issues**: Security vulnerabilities or bugs that must be fixed
3. **Major Issues**: Important problems affecting maintainability or performance
4. **Minor Issues**: Style inconsistencies or minor improvements
5. **Positive Highlights**: Well-written sections or good practices
6. **Recommendations**: Specific actionable improvements with code examples
## Examples
### Example 1: Simple Function Review
**Input Code:**
```python
def calc(a, b):
return a + b
```
**Review:**
- **Summary**: Simple addition function, but lacks clarity
- **Minor Issues**:
- Function name "calc" is too generic
- Missing type hints
- No docstring explaining purpose
- Parameter names could be more descriptive
- **Recommendations**:
```python
def calculate_sum(first_number: float, second_number: float) -> float:
"""Calculate the sum of two numbers.
Args:
first_number: The first number to add
second_number: The second number to add
Returns:
The sum of the two numbers
"""
return first_number + second_number
```
### Example 2: Security Issue
**Input Code:**
```javascript
app.get('/user', (req, res) => {
const query = `SELECT * FROM users WHERE id = ${req.query.id}`;
db.query(query, (err, result) => {
res.json(result);
});
});
```
**Review:**
- **Critical Issues**:
- SQL injection vulnerability from unsanitized user input
- No error handling could expose sensitive information
- **Recommendations**:
```javascript
app.get('/user', async (req, res) => {
try {
const userId = parseInt(req.query.id, 10);
if (isNaN(userId)) {
return res.status(400).json({ error: 'Invalid user ID' });
}
const query = 'SELECT * FROM users WHERE id = ?';
const result = await db.query(query, [userId]);
res.json(result);
} catch (error) {
console.error('Database error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
```
## Limitations
- Cannot execute or test the code
- May not be familiar with all language-specific idioms
- Cannot access external documentation or API references
- Reviews are based on visible code only, not the full codebase context
## When NOT to Use This Skill
- For generating new code (use general coding assistance instead)
- For debugging runtime errors (requires execution environment)
- For architectural decisions requiring broader system context
- For language-specific style guide enforcement (use linters instead)Testing Your Skill Locally
Before uploading to Claude, test your skill structure locally to ensure the YAML is valid and the instructions are clear.
Validation Checklist
- Verify YAML frontmatter has correct syntax (three dashes before and after)
- Ensure name uses only lowercase letters, numbers, and hyphens
- Check that description is under 1024 characters
- Confirm instructions are clear and actionable
- Review examples for accuracy and relevance
Uploading to Claude.ai
Once your SKILL.md file is ready, package and upload it to Claude.ai:
Step 1: Create a ZIP File
Compress your skill directory into a ZIP file. The directory structure should remain intact:
code-review-assistant.zip
└── code-review-assistant/
└── SKILL.mdStep 2: Upload Through Settings
- Open Claude.ai and go to Settings
- Navigate to Capabilities tab
- Find the Skills section
- Click “Upload Skill”
- Select your ZIP file
- Review the automatically parsed metadata
- Confirm the upload
Claude will automatically parse your SKILL.md file and display the name, description, and license. If you see a YAML or frontmatter error, double-check the three-dash formatting and indentation.
Using Your Skill in Claude.ai
Once uploaded, your skill is ready to use. Test it with various prompts to ensure it activates correctly:
// Test prompt 1
"Can you review this Python function for me?"
// Test prompt 2
"Please perform a code review on the following JavaScript:"
// Test prompt 3
"Check this code for security issues:"Claude should automatically recognize these triggers and load your Code Review Assistant skill. You will see the skill name appear in Claude’s chain of thought as it processes your request.
Integrating with Claude API
For production applications, you will want to use skills through the Claude API. This requires uploading your skill programmatically and referencing it in API calls.
Node.js Implementation
First, install the Anthropic SDK:
npm install @anthropic-ai/sdkThen create a script to upload and use your skill:
const Anthropic = require('@anthropic-ai/sdk');
const fs = require('fs').promises;
const path = require('path');
const client = new Anthropic({
apiKey: process.env.ANTHROPIC_API_KEY
});
async function uploadSkill() {
try {
// Read the SKILL.md file
const skillPath = path.join(__dirname, 'code-review-assistant', 'SKILL.md');
const skillContent = await fs.readFile(skillPath, 'utf-8');
// Upload the skill
const skill = await client.skills.create({
name: 'code-review-assistant',
description: 'Perform thorough code reviews following industry best practices.',
files: [{
name: 'SKILL.md',
content: skillContent,
type: 'text/markdown'
}]
});
console.log('Skill uploaded successfully:', skill.id);
return skill.id;
} catch (error) {
console.error('Error uploading skill:', error);
throw error;
}
}
async function useSkill(skillId, codeToReview) {
try {
const message = await client.messages.create({
model: 'claude-sonnet-4-5-20250929',
max_tokens: 4096,
tools: [{
type: 'code-execution-2025-08-25'
}],
container: {
skill_ids: [skillId]
},
messages: [{
role: 'user',
content: `Please review this code:\n\n${codeToReview}`
}]
});
console.log('Code review completed:');
console.log(message.content[0].text);
return message.content[0].text;
} catch (error) {
console.error('Error using skill:', error);
throw error;
}
}
// Example usage
async function main() {
const skillId = await uploadSkill();
const sampleCode = `
function getUserData(id) {
return db.query("SELECT * FROM users WHERE id = " + id);
}
`;
await useSkill(skillId, sampleCode);
}
main();Python Implementation
Install the Anthropic Python SDK:
pip install anthropic --break-system-packagesPython implementation for skill management:
import os
from anthropic import Anthropic
client = Anthropic(api_key=os.environ.get("ANTHROPIC_API_KEY"))
def upload_skill():
"""Upload the code review skill to Claude API"""
try:
# Read the SKILL.md file
skill_path = os.path.join(os.path.dirname(__file__),
'code-review-assistant',
'SKILL.md')
with open(skill_path, 'r') as f:
skill_content = f.read()
# Upload the skill
skill = client.skills.create(
name='code-review-assistant',
description='Perform thorough code reviews following industry best practices.',
files=[{
'name': 'SKILL.md',
'content': skill_content,
'type': 'text/markdown'
}]
)
print(f'Skill uploaded successfully: {skill.id}')
return skill.id
except Exception as e:
print(f'Error uploading skill: {e}')
raise
def use_skill(skill_id: str, code_to_review: str):
"""Use the skill to review code"""
try:
message = client.messages.create(
model='claude-sonnet-4-5-20250929',
max_tokens=4096,
tools=[{
'type': 'code-execution-2025-08-25'
}],
container={
'skill_ids': [skill_id]
},
messages=[{
'role': 'user',
'content': f'Please review this code:\n\n{code_to_review}'
}]
)
print('Code review completed:')
print(message.content[0].text)
return message.content[0].text
except Exception as e:
print(f'Error using skill: {e}')
raise
# Example usage
if __name__ == '__main__':
skill_id = upload_skill()
sample_code = """
def process_user_input(data):
query = "SELECT * FROM users WHERE name = '" + data + "'"
return execute_query(query)
"""
use_skill(skill_id, sample_code)C# Implementation
Install the Anthropic .NET SDK via NuGet:
dotnet add package Anthropic.SDKC# implementation for skill management:
using System;
using System.IO;
using System.Threading.Tasks;
using Anthropic.SDK;
using Anthropic.SDK.Messaging;
namespace ClaudeSkills
{
public class CodeReviewSkill
{
private readonly AnthropicClient _client;
public CodeReviewSkill(string apiKey)
{
_client = new AnthropicClient(apiKey);
}
public async Task<string> UploadSkillAsync()
{
try
{
// Read the SKILL.md file
var skillPath = Path.Combine(
Directory.GetCurrentDirectory(),
"code-review-assistant",
"SKILL.md"
);
var skillContent = await File.ReadAllTextAsync(skillPath);
// Upload the skill
var skill = await _client.Skills.CreateAsync(new SkillCreateRequest
{
Name = "code-review-assistant",
Description = "Perform thorough code reviews following industry best practices.",
Files = new[]
{
new SkillFile
{
Name = "SKILL.md",
Content = skillContent,
Type = "text/markdown"
}
}
});
Console.WriteLine($"Skill uploaded successfully: {skill.Id}");
return skill.Id;
}
catch (Exception ex)
{
Console.WriteLine($"Error uploading skill: {ex.Message}");
throw;
}
}
public async Task<string> UseSkillAsync(string skillId, string codeToReview)
{
try
{
var request = new MessageRequest
{
Model = "claude-sonnet-4-5-20250929",
MaxTokens = 4096,
Tools = new[]
{
new Tool { Type = "code-execution-2025-08-25" }
},
Container = new Container
{
SkillIds = new[] { skillId }
},
Messages = new[]
{
new Message
{
Role = "user",
Content = $"Please review this code:\n\n{codeToReview}"
}
}
};
var response = await _client.Messages.CreateAsync(request);
var reviewText = response.Content[0].Text;
Console.WriteLine("Code review completed:");
Console.WriteLine(reviewText);
return reviewText;
}
catch (Exception ex)
{
Console.WriteLine($"Error using skill: {ex.Message}");
throw;
}
}
}
class Program
{
static async Task Main(string[] args)
{
var apiKey = Environment.GetEnvironmentVariable("ANTHROPIC_API_KEY");
var skillManager = new CodeReviewSkill(apiKey);
var skillId = await skillManager.UploadSkillAsync();
var sampleCode = @"
public User GetUser(string userId)
{
var sql = ""SELECT * FROM Users WHERE Id = '"" + userId + ""'"";
return Database.ExecuteQuery(sql);
}
";
await skillManager.UseSkillAsync(skillId, sampleCode);
}
}
}Installing Skills in Claude Code
Claude Code uses a filesystem-based approach. Skills are automatically discovered from specific directories:
~/.claude/skills/ # Global skills
.claude/skills/ # Project-specific skillsTo install your skill in Claude Code:
- Copy your code-review-assistant directory to ~/.claude/skills/
- Restart Claude Code if it is currently running
- Claude will automatically discover and load your skill
- Use it by mentioning code reviews in your commands
Testing and Validation
Thorough testing ensures your skill works reliably. Create a test case library covering different scenarios:
Test Cases
Normal usage tests:
// Test 1: Security vulnerability
const vulnerableCode = `
app.post('/login', (req, res) => {
const query = \`SELECT * FROM users WHERE email='${req.body.email}'\`;
db.query(query);
});
`;
// Test 2: Code quality issues
const poorQualityCode = `
function x(a,b,c){return a+b+c;}
`;
// Test 3: Well-written code
const goodCode = `
/**
* Calculate the total price including tax
* @param {number} basePrice - Base price before tax
* @param {number} taxRate - Tax rate as decimal (e.g., 0.08 for 8%)
* @returns {number} Total price including tax
*/
function calculateTotalPrice(basePrice, taxRate) {
if (basePrice < 0 || taxRate < 0) {
throw new Error('Price and tax rate must be non-negative');
}
return basePrice * (1 + taxRate);
}
`;Edge case tests:
// Test 4: Empty code
const emptyCode = '';
// Test 5: Non-code text
const notCode = 'This is a random paragraph of text.';
// Test 6: Incomplete code
const incompleteCode = `
function calculate(
// missing parameters and implementation
`;Common Issues and Troubleshooting
If your skill is not working as expected, check these common issues:
Skill Not Activating
- Description may be too narrow. Add more trigger phrases.
- Make sure prompts clearly match the description keywords.
- Try explicitly mentioning the skill name in your prompt.
Inconsistent Results
- Add more specificity to instructions.
- Include validation steps in the skill.
- Provide more examples covering edge cases.
YAML Parsing Errors
- Verify three dashes before and after frontmatter.
- Check indentation is consistent (use spaces, not tabs).
- Ensure name has no uppercase letters or special characters.
Best Practices Summary
Following these best practices will help you create effective, reliable skills:
- Keep SKILL.md under 500 lines for optimal performance
- Use gerund form for skill names (code-reviewing, not code-review)
- Write descriptions in third person with clear triggers
- Include both positive and negative examples
- Specify what the skill cannot do to manage expectations
- Test with diverse inputs including edge cases
- Version your skills and track changes
- Document limitations clearly
Next Steps
You have now created, uploaded, and tested your first Claude Agent Skill. In Part 3, we will explore advanced skill development patterns including progressive disclosure with reference files, executable scripts for complex operations, and organizing multi-file skills for maximum efficiency.
The Code Review Assistant skill we built today demonstrates the fundamental structure, but skills can be much more sophisticated. Coming up, we will add executable Python scripts for automated analysis, reference documentation for language-specific guidelines, and assets like code templates.
References
- Claude Help Center – “How to create custom Skills” (https://support.claude.com/en/articles/12512198-how-to-create-custom-skills)
- Claude Code Docs – “Extend Claude with skills” (https://code.claude.com/docs/en/skills)
- Claude Developer Platform – “Agent Skills Overview” (https://platform.claude.com/docs/en/agents-and-tools/agent-skills/overview)
- Skywork AI – “How to Create Your First Claude Skill: Step-by-Step Tutorial” (https://skywork.ai/blog/ai-agent/how-to-create-claude-skill-step-by-step-guide/)
- Lee Han Chung – “Claude Agent Skills: A First Principles Deep Dive” (https://leehanchung.github.io/blogs/2025/10/26/claude-skills-deep-dive/)
- Codecademy – “How to Build Claude Skills: Lesson Plan Generator Tutorial” (https://www.codecademy.com/article/how-to-build-claude-skills)
- Claude Help Center – “Teach Claude your way of working using skills” (https://support.claude.com/en/articles/12580051-teach-claude-your-way-of-working-using-skills)
- GitHub – anthropics/skills – “Public repository for Agent Skills” (https://github.com/anthropics/skills)
- Claude Blog – “How to create Skills for Claude: steps and examples” (https://claude.com/blog/how-to-create-skills-key-steps-limitations-and-examples)
