Skip to content
Beta. VectorStep is in beta.Let us know if something breaks.

Writing good skills

Nothing here is enforced by the schema — a skill can be as vague, overstuffed, or mis-scoped as you write it to be. A badly written skill doesn’t error; it just sits there costing a line of system prompt and never getting loaded, or getting loaded for the wrong conversations. For the mechanical reference — SKILL.md format, progressive disclosure, granting, script execution — see Skills. This guide is the judgment calls the reference page doesn’t make for you.

A skill titled “operations” that covers incident response, deployment rollbacks, and on-call handoff is three skills wearing a trenchcoat — its description has to somehow signal all three situations at once, which means it either routes correctly for none of them or the agent loads it “just in case” every time any of the three come up, defeating the point of loading it selectively at all. Split it: incident-triage, deployment-rollback, oncall-handoff — three skills an agent can be granted independently, each with a description that names exactly the one situation it covers.

Write the description like a router, not a summary

Section titled “Write the description like a router, not a summary”

description is the only thing a skill costs when it isn’t in use, and it’s the only signal the agent has to decide whether to load the rest at all — see Skills — Writing a good description for the mechanical version of this. The judgment call underneath it: write for when, not what.

  • Routes correctly: “Use when the user is reporting a production incident and needs help classifying severity, identifying the right escalation path, or drafting an initial status update.”
  • Doesn’t route at all: “Incident response skill.” — true, but gives the agent nothing to match a real conversation against. It either never fires or fires on a hunch.

A useful test: could a colleague who’s never seen the skill’s body correctly guess, from the description alone, whether this specific message should trigger it? If the answer is “not really,” the description is a title, not a router.

Put the routine in the body, the exceptions in bundled files

Section titled “Put the routine in the body, the exceptions in bundled files”

SKILL.md’s body (Level 2) is what the agent reads once it’s decided the skill is relevant — put the workflow it should follow every time here: numbered steps, what to gather, what to output. A bundled file (Level 3) is for the part that’s long, changes independently of the workflow, or isn’t needed on every single invocation — a full escalation matrix, a style guide with a dozen worked examples, a reference schema. The body should tell the agent when to go read the bundled file, not duplicate its contents; if you’re pasting the reference file’s content into the body “just in case,” the split isn’t earning its keep.

A skill with no bundled files at all is completely normal — most are. Reach for one only when the body would otherwise be uncomfortably long or contain material that’s genuinely reference-shaped rather than instructional.

Covered in full in Writing good agents — Skill or system prompt? — the short version: soul.md is for what the agent always is, a skill is for what it sometimes needs. If you’re writing a new skill and finding yourself copying half of it into every agent’s soul.md anyway “to be safe,” that’s a sign either the description isn’t routing correctly (fix the description first) or the content genuinely isn’t situational and belongs in soul.md after all.

A skill can tell an agent to use tools it’s already been granted — “use the filesystem tool to read X” — but granting the skill does not grant the tool. tools: and skills: are independent lists in agent.yaml; an agent without grafana in its tools: still can’t call Grafana no matter what a loaded skill’s body says to do. Write a skill’s instructions assuming the tools it references are already granted, and say so explicitly in the skill’s own text (“requires the filesystem and fetch tools”) so an author granting the skill to a new agent knows what else it needs.

Bundled scripts are the exception, not the default

Section titled “Bundled scripts are the exception, not the default”

A skill can bundle a script under scripts/ and reach for it via run_skill_script — but unlike everything else in this guide, that capability is off by default and gated by an operator, not by how you write the skill (see Skills — Running a skill’s bundled scripts). Write a skill’s instructions to work whether or not script execution ends up enabled in a given deployment — lean on prose reasoning for anything a model can reliably do itself, and reach for a bundled script only for something that genuinely needs to run: a deterministic calculation, a lookup against a local dataset, something you would not trust an LLM to get right by reasoning alone every time. If the skill’s core value falls apart the moment allow_script_execution is off, that’s worth flagging to whoever’s granting it, not discovering at run time.

validate_skill (REST POST /skills/validate, or the Gateway MCP tool of the same name) is a dry-run — frontmatter parses, name matches, and description is non-empty, all checked with nothing written. Cheap to run, and it catches the two mistakes that are otherwise easy to ship silently: a name: that doesn’t match the directory (the skill just won’t load, no error surfaced to the agent granting it) and an empty description (same failure mode). Run it before create_skill/update_skill, the same way validate_agent is the recommended step before writing an agent.

Treat editing a skill exactly like editing the soul.md of every agent that grants it — because that’s what actually happens. A skill’s own content (its SKILL.md body plus every bundled file and script) is folded into the version of every agent currently granting it, the same content-hash field VectorStep already uses to scope calibration buckets. Editing SKILL.md, adding or changing a bundled reference file, or swapping a bundled script all start a new calibration history — for every granting agent, even one that rarely loads the skill at runtime. Adding or removing which skills an agent is granted works the same way, as does tools: (see Creating agents).

That means a small wording tweak to a widely-granted skill isn’t free the way it might look — if an agent had a validated, enforced calibration gate (calibration: {enforce: true}) on a step, editing a skill it’s granted resets that gate back to its uncalibrated fallback behaviour until enough new labelled runs land. Prefer to batch your skill edits the same way you’d batch agent config changes, rather than making several small edits to a high-traffic skill in quick succession. An agent with no skill grants at all is never affected by any skill edit, on this or any other skill.

VectorStep’s Skill Library → Versions tab shows this history with diffs, and an agent’s own Versions tab labels a reset “changed by skill edit: <name>” when that’s specifically what caused it — see How confidence and calibration work for what a reset actually does to a step’s gate.

A skill’s description is the only thing that costs context when it isn’t in use, and it’s the only signal the agent has to decide whether to load the full body at all — see Write the description like a router, not a summary above. Whether that description is actually working is a question with real data behind it, not a guess: Insights — Skills (or a skill’s own detail page → Usage tab) shows load rate — of the steps whose agent was granted this skill, what fraction actually called load_skill — for every skill, over a selectable time range.

A “Granted but never loaded” panel on that page calls out any skill with real grants and zero loads in range — the clearest possible signal that a description isn’t triggering the model the way you intended. If you see a skill there, the fix is almost always to make the description read more like the “Good” example above: concrete triggering conditions, not a one-line summary of what’s inside. Iterate, re-grant if needed, and watch the load rate move; the version history on the skill’s detail page lets you correlate a description edit with the load rate before and after it, per version.

  • Skills — the full SKILL.md reference, progressive disclosure model, and script execution.
  • Writing good agents — the same treatment for agent.yaml/soul.md, including the skill-vs-system-prompt judgment call this guide builds on.
  • Tutorial: give your agent a skill — write one from scratch and watch an agent load it mid-run.