Skip to main content
This walks through one full, real skill package — jaas.demo.personal-notes, shipped as an example in the jaas-skills repo at examples/skills/personal-notes/. It’s small on purpose: a scratchpad skill that jots a note under a topic and can list what’s already there.
Only manifest.yaml is strictly required on disk. schema.json, permissions.yaml, and dependencies.yaml each have an unambiguous empty default and are filled in automatically when absent — see Package layout. This example includes all five anyway, since a real skill almost always wants a real schema and a documented permission list.

The file tree

manifest.yaml

The identity card — id, version, owner, entrypoint, and the runtime this skill targets.
manifest.yaml
entrypoint: SKILL.md is the important line for this section — the manifest can name SKILL.md, prompt.md, executor.py, executor.js, or module.wasm as the entrypoint; whichever it names is packaged alongside the four documents below, but its contents are never validated as structured data, since the format is entirely runtime-family-dependent. A prompt runtime family (like this one) almost always uses SKILL.md.

SKILL.md

The entrypoint itself — the instructions an agent actually runs. Front-matter name/description are for documentation only; the manifest’s id/name are the authoritative identity, so the two are allowed to drift (though keeping them aligned avoids confusion).
SKILL.md

schema.json

The input/output contract, as two JSON Schema documents.
schema.json
The minimal valid shape, if a skill has no meaningful input/output contract to declare, is { "inputs": { "type": "object", "properties": {} }, "outputs": { "type": "object", "properties": {} } } — this is also exactly what the registry fills in automatically when the file is absent.

permissions.yaml

A flat list of scopes this skill asks for. No fixed enum — free-form strings, scanned by the PERMISSIONS/SUPPLY_CHAIN guardrail categories, not validated against a schema (a typo like fs:writte is silently accepted as its own custom scope, not rejected).
permissions.yaml

dependencies.yaml

Empty here — this skill depends on nothing else.
dependencies.yaml
Non-empty looks like this, and every entry must resolve to an already-published skill at publish time:

Validating before you publish

This runs the same structural checks (§manifest fields, ID & version policy, dependency policy) that a publish would, without a guardrails scan and without touching the registry — the fast local loop while you’re iterating.

Checklist

  • id follows vendor.domain.capability, globally unique
  • version is strict SemVer
  • entrypoint names a file that actually exists in the package
  • schema.json has both inputs and outputs as top-level keys (if present at all)
  • Every dependencies.yaml entry resolves and has no cycle
  • No hardcoded secrets, no .env/config.secrets.json-style filenames
  • Archive under 50MB
Next: publish this exact skill through the web UI, screenshots included.