Skills
View SourceExtend Claude with specialized capabilities using Agent Skills.
Official Documentation: This guide is based on the official Claude Agent SDK documentation. Examples are adapted for Elixir.
Agent Skills extend Claude with specialized capabilities that Claude autonomously invokes when relevant. Skills are packaged as SKILL.md files containing instructions, descriptions, and optional supporting resources.
For comprehensive information about Skills, including benefits, architecture, and authoring guidelines, see the Agent Skills overview.
How Skills work with the SDK
When using the ClaudeCode SDK, Skills are:
- Defined as filesystem artifacts -- Created as
SKILL.mdfiles in specific directories (.claude/skills/) - Loaded from filesystem -- Skills are loaded from configured filesystem locations. You must specify
setting_sourcesto load Skills from the filesystem - Automatically discovered -- Once filesystem settings are loaded, Skill metadata is discovered at startup from user and project directories; full content is loaded when triggered
- Model-invoked -- Claude autonomously chooses when to use them based on context
- Enabled via allowed_tools -- Add
"Skill"to yourallowed_toolsto enable Skills
Unlike subagents (which can be defined programmatically), Skills must be created as filesystem artifacts. The SDK does not provide a programmatic API for registering Skills.
Default behavior: By default, the SDK does not load any filesystem settings. To use Skills, you must explicitly configure
setting_sources: ["user", "project"]in your options. You must also include"Skill"inallowed_toolsor Skills will not be available.
Using Skills with the SDK
To use Skills, you need to:
- Include
"Skill"in yourallowed_toolsconfiguration - Configure
setting_sourcesto load Skills from the filesystem
Once configured, Claude automatically discovers Skills from the specified directories and invokes them when relevant to the user's request.
{:ok, result} = ClaudeCode.query("Help me process this PDF document",
cwd: "/path/to/project", # Project with .claude/skills/
setting_sources: ["user", "project"], # Load Skills from filesystem
allowed_tools: ["Skill", "Read", "Write", "Bash"] # Enable Skill tool
)Skill locations
Skills are loaded from filesystem directories based on your setting_sources configuration:
| Source | Directory | Loaded when |
|---|---|---|
| Project Skills | .claude/skills/ (relative to cwd) | setting_sources includes "project" |
| User Skills | ~/.claude/skills/ | setting_sources includes "user" |
| Plugin Skills | Bundled with installed Claude Code plugins | Plugins are configured via the plugins option |
Use setting_sources: ["user", "project"] to load Skills from both personal and project directories.
Creating Skills
Skills are defined as directories containing a SKILL.md file with YAML frontmatter and Markdown content. The description field in the frontmatter determines when Claude invokes your Skill.
Example directory structure:
.claude/skills/processing-pdfs/
SKILL.mdExample SKILL.md:
---
description: Extract text and data from PDF documents using poppler utilities
allowed-tools:
- Bash
- Read
- Write
---
# PDF Processing
When asked to process a PDF document:
1. Use `pdftotext` to extract raw text
2. Parse the extracted text for structured data
3. Return the results in the requested formatFor complete guidance on creating Skills, including multi-file Skills and examples, see:
- Agent Skills in Claude Code -- Complete guide with examples
- Agent Skills Best Practices -- Authoring guidelines and naming conventions
Tool restrictions
The
allowed-toolsfrontmatter field inSKILL.mdis only supported when using the Claude Code CLI directly. It does not apply when using Skills through the SDK.When using the SDK, control tool access through the
allowed_toolsoption in your session or query configuration.
To restrict which tools are available when Skills run, set the allowed_tools option:
{:ok, result} = ClaudeCode.query("Analyze the codebase structure",
setting_sources: ["user", "project"], # Load Skills from filesystem
allowed_tools: ["Skill", "Read", "Grep", "Glob"] # Restricted toolset
)Discovering available Skills
To see which Skills are available, you can ask Claude directly or inspect the ClaudeCode.Message.SystemMessage emitted at the start of each query. The skills field on the system message contains the list of discovered Skill names.
Asking Claude
{:ok, result} = ClaudeCode.query("What Skills are available?",
setting_sources: ["user", "project"],
allowed_tools: ["Skill"]
)Inspecting the system message
alias ClaudeCode.Message.SystemMessage
session
|> ClaudeCode.stream("Hello")
|> Enum.each(fn
%SystemMessage{skills: skills} ->
IO.puts("Available skills: #{inspect(skills)}")
_ ->
:ok
end)The skills field on ClaudeCode.Message.SystemMessage is a [String.t()] list of Skill names discovered from the configured setting sources.
Testing Skills
Test Skills by asking questions that match their descriptions. Claude automatically invokes the relevant Skill if the description matches your request:
{:ok, result} = ClaudeCode.query("Extract text from invoice.pdf",
cwd: "/path/to/project",
setting_sources: ["user", "project"],
allowed_tools: ["Skill", "Read", "Bash"]
)To verify that a Skill was actually invoked, use ClaudeCode.Stream.tool_uses/1 to inspect tool calls in the stream:
alias ClaudeCode.Content.ToolUseBlock
session
|> ClaudeCode.stream("Extract text from invoice.pdf")
|> ClaudeCode.Stream.tool_uses()
|> Enum.each(fn %ToolUseBlock{name: name, input: input} ->
IO.puts("Tool called: #{name} with input: #{inspect(input)}")
end)Disabling Skills
Skills can be disabled using the disable_slash_commands option. This disables both Skills and slash commands:
{:ok, session} = ClaudeCode.start_link(
disable_slash_commands: true
)The
disable_slash_commandsoption disables Skills as well as slash commands. There is no separate option to disable only Skills while keeping slash commands active.
Troubleshooting
Skills not found
Check setting_sources configuration. Skills are only loaded when you explicitly configure setting_sources. This is the most common issue:
# Wrong -- Skills won't be loaded (no setting_sources)
{:ok, session} = ClaudeCode.start_link(
allowed_tools: ["Skill"]
)
# Correct -- Skills will be loaded from filesystem
{:ok, session} = ClaudeCode.start_link(
setting_sources: ["user", "project"],
allowed_tools: ["Skill"]
)Check allowed_tools configuration. Even with setting_sources configured, Skills require "Skill" in allowed_tools:
# Wrong -- Skills are loaded but not enabled as a tool
{:ok, session} = ClaudeCode.start_link(
setting_sources: ["user", "project"]
)
# Correct -- Skills are loaded and enabled
{:ok, session} = ClaudeCode.start_link(
setting_sources: ["user", "project"],
allowed_tools: ["Skill"]
)Check working directory. The SDK loads project Skills relative to the cwd option. Ensure it points to a directory containing .claude/skills/:
{:ok, session} = ClaudeCode.start_link(
cwd: "/path/to/project", # Must contain .claude/skills/
setting_sources: ["user", "project"],
allowed_tools: ["Skill"]
)Verify filesystem location:
# Check project Skills
ls .claude/skills/*/SKILL.md
# Check personal Skills
ls ~/.claude/skills/*/SKILL.md
Skill not being used
Check the Skill tool is enabled. Confirm "Skill" is in your allowed_tools.
Check the description. Ensure the description field in the SKILL.md frontmatter is specific and includes relevant keywords. Claude uses the description to decide when to invoke a Skill. See Agent Skills Best Practices for guidance on writing effective descriptions.
Additional troubleshooting
For general Skills troubleshooting (YAML syntax, debugging, etc.), see the Claude Code Skills troubleshooting section.
Next steps
- Slash Commands -- User-invoked commands (also loaded via
setting_sources) - Subagents -- Similar filesystem-based agents with programmatic options
- Plugins -- Plugin configuration and management
- Modifying System Prompts -- Customize Claude's behavior