Skip to content

Agent Skills

Last updated View as MarkdownAgent setup

Agent Skills ↗︎ are on-demand instructions, references, and scripts for an agent. Each skill is a directory with a SKILL.md file. The agent sees a short catalog of skill names and descriptions. It loads a full skill only when a task matches it, so a large library of skills does not fill every prompt.

For background on the format and how agents use it, refer to Agent Skills concepts.

What a skill looks like

A skill is a directory named after the skill. It holds a SKILL.md file and, optionally, other files:

release-notes/
  SKILL.md
  references/style-guide.md
  scripts/format-release-notes.js
  assets/logo.png

SKILL.md starts with YAML frontmatter, followed by Markdown instructions:

---
name: release-notes
description: Turn a list of changes into release notes. Use when the user asks for release notes or a changelog entry.
metadata:
  author: my-team
---

1. Read `references/style-guide.md`.
2. Group the changes under Features, Fixes, and Other.

The files in scripts/, references/, and assets/ are the skill's resources. The model reads them when the instructions call for them.

Load skills into an agent

Skills come from a source. Return sources from getSkills() in a Think agent:

src/server.jsjs
import { Think, skills } from "@cloudflare/think";

export class MyAgent extends Think {
	getSkills() {
		return [
			skills.workspace(this.workspace),
			skills.r2(this.env.SKILLS_BUCKET, { prefix: "skills/" }),
		];
	}
}
src/server.tsts
import { Think, skills } from "@cloudflare/think";

export class MyAgent extends Think<Env> {
	getSkills() {
		return [
			skills.workspace(this.workspace),
			skills.r2(this.env.SKILLS_BUCKET, { prefix: "skills/" }),
		];
	}
}
Source Reads skills from
skills.workspace() Files in a @cloudflare/computer Workspace
skills.r2() Objects in an R2 bucket
skills.manifest() Skills defined in code

Sources are applied in order. The first source to provide a skill name wins. Later duplicates are skipped with a logged warning. Think lists every source again before each turn, so new, edited, and deleted skills take effect on the next turn.

The Pi harness takes the same sources through skills(sources) from agents/harness/pi.

Skill tools

When at least one skill is available, the agent gets these tools:

Tool Purpose
activate_skill Load a skill's instructions and its list of resources
read_skill_resource Read a resource by { name, path } or a qualified path such as name/path
run_skill_script Run a bundled script, when a script runner is set

The catalog is added to the system prompt as its own context block, below your getSystemPrompt() instructions. When a skill changes mid-conversation, Think does not rebuild the system prompt, so the model provider's prompt cache stays warm. The new catalog reaches the model as a reminder on the next turn and moves into the system prompt at the next compaction or history clear.

Use getSystemPrompt() or a context block from configureContext() for instructions that apply to every turn. Use skills for procedures, references, and scripts that only some tasks need.

Validation

Every SKILL.md is checked against these rules from the Agent Skills specification ↗︎:

  • name must be 1 to 64 lowercase letters, digits, and hyphens. It cannot start or end with a hyphen or contain --. It must match the skill's directory name.
  • description is required and can be at most 1,024 characters.
  • compatibility, when present, must be 1 to 500 characters.
  • metadata, when present, must map string keys to string values.
  • allowed-tools, when present, must be a space-separated string.

Skills load the way the specification's guide for agents ↗︎ recommends. Skills written for other agents often break a rule, so a skill that breaks one still loads, and the agent logs a warning:

Problem What happens
name breaks the format, is too long, or does not match the directory Loads, with a warning
name is missing Loads with the directory name, with a warning
description or compatibility is too long Loads, with a warning
license, metadata, or allowed-tools is invalid Loads without that field, with a warning
A value contains an unquoted : , so the YAML does not parse Loads after quoting the value, with a warning
description is missing Skipped and logged
name contains /, \, or control characters, or is . or .. Skipped and logged
The frontmatter is missing or still not YAML Skipped and logged

Skipped skills are not shown to the model. Frontmatter keys that the specification does not define are kept in skill.extraFields. A skill with disable-model-invocation: true is not shown to the model.

To check a skill before you write or publish it, parse it with parseSkill() from agents/skills. A skill follows the specification when it parses with no warnings:

import { parseSkill } from "agents/skills";

const parsed = parseSkill(markdown, { directory: "release-notes" });
if (!parsed.ok) {
	console.log(parsed.error.message); // the skill cannot be used
} else {
	for (const warning of parsed.warnings) {
		console.log(warning.code, warning.message); // for example, "name-mismatch"
	}
}
import { parseSkill } from "agents/skills";

const parsed = parseSkill(markdown, { directory: "release-notes" });
if (!parsed.ok) {
	console.log(parsed.error.message); // the skill cannot be used
} else {
	for (const warning of parsed.warnings) {
		console.log(warning.code, warning.message); // for example, "name-mismatch"
	}
}

Use skills without Think

The parser, sources, and catalog live in agents/skills and do not depend on Think. loadSkillCatalog() merges sources. createSkillTools() from agents/skills/tools/ai-sdk builds the AI SDK ↗︎ tools for a catalog:

import { streamText } from "ai";
import { loadSkillCatalog, workspace } from "agents/skills";
import { createSkillTools } from "agents/skills/tools/ai-sdk";

// `computerWorkspace` is a Workspace from @cloudflare/computer.
const catalog = await loadSkillCatalog([workspace(computerWorkspace)]);

const result = streamText({
	model,
	system: catalog.prompt() ?? undefined,
	tools: createSkillTools(catalog),
	messages,
});
import { streamText } from "ai";
import { loadSkillCatalog, workspace } from "agents/skills";
import { createSkillTools } from "agents/skills/tools/ai-sdk";

// `computerWorkspace` is a Workspace from @cloudflare/computer.
const catalog = await loadSkillCatalog([workspace(computerWorkspace)]);

const result = streamText({
	model,
	system: catalog.prompt() ?? undefined,
	tools: createSkillTools(catalog),
	messages,
});

Pages in this section

Was this helpful?