What Are Agent Skills and How Do You Publish Them in 2026?

A skill is a folder with a SKILL.md file that tells an agent how to do one specific job with your product. This page covers writing one for your own API, serving it from your own domain, and generating the discovery index that crawlers and agent tooling look for.

CategoryAgent & AI protocols StatusEmerging Maintained byAnthropic, now an open standard Glippy checkAgent Interactivity (category 10)

Agent Skills (SKILL.md) is an open format for packaging instructions, scripts and reference files into a folder that an AI agent loads on demand. Every skill is a directory containing a SKILL.md file with YAML frontmatter and Markdown instructions, optionally alongside scripts/, references/ and assets/. For a product team it is the mechanism for handing an agent a written procedure for using your API, your CLI or your checkout flow, instead of hoping the model guessed the details right.

Why Agent Skills matter for AI visibility

MCP gives an agent tools it can call. A skill gives it the procedure: which call comes first, what the amounts are denominated in, which endpoint is irreversible, and when to stop and ask a human. Agents load skills through progressive disclosure, in three stages. At startup only the name and description of each skill are in context, roughly 100 tokens per skill. When a task matches the description, the full SKILL.md body is read in. Bundled files under references/ and scripts/ load only when the instructions point at them. That makes the description the surface that decides whether your skill is ever activated, and makes body length a real cost you pay on every activation.

The format is settled and widely adopted: the same folder works in Claude Code, Codex, Gemini CLI, Cursor, GitHub Copilot, VS Code and a long list of other clients. Distribution is not settled. Cloudflare published an Agent Skills Discovery RFC that puts an index at /.well-known/agent-skills/index.json so a domain can advertise its own skills without prior configuration, and Cloudflare's Agent Readiness score checks that path, as does Glippy. But the RFC is still a draft at version 0.2.0, the proposal to fold it into the Agent Skills specification proper is an open issue, and no mainstream agent client fetches the path by default yet. Publishing the index costs a few hundred bytes, so it is worth doing now, but treat it as positioning for the next wave of agent crawlers rather than as a live traffic channel.

Where the spec lives

The format and the web discovery convention are maintained in two different places, and only the first of the two is stable.

  • Agent Skills specification - the primary source for the SKILL.md format: every frontmatter field with its exact constraints, the optional directory layout, and the progressive disclosure model. Read this before you write a line.
  • Agent Skills Discovery RFC (Cloudflare) - draft 0.2.0, published January 2026 and updated in March. Defines /.well-known/agent-skills/index.json, the digest format, the HTTP requirements, and the security rules clients are told to apply. The examples/ directory has working index generators for Next.js, Astro, TanStack and CGI.
  • agentskills/agentskills - the specification repository and the place to track discovery. Also ships skills-ref, a Python reference library whose validate command checks your frontmatter before you deploy.
  • Anthropic Agent Skills overview - the vendor documentation from the team that originated the format, including guidance on writing descriptions that get matched.
  • Claude Code skills documentation - exact install locations, precedence rules between personal, project and plugin skills, and the plugin route for shipping a skill to other people's machines.
  • Cloudflare Agent Readiness score - April 2026. Useful for checking which agent-facing well-known paths another crawler grades you on, Agent Skills included.

Three ways to implement Agent Skills

Start by writing the skill itself, which is the part every route has in common. Then pick how you serve it: a hand-written index next to a static SKILL.md is enough for a documentation site or a small product, while a generated route handler is what you want once more than one team can add a skill and you cannot trust a human to recompute digests. The first example writes the skill, the second publishes it as static files, the third generates the index from your application at build time.

01

Write the SKILL.md that documents your product

This is the artefact. Only name and description are required, but a product skill is worth the optional fields: compatibility states what the agent needs to have, and metadata is where you pin a version. The body is plain Markdown with no format restrictions, so write the procedure the way you would write it for a new engineer.

markdownpublic/.well-known/agent-skills/acme-invoices/SKILL.md
---
name: acme-invoices
description: Create, send and void invoices with the Acme Billing API. Use when a task mentions Acme invoices, billing, dunning, credit notes or the /v1/invoices endpoint.
license: Apache-2.0
compatibility: Requires network access to api.acme.example and an ACME_API_KEY environment variable.
metadata:
  author: acme
  version: "1.4.0"
---

# Acme invoices

## Before you start

Read ACME_API_KEY from the environment and never print it. Every request goes to
https://api.acme.example/v1 with an `Authorization: Bearer` header.

## Create and send an invoice

1. Find the customer with `GET /v1/customers?email=...`. If more than one row
   comes back, stop and ask the user which one to use.
2. Create the draft with `POST /v1/invoices`, passing `customer_id`, `currency`
   and a `line_items` array. The response is a draft. Nothing has been emailed.
3. Send it with `POST /v1/invoices/{id}/send`. This is the only irreversible
   step, so confirm with the user before calling it.

## Rules that are easy to get wrong

- Amounts are integer minor units. 1250 means 12.50 EUR, never 1250.00.
- Never void a paid invoice. Issue `POST /v1/credit-notes` instead.
- The rate limit is 100 requests per minute. On a 429, wait for `Retry-After`.

See `references/errors.md` for the full error-code table.

What this does: the description is the only thing an agent sees until it decides to activate the skill, so it names the product, the domain words and the endpoint path a user is likely to mention. Everything after the frontmatter loads only once that match happens, which is why the error table sits in references/ rather than inline.

02

Publish the static discovery index

The minimum viable publishing route: drop the skill folder under your public directory and write the index by hand. Each entry is one artefact. Use skill-md for a lone file and archive for a skill that ships scripts, references or assets as a .tar.gz or .zip. The url can be path-absolute, fully qualified or relative, so a CDN-hosted archive is fine.

jsonpublic/.well-known/agent-skills/index.json
{
  "$schema": "https://schemas.agentskills.io/discovery/0.2.0/schema.json",
  "skills": [
    {
      "name": "acme-invoices",
      "type": "skill-md",
      "description": "Create, send and void invoices with the Acme Billing API. Use when a task mentions Acme invoices, billing, dunning, credit notes or the /v1/invoices endpoint.",
      "url": "/.well-known/agent-skills/acme-invoices/SKILL.md",
      "digest": "sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"
    },
    {
      "name": "acme-webhooks",
      "type": "archive",
      "description": "Verify signatures on and replay Acme webhook deliveries. Use when debugging missed, duplicated or rejected Acme webhook events.",
      "url": "https://cdn.acme.example/skills/acme-webhooks-1.4.0.tar.gz",
      "digest": "sha256:2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae"
    }
  ]
}

What this does: the $schema value is a version identifier, not a document you should expect to fetch at runtime, and clients are told to match it against schemas they know rather than dereference it. The digest is a SHA-256 over the exact bytes you serve, formatted sha256: plus 64 lowercase hex characters, and a verifying client rejects the artefact if it does not match.

03

Generate the index from your app at build time

Hand-written digests rot the first time somebody fixes a typo in a SKILL.md. This route handler, adapted from the Next.js example in the discovery RFC repository, walks the skills directory, reads the frontmatter with gray-matter, hashes the file it will actually serve and emits the index. Equivalent examples for Astro, TanStack and plain CGI live in the same repository.

typescriptapp/.well-known/agent-skills/index.json/route.ts
import { createHash } from "crypto";
import { readdir, readFile } from "fs/promises";
import { join } from "path";
import matter from "gray-matter";

const SCHEMA_URI = "https://schemas.agentskills.io/discovery/0.2.0/schema.json";
const SKILLS_DIR = join(process.cwd(), "public/.well-known/agent-skills");

// Pre-render at build time so the index ships as a plain static asset.
export const dynamic = "force-static";

export async function GET() {
  let entries;
  try {
    entries = await readdir(SKILLS_DIR, { withFileTypes: true });
  } catch {
    return Response.json({ $schema: SCHEMA_URI, skills: [] });
  }

  const skills = [];
  for (const dir of entries.filter((e) => e.isDirectory())) {
    let content;
    try {
      content = await readFile(join(SKILLS_DIR, dir.name, "SKILL.md"));
    } catch {
      continue; // no SKILL.md in this folder, not a skill
    }
    const { data } = matter(content.toString("utf-8"));
    if (!data.name || !data.description) continue; // invalid frontmatter
    skills.push({
      name: data.name,
      type: "skill-md",
      description: data.description,
      url: `/.well-known/agent-skills/${dir.name}/SKILL.md`,
      digest: `sha256:${createHash("sha256").update(content).digest("hex")}`,
    });
  }

  skills.sort((a, b) => a.name.localeCompare(b.name));
  return Response.json({ $schema: SCHEMA_URI, skills });
}

What this does: the digest is computed from the same bytes the file server will hand out, so an edit to a SKILL.md can never leave a stale hash in the index. Skills whose frontmatter is missing name or description are skipped rather than published broken, and the sorted output keeps the deployed file stable across builds.

Implementation guidelines

These are the things that break a published skill in production, roughly in the order you will hit them.

  1. Write the description for retrieval, not for a brochure. It is capped at 1024 characters and it is the only text an agent sees before deciding to activate. Say what the skill does and when to use it, and include the product name, the domain vocabulary and the endpoint or command names a user would actually type.
  2. Make the folder name and the frontmatter name identical. name must be 1 to 64 characters of lowercase letters, digits and single hyphens, must not start or end with a hyphen, must not contain a double hyphen, and must match the parent directory. Get this wrong and the skill silently fails to load.
  3. Regenerate digests in the same step that publishes the bytes. A minifier, a line-ending conversion or a CDN transform between your build and your origin will change the hash and make a verifying client reject the artefact. Compute the digest from the file you serve, never from the file in your editor.
  4. Keep SKILL.md short and push the detail into references/. The whole body enters context on activation, so aim for under 500 lines and around 5000 tokens. Error tables, schemas and long worked examples belong in separate files one level down that the instructions point at by relative path.
  5. Serve the content types the RFC asks for. application/json for the index, text/markdown or text/plain for SKILL.md, application/gzip or application/zip for archives. Support GET and HEAD, return a real 404 for a missing skill, set Cache-Control, and add CORS headers if you want browser-based clients to read it.
  6. Version through URLs and metadata, not by mutating in place. Serve archives at a versioned path and pin the version in the frontmatter metadata map. A cached client then keeps a digest that still matches, and you swap the index in one deploy rather than leaving a window where hash and file disagree.
  7. Assume nothing under scripts/ will run. Clients are told not to execute bundled scripts by default and to allowlist the origins they load skills from. Write instructions that work through your public API on their own, with any bundled script as a shortcut rather than the only path.

Do this, not that

Do

  • Set $schema in index.json so a client can tell which draft you wrote against, and bump it deliberately when the draft moves.
  • Document the failure modes in the body: rate limits, idempotency keys, which single call is irreversible, and when to stop and ask.
  • Run skills-ref validate ./acme-invoices in CI so a frontmatter mistake fails the build instead of shipping an unloadable skill.
  • Ship the same folder through a plugin marketplace as well, so users of agents that do not fetch well-known paths can still install it.

Do not

  • Do not put API keys, internal hostnames or staging URLs in a SKILL.md you serve from a public origin.
  • Do not paste your full API reference into the body. It loads in its entirety every time the skill activates.
  • Do not hand-edit a digest, and never ship the sha256:c4d5e6f7... style placeholder copied out of the RFC example.
  • Do not redirect /.well-known/agent-skills/index.json to an HTML docs page. A clean 404 is more useful than HTML served with a JSON content type.

How Glippy checks this

Glippy requests /.well-known/agent-skills/index.json from the origin once per run, parses the response as JSON, and looks for a $schema string that names the agentskills discovery schema together with a skills array. A valid index scores the Agent Skills check in full inside Agent Interactivity, category 10, which carries a 0.2x weight in the overall score. A file that is reachable but has no recognisable $schema scores half and reports the skill count it could read. A missing file is informational and costs you nothing directly, but it also does not count as an agent discovery surface: a page with no forms and no discovery surface at all is marked not applicable and scored at a flat baseline rather than rewarded. The same logic runs in the Chrome extension, the desktop crawler and the MCP server. Full detail on the category is on the Agent Interactivity checker page.

Check your Agent Skills setup

Glippy runs 240+ checks across 16 categories on any page, including Agent Interactivity (category 10). No sign-up required.

Frequently asked questions

No, they solve different halves of the same problem. MCP gives an agent tools it can call over a live connection; an Agent Skill gives it a written procedure, in Markdown, that it loads into context. They compose well: a skill can tell an agent which MCP tool to reach for, in what order, and what to verify before a destructive call. If you already publish an MCP server, a skill that explains how to drive it is usually the cheaper of the two to add.

Not by default. The path comes from Cloudflare's Agent Skills Discovery RFC, a draft at version 0.2.0 first published in January 2026, and the proposal to fold it into the Agent Skills specification proper is still an open issue on the spec repository. What does read it today is tooling: Cloudflare's Agent Readiness score and Glippy both check the path, and framework examples exist for generating it. The file is a few hundred bytes, so publish it as positioning for the next wave of agent crawlers, not as a traffic source.

Only two: name and description. name is 1 to 64 characters of lowercase letters, digits and single hyphens and must match the parent folder name. description is up to 1024 characters and should state both what the skill does and when to use it. The optional fields are license, compatibility (up to 500 characters, for environment requirements), metadata (a string-to-string map for anything the spec does not define) and the experimental allowed-tools. Keep custom keys inside metadata, since some validators flag frontmatter keys outside the defined set.

In Claude Code there are three locations: ~/.claude/skills/ for personal skills, .claude/skills/ committed to a repository for anyone working in that project, and a plugin's skills/ directory for skills you distribute. For a product team the plugin route is the one that scales: put the skill folder in a plugin, list that plugin in a .claude-plugin/marketplace.json at your repository root, and users run /plugin marketplace add owner/repo followed by /plugin install your-plugin@your-marketplace. Other clients read the same SKILL.md folder from their own configured locations.

Reviewed against the primary sources on . These standards move quickly, so check the linked specs before you ship.

Check your site for AI search readiness

Start free with the Glippy Chrome extension for instant page checks. Scaling up? Automate audits across many URLs and your whole sitemap with the Glippy MCP server.

Add Glippy to Chrome – free Automate with the MCP server →