Extend Claude with specialized capabilities using Agent Skills in the ClaudeCode SDK.

Official Documentation: This guide is based on the official Agent SDK documentation. Examples are adapted for Elixir.

Overview

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:

  1. Defined as filesystem artifacts -- Created as SKILL.md files in specific directories (.claude/skills/)
  2. Loaded from filesystem -- Skills are loaded from configured filesystem locations. You must specify setting_sources to load Skills from the filesystem.
  3. Automatically discovered -- Once filesystem settings are loaded, Skill metadata is discovered at startup from user and project directories; full content loaded when triggered
  4. Model-invoked -- Claude autonomously chooses when to use them based on context
  5. Enabled via allowed_tools -- Add "Skill" to your allowed_tools to 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" in allowed_tools or Skills will not be available.

Using Skills with the SDK

To use Skills with the SDK, you need to:

  1. Include "Skill" in your allowed_tools configuration
  2. Configure setting_sources to 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:

  • Project Skills (.claude/skills/): Shared with your team via git -- loaded when setting_sources includes "project"
  • User Skills (~/.claude/skills/): Personal Skills across all projects -- loaded when setting_sources includes "user"
  • Plugin Skills: Bundled with installed Claude Code plugins -- configured via the plugins option

Creating Skills

Skills are defined as directories containing a SKILL.md file with YAML frontmatter and Markdown content. The description field determines when Claude invokes your Skill.

Example directory structure:

.claude/skills/processing-pdfs/
└── SKILL.md

For complete guidance on creating Skills, including SKILL.md structure, multi-file Skills, and examples, see:

Tool Restrictions

The allowed-tools frontmatter field in SKILL.md is 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 main allowed_tools option in your query configuration.

To restrict tools for Skills in SDK applications, use 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 in your SDK application, simply ask Claude:

{:ok, result} = ClaudeCode.query("What Skills are available?",
  setting_sources: ["user", "project"],
  allowed_tools: ["Skill"]
)

Claude will list the available Skills based on your current working directory and installed plugins.

Inspecting the system message (Elixir-specific)

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:

{:ok, result} = ClaudeCode.query("Extract text from invoice.pdf",
  cwd: "/path/to/project",
  setting_sources: ["user", "project"],
  allowed_tools: ["Skill", "Read", "Bash"]
)

Claude automatically invokes the relevant Skill if the description matches your request.

To verify 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_commands option 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"]
)

For more details on setting_sources, see the ClaudeCode.Options module documentation.

Check working directory. The SDK loads 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"]
)

See the "Using Skills with the SDK" section above for the complete pattern.

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 it's specific and includes relevant keywords. 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.

Skills Guides

SDK Guides