Skills
Coming soonA skill is a block of instructions the agent loads on demand. Only the skill's name and
description sit in the system prompt. The agent reads the body with the built-in skill tool when
it decides the skill applies. Use a skill for a procedure that is too long to keep in
instructions= and is needed in only some turns.
This feature is very early in development. Expect API changes
Add a skill
The skills= slot on the Harness takes LocalSkillBundle and RegistrySkill objects. A bundle is a SKILL.md plus the files beside it.
From a directory
Write each skill as its own directory under a skills/ directory in your project. This is the
layout every coding agent uses for skills, so an author who has written one before can read yours:
hello_agent/
├── main.py
└── skills/
└── refund-policy/
└── SKILL.mdLocalSkillBundle.from_directory() reads one such directory into a bundle:
from pathlib import Path
from mistralai.agents import agents
SKILLS = Path(__file__).with_name("skills")
harness = agents.Harness(
skills=[
agents.LocalSkillBundle.from_directory(SKILLS / "refund-policy"),
]
)skills/ folder and skill directory named after the skill is just a convention for now (from_directory()
takes any path you want), but it could become the discoverability standard. Try adopting this convention
to save later migration work.
Resolve the path against __file__ rather than the working directory: agents serve and agents deploy import your module from a root you do not choose.
SKILL.md is YAML frontmatter and then the body:
---
name: refund-policy
description: The refund rules. Use when a customer asks for a refund.
---
Refund a customer only when the order is under 30 days old.
...name must be lowercase letters, numbers and hyphens. description is what the model sees in the
system prompt, so write it as what this is, and when to use it. The body must not be empty.
Write each frontmatter field on one line. The harness reads the frontmatter line by line, so a
multi-line YAML value such as description: > is not supported.
From code
This feature may not be supported in the future. Representing skills in memory has some drawbacks, so it's likely that the file-based approach will be the only one.
When the body has no directory to live in — an in-process run, a test, a body you generate — build the bundle yourself:
import base64
from mistralai.agents import agents
SKILL_MD = b"""---
name: refund-policy
description: The refund rules. Use when a customer asks for a refund.
---
Refund a customer only when the order is under 30 days old.
"""
harness = agents.Harness(
skills=[
agents.LocalSkillBundle(
files=[
agents.LocalSkillFile(
path="SKILL.md",
content_base64=base64.b64encode(SKILL_MD).decode("ascii"),
)
]
)
]
)from_directory() produces exactly this, so the two are the same feature. Prefer a directory when
the skill can have one: the SKILL.md you edit is the file that ships, and it stays a document
rather than a byte string in your source.
Skill files in the sandbox
With sandbox=agents.BashkitSandbox(), agents.LocalDockerSandbox() or
agents.DangerousLocalSandbox(), each skill is a folder in the sandbox. The folder holds
SKILL.md and every file beside it, at /skills/<name>-<digest>/:
/skills/
└── refund-policy-3f2a9c41d07e/
├── SKILL.md
├── reference/
│ └── rules.md
└── scripts/
└── check.shThe model loads SKILL.md with the skill tool. It reads reference/ with read_file and runs
scripts/ with bash, as for any other file. A script keeps its executable bit.
/skills is outside the workdir, so a skill never lands in files the agent commits. On Bashkit, the
model cannot change a skill. The digest in the folder name changes when any file of the skill
changes.
Two sandboxes put the folders elsewhere, and the system prompt gives the model the real path:
LocalDockerSandbox():read_fileandgrepreach only the workdir and/tmp, so the folders are under/tmp.DangerousLocalSandbox(): commands run on your machine, so the folders are under/tmp. A machine without/tmp, such as Windows, uses its temporary directory, and onlybashreaches the files there.
from pathlib import Path
from mistralai.agents import agents
SKILLS = Path(__file__).with_name("skills")
harness = agents.Harness(
sandbox=agents.BashkitSandbox(),
skills=[agents.LocalSkillBundle.from_directory(SKILLS / "refund-policy")],
)Without a sandbox
The default NoSandbox() has no place for skill files:
- A skill with only
SKILL.mdstill loads, from an in-process store, with aDeprecationWarning. - A skill with any other file fails while the harness compiles, before any model call.
Use BashkitSandbox() for skills.
Skills from the AI Registry
A registry skill is a skill stored in the Mistral AI Registry and named by its UUID. Pass it in
the skills= list, next to your local ones:
harness = agents.Harness(
sandbox=agents.BashkitSandbox(),
skills=[
agents.LocalSkillBundle.from_directory(SKILLS / "refund-policy"),
agents.RegistrySkill(id="0199c5e1-8a3b-7d42-9f10-3c5e7a2b1d84", version=3),
],
)The SDK gets each registry skill with your MISTRAL_API_KEY when the session starts. It writes the
skill to /skills like a local one, with its assets. The model cannot tell the two apart. If the
registry does not answer within 30 seconds, the session does not start.
version= is required. Every session serves the exact version you name, so two sessions never run
different versions of one skill.
A registry skill needs a sandbox that can hold its files: BashkitSandbox(), LocalDockerSandbox()
or DangerousLocalSandbox(). With any other sandbox, the harness refuses it while it compiles.
Slots this backend does not serve
Harness carries further skill slots that this backend refuses while the harness compiles, before
any model call:
| Slot | What happens |
|---|---|
skills=[agents.SkillDefinition(...)] | Unsupported Agents SDK feature 'skills': inline SkillDefinitions are not supported on this backend; pass local_skills= instead |
registry_skills= without a sandbox that can hold files | Unsupported Agents SDK feature 'registry_skills': registry skills are served as sandbox files; use sandbox=BashkitSandbox() |
local_skills= with files beside SKILL.md, and no sandbox | Unsupported Agents SDK feature 'local_skills': a skill with files other than SKILL.md needs a sandbox to hold them; use sandbox=BashkitSandbox() |
registry_skills= in agents serve or agents deploy | Unsupported Agents SDK feature 'registry_skills': registry skills run in-process with BashkitSandbox only; pass local_skills= instead |
Run modes
See Run modes
How a skill reaches the image in agents deploy mode ?
agents deploy builds your project's Dockerfile from the revision you pushed, with the project
directory as the build context. A skills/ directory in your project is in that context already, so
the COPY . /app in the run modes Dockerfile carries it into the image —
it needs no COPY of its own.
If you exclude it in .dockerignore, or leave the file untracked, and from_directory() raises while the
worker imports your module: the rollout fails.
:::note skills in AI registry
agents deploy also uploads each LocalSkillBundle in skills= to the Mistral AI Registry, so it appears
in the agent's catalog on the control plane. That copy carries the SKILL.md body only, and the
deployed worker does not read it: the worker serves the bundles its own harness builds, from the
code that ships in its image. The Registry copy is a catalog entry, not the source of truth.
For the same reason, registry skills a caller asks for when it starts a session are ignored by a worker built with this SDK. :::