# Skills (https://www.agentenvframework.com/docs/agents/skills)

> Write a skill once, use on any agent

1. Write a skill once. This one says how replies from the inbox are written.
2. `agent-env a2a-agent add-skill` sends it to each agent’s skill-config, and each installs it its own way. `BadAgent` has no skill-config, so the install fails.
3. Every agent that took the skill lists it on its card, as `skill-email-etiquette`.
4. Each agent gets the same prompt: “Reply to Dana’s email about the Q3 numbers.”
5. The agents with the skill greet Dana, say which request they answer and sign off as the Finance team. `BadAgent` does none of it.

Parts of the scene:

- **A skill**: A directory named after the skill, with a `SKILL.md` in the Agent Skills format: `name` and `description` in its front matter, then the instructions.
- **agent-env a2a-agent add-skill**: Installs a skill on one deployed instance through its skill-config. In a task, an `add_skills` step, or the `skills` of `deploy_agent`, does the same for an agent the task deployed.
- **Claude Code**: Claude Code wrapped as an A2A agent. Its skill-config handler writes the skill to `.claude/skills/<name>/SKILL.md`, where Claude Code loads skills from. The framework does not ship this wrapper.
- **Codex**: Codex wrapped as an A2A agent. Its skill-config handler writes the skill to `~/.codex/skills/<name>/SKILL.md`, where Codex loads skills from. The framework does not ship this wrapper.
- **claudius, version 2**: `ClaudiusRLMAgent` with the skill-config additions: it writes each skill to `/app/skills` and puts it in Claude’s system prompt.
- **Your agent**: Your skill-config handlers decide where the skill goes and how the agent uses it. The framework only sends the skill and never needs to know.
- **BadAgent**: An agent whose card does not list skill-config. `add-skill` stops before it sends anything, with `RuntimeError: Deployed agent … does not advertise urn:agentenv:skill-config/v1`. In a task, `deploy_agent` skips its skills and `add_skills` fails.
- **The prompt**: The same message to every agent, as a `prompt_agent` step sends it. Nothing in it mentions the skill; each agent decides to use what it has installed.
- **The replies**: Illustrative replies. The agents with the skill follow its three rules: a greeting by first name, the request they answer and the sign-off. `BadAgent` never got the skill, so its reply follows none of them.
- **On the card**: The SDK adds each installed skill to the agent card’s `skills`, as `skill-<name>` with the tag `skill`, so anything that reads the card sees what the agent can do.

Skills follow the [Agent Skills](https://agentskills.io/specification) specification: a folder with
a `SKILL.md` whose front matter gives the skill's name and description. Any skill can be added to
any agent that implements the skill-config extension, and how the agent installs and uses it is up
to the agent. This page gives `ClaudiusRLMAgent` one.

## Write a skill

A skill is a directory named after the skill, with its `SKILL.md` at the top:

```markdown title="skills/email-etiquette/SKILL.md"
---
name: email-etiquette
description: How replies from this inbox are written. Use before sending any email.
---

# Email etiquette

- Greet the recipient by first name.
- Say in one line which request you are answering.
- Sign off with "Best, the Finance team".
```

The name is lowercase letters, digits and hyphens. It names the skill everywhere: on the agent, on
its card and in the store.

## Accept skills in your agent

Installing a skill is up to the agent, because only the agent knows where its runtime reads skills
from. A skill arrives in one of two forms: inline, as the text of its `SKILL.md`, or as a bundle,
with a download grant for each of its files. `ClaudiusRLMAgent` writes either one to `/app/skills`
and puts every installed skill in Claude's system prompt, on sub-calls too:

```python title="claudius/agent.py (additions)"
from pathlib import Path

from agentenv_protocol.a2a_agent import (
    SKILL_CONFIG_V1,
    BundleSkillRequest,
    InlineSkillRequest,
    download,
    extension,
)

SKILLS = Path("/app/skills")


class ClaudiusRLMAgent(AgentEnvAgent):
    async def run(self, request: TaskRequest[ClaudiusRLMConfig]) -> TaskResult:
        skills = [(SKILLS / skill["name"] / "SKILL.md").read_text() for skill in request.skills]
        system = "\n\n".join([request.config.system_prompt, *skills])
        ...  # solve() sends system=system on every call to Claude

    @extension(SKILL_CONFIG_V1.add.inline)
    async def add_inline_skill(self, request: InlineSkillRequest):
        path = SKILLS / request.name / "SKILL.md"
        path.parent.mkdir(parents=True, exist_ok=True)
        path.write_text(request.skill_md)
        return {"name": request.name}

    @extension(SKILL_CONFIG_V1.add.bundle)
    async def add_bundle_skill(self, request: BundleSkillRequest):
        for file in request.skill_bundle.files:
            await download(file.object, SKILLS / request.name / file.path)
        return {"name": request.name}
```

Binding the two handlers turns on skill-config. The SDK does the rest: it refuses a second skill
with the same name, lists installed skills at `GET /ext/skill-config`, adds each one to the agent
card, and passes them to every later task as `request.skills`.

The change makes a new version of the agent. Register and deploy it the way [Deploying your
agent](https://www.agentenvframework.com/docs/agents/deploying.md) did:

```bash title="Terminal"
agent-env a2a-agent put --id claudius --dockerfile claudius/Dockerfile --skip-validation
agent-env a2a-agent deploy --id claudius
```

```text title="Output (trimmed)"
Created artifact: id=a2a-agent-claudius version=2
Created A2A agent: id=claudius version=2 image=a2a-agent-claudius:2
...
Deployed!
Instance ID: claudius-j5rwex20
A2A URL: http://localhost:43011
```

## Add a skill to a running agent

`agent-env a2a-agent add-skill` installs a skill on a deployed instance. With `--skill-md-path`, it
sends the file inline:

```bash title="Terminal"
agent-env a2a-agent add-skill --instance-id claudius-j5rwex20 \
  --skill-md-path skills/email-etiquette/SKILL.md
```

```text title="Output"
Deployed agent: claudius v2 @ http://localhost:43011
Registering inline skill from skills/email-etiquette/SKILL.md (name=email-etiquette)...
Registered: {'name': 'email-etiquette'}
```

The agent now lists it:

```bash title="Terminal"
curl -sS http://localhost:43011/ext/skill-config
```

```text title="Output"
{"skills":{"email-etiquette":{"description":"How replies from this inbox are written. Use before sending any email."}}}
```

Its card lists it too, under `skills`, with the id `skill-email-etiquette` and the tag `skill`, so
anything that reads the card sees what the agent can do.

## Store a skill

A skill you reuse across tasks belongs in the store, as a versioned artifact like any other. Its id
must match the name in its front matter:

```bash title="Terminal"
agent-env artifact skill put --id email-etiquette --skill-dir skills/email-etiquette
```

```text title="Output (start)"
Uploading SkillArtifact bundle (1 file(s))...
Created SkillArtifact: id=email-etiquette version=1 spec_version=0.1 skill_files_id=email-etiquette__files skill_s3_url=file:///root/.local/state/agent-env/object_store/artifacts/skill/email-etiquette/1
```

The put checks the front matter, stores every file in the directory, and prints the skill it
stored. `agent-env a2a-agent add-skill --skill-artifact-id email-etiquette` then installs the stored
skill on an instance, as a bundle.

## Skills in a task

In a [task](https://www.agentenvframework.com/docs/tasks.md), a skill reaches the agent in one of two places. `deploy_agent` takes a
`skills` list and installs each one right after the deploy, inline as `skill_md`:

```json title="A deploy_agent step with a skill"
{"id": "agent", "type": "deploy_agent", "env_ids": ["email"], "a2a_agent_id": "claudius",
 "agent_name": "assistant",
 "skills": [{"name": "email-etiquette",
             "description": "How replies from this inbox are written. Use before sending any email.",
             "skill_md": "---\nname: email-etiquette\n..."}]}
```

An `add_skills` step installs skills on an agent that is already deployed, by `agent_name`. Each
entry is a stored skill, by `skill_artifact_id` and an optional `skill_artifact_version`, or an
inline one, by `name`, `description` and `body`, which the step turns into a `SKILL.md`:

```json title="An add_skills step"
{"id": "etiquette", "type": "add_skills", "agent_name": "assistant",
 "skills": [{"skill_artifact_id": "email-etiquette", "skill_artifact_version": 1}]}
```

`add_skills` also writes skills of its own: for each CLI in `cli_artifact_ids`, a skill that says
where the CLI is installed and how to discover its subcommands, and for each universe in
`file_artifact_universe_ids`, one that says where its files were loaded.