Skills

A skill is a block of instructions the agent loads on demand. You write the skill, a SKILL.md and the files beside it; the SDK puts its files in the sandbox and lists only its name and description in the system prompt. The agent reads the body with the built-in skill.read tool when it decides the skill applies.

Use a skill for a procedure too long to keep in instructions= and needed in only some turns. The API reference lists every kind of skill and its options.

Add a skill

Add a skill

skills= on the Agent takes every skill of the agent, in one list: local skill bundles and registry skills.

From a directory

From a directory

Write each skill as its own directory under a skills/ directory in your project, named after the skill, the layout every coding agent uses:

hello_agent/
├── main.py
└── skills/
    └── refund-policy/
        └── SKILL.md

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.

description is what the model sees in the system prompt, so write it as what this is, and when to use it. LocalSkillBundle.from_directory() reads one such directory into a bundle. The example uses MistralSandbox. It needs the cloud_user role (Requirements) and the sandbox extra: uv add "mistralai-agents[koyeb]".

import asyncio
from pathlib import Path

from mistralai import agents

SKILLS = Path(__file__).with_name("skills")  # resolve against __file__, not the working directory

agent = agents.Agent(
    model="mistral-large-4",
    name="refund-agent",
    environment=agents.MistralSandbox(),
    skills=[
        agents.LocalSkillBundle.from_directory(SKILLS / "refund-policy"),
    ],
)

async def main() -> None:
    result = await agent.run("My order is 45 days old. Can I get a refund? Answer in one sentence.")
    if isinstance(result, agents.Completed):
        print(result.text)
        print([call.name for call in result.tool_calls])

if __name__ == "__main__":
    asyncio.run(main())
Output
No, since your order is 45 days old and refunds are only available for orders under 30 days old.
['skill.read']

The model saw the skill's description, loaded its body with skill.read, and applied the rule.

When the body has no directory, such as in a test, build the bundle from agents.LocalSkillFile objects instead.

Skill files in the sandbox

Skill files in the sandbox

A skill needs a sandbox that can hold its files, such as MistralSandbox or a local sandbox: each skill is a folder there, with SKILL.md and every file beside it, and the model reads and runs those files as any other. The default NoSandbox() cannot hold files, so the SDK refuses skills on it before any model call.

Skills from the AI Registry

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 skills=, in the same list as your local skills:

import uuid
from pathlib import Path

from mistralai import agents

SKILLS = Path(__file__).with_name("skills")

agent = agents.Agent(
    model="mistral-large-4",
    environment=agents.MistralSandbox(),
    skills=[
        agents.LocalSkillBundle.from_directory(SKILLS / "refund-policy"),
        agents.RegistrySkill(id=uuid.UUID("0199c5e1-8a3b-7d42-9f10-3c5e7a2b1d84"), version=3),
    ],
)

Set version=: the session refuses a registry skill without one, so every session runs the exact version you name. If the SDK cannot fetch the skill, the session fails to open. A deployed agent sees only the skills shared with its workspace: share each registry skill with the workspace before you deploy.

To serve every registry skill the session's user can access, add agents.all_registry_skills() to skills=. Each skill adds prompt tokens, so name the ones you need as RegistrySkill entries when there are many. Its options are in the API reference.

Ship a skill with a deploy

Ship a skill with a deploy

agents deploy builds the commit you pushed, so a committed skills/ directory ships with it and needs no COPY of its own. If you exclude it in .dockerignore, or leave it uncommitted, from_directory() raises when the deployed agent starts.

Common issues

Common issues

You seeFix
Unsupported Agents SDK feature 'skills': ... NoSandbox() cannot hold filesPass a sandbox, such as environment=agents.MistralSandbox().
Unsupported Agents SDK feature 'skills': registry skill <id> has no versionSet version= on the RegistrySkill.
The deployed agent fails to start, and from_directory() raisedCommit the skills/ directory, and remove it from .dockerignore.

Structured output: get the agent's answer back as a Pydantic model.