Skip to content
AI Workforce OS
ES EN
Create account

Skills format

A skill is reusable know-how that an agent loads when it needs it: a folder with a SKILL.md file and optional text resources, in the open Agent Skills format. This page is for anyone who writes, imports or reviews skills. Since it is plain prompt text plus one gateway tool, it works the same with every model provider.

7 min read

On this page
  1. What a skill package is
  2. SKILL.md frontmatter
  3. Complete example
  4. Package limits
  5. Built-in starter skills
  6. Lifecycle and review
  7. Scanning, import and export
  8. Skills API routes
  9. Assignment to agents
  10. How skills reach the model
  11. Limits and cleaning of loaded text
Back to top

What a skill package is#

A package is a folder whose only required file is SKILL.md: a YAML frontmatter block followed by markdown instructions. It may include supporting files such as templates, examples, reference documents or scripts, always as text.

Each version stores its files in the organization's Drive, at /Shared/Skills/<name>/v<n>/, and the database keeps the manifest: path, size and sha256 of each file. Versions are immutable; restoring an old version creates a new version with the same content.

Built-in client-email packageText
client-email/
├── SKILL.md
└── examples/
    └── follow-up-example.md

SKILL.md frontmatter#

SKILL.md must be valid UTF-8, start with a --- line and close the block with another one. The instructions after the frontmatter cannot be empty.

The parser understands a small subset of YAML, on purpose:

  • Scalars, quoted strings and > or | blocks.
  • Inline or block lists.
  • A single level of nested map, only for metadata.
  • It rejects anchors, aliases, tags, inline maps and tabs in indentation.
FieldRequiredRule or limit
nameYesPattern ^[a-z0-9-]{1,64}$. Unique per organization and immutable once created.
descriptionYesUp to 1024 characters. It is what the agent reads to decide whether to use the skill: say what it does and when to use it.
licenseNoFree text.
compatibilityNoFree text; preserved on export.
allowed-toolsNoUp to 20 entries. Informational: it never grants tools.
metadataNoKey-to-text map, up to 30 entries (for example category).

Note

Other top-level scalar keys are preserved on import and export (up to 20 keys and 300 bytes per value), so a round trip loses nothing. The in-app editor does not show compatibility or those extra keys: saving from the editor drops them.

Complete example#

This is the SKILL.md of the English client-email skill, exactly as the backend ships it. The name uses lowercase and hyphens, the description says what it does and when to use it, and metadata carries the category and the built-in marker.

SKILL.mdYAML
---
name: client-email
description: "Write a clear email to a client: subject, brief context, a concrete request or answer and the next step. Use it for replies, follow-ups, friendly collections and announcements."
metadata:
  category: communication
  builtin: "true"
---

# Client email

## When to use it
To write replies, follow-ups or notices to clients.

## Steps
1. Identify the goal in one sentence: inform, ask, confirm, collect or apologize.
2. Specific subject (at most 8 words) with the topic and, if relevant, the date or reference.
3. First paragraph: the answer or request. Then the minimum context.
4. Close with a single next step, who does it and by when.
5. Adjust the tone: warm and professional; in complaints acknowledge the problem first; in collections be firm and kind.

## Quality criteria
- Under 150 words unless more is needed.
- No figure, date or commitment that is not in the task data.
- Check names, amounts and attachments mentioned.

## Limits
Leave the email as a draft; sending it is decided by a person or by the organization policy.

Package limits#

These caps are validated on create, edit and import. A violation returns a stable error code, for example file_too_large, too_many_files, extension_not_allowed or zip_bomb.

LimitValue
Uploaded file on import4 MB
Total size of a version2 MB
Size of one file512 KB
Files per skill (including SKILL.md)40
Zip entries inspected300
PathsUp to 4 levels (the file name counts) and 160 characters. Each segment starts with a letter or digit and uses A-Z, a-z, 0-9, ., _ or -, up to 64 characters.
Allowed extensionsmd, txt, csv, json, yaml, yml, py, js, sh (UTF-8 text only, no NUL bytes)
Compression ratio of a zip entryUp to 100 to 1 (checked on entries of 64 KB or more)

Built-in starter skills#

The backend ships a catalog of starter skills embedded in the binary, in Spanish and English, in the same format as an import. There are 55 per language today, including bank-reconciliation, b2b-proposal, service-contract-review, month-end-close, client-email and project-plan.

The first time an organization opens the library, they are created as active in its language. If the organization already has any skill, even a deleted one, none are created, and deleted starters do not come back. Role templates declare default_skills (names only) and agents of that role receive them without needing an assignment.

Text
backend/internal/skills/builtin/
├── es/<name>/SKILL.md
└── en/<name>/SKILL.md

Lifecycle and review#

A skill is in one of four statuses: draft, in_review, active or disabled. Members (skills:write permission) create drafts, edit, import and restore versions. Administrators (skills:review permission) approve, reject, disable and delete.

  • An import always enters in_review.
  • If a member edits an active skill, the new version lands in in_review and agents keep using the approved one. If an administrator edits it, the edit counts as the review and it becomes active.
  • A review names the version the reviewer read: if it is no longer current, it fails with stale_review. File hashes are checked again before activation.
  • A rejection stores a note and returns the skill to draft, or keeps it active with the previous approved version when there is one.
  • Deletion is soft and removes assignments. Creating another skill with the same name brings it back with a new version.
Text
create (member)  → draft ──submit──▶ in_review ──approve (admin)──▶ active
import (member)  → in_review (always)
reject → draft, or stays active with the previous approved version
active ──disable (admin)──▶ disabled · delete (admin, soft)

Scanning, import and export#

Every saved version is scanned and the warnings are stored for the reviewer. The scan informs; it does not decide: the reviewer sees everything before approving.

  • injection: a file's injection score exceeds the threshold.
  • script: the package ships executable files as text (py, js, sh).
  • allowed_tools: the frontmatter declares allowed-tools.
  • import_note: the import adjusted something, for example it skipped an empty file.
  1. Import a .zip or a single SKILL.md with POST /api/v1/skills/import (multipart field file).
  2. Validation is strict: it rejects absolute paths or paths with .., backslashes, drive letters, symlinks and special files, encrypted entries, duplicates that differ only in case, and zip bombs. It strips a single wrapper folder and ignores __MACOSX, .DS_Store and Thumbs.db.
  3. An administrator reviews the version and approves or rejects it.
  4. To take it out, GET /api/v1/skills/{id}/export returns a deterministic zip in the standard layout (<name>/SKILL.md plus resources, no executable bits), or only SKILL.md with ?format=md.

Note

Import from a URL deliberately does not exist: it would need an SSRF-safe downloader.

Skills API routes#

Every route lives under /api/v1, behind authentication and per-organization isolation. A review body carries the decision (approve or reject), the reviewed version and a note.

Approve version 3HTTP
POST /api/v1/skills/{id}/review
Content-Type: application/json

{ "decision": "approve", "version": 3, "note": "Checked tone and limits" }
Route (under /api/v1)PermissionWhat it does
GET /skillsskills:readLists the organization's skills.
POST /skillsskills:writeCreates a draft from the editor.
POST /skills/importskills:writeImports a .zip or a SKILL.md; it enters in_review.
PUT /skills/{id}skills:writeSaves a new version.
POST /skills/{id}/versions/{n}/restoreskills:writeCreates a new version with the content of version n.
POST /skills/{id}/submitskills:writeMoves from draft to in_review.
POST /skills/{id}/reviewskills:reviewApproves or rejects a specific version.
POST /skills/{id}/disableskills:reviewStops serving the skill to agents.
GET /skills/{id}/exportskills:readExports the zip or, with ?format=md, only SKILL.md.

Assignment to agents#

An assignment links a skill to an agent through PUT and DELETE on /api/v1/agents/{id}/skills/{skillId}, with the agents:write permission. There are also role defaults: the names in a role template's default_skills reach every agent of that role without an explicit assignment.

An agent only receives a skill if it is assigned (or is a default of its role), is neither deleted nor disabled, and has an approved version. While an edit waits for review, the approved version is served. In any other case the load is denied with skill_not_available.

How skills reach the model#

Disclosure is progressive, in two steps. On every run, the backend adds to the request only the name and description of the skills the agent can use, with caps: at most 12 skills, 240 characters per description and 3000 bytes in total. The runtime renders them in the prompt with an explicit rule: a skill guides how to work and never grants tools, connections, permissions, autonomy or approvals.

If a skill applies, the agent asks for it with a tool request. The Tool Gateway serves it as a native read tool, subject to the kill switch, pause and audit, and answers with the approved version's body without frontmatter plus the file list. With file it returns a single resource.

Agent tool requestJSON
{ "tool": "skill", "action": "load", "args": { "name": "client-email", "file": "examples/follow-up-example.md" } }

Limits and cleaning of loaded text#

Once loaded, the skill enters the task context with several safeguards:

  • The text travels in its own channel (loaded_skills) as trusted organization configuration, inside dedicated delimiters, because a person approved that exact version.
  • It is still cleaned (invisible characters, credentials, forged delimiters) and scored for injection.
  • It is bounded in the backend (24,000 characters per answer, 4 loads per task) and again in the runtime (3 skills, 12,000 characters each, 20,000 in total).
  • Scripts are inert text: running them requires the code tool, with human approval.