Guide

Give your agents reusable skills

  • Intermediate
  • 20 min
  • Published
  • Verified

What you'll learn

  • When to choose between an agent skill and agent context for a piece of team knowledge
  • Package a skill directory in a mixin kit
  • Add always-available guidance
  • Understand how an agent finds bundled skills
  • Verify that an agent in a new sandbox can see and use the skill

The terminals below are simulated. Nothing runs on your machine, and each one starts from the state the guide has reached at that point.

Your team has a way of doing things. Changelog entries go under a particular heading, link a ticket, and read a certain way. Everyone learned it in their first code review. An agent in a new sandbox didn’t, so it writes the entry wrong. Then someone pastes the convention into the prompt, and does it again in the next sandbox.

You can write that convention down once, as an agent skill, and package it in a kit. Then every sandbox that includes the kit starts with the skill in the place its agent looks for skills. The skill doesn’t depend on any one agent or project, or on anyone remembering to paste it.

In this guide you will:

  1. write a small skill: a SKILL.md and one reference file;
  2. package it in a mixin kit, and watch the build reject the kit while the skill is missing from the image;
  3. add a short piece of always-on guidance next to the skill;
  4. run the kit with Claude Code in a sandbox, check where the skill landed, and have the agent use it.

Prerequisites

  • sbx 0.45 or later. Kits v3 need at least 0.45, and this guide was written against 0.47.0. If you’ve never run a sandbox, start with Run your first sandbox.
  • Docker with Buildx, to build and validate the kit.
  • An Anthropic credential for Claude Code. Store an API key with sbx secret set anthropic, or sign in with /login the first time Claude Code starts. See Working with secrets for how that credential stays on your host.

You don’t need to have written a kit before. Every file is given in full below. For a slower introduction to kit descriptors, see Build your first Docker Sandbox kit.

Skill or context?

A kit asks the sandbox for things through capabilities: typed entries in its descriptor’s capabilities list, such as com.docker.sandbox/agent-skill@1. The name says what’s being asked for, and the @1 is the version of that capability’s contract, which is independent of the kit format and the sbx release.

Kits can hand an agent knowledge through two of these, and choosing between them is the first decision:

You want the agent to… Use What it is
Follow a procedure when a task calls for it agent-skill@1 A directory with a SKILL.md and any supporting files
Always know a few facts about this environment agent-context@1 A short Markdown body, indexed in the agent’s instruction file

The example in this guide is a team’s changelog convention, and it uses both. Writing a changelog entry is a procedure the agent needs only when it’s editing the changelog, so the convention itself becomes a skill. A short note telling the agent that the convention exists, and that it should never invent a ticket ID, becomes context.

Starting state

Make a working directory with a changelog to try the skill on:

acme-demo/
├── CHANGELOG.md
└── acme-skills/                  the kit
    ├── acme-skills.yaml          descriptor
    ├── acme-skills.dockerfile    puts the skill in the image
    ├── context.md                always-on guidance
    └── skills/
        └── changelog-entry/      the skill
            ├── SKILL.md
            └── references/
                └── format.md

Start with acme-demo/CHANGELOG.md:

# Changelog

## Unreleased

Write the skill

A skill is a directory. Its SKILL.md has YAML front matter that tells the agent what the skill is for, and a body that tells the agent what to do. Supporting files go in the same directory, and the body links to them by relative path.

Create acme-skills/skills/changelog-entry/SKILL.md:

---
name: changelog-entry
description: Add an entry to CHANGELOG.md in Acme's format. Use when a change
  is ready for review, or when asked to update the changelog.
---

# Add a changelog entry

1. Find the ticket ID for the change. Acme ticket IDs look like `ACME-123`.
   If you can't find one, ask. Never invent an ID.
2. Open `CHANGELOG.md` and find the `## Unreleased` section.
3. Put the entry under the right subsection, creating the subsection if it's
   missing. [The format reference](references/format.md) lists the
   subsections and their order.
4. Write one line in the format the reference describes.

Show the line you added when you're done.

Create acme-skills/skills/changelog-entry/references/format.md:

# Changelog format

Subsections, in this order: `### Added`, `### Changed`, `### Fixed`,
`### Security`.

Each entry is one line: an imperative sentence describing what the user
sees, then the ticket as a link.

    - Stop retrying API requests after five attempts. ([ACME-412](https://tracker.acme.example.com/ACME-412))

The name in the front matter matches the directory name, changelog-entry. That matters later: the directory name is the name the sandbox gives the skill in the agent’s skills directory.

Declare the skill in a kit

A kit is an image plus a descriptor that tells Docker Sandboxes what the image is for. This kit adds things to an agent’s environment rather than running anything itself, so it’s a mixin.

Create acme-skills/acme-skills.yaml:

# syntax=docker/sandbox-kit:3
schemaVersion: "3"
kind: mixin
displayName: Acme team skills
description: Acme's engineering conventions, packaged as agent skills.
version: "1.0.0"

capabilities:
  - type: com.docker.sandbox/agent-skill@1
    name: Changelog entry
    config:
      path: /usr/share/acme-skills/changelog-entry

  - type: com.docker.sandbox/agent-context@1
    config:
      contentFile: ./context.md

Two entries, one for each kind of knowledge:

  • agent-skill@1 points at the skill’s directory inside the kit’s image. path must be an absolute path, and that directory must contain a SKILL.md. The entry’s name is only a label for display. The skill’s name comes from the last part of the path, changelog-entry.
  • agent-context@1 with contentFile adds a body of guidance from the build context. The build copies it into the image, so the published kit doesn’t depend on your directory.

The path is under /usr/share/acme-skills/, not under an agent’s skills directory such as ~/.claude/skills. That’s on purpose. The kit doesn’t know which agent it will be used with, so it keeps the skill in a location named for the kit, and Docker Sandboxes exposes it wherever the agent looks.

Now create acme-skills/context.md:

This sandbox includes Acme's team skills. Every user-facing change needs a
changelog entry: use the `changelog-entry` skill to write it. Acme ticket IDs
look like `ACME-123`. Never invent one; ask instead.

Keep context short. Every agent in the sandbox can find it, every session, whether or not the task needs it. The procedure itself stays in the skill.

Build it, and watch it fail

The descriptor promises a skill at /usr/share/acme-skills/changelog-entry, but nothing puts it there yet. Build anyway, from acme-demo/. The failure is expected:

Host machine (acme-demo)

docker buildx build -f acme-skills/acme-skills.yaml -t acme-skills:dev acme-skills/
 => [internal] load build definition from acme-skills.yaml
 => => transferring dockerfile: 461B
 => resolve image config for docker-image://docker.io/docker/sandbox-kit:3
 => [internal] load kit descriptor acme-skills.yaml · sandbox-kit 3.0.0-m.8 (129be2ff)
 => => transferring dockerfile: 461B
 => [internal] load guidance context.md
 => => transferring context: 245B
 => [kit] stage guidance /usr/share/sandbox/kit/acme-skills/context.md
 => [kit] stage sources /usr/share/sandbox/kit/acme-skills
--------------------
   1 | >>> # syntax=docker/sandbox-kit:3
   2 |     schemaVersion: "3"
   3 |     kind: mixin
--------------------
ERROR: failed to solve: conformance:
  - agent-skill-content (agent-skill@1/content-present): bundled skill "changelog-entry" does not carry /usr/share/acme-skills/changelog-entry/SKILL.md

The # syntax=docker/sandbox-kit:3 line sends the build to the kit frontend. The frontend checks the image it’s about to produce against the descriptor before it exports anything. A mixin with no recipe is a valid, declaration-only kit, but this one declares a skill whose SKILL.md isn’t in the image. A kit like that is refused at build time, so it can’t be published and then fail in someone’s sandbox later.

The descriptor says what the kit provides. Only the image carries the files.

Put the skill in the image

Create acme-skills/acme-skills.dockerfile. The frontend finds it because it shares the descriptor’s file name:

# syntax=docker/dockerfile:1
FROM scratch
COPY skills/changelog-entry /usr/share/acme-skills/changelog-entry

FROM scratch starts from an empty image, so the mixin carries only the skill. When the kit is used, these files are layered onto the workload’s filesystem without bringing a base distribution along.

Build again:

Host machine (acme-demo)

docker buildx build -f acme-skills/acme-skills.yaml -t acme-skills:dev acme-skills/
[+] Building 3.1s (9/9) FINISHED
 => [internal] load build definition from acme-skills.yaml
 => => transferring dockerfile: 461B
 => resolve image config for docker-image://docker.io/docker/sandbox-kit:3
 => [internal] load kit descriptor acme-skills.yaml · sandbox-kit 3.0.0-m.8 (129be2ff)
 => => transferring dockerfile: 461B
 => [internal] load guidance context.md
 => => transferring context: 245B
  => [internal] load .dockerignore
 => => transferring context: 2B
 => [internal] load build context
 => => transferring context: 1.24kB
 => [1/1] COPY skills/changelog-entry /usr/share/acme-skills/changelog-entry
 => [kit] stage guidance /usr/share/sandbox/kit/acme-skills/context.md
 => [kit] stage sources /usr/share/sandbox/kit/acme-skills
 => exporting to image
 => => exporting layers
 => => naming to docker.io/library/acme-skills:dev
 => => unpacking to docker.io/library/acme-skills:dev

The descriptor and the image now agree, and the kit is valid. You don’t need this local image to run the kit. sbx builds a kit directory itself, as you’ll see next. A successful buildx build tells you the kit passed validation.

Run the skill with an agent

A skill needs an agent to read it. Use two published Docker kits as the base: the shell workload, and the Claude Code mixin on top of it. Then add your kit from its directory. From acme-demo/:

Host machine (acme-demo)

sbx run docker/sbx-kit-shell:1.0.0 --name acme-skills-demo --kit docker/sbx-kit-claude-mixin:2.1.281 --kit ./acme-skills
agent@acme-skills-demo:~/acme-demo$

sbx builds ./acme-skills and adds the three kits together. Because the workload is a shell, it opens a shell rather than starting an agent. That gives you a chance to look around before you start Claude Code.

Find the skill

The Claude Code mixin declares /home/agent/.claude/skills as its skills directory with agent-skills@1. Your skill should be there, under its directory name:

Sandbox shell (acme-skills-demo)

ls -R /home/agent/.claude/skills/changelog-entry
/home/agent/.claude/skills/changelog-entry:
SKILL.md  references

/home/agent/.claude/skills/changelog-entry/references:
format.md

The whole directory is there, with the reference file at the same relative path, so the link in SKILL.md still works.

Find the context

Every kit carries its own sources under /usr/share/sandbox/kit/<stem>/, where <stem> is the descriptor’s file name without .yaml. Your context body was copied there:

Sandbox shell (acme-skills-demo)

ls /usr/share/sandbox/kit/acme-skills/
context.md  kit.dockerfile  kit.yaml
grep -F "acme-skills/context.md" /home/agent/.claude/CLAUDE.md
- Acme team skills: /usr/share/sandbox/kit/acme-skills/context.md

kit.yaml is the descriptor as it was published, and kit.dockerfile is the recipe that built the kit’s files. Claude Code reads /home/agent/.claude/CLAUDE.md when it starts. Docker Sandboxes keeps a “Kits” index in that file, with one entry for each kit that contributed guidance. Each entry points at that kit’s body instead of copying it in. Adding more kits makes the index longer, but the instruction file stays short.

Ask the agent

Start Claude Code and check that it can see the skill:

Sandbox shell (acme-skills-demo)

claude
Claude Code v2.1.281
Which skills can you use here?
I have one skill available: changelog-entry, which adds an entry to
CHANGELOG.md in Acme's format.
We fixed the API client so it stops retrying after five attempts. That was ACME-412. Add a changelog entry.
I'll use the changelog-entry skill.

Added to CHANGELOG.md under ## Unreleased > ### Fixed:

- Stop retrying API requests after five attempts. ([ACME-412](https://tracker.acme.example.com/ACME-412))

Exit Claude Code with /exit, then look at the file itself. Your project directory is the sandbox’s workspace, so the change also appears on your host:

Sandbox shell (acme-skills-demo)

cat CHANGELOG.md
# Changelog

## Unreleased

### Fixed

- Stop retrying API requests after five attempts. ([ACME-412](https://tracker.acme.example.com/ACME-412))

The agent created the subsection, wrote an imperative one-liner, and linked the ticket. Nobody explained the convention in the prompt. Without the kit, the agent would have written the entry in its own style.

Exit the shell when you’re done. To clean up, run sbx rm acme-skills-demo on the host.

Understand what happened

Your kit says where the skill is in the image. The agent kits say where their agents look for skills. Docker Sandboxes connects the two when it creates the sandbox:

How a bundled skill reaches an agent On the left, the acme-skills mixin declares one skill with agent-skill@1. Its source is the directory /usr/share/acme-skills/changelog-entry in the kit's image, holding SKILL.md and references. On the right, agent kits declare where their agents look for skills with agent-skills@1: the Claude Code mixin at ~/.claude/skills and the Codex workload at ~/.agents/skills. When the sandbox is created, sbx exposes the skill as changelog-entry in every declared destination. SKILL MIXIN · SOURCE changelog-entry agent-skill@1 SKILL.md + references/ /usr/share/acme-skills/… AGENT KITS · DESTINATIONS Claude Code mixin agent-skills@1 ~/.claude/skills/ Codex workload agent-skills@1 ~/.agents/skills/ sbx exposes it at each

Most of how skills behave in a kit follows from that split between source and destination.

The skill kit doesn’t name an agent. Destinations come from the kits that supply agents. Put the same kit with the Codex workload and the skill appears in Codex’s directory instead. Use both agents in one sandbox and it appears in both:

$ sbx run docker/sbx-kit-codex:0.155.1 --name acme-codex --kit ./acme-skills

A skill needs a destination. A sandbox with only the shell workload and your kit has no agent, so no kit declares a skills directory. Your agent-skill@1 entry is required by default, so Docker Sandboxes refuses to create that sandbox instead of quietly leaving the skill out. If a kit is still useful without its skills, mark the entry optional: true.

Names have to be unique. If something already exists at changelog-entry in a destination, Docker Sandboxes won’t overwrite it. That includes a skill a teammate shared from the host. The request can’t be satisfied, and a required skill stops the sandbox from being created. Two kits that use the same skill name with different source paths can’t be combined. Give your skills names a teammate is unlikely to choose for something else. To keep the source directory’s name but change the skill’s name, set config.name on the entry.

Keep sources out of discovery directories. If your Dockerfile copied the skill straight into /home/agent/.claude/skills, it would work for only one agent. Docker Sandboxes manages the discovery directories itself, so sources belong in a location named for your kit.

A skill grants nothing. agent-skill@1 and agent-context@1 don’t add anything to the kit’s permission surface, which is the set of grants the user reviews and approves. They’re files for the agent to read. Docker Sandboxes never runs a skill’s scripts to register it, and running one is up to the agent. If a skill needs to reach a service, the network rule and credential come from a separate grant, which is reviewed as usual. Treat skill and context text as you would any instructions you give an agent. A kit from a publisher you don’t trust can give your agent bad instructions, even though it can’t grant itself any access.

Bundled skills are separate from host skills. The agent’s skills directory can also receive skills you share from your host. That’s controlled per environment with the skills setting (off, readonly, or readwrite) in a sandbox environment file. Bundled skills appear even when host sharing is off, because the image, not your host, supplies them. If you share your host’s skills, a kit can’t change them unless both the kit and your settings allow write access.

Where to go next

Learning paths

Part of a learning path

This works on its own and is also included in the paths below. Open a path to see the complete route.