docs/src/content/docs/guides/agents/skills.mdx
import { Tabs, TabItem } from '@astrojs/starlight/components';
mistral.rs implements uploaded Skills for both the OpenAI-compatible Responses API and the Anthropic-compatible Messages API. You can upload a skill bundle to /v1/skills, reference it from a request, and let the model use the bundled instructions and files during the run.
This page documents the mistral.rs compatibility surface. For the upstream API shapes and authoring model, see OpenAI's Tools and Skills guide, Anthropic's Agent Skills overview, and Anthropic's Skills API guide.
Start a server with the Skills runtime available:
mistralrs serve --agent -m <model>
--agent is the recommended way to run Skills because it turns on the full local agent runtime: web search, Python code execution, and shell execution. Skills require the shell executor, so the minimum server flag is --enable-shell; use it only when you want Skills without the other agentic tools.
For executor flags, sandboxing, and approvals, see shell execution and permissions and approvals. For request files used alongside Skills, see OpenAI-compatible file inputs.
Supported today:
POST /v1/skills.container.skills references on POST /v1/messages.GET /v1/skills, POST /v1/skills/{skill_id}/versions, and GET /v1/skills/{skill_id}/versions.tools[].type = "shell" with environment.type = "container_auto".skill_reference entries with "version": "latest", an omitted version, or a concrete version.container.skills[] entries with type = "custom", skill_id, and "version": "latest", an omitted version, or a concrete version.shell_call and shell_call_output, so clients can inspect what ran.max_tool_rounds to bound agentic tool loops, and files to require specific output files and surface them as downloadable File objects./v1/skills.Not implemented today:
pptx, xlsx, docx, and pdf.agentskills.io registry installation or .agents/skills scanning.mistral.rs implements the OpenAI-compatible server/runtime layer. It does not act as a separate skill registry client.
Use the OpenAI Python package. The current OpenAI package may not expose a typed skills resource, so the example uses the package's low-level client.post(...) helper.
The Python and Rust SDK examples mount local skill directories in-process. That bypasses /v1/skills; server clients should use uploaded Skills and skill_reference.
from pathlib import Path
from openai import OpenAI
client = OpenAI(base_url="http://localhost:1234/v1", api_key="not-used")
def upload_skill(zip_path: Path) -> dict:
with zip_path.open("rb") as handle:
return client.post(
"/skills",
cast_to=dict,
files={"file": (zip_path.name, handle, "application/zip")},
)
skill = upload_skill(Path("my-skill.zip"))
response = client.responses.create(
model="default",
input="Use the uploaded skill. Read its instructions, then complete the task.",
tools=[
{
"type": "shell",
"environment": {
"type": "container_auto",
"skills": [
{
"type": "skill_reference",
"skill_id": skill["id"],
"version": "latest",
}
],
},
}
],
tool_choice="required",
extra_body={
"max_tool_rounds": 6,
"files": [{"name": "report.html", "format": "html"}],
},
)
print(response.output_text)
To upload a new version of an existing skill, post another zip to /skills/{skill_id}/versions with the same client.post(..., files=...) pattern. Requests can reference "version": "latest" or a specific version.
import json
import urllib.request
base_url = "http://localhost:1234"
skill_id = "skill_..."
payload = {
"model": "default",
"max_tokens": 1024,
"agent_permission": "auto",
"max_tool_rounds": 6,
"container": {
"skills": [{"type": "custom", "skill_id": skill_id, "version": "latest"}],
},
"tools": [{"type": "code_execution_20250825", "name": "code_execution"}],
"messages": [
{"role": "user", "content": "Use the uploaded skill to complete the task."}
],
}
request = urllib.request.Request(
f"{base_url}/v1/messages",
data=json.dumps(payload).encode("utf-8"),
headers={
"content-type": "application/json",
"x-api-key": "not-used",
"anthropic-version": "2023-06-01",
"anthropic-beta": "code-execution-2025-08-25,skills-2025-10-02",
},
method="POST",
)
with urllib.request.urlopen(request) as response:
print(json.loads(response.read().decode("utf-8")))
To list uploaded custom Skills with Anthropic-shaped objects, call
GET /v1/skills?source=custom with anthropic-version: 2023-06-01 and
anthropic-beta: skills-2025-10-02.
from mistralrs import ChatCompletionRequest, Runner, ShellConfig, ShellSkillMount, Which
runner = Runner(
which=Which.Plain(model_id="Qwen/Qwen3-4B"),
shell_config=ShellConfig(),
)
resp = runner.send_chat_completion_request(
ChatCompletionRequest(
model="default",
messages=[{"role": "user", "content": "Use my-skill to complete the task."}],
shell_skills=[
ShellSkillMount(
name="my-skill",
description="Local task-specific skill.",
source_path="skills/my-skill",
)
],
max_tool_rounds=6,
)
)
print(resp.choices[0].message.content)
use mistralrs::{ModelBuilder, RequestBuilder, ShellConfig, TextMessageRole, TextMessages};
let model = ModelBuilder::new("Qwen/Qwen3-4B")
.with_shell_execution(ShellConfig::default())
.build()
.await?;
let messages = TextMessages::new()
.add_message(TextMessageRole::User, "Use my-skill to complete the task.");
let req = RequestBuilder::from(messages)
.with_shell_skill(
"my-skill",
"Local task-specific skill.",
"skills/my-skill",
)
.with_max_tool_rounds(6);
let resp = model.send_chat_request(req).await?;
println!("{}", resp.choices[0].message.content.as_ref().unwrap());
Uploaded skills are stored under --skills-dir, which defaults to the system temp directory. Each upload receives a skill_... id and version metadata. The server stores the uploaded version once, then copies only the skills referenced by a request into that request's shell session working directory.
The shell working directory is still controlled by --shell-workdir; without it, each session uses a per-session temp directory. Session state, tool records, and generated files follow the same lifecycle described in persist sessions.