⚙️ Developer (pro) · Guide 1 of 2
Writing One Agent Skill That Works in Several Apps
9 min read · Last reviewed 23 Sep 2026

You wrote a skill, it works beautifully in one app, and then a colleague drops the same folder into a different agent and nothing happens. The file format is genuinely shared — SKILL.md with YAML frontmatter is an open specification now — but the conventions wrapped around it are not. Where the folder must live, which frontmatter fields the host reads, how the description is turned into a trigger, whether bundled scripts are allowed to run: each of those is a per-host decision, and portability is mostly a matter of not depending on any of them.
This guide is about the parts the spec does not settle. For the format itself, read the Agent Skills specification — we deliberately do not repeat it here.
Agent Skills Specification 🧩 SkillOpen source
The open SKILL.md standard: specification, docs and a reference validator
What is actually shared
Across every host examined below, three things are stable. A skill is a directory. It contains a SKILL.md with YAML frontmatter and a Markdown body. name and description are required, and everything else is optional.
The specification defines a small set of optional fields on top of that: license, compatibility (max 500 characters, for environment requirements), metadata (a string-to-string map hosts can use for their own properties), and allowed-tools, which the spec itself marks as experimental with the note that "support for this field may vary between agent implementations". The hard limits are name at 64 characters, lowercase letters, digits and single hyphens, matching the parent directory name; and description at 1,024 characters.
That is the portable surface. Write to it and your skill is loadable everywhere. Write to anything beyond it and you have picked a host.
Where the folder lives
This is where most portability failures actually happen — the skill is fine, the agent simply never looked in that directory.
| App | Project paths | Personal paths |
|---|---|---|
| Claude Code | .claude/skills/, <subdir>/.claude/skills/, plugin skills/ | ~/.claude/skills/ |
| Cursor | .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/ | ~/.cursor/skills/, ~/.agents/skills/, ~/.claude/skills/, ~/.codex/skills/ |
| OpenAI Codex | .agents/skills in the working directory, its parents and the repo root | $HOME/.agents/skills, plus /etc/codex/skills |
| GitHub Copilot | .github/skills, .claude/skills, .agents/skills | ~/.copilot/skills, ~/.agents/skills |
| OpenCode | .opencode/skills/, .claude/skills/, .agents/skills/ | ~/.config/opencode/skills/, ~/.claude/skills/, ~/.agents/skills/ |
Read that table twice, because the useful pattern is hiding in it. .agents/skills/ is read by Cursor, Codex, Copilot and OpenCode. .claude/skills/ is read by Cursor, Copilot and OpenCode as well as Claude Code. Claude Code's own documentation lists only its .claude/skills and plugin paths — it does not document reading .agents/skills, so do not assume it does.
The practical consequence: if you publish one directory, publish .agents/skills/<name>/, and tell Claude Code users to symlink or copy it into .claude/skills/. Codex documents that it follows symlinked skill folders, which makes a single source of truth on disk realistic. Tools like the ones in /skills exist mostly to automate exactly this fan-out.
Skill Flow 🧩 SkillOpen source
Install, manage and share Agent Skills across every major coding agent from one place
Frontmatter: what each host reads, and what it ignores
The two required fields work everywhere. Beyond that, hosts diverge sharply, and the divergence is mostly additive — each app has invented fields the others have never heard of.
Claude Code reads by far the largest set: when_to_use, disable-model-invocation, user-invocable, allowed-tools and disallowed-tools, argument-hint, arguments, model, effort, context: fork with agent and background, hooks, paths, shell, plus the spec's metadata, license and compatibility. Cursor documents paths, disable-model-invocation, icon, color and metadata. Copilot documents license and allowed-tools. OpenCode documents license, compatibility and metadata, and states plainly that unknown frontmatter fields are ignored. Codex keeps its extras out of the frontmatter altogether, in an optional agents/openai.yaml sidecar carrying policy.allow_implicit_invocation and dependencies.tools.
Two things follow. First, extra fields are usually harmless: disable-model-invocation in a skill loaded by Copilot is just noise. Claude Code goes further and states that if the YAML fails to parse at all, the skill still loads with no fields set — which is worse than it sounds, because a skill that loads with no description will never trigger. Validate your YAML; skills-ref validate ./my-skill from the specification's reference library does it.
Second, do not encode behaviour you actually depend on in a host-specific field. If your skill is only safe when disable-model-invocation is set, it is not safe in Codex or Copilot, and you should say so in compatibility rather than hope.
Descriptions: triggering without hijacking
The description is the only part of your skill that is always in context. Every host pre-loads name and description for all installed skills and asks the model to choose from them. That makes the description a piece of prompt engineering with two failure modes, and they pull in opposite directions.
Under-triggering is the obvious one: "Helps with documents" matches nothing. The fix is well documented — state what the skill does and when to use it, in third person, with the concrete nouns a user would actually type. Anthropic's authoring guide has the canonical good and bad examples and the rule that the description must be third person because it is injected into the system prompt.
Over-triggering is the one that makes people uninstall your skill. A description that says "use for any code-related task" will fire on unrelated requests, burn context, and bias the model toward your workflow when the user wanted something else. Scope boundaries belong in the description itself: name the file types, the commands, the domain. If your skill only handles Terraform, say Terraform, not "infrastructure".
There is a portability wrinkle here that is easy to miss. Claude Code truncates the combined description and when_to_use at 1,536 characters in its skill listing to save context; the spec caps description at 1,024. Codex's guidance says to "front-load the key use case and trigger words so a host can still match the skill if descriptions are shortened". Assume the first sentence is the only part guaranteed to survive, and put the trigger words there.
Progressive disclosure is a portability feature
Progressive disclosure — a short SKILL.md that points at reference files loaded only when needed — is usually explained as a context-budget optimisation. It is that: the specification's shape is roughly 100 tokens of metadata always resident, an instruction body it recommends keeping under 5,000 tokens, and resources read on demand. Claude Code and the spec both recommend keeping SKILL.md under 500 lines.
But it is also what makes one skill serve several hosts. A thin SKILL.md that says "for the API reference see references/api.md, for the migration rules see references/migrations.md" contains almost nothing host-specific, because navigation instructions are host-agnostic in a way that procedure text is not. The bulky, opinionated material sits in files the agent reads with an ordinary file read — an operation every one of these apps can perform.
Keep references one level deep from SKILL.md. Agents preview long files with partial reads, and a reference that points at another reference tends to get skimmed rather than read. If a reference file runs past about 100 lines, give it a table of contents so a partial read still shows the full scope.
Bundled scripts are the least portable thing you can ship
The spec blesses scripts/, and every host here can in principle run them. What differs is the permission story, and that difference is not cosmetic.
Copilot's documentation is the most explicit: allowed-tools can pre-approve the shell or bash tool, with the warning to only do that if you have reviewed the skill and every referenced script and fully trust the source. Omit it and Copilot asks for confirmation before running terminal commands. Claude Code treats allowed-tools as tools it may use without asking permission during the turn that invokes the skill, and adds disallowed-tools to remove tools from the pool. OpenCode applies a permission decision to the skill itself — allow, deny or ask before the skill is even loaded. Cursor documents the scripts/ convention but does not document a skill-level tool permission field at all; if you need that behaviour there, verify it yourself rather than assuming.
So: a skill whose main path requires executing a bundled script will work cleanly in some apps, prompt in others, and be silently blocked in a locked-down configuration. Design for that. State dependencies explicitly in compatibility (the field exists for exactly this: "Requires git, docker, jq, and access to the internet"). Prefer a script for fragile, deterministic, must-not-vary operations, and prose for everything where the agent's judgement is fine — the degrees-of-freedom argument in the vendor guidance is the right frame. And give the skill a documented path that works when scripts are refused, even if it is slower.
If you are publishing to others, have the bundle scanned before you tag a release. Skills are executable content from a stranger, and reviewers treat them that way.
SkillSpector 🧩 SkillOpen source
NVIDIA's scanner for Agent Skills: finds prompt injection, exfiltration and supply-chain risks
Testing in more than one app
One install is not a test. A minimum honest matrix is: two hosts, two scenarios each.
Start with a trigger test. In a fresh session, issue three prompts that should fire the skill and three that should not, and check the result in each app. Cursor and Codex both let you invoke a skill by name (/ in Cursor, $ in Codex CLI), which is useful for separating two different failures: if manual invocation works and automatic does not, your description is the problem, not your instructions.
Then a body test: does the agent follow the procedure, find the reference files, and run or skip scripts the way you intended? Watch which files it actually opens. A bundled file the agent never reads in any host is either badly signposted or unnecessary.
Test with more than one model, too. The same skill that a large model finds over-explained can leave a small fast model without enough guidance, and across these apps you have no control over which model the user brings.
The compatibility matrix your README owes people
Users cannot infer any of this from a repository. State it. A short table is enough:
- Tested in — the apps and, ideally, model tiers you actually ran it against.
- Install path per app — the concrete directory, not "your skills folder".
- Requires scripts? — yes/no, and what happens when execution is refused.
- Host-specific fields used — and what is lost without them.
- External dependencies — binaries, network access, credentials.
Do not claim support you have not tested. "Should work anywhere that reads SKILL.md" is the sentence that generates issues. Two honest rows beat five optimistic ones, and it is the single thing that makes a directory listing on /skills or a page like /ecosystem useful rather than decorative.
What to do next
- Move everything host-specific out of
SKILL.mdand intocompatibility,metadataor your README. - Rewrite the description in third person, front-loaded with trigger words, with explicit scope boundaries.
- Split the body until
SKILL.mdis a navigation page, with references one level deep. - Publish at
.agents/skills/<name>/and document the symlink for Claude Code users. - Run the six-prompt trigger test in two apps and write the results into the README as a matrix.
If you are also shipping an MCP server alongside the skill, the same judgement problems recur in a different shape — see Building an MCP Server: The Decisions the Spec Leaves to You. For the approval and review process around distributing any of this inside a company, /guides/business covers the governance side, and /guides/solo-builder covers the case where the only reviewer is you.
Read the official docs for…
- The format itself — field limits, naming rules, directory conventions and the
skills-refvalidator: agentskills.io/specification. - Authoring quality — descriptions, degrees of freedom, evaluation-driven iteration and scripts: Skill authoring best practices and Claude Code skills.
- Cursor's loader — which directories it scans and which frontmatter fields it reads: cursor.com/docs/context/skills.
- Codex discovery and the
openai.yamlsidecar — precedence order and invocation policy: learn.chatgpt.com/docs/build-skills. - Copilot surfaces and permissions — which Copilot products load skills, and the
allowed-toolswarning: About agent skills. - OpenCode's permission model — allow/deny/ask on skill loading: opencode.ai/docs/skills.
Mentioned in this guide
SkillSpector 🧩 SkillOpen source
NVIDIA's scanner for Agent Skills: finds prompt injection, exfiltration and supply-chain risks
Agent Skills Specification 🧩 SkillOpen source
The open SKILL.md standard: specification, docs and a reference validator
Skill Flow 🧩 SkillOpen source
Install, manage and share Agent Skills across every major coding agent from one place