Skills
Extend agents with portable markdown instructions and optional tool code. Compatible with Anthropic Agent Skills.
A skill is a portable unit of instruction that extends an agent's knowledge or capabilities. Appstrate skills implement Anthropic's Agent Skills format: a folder containing a SKILL.md file with YAML frontmatter.
A skill authored for Claude (Claude Code, the Messages API, the Agent SDK) runs in Appstrate without modification. Appstrate adds one layer on top: AFPS packaging (ZIP + semver + registry + SRI integrity) so skills can be versioned, shared across tenants, and pinned by agents.
Via the UI
Open the dashboard → Skills in the sidebar (/skills). From there you can:
- Browse the list of system and org-owned skills, search, and open a detail page
- Create a new skill (
/skills/new) — opens the package editor with aSKILL.mdtemplate - Import an existing
.afpsZIP via the import modal (drag-and-drop or file picker), including GitHub repo imports - Open a detail page (
/skills/:scope/:name) — source viewer, version history, diff against latest published version, and "Used by" tab listing agents that depend on this skill - Edit the draft (
/skills/:scope/:name/edit), then publish a new version via the Create Version modal (forward-only semver, SHA-256 integrity computed server-side) - Fork a system skill to create a local, mutable copy
- Install / uninstall a skill on the current application with a single toggle
- Yank a published version (marks it as withdrawn without deleting history)
Package IDs are scoped names: @appstrate/my-skill. There is no sk_ prefix. System skills live under @appstrate/… and are read-only (fork them to customize).
Pure SKILL.md skills
The minimal skill is a single file with YAML frontmatter and a Markdown body.
---
name: word-count
description: Counts the number of words in a given text. Use this skill when the user asks to count words or analyze text length.
---
# Word Count
Call this skill when you need to count words in a text input. Returns the count as a formatted string.
## Usage
Pass the text to analyze as a parameter. The skill returns a one-line summary.Frontmatter fields (only these two are parsed):
| Field | Required | Description |
|---|---|---|
name | Yes | Machine identifier. Lowercase, alphanumeric, hyphens |
description | Yes | Used by the model to decide when to invoke the skill |
The Markdown body is injected into the agent container at .pi/skills/{id}/SKILL.md at run time. The model reads it alongside its system prompt.
Skills with executable tools
A skill can also ship executable code that registers callable tools. This is the Pi Coding Agent SDK extension format (Appstrate-specific, not part of the Anthropic Skills standard). The entrypoint is a TypeScript file whose name is declared in the AFPS manifest's entrypoint field (commonly skill.ts for skills, {tool-name}.ts for tools).
// skill.ts
import { Type } from "@mariozechner/pi-ai";
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
export default function (pi: ExtensionAPI) {
pi.registerTool({
name: "word_count",
label: "Word Count",
description: "Count the number of words in a given text.",
parameters: Type.Object({
text: Type.String({ description: "The text to count words in" }),
}),
async execute(_toolCallId, params) {
const { text } = params as { text: string };
const count = text.trim().split(/\s+/).filter(Boolean).length;
return {
content: [{ type: "text", text: `The text contains ${count} words.` }],
details: { count },
};
},
});
}Gotchas:
- Import
Typefrom@mariozechner/pi-aiandExtensionAPIfrom@mariozechner/pi-coding-agent. Both are pinned to^0.67.2in the current runtime. - The default export is a function that receives the
piAPI and callspi.registerTool(...)imperatively — not an object literal. parametersuses TypeBox (Type.Object,Type.String,Type.Union, …). JSON Schema is generated from the TypeBox schema.executesignature is(_toolCallId, params). Return{ content: [{ type: "text", text: "..." }], details?: {...} }.
A skill without executable code is 100% Anthropic-compatible. A skill that ships an extension requires an agent runtime that supports the Pi Coding Agent SDK.
Packaging (AFPS)
To publish a skill into Appstrate, package the skill folder as an AFPS ZIP. The AFPS spec adds a manifest.json at the root of the ZIP that declares the package type and version:
my-skill.afps (ZIP)
├── manifest.json # type, name, version, description, integrity
├── SKILL.md # YAML frontmatter + markdown body
└── skill.ts # optional extension codemanifest.json minimum:
{
"$schema": "https://afps.appstrate.dev/schema/v1/skill.schema.json",
"type": "skill",
"name": "@my-org/word-count",
"version": "1.0.0",
"description": "Counts the number of words in a given text."
}name is a scoped identifier (@scope/name). Optional manifest fields include displayName, keywords, license, repository, and dependencies (for skills that depend on other skills/tools/providers). See the AFPS spec for the full schema.
Publishing
Publish a skill through the REST API. There is no dedicated CLI command today (see CLI Reference).
# First upload (creates the package)
curl -X POST http://localhost:3000/api/packages/skills \
-H "Authorization: Bearer ask_your_key" \
-H "X-App-Id: app_xxx" \
-F "[email protected]"
# New version of an existing package (scoped id in the path)
curl -X POST http://localhost:3000/api/packages/skills/@my-org/word-count/versions \
-H "Authorization: Bearer ask_your_key" \
-H "X-App-Id: app_xxx" \
-F "[email protected]"Versions follow semver and are forward-only: you cannot publish a version lower than or equal to an existing one. Each upload is integrity-checked with an SHA-256 SRI hash (computeIntegrity) and served back on download via the X-Integrity header.
Using a skill in an agent
Skill attachment is manifest-only. An agent declares skill dependencies with semver ranges in its manifest.json:
{
"type": "agent",
"name": "@my-org/email-triage",
"dependencies": {
"skills": {
"@appstrate/word-count": "^1.0.0"
}
}
}At run time, Appstrate resolves each semver range, downloads the pinned version, and injects the SKILL.md into the agent container at .pi/skills/{id}/SKILL.md. If the skill ships an extension, its tools are registered with the agent's tool list automatically.
There is no dedicated PUT /api/agents/.../skills endpoint — to change an agent's skill dependencies, update its manifest and publish a new version.
System skills
Appstrate ships system skills in system-packages/ (loaded at boot with orgId: null). They are available to every organization without installation. Fork a system skill to create a local, mutable copy:
curl -X POST http://localhost:3000/api/packages/@appstrate/word-count/fork \
-H "Authorization: Bearer ask_your_key" \
-H "X-App-Id: app_xxx" \
-H "Content-Type: application/json" \
-d '{ "name": "@my-org/word-count-custom" }'Portability guarantee
- A pure
SKILL.mdskill moves freely between Claude Code, the Claude Agent SDK, Appstrate, and any other Agent Skills-compliant host. - Only the AFPS wrapper is Appstrate-specific. Inside the ZIP, the skill folder is standard.
- AFPS is published under CC-BY at github.com/appstrate/afps-spec.
Reference
- Example:
examples/custom-skill/in the Appstrate monorepo. - Anthropic Agent Skills docs: docs.anthropic.com/en/docs/build-with-claude/agent-skills.
- AFPS spec: github.com/appstrate/afps-spec.