Skip to main content

Overview

The most common way to ship skills with MeshAgent is to package the skills in a separate image, mount that image into the service container, copy the skills into a writable working directory, and then launch the agent with one --skill-dir flag per discovered skill. This page walks through that full pattern using the existing anthropic_skills example in this docs repo. For the full service YAML reference, see Packaging and Deploying Services.

End-to-end example: package a skills repo as a ChatBot

This example assumes you already have a repo or folder of Agent Skills you want to ship. The anthropic_skills sample models the pattern where those skills are packaged into an image and then mounted at /skills inside the MeshAgent service container.

Step 1: Package your skills into an image

Start with a skills repo that contains one or more skill directories, each with its own SKILL.md. In the existing sample, the skills come from the open Anthropic skills repo. At runtime, the MeshAgent service expects those skills to be present in an image that can be mounted read-only into the container.
Clone the skills repo:
bash
git clone https://github.com/anthropics/skills.git
cd skills
Create a scratch Dockerfile in the repo root:
FROM scratch
COPY skills/ /skills/
Build and push the image:
bash
docker buildx build . \
  -t "<REGISTRY>/<NAMESPACE>/<IMAGE_NAME>:<TAG>" \
  --platform linux/amd64 \
  --push
What the build arguments mean
  • <REGISTRY>: Container registry host such as docker.io, ghcr.io, or us-west1-docker.pkg.dev.
  • <NAMESPACE>: Your registry account or organization name.
  • <IMAGE_NAME>: The repository name for the image.
  • <TAG>: A version label such as latest or 2026-03-03.
  • docker buildx build .: Builds from the current directory using Buildx.
  • -t: Tags the image with the full name.
  • --platform linux/amd64: Builds a Linux AMD64 image.
  • --push: Pushes the built image to the registry.
Docker Hub noteFor Docker Hub you can omit the registry and use:
bash
docker buildx build . \
  -t "<NAMESPACE>/<IMAGE_NAME>:<TAG>" \
  --platform linux/amd64 \
  --push
If you do not want to build your own image, you can keep using the prebuilt image already referenced by the sample:
docker.io/tulamasterman/anthropic-skills:latest

Step 2: Create the MeshAgent service spec

Use a meshagent.yaml file that mounts the skills image and starts a ChatBot which loads the discovered skills.
version: v1
kind: ServiceTemplate
variables: []

metadata:
  name: claudeskills
  description: An agent that can assist you with questions, powered by Anthropic's Claude model
  annotations:
    meshagent.service.id: meshagent.claudeskills

agents:
  - name: claudeskills
    annotations:
      meshagent.agent.type: ChatBot

container:
  image: meshagent/cli:default
  command: /bin/bash /var/start.sh
  storage:
    room:
      - path: /data
        read_only: false
    images:
      - image: docker.io/tulamasterman/anthropic-skills:latest
        path: /skills
        read_only: true
    files:
      - path: /var/start.sh
        text: |
          #!/bin/bash
          set -e

          mkdir -p /data/skills
          skills_src="/skills"
          if [ -d /skills/skills ]; then
            skills_src="/skills/skills"
          fi
          if [ -d "$skills_src" ]; then
            cp -R -n "$skills_src"/* /data/skills/ 2>/dev/null || true
          fi

          skill_args=()
          while IFS= read -r -d '' skill_md; do
            skill_dir="$(dirname "$skill_md")"
            skill_args+=(--skill-dir "$skill_dir")
          done < <(find /data/skills -type f -iname 'skill.md' -print0)

          exec /usr/bin/meshagent chatbot join \
            --model=claude-sonnet-4-6 \
            --require-shell \
            --shell-image=meshagent/shell-terminal:default \
            --shell-tool-room-path=/:/data \
            --script-tool \
            --web-search \
            --use-memory agents/claudeskills/memories \
            --require-storage \
            --storage-tool-room-path=/:/data \
            -rr=agents/claudeskills/rules.txt \
            "${skill_args[@]}" \
            --rule='You have customizable rules stored in agents/claudeskills/rules.txt, you can use the read_file tool to read your rules. You can use the write_file tool to update the contents of the rules file or other text files. Use the read_file tool to read PDFs, examine images, or read files with a text/* mime type from attachments or files.' \
            --rule='You are a MeshAgent agent. MeshAgent is an agent operating system. You can find out more at www.meshagent.com and docs.meshagent.com' \
            --rule='The root of the filesystem that the user sees (the "Room" files) has been mounted in the /data folder. If the user is currently viewing a file, the file path is relative to /data. To write a file the user can see, you must write it inside /data and tell them the path, relative to the /data folder, not including the data folder'

  environment:
    - name: MESHAGENT_TOKEN
      token:
        identity: claudeskills
        api:
          livekit: {}
          queues:
            list: true
          messaging:
            broadcast: true
            list: true
            send: true
          database:
            list_tables: true
          sync: {}
          storage: {}
          containers:
            logs: true
            use_containers: true
          developer:
            logs: true
          agents:
            register_agent: true
            register_public_toolkit: true
            register_private_toolkit: true
            call: true
            use_agents: true
            use_tools: true
            allowed_toolkits: null
          memory:
            list: true
            memories:
              - name: memories
                namespace: ["agents", "claudeskills"]
                permissions:
                  ingest: true
                  recall: true
                  query: true

If you built your own image in Step 1, update the image mount in the service spec:
  • container.storage.images.image: <REGISTRY>/<NAMESPACE>/<IMAGE_NAME>:<TAG>

Step 3: Understand what this service is doing

The sample follows the same pattern you used for the Anthropic skills packaging flow:
  • It mounts the room filesystem at /data.
  • It mounts the skills image at /skills.
  • It copies the skills into /data/skills on startup.
  • It finds every SKILL.md and turns each parent directory into a --skill-dir argument.
  • It starts meshagent chatbot join with the required tools and rules so the agent can actually use the skills.
The startup script is important because it does three things that make the whole pattern work:
  1. It copies the skill files into /data/skills, giving the running service a predictable local path to work from.
  2. It discovers every SKILL.md and expands those directories into repeated --skill-dir flags before launching the agent.
  3. It launches the agent with the tools and rules the packaged skills need in order to be useful.
The result is that your deployed agent can mount a repo of skills, discover them automatically, and load them without hardcoding every individual skill path into the service command.

Step 4: Validate the service spec

bash
meshagent service validate-template --file meshagent.yaml
This checks that the ServiceTemplate is structurally valid before deployment.

Step 5: Deploy it to a room

bash
meshagent service create-template --file meshagent.yaml --room quickstart
Because this sample is a ServiceTemplate with no required input variables, you can deploy it directly to a room. If you want the service to be available only in one room, keep the --room flag. If you later convert the sample to a plain Service, you can follow the normal room vs project deployment rules from the packaging docs.

Step 6: Verify that the skills are available

After deployment:
  1. Open the room in MeshAgent Studio.
  2. Start chatting with the deployed agent.
  3. Give it a task that should trigger one of the packaged skills.
  4. Check the agent logs if you need to confirm startup or skill discovery behavior.
If the skills depend on shell access, storage, web search, or other tools, make sure those are enabled in the service command. Packaging the skill files alone is not enough.

Why this pattern works well

This approach is a good default when:
  • you already have a skill repo you want to reuse
  • you want multiple skills to ship together
  • you want the service to discover whatever skills are present in the mounted image
  • you want stable, versioned skill content in deployment
It is especially useful when the skills repo is maintained separately from the MeshAgent service spec.

Editable skills vs image-baked skills

There are two common operating modes:
  • Image-baked skills: versioned, reproducible, and best for stable deployments.
  • Copied skills under /data/skills: easier to inspect, reference, or customize from the room because /data is mounted from room storage in this sample.
The Anthropic skills example uses both:
  • The mounted image acts as the default packaged source of truth.
  • On startup, the skills are copied into /data/skills, which lives in room storage.
  • Because the sample uses cp -R -n, files that already exist in room storage are not overwritten by later startups.
In practice, that means you can seed a room with the skills from the image, then edit or customize the copied skill files in the room, and the agent will load the room-backed copies from /data/skills. This gives you a useful hybrid model:
  • ship a stable base set of skills in the image
  • let a specific room inspect or customize those skills after deployment
  • keep the agent pointed at the room-backed paths it actually uses at runtime

Adapting this pattern to your own repo

If your skills repo differs from the sample:
  • change the mounted image in storage.images
  • adjust the source path if your repo uses a different root layout
  • keep the find ... SKILL.md loop so every skill becomes its own --skill-dir
  • enable whatever tools the skills actually depend on
If you only have one skill and a fixed layout, you can simplify the command and pass a single explicit --skill-dir. The discovery loop becomes most useful when you are shipping a repo of skills.

See also