SkillsTool - Agent Skills System¶
Extend AI agent capabilities with reusable, composable knowledge modules defined in Markdown with YAML front-matter. Based on Claude Code's Agent Skills, skills enable AI agents to perform specialized tasks through semantic matching.
Features: - Define skills as Markdown files with YAML frontmatter - Automatic skill invocation through semantic matching - Support for reference files and helper scripts - Progressive disclosure of detailed information - Project-wide or user-wide skill scopes - Customizable tool descriptions and templates - Load skills from filesystem directories, classpath JARs, or remote JAR dependencies
Overview¶
Skills are markdown files that teach the AI agent how to perform specific tasks. Unlike traditional tools that execute code, skills provide knowledge and instructions to the AI, enabling it to handle specialized domains effectively.
How Skills Work: 1. Discovery: At startup, SkillsTool loads skill names and descriptions 2. Semantic Matching: When a user request matches a skill's description, the AI invokes it 3. Execution: The full skill content is loaded and the AI follows its instructions
Extend agent capabilities with reusable, composable knowledge modules defined in Markdown with YAML front-matter:
---
name: ai-tutor
description: Use when user asks to explain technical concepts
---
# AI Tutor
[Detailed skill documentation...]
Skills can include executable scripts and reference materials, loaded dynamically from filesystem directories, classpath locations, or packaged JAR dependencies.
Basic Usage¶
Create a Skill¶
File: examples/.claude/skills/my-skill/SKILL.md
---
name: my-skill
description: What this skill does and when to use it. Include specific
capabilities and trigger keywords users would naturally say.
---
# My Skill
## Instructions
Provide clear, step-by-step guidance for the AI agent.
## Examples
Show concrete examples of using this skill.
Register SkillsTool¶
ChatClient chatClient = chatClientBuilder
.defaultToolCallbacks(SkillsTool.builder()
.addSkillsDirectory("examples/.claude/skills")
.build())
.build();
// AI automatically invokes skills based on semantic matching
String response = chatClient.prompt()
.user("Help me with PDF processing")
.call()
.content();
// If a "pdf" skill exists with matching description, it's automatically invoked
Skill File Structure¶
Every skill requires a SKILL.md file. Supporting files are optional:
examples/.claude/skills/
└── my-skill/
├── SKILL.md # Required: Skill definition
├── reference.md # Optional: Detailed documentation
├── examples.md # Optional: Usage examples
├── scripts/ # Optional: Helper scripts
│ └── process.py
└── pyproject.toml # Optional: Python dependencies
SKILL.md Format¶
---
name: my-skill
description: Comprehensive description including what the skill does,
when to use it, and trigger keywords users might mention.
allowed-tools: Read, Grep, Bash
model: claude-sonnet-4-5-20250929
---
# Skill Title
## Overview
Brief introduction to what this skill does.
## Instructions
Step-by-step guidance for the AI agent.
## Examples
Concrete examples demonstrating skill usage.
## Additional Resources
- For complete details, see [reference.md](reference.md)
- For usage examples, see [examples.md](examples.md)
Frontmatter Fields¶
| Field | Required | Description |
|---|---|---|
name |
Yes | Skill identifier: lowercase letters, numbers, hyphens only (max 64 chars) |
description |
Yes | What it does + when to use it (max 1024 chars). Used for semantic matching |
allowed-tools |
No | Comma-separated tools the agent can use without asking permission |
model |
No | Specific model to use when this skill is active |
Name Field¶
# ✅ GOOD
name: pdf-processor
name: data-analysis
name: api-documentation
# ❌ BAD
name: PDF Processor # No spaces or capitals
name: data_analysis # Use hyphens, not underscores
name: my-super-long-skill-name-that-exceeds-the-limit # Too long
Description Field¶
The description is critical for semantic matching. Include: - What the skill does - When to use it - Trigger keywords users might say - Specific capabilities
# ✅ GOOD
description: Extract text and tables from PDF files, fill forms, merge documents.
Use when working with PDF files or when the user mentions PDFs, forms,
or document extraction.
# ❌ BAD
description: Helps with documents # Too vague
description: PDF tool # Lacks context and triggers
Allowed-Tools Field¶
Comma-separated list of tools the AI can use when this skill is active:
Without this field: AI must ask user permission before using tools With this field: AI can use listed tools automatically
Model Field¶
Override the default model for this specific skill:
Skill Locations¶
Skills can come from multiple sources — filesystem directories, classpath resources, or packaged JAR dependencies.
| Source | Example | Scope |
|---|---|---|
| Personal directory | ~/.claude/skills/ |
User, across all projects |
| Project directory | .claude/skills/ |
Team in this repository |
| Classpath / JAR | ClassPathResource("META-INF/skills/my-skills") |
Packaged with a library or dependency |
| Remote JAR | UrlResource("jar:https://…/skills.jar!/skills") |
Distributed skill packages |
Tip: Use project skills (.claude/skills/) for team collaboration by committing them to version control.
Loading from Directories¶
SkillsTool skillsTool = SkillsTool.builder()
.addSkillsDirectory(".claude/skills") // Project skills
.addSkillsDirectory(System.getenv("HOME") + "/.claude/skills") // Personal skills
.build();
Alternative using addSkillsDirectories:
SkillsTool skillsTool = SkillsTool.builder()
.addSkillsDirectories(List.of(
".claude/skills",
System.getenv("HOME") + "/.claude/skills"
))
.build();
Loading from Spring Resources¶
For better integration with Spring Boot, you can load skills from Spring Resource objects.
This works for filesystem paths, classpath locations, and JAR entries.
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
@Configuration
public class SkillsConfig {
@Autowired
private ResourceLoader resourceLoader;
@Bean
public SkillsTool skillsTool() {
Resource skillsResource = resourceLoader.getResource("classpath:.claude/skills");
return SkillsTool.builder()
.addSkillsResource(skillsResource)
.build();
}
}
Loading multiple resources:
@Bean
public SkillsTool skillsTool() {
List<Resource> skillResources = List.of(
resourceLoader.getResource("classpath:.claude/skills"),
resourceLoader.getResource("file:${user.home}/.claude/skills")
);
return SkillsTool.builder()
.addSkillsResources(skillResources)
.build();
}
Loading from Classpath JARs (SkillsJars)¶
Skills can be packaged inside JAR files and distributed as Maven/Gradle dependencies — referred to as SkillsJars. This allows teams to share reusable skill libraries across projects.
A SkillsJar is a regular JAR that stores SKILL.md files under a well-known path:
Add the JAR as a dependency, then point SkillsTool at the classpath prefix:
// pom.xml
<dependency>
<groupId>com.skillsjars</groupId>
<artifactId>anthropics__skills__pdf</artifactId>
<version>2026_02_06-1ed29a0</version>
</dependency>
SkillsTool skillsTool = SkillsTool.builder()
.addSkillsResource(new ClassPathResource("META-INF/resources/skills/anthropics/skills"))
.build();
You can mix sources freely — filesystem directories, classpath JARs, and explicit JAR URLs:
SkillsTool skillsTool = SkillsTool.builder()
.addSkillsDirectory(".claude/skills") // Local filesystem
.addSkillsResource(new ClassPathResource("META-INF/skills/my-org/my-skills")) // From a dependency JAR
.addSkillsResource(new UrlResource("jar:file:/opt/skills.jar!/skills")) // Explicit JAR URL
.build();
How JAR Loading Works¶
Skills.loadResource() resolves JAR-based resources using three strategies, in order:
- Spring
PathMatchingResourcePatternResolver— scansclasspath*:…/**/SKILL.md. Works for well-formed JARs with explicit directory entries. JarURLConnectiondirect scan — used when the resource URL uses thejar:protocol (e.g., aUrlResource).- Manual classpath JAR scan — fallback for
ClassPathResourcereferences pointing to directories inside JARs that lack explicit directory entries (a known limitation of Spring's resolver).
All three strategies produce the same result: a list of Skill objects ready for use.
Builder Configuration¶
Basic Builder¶
Custom Tool Description Template¶
String customTemplate = """
Execute a custom skill...
<available_skills>
%s
</available_skills>
""";
SkillsTool.builder()
.addSkillsDirectory(".claude/skills")
.toolDescriptionTemplate(customTemplate)
.build();
Multiple Skill Directories¶
SkillsTool.builder()
.addSkillsDirectory(".claude/skills")
.addSkillsDirectory("~/.claude/skills")
.addSkillsDirectory("/shared/team-skills")
.build();
With Spring Resources¶
import org.springframework.core.io.Resource;
@Bean
public SkillsTool skillsTool(ResourceLoader resourceLoader) {
Resource projectSkills = resourceLoader.getResource("classpath:.claude/skills");
Resource userSkills = resourceLoader.getResource("file:${user.home}/.claude/skills");
return SkillsTool.builder()
.addSkillsResource(projectSkills)
.addSkillsResource(userSkills)
.build();
}
With JAR / Classpath Dependencies¶
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.UrlResource;
@Bean
public SkillsTool skillsTool() {
return SkillsTool.builder()
.addSkillsDirectory(".claude/skills") // Local filesystem
.addSkillsResource(new ClassPathResource("META-INF/skills/my-org/my-skills")) // From a dependency JAR
.addSkillsResource(new UrlResource("jar:file:/opt/skills.jar!/skills")) // Explicit JAR URL
.build();
}
Best Practices¶
1. Write Effective Descriptions¶
Include specific capabilities:
# ✅ GOOD
description: Process Excel files: read sheets, analyze data, create charts,
generate reports. Use when working with .xlsx, .xls files, spreadsheets,
or when user mentions Excel, data analysis, or pivot tables.
Include trigger keywords:
# ✅ GOOD
description: Analyze SQL databases, write optimized queries, explain execution
plans. Use when user mentions: databases, SQL, MySQL, PostgreSQL, queries,
tables, indexes, or performance tuning.
Too vague (avoid):
2. Keep Skills Focused¶
SKILL.md should be: - Under 500 lines - Focused on one domain/task - Quick to read and understand
Use supporting files for details:
## Quick Start
[Essential 50-100 line instructions here]
## Additional Resources
- For complete API reference, see [reference.md](reference.md)
- For 20+ examples, see [examples.md](examples.md)
- For advanced techniques, see [advanced.md](advanced.md)
3. Progressive Disclosure¶
Structure skills to provide information when needed:
# PDF Processing Skill
## Basic Operations
Quick instructions for common PDF tasks...
## Advanced Features
For complex operations like form filling, see [advanced.md](advanced.md)
## API Reference
For complete API documentation, see [reference.md](reference.md)
The AI loads supporting files only when needed, preserving context.
4. Provide Clear Examples¶
## Examples
### Extract Text from PDF
\```bash
python scripts/extract_text.py input.pdf output.txt
\```
### Merge Multiple PDFs
\```bash
python scripts/merge.py file1.pdf file2.pdf output.pdf
\```
### Fill PDF Form
\```python
from pdf_processor import fill_form
fill_form('template.pdf', {
'name': 'John Doe',
'date': '2025-01-04'
}, 'filled.pdf')
\```
5. Tool Access for Skills¶
Skills often need to read files or run scripts. Register necessary tools:
ChatClient chatClient = chatClientBuilder
.defaultToolCallbacks(SkillsTool.builder()
.addSkillsDirectory("examples/.claude/skills")
.build())
// Required for skills to load reference files
.defaultTools(FileSystemTools.builder().build())
// Required for skills to execute scripts
.defaultTools(new ShellTools())
.build();
Without FileSystemTools: Skills can't read reference.md, examples.md
Without ShellTools: Skills can't execute scripts in scripts/ directory
How Skills Are Invoked¶
Three-Step Process¶
- Discovery (at startup):
- SkillsTool scans
.claude/skills/directories - Loads
nameanddescriptionfrom eachSKILL.md -
Creates lightweight skill registry
-
Activation (on user request):
- User: "Help me extract data from this PDF"
- AI matches request to skill descriptions
-
AI invokes skill:
Skill(command="pdf-processor") -
Execution:
- SkillsTool loads full
SKILL.mdcontent - Returns content + base directory to AI
- AI follows skill instructions
Invocation Example¶
// User request
"I need to process a PDF file and extract tables"
// AI recognizes "pdf" skill matches
// AI calls: Skill(command="pdf")
// SkillsTool returns:
"Base directory for this skill: /project/.claude/skills/pdf
# PDF Processing Skill
[full SKILL.md content here...]
"
// AI follows instructions from skill
Complete Example¶
Example Skill: API Documentation Generator¶
File: .claude/skills/api-docs/SKILL.md
---
name: api-docs
description: Generate API documentation from source code. Analyze REST endpoints,
GraphQL schemas, or OpenAPI specs. Use when user mentions: API docs, documentation
generation, endpoint documentation, REST API, GraphQL, OpenAPI, Swagger.
allowed-tools: Read, Grep, Bash, Write
model: claude-sonnet-4-5-20250929
---
# API Documentation Generator
## Overview
This skill helps generate comprehensive API documentation from source code,
focusing on REST endpoints, request/response formats, and usage examples.
## Instructions
### Step 1: Analyze Source Code
1. Use Grep to find controller/route files
2. Read files to understand endpoint structure
3. Identify request/response types
### Step 2: Extract Information
For each endpoint, document:
- HTTP method and path
- Request parameters (query, path, body)
- Response format
- Error codes
- Authentication requirements
### Step 3: Generate Documentation
Create markdown documentation with:
- Endpoint overview table
- Detailed endpoint descriptions
- Code examples in multiple languages
- Response schemas
## Example
### Finding Spring Boot Endpoints
\```bash
grep -r "@GetMapping\\|@PostMapping\\|@PutMapping\\|@DeleteMapping" src/
\```
### Documenting an Endpoint
\```markdown
## GET /api/users/{id}
Retrieve user by ID.
**Parameters:**
- `id` (path, required): User ID
**Response:** `200 OK`
\```json
{
"id": 1,
"name": "John Doe",
"email": "john@example.com"
}
\```
**Errors:**
- `404`: User not found
\```
## Additional Resources
- For OpenAPI integration, see [openapi.md](openapi.md)
- For GraphQL schemas, see [graphql.md](graphql.md)
Registration¶
@Configuration
public class SkillsConfig {
@Bean
public ChatClient chatClient(ChatClient.Builder chatClientBuilder) {
return chatClientBuilder
.defaultToolCallbacks(SkillsTool.builder()
.addSkillsDirectory("examples/.claude/skills")
.build())
.defaultTools(FileSystemTools.builder().build())
.defaultTools(new ShellTools())
.defaultTools(GrepTool.builder().build())
.build();
}
}
Usage¶
ChatClient chatClient = ...; // from bean
String response = chatClient.prompt()
.user("Generate API documentation for my Spring Boot application")
.call()
.content();
// AI automatically:
// 1. Recognizes "API documentation" matches api-docs skill
// 2. Invokes the skill
// 3. Follows skill instructions to generate docs
Advanced Usage¶
Custom Skill with Scripts¶
Directory structure:
.claude/skills/data-analysis/
├── SKILL.md
├── scripts/
│ ├── analyze.py
│ ├── visualize.py
│ └── report.py
└── pyproject.toml
SKILL.md:
---
name: data-analysis
description: Analyze CSV/Excel data, create visualizations, generate statistical
reports. Use when user mentions: data analysis, CSV, Excel, statistics,
charts, graphs, visualization.
allowed-tools: Read, Bash, Write
---
# Data Analysis Skill
## Setup
First, install dependencies:
\```bash
cd .claude/skills/data-analysis
pip install -r requirements.txt
\```
## Usage
### Analyze CSV
\```bash
python scripts/analyze.py data.csv --output report.txt
\```
### Create Visualization
\```bash
python scripts/visualize.py data.csv --type bar --output chart.png
\```
## Examples
[Detailed examples here...]
Namespace Collision Handling¶
If multiple skills have the same name:
// Use fully qualified names: directory:skill-name
Skill(command="project-a:pdf")
Skill(command="project-b:pdf")
Spring Boot Integration¶
Configuration Properties¶
# application.properties
skills.directory=.claude/skills
skills.user.directory=${user.home}/.claude/skills
Configuration Class¶
@Configuration
public class SkillsConfiguration {
@Value("${skills.directory}")
private String projectSkillsDir;
@Value("${skills.user.directory}")
private String userSkillsDir;
@Bean
public SkillsTool skillsTool() {
return SkillsTool.builder()
.addSkillsDirectory(projectSkillsDir)
.addSkillsDirectory(userSkillsDir)
.build();
}
@Bean
public ChatClient chatClient(
ChatClient.Builder chatClientBuilder,
SkillsTool skillsTool) {
return chatClientBuilder
.defaultToolCallbacks(skillsTool)
.defaultTools(FileSystemTools.builder().build())
.defaultTools(new ShellTools())
.build();
}
}
Troubleshooting¶
Skill Not Found¶
Problem: AI doesn't invoke skill even though it should match
Solutions:
1. Check skill description includes trigger keywords
2. Verify SKILL.md exists and has frontmatter
3. Ensure skill directory is registered
4. Check logs for skill loading errors
Skill Can't Access Files¶
Problem: Skill tries to read reference.md but fails
Solution: Register FileSystemTools
ChatClient chatClient = chatClientBuilder
.defaultToolCallbacks(skillsTool)
.defaultTools(FileSystemTools.builder().build()) // Add this
.build();
Skill Can't Run Scripts¶
Problem: Skill can't execute Python/shell scripts
Solution: Register ShellTools
ChatClient chatClient = chatClientBuilder
.defaultToolCallbacks(skillsTool)
.defaultTools(new ShellTools()) // Add this
.build();
Skills Not Found in JAR¶
Problem: SkillsTool loads 0 skills from a ClassPathResource pointing inside a JAR
Cause: The JAR lacks explicit directory entries (common with some build tools), so Spring's PathMatchingResourcePatternResolver can't enumerate the contents.
Solution: Skills.loadResource() automatically falls back to scanning all classpath JARs via ClassLoader.getResources("META-INF/MANIFEST.MF"). If skills are still not found, verify the JAR entry prefix matches the ClassPathResource path:
# Inspect JAR entries
jar tf my-skills.jar | grep SKILL.md
# META-INF/skills/my-org/my-skills/pdf/SKILL.md
# Use the matching prefix
new ClassPathResource("META-INF/skills/my-org/my-skills")
Multiple Skills with Same Name¶
Problem: Naming collision between skills
Solution: Use fully qualified names
Limitations¶
- Skill descriptions limited to 1024 characters
- Skill names limited to 64 characters
- Only
SKILL.mdfiles are recognized (exact name) - YAML frontmatter must be valid
- Semantic matching depends on AI's understanding
Best Practices Summary¶
- Descriptive names: Use clear, hyphenated names
- Rich descriptions: Include capabilities and trigger keywords
- Keep focused: One skill = one domain/task
- Progressive disclosure: Use supporting files for details
- Provide examples: Show concrete usage patterns
- Register tools: FileSystemTools for files, ShellTools for scripts
- Version control: Commit project skills to git, or package them as SkillsJar dependencies
- Test thoroughly: Verify AI invokes skill correctly
- Document well: Clear instructions for AI to follow
- Maintain skills: Update as requirements change
- Share via JARs: Package reusable skills as Maven/Gradle dependencies (SkillsJars) for cross-project sharing
See Also¶
- FileSystemTools - For reading reference files
- ShellTools - For executing skill scripts
- Claude Code Skills Documentation - Official reference
- Claude Code Skills Article - Implementation patterns