A dark green field of nine outlined cards representing skill files, three highlighted in emerald and coral, next to the headline Agent Skills: publish a skills index.
A dark green field of nine outlined cards representing skill files, three highlighted in emerald and coral, next to the headline Agent Skills: publish a skills index.
AI Readiness

Publish an Agent Skills Index for Your Website

SKILL.md has a real specification. The well-known path a website uses to advertise one does not, yet. What to publish, in both places, and how to verify it.

AAsif Rahman 27 Sept 2026 11 min read
#Agent Skills#SKILL.md#well-known#AI agent discovery

This guide covers P3 · Capabilities.

Table of contents

Quick summary

If you want to…Do thisTimeWhat it changes
Give an agent a reusable, on-demand capabilityWrite a SKILL.md with name + description frontmatter, plus any scripts or reference files it needs20–40 minThe agent loads the instructions only when the task matches, not on every turn
Make that skill findable from your websiteServe a JSON index at /.well-known/agent-skills/index.json15 minAn agent that lands on your domain can list what you offer before it asks
Cover both conventions in the wildAlso serve /.well-known/skills/index.json with the same content5 minNeither a legacy-path nor a new-path client comes back empty
Check what's liveScan the domain at aiscan.site (check P3) or curl both paths yourself2 minConfirms the file parses, and which path answered

Two different things are true here, and neither one is us. The SKILL.md file format is a real, published specification, maintained at agentskills.io. The two .well-known paths websites use to advertise one are not a joint standard; they're a convention several vendors converged on independently, and not on the same path. That gap is most of this article.

An AIScan scan checks this under P3, and, full disclosure up front, our own probe currently reads only the newer of the two paths. More on exactly where that leaves you further down.

Why this file exists at all

An agent that can browse the web meets thousands of sites. Most of them it will visit once. A handful, the ones it works with daily, or the one it landed on because a user pasted a link, are worth knowing more about: not a general crawl of every page, but a packaged, reusable set of instructions for doing one thing on that particular site well.

That packaged unit is a Skill. Anthropic's own description of the format, published in its developer docs, frames it, in its own words, as "organized like an onboarding guide you'd create for a new team member": not a wall of text handed over on day one, but a folder the new hire opens page by page, only when a task calls for that page. A skill for filling out expense reports doesn't need to be read before the agent has an expense report to file.

Mechanically, a skill is a directory with one required file, SKILL.md, plus whatever else it needs:

publish-agent-skills-index/
├── SKILL.md          # required: frontmatter + instructions
├── scripts/           # optional: code the agent can run
├── references/        # optional: longer docs, read on demand
└── assets/             # optional: templates, schemas, examples

SKILL.md itself is YAML frontmatter followed by ordinary Markdown. The published specification sets two required fields and four optional ones:

FieldRequiredWhat it's for
nameYesLowercase, hyphenated, ≤64 chars, must match the folder name
descriptionYes≤1024 chars: what the skill does and when to use it
licenseNoA license name, or a pointer to a bundled license file
compatibilityNoEnvironment needs, e.g. "requires Python 3.14+ and uv"
metadataNoFree-form key/value pairs for anything the spec doesn't define
allowed-toolsNoA pre-approved tool list, marked experimental

The description field is doing more work than its name suggests. It is the only thing an agent reads before deciding whether to open the file at all. According to Anthropic's own guidance on the format, this is what gets matched against the user's request, so a vague description ("Helps with PDFs") never triggers, while a specific one ("Extract text and tables from PDF files, fill forms, merge documents. Use when working with PDF files…") does. That's the whole reason this loads in stages: name and description sit in every agent's context for every installed skill, all the time, at roughly 100 tokens each. The full SKILL.md body, recommended under 5,000 tokens, loads only once a task matches. Anything in scripts/, references/ or assets/ loads only if the body itself points there. A skill can bundle a hundred pages of reference material and cost nothing until one page is actually opened.

None of that (the folder, the frontmatter, the three-tier loading) says anything about a website. It's a convention for packaging a capability, agnostic to where the folder lives: on a coding agent's local disk, uploaded through an API, or fetched from somewhere else entirely. That "somewhere else" is where the second half of this article starts, and it's also where the convention stops being settled.

The part that isn't in the spec: how a website advertises one

If skills are meant to be reusable and an agent is meant to discover them without a human pointing it at a URL, the obvious move is the one already worn smooth by robots.txt, sitemap.xml and llms.txt: a fixed, predictable path under /.well-known/ that any agent can probe without being told it exists first.

Multiple sites now do exactly that. The trouble is they don't all probe, or serve, the same path.

We measured this directly across 52 documentation sites: /.well-known/agent-skills/index.json (the newer form) is served by 10 of them; /.well-known/skills/index.json (the older, shorter form) by 5; the union is 11, and only 4 sites answer at both. A client written against one path finds fewer than half of what's actually out there.

The split isn't random, and it isn't only in a sample from weeks ago. We re-verified it live while writing this piece. docs.stripe.com answers only the legacy path: fetched from that domain directly, it returns 200, application/json, listing skills named things like connect-recommend and connect-required-verification-information, each carrying a description and a files array pointing at its own SKILL.md and reference docs. The newer path on the same host returns Stripe's ordinary branded 404 page, confirmed against a deliberately nonexistent control path on the same host returning an identical 404 shell: this is a real absence, not a soft-200. GitBook, Pydantic, Resend, Supabase, Vercel and Prisma run the other way, answering the newer path only.

Two live files, two different shapes of the same idea. Fetched from vercel.com directly, the newer-path index carries a $schema field pointing at a discovery schema hosted under the agentskills.io domain, and its entries don't point at a SKILL.md folder at all; they point at a downloadable archive:

{
  "name": "deploy-to-vercel",
  "description": "Deploy applications and websites to Vercel...",
  "type": "archive",
  "url": "https://github.com/vercel-labs/agent-skills/releases/download/.../deploy-to-vercel.tar.gz",
  "digest": "sha256:a1241958..."
}

Stripe's entries are closer to a directory listing: a name, a description, and a files array of relative paths an agent fetches individually. We checked our own site too, because the honest version of a how-to includes what we do, not only what others do: verified on aiscan.site itself, /.well-known/agent-skills/index.json answers 200, and its shape is a third variant again, a schemaVersion string, and entries carrying skillUrl and claudeMd fields that neither Stripe's nor Vercel's format uses.

SitePath that answersEntry shapePoints to
Stripe (docs.stripe.com)legacy onlyname, description, files[]individual files over HTTP
Vercel (vercel.com)newer onlyname, description, type, url, digestone downloadable archive
AIScan (aiscan.site)newer onlyschemaVersion, skillUrl, claudeMdits own bespoke fields

Three real, live files, three different sets of fields, verified live on all three domains within the same afternoon. Neither shape is wrong; the specification governs what's inside a skill folder, not how an index describes where to find one. But a scanner or a client built against one shape can misread the other, and a reader trying to copy "the" format from a single example is copying a choice, not a rule. If you're writing a client against this convention today, read the file rather than assuming its shape. If you're publishing one, that same variance is your argument for keeping it boring and close to the specification's own examples rather than inventing new fields.

Publish your own: two paths, write both

Path 1: you already have (or are ready to write) a SKILL.md. This is the every-day case: an internal process, a support workflow, an API your own agents already use, packaged once as a skill so any compatible agent can pick it up.

  1. Create the folder and the required frontmatter:
    ---
    name: your-skill-name
    description: What this does, specifically, and when an agent should reach for it.
    ---
    
    # Your Skill Name
    
    ## Instructions
    Step-by-step guidance the agent follows.
    
  2. Add scripts/, references/ or assets/ only if the task genuinely needs them. An empty references/ folder costs nothing, but don't manufacture one just to look complete.
  3. Validate it before publishing. skills-ref validate ./your-skill-name, the reference checker the specification itself points to, catches frontmatter and naming mistakes (a name with a leading hyphen, a description over 1,024 characters) before an agent ever sees them.
  4. Serve the folder somewhere fetchable: a static path on your own domain, or a release archive if you'd rather ship a single downloadable file, matching Vercel's type: "archive" shape.
  5. Write the index and serve it at both paths, byte-identical:
    {
      "skills": [
        {
          "name": "your-skill-name",
          "description": "Same description as the SKILL.md frontmatter.",
          "files": ["SKILL.md"]
        }
      ]
    }
    
    /.well-known/agent-skills/index.json and /.well-known/skills/index.json: same file, two paths. It costs one extra static-file rule and closes the gap this article measured on 52 real sites.

Path 2: you run a documentation platform and want to know if it already does this for you. Adoption tracks the platform, not the writer. On the same 52-site sample, Starlight and GitBook sites answer 3-for-3 and 2-for-2; Mintlify answers 4 of 5; Next.js custom docs sites 11 of 13; Docusaurus and Hugo both answer 2 of 3. Hand-rolled documentation stacks, sites that built their own instead of adopting one of the above, answer only 8 of 19. If your docs run on a generated platform, check whether it already writes this file before adding your own. If you hand-rolled your stack, budget the same static-file work as Path 1, because nothing is generating it for you.

Nobody's CMS panel handles this yet, ourselves included. ThinkRank manages WordPress's robots.txt, robots meta, schema and llms.txt from one settings screen, but a skills index isn't one of its fields today, so a WordPress site adds this the same way a hand-rolled stack does: a static file dropped at both well-known paths. Same story on StoreSEO for a Shopify storefront. It generates your llms.txt from live products and pages, but a skills index today means a static asset served through a theme app extension, not a settings toggle.

Verify it actually landed

for p in agent-skills/index.json skills/index.json; do
  echo "=== $p ==="
  curl -sL -o /tmp/skills-check.json -w '%{http_code} %{content_type}\n' \
    "https://yoursite.com/.well-known/$p"
  python3 -c "import json; d=json.load(open('/tmp/skills-check.json')); print(len(d.get('skills',[])),'skill(s) parsed')" 2>/dev/null \
    || echo "did not parse as JSON, check the content-type above"
done

Pass looks like 200 application/json on the path(s) you serve, and the Python line printing a skill count rather than a parse error. Two failure modes are common, and both are silent unless you check for them. First, the file returns 200 but with content-type: text/html, meaning your host is answering with an app shell rather than the file you dropped; compare against a deliberately nonexistent path under the same .well-known/ prefix, since an identical body and byte count on both means neither is really being served. Second, it validates as JSON but every files entry 404s, because the referenced SKILL.md never got deployed alongside the index that points at it.

What AIScan checks, and what it can't

Check P3 currently probes /.well-known/agent-skills/index.json only. That's a real limitation we're disclosing rather than fixing quietly: on the 52-site sample above, a scanner reading one path finds 10 or 5 of the 11 sites actually publishing something, depending which path it picked. If your file lives only at the legacy /.well-known/skills/index.json path, as Stripe's does, a P3 pass today reads as a fail. Serving both paths, as Path 1 above recommends, sidesteps the gap entirely rather than waiting on it.

What P3 can verify: the path resolves, returns parseable JSON, and the response isn't a soft-200 app shell. What it can't verify, and what no automated check can, is whether your SKILL.md files are actually well-written enough for an agent to complete the task they describe, or whether the archive a type: "archive" entry points at is the version you meant to ship. Both are review questions, not scan questions. The skills-ref validator referenced in the specification checks format compliance, not quality.

Publishing an MCP server card is the closest sibling task: a different well-known file, a different discovery purpose (tools an agent can call, rather than instructions it can load), but the same shape of problem, a static file at a fixed path that most sites still don't serve. If you're doing one, budget time for the other.

Run the scan on your own site

npx aiscan-cli yoursite.com, or paste the URL at aiscan.site, free, no account. It runs check P3 alongside the rest of the capabilities dimension (P1, P2, P4, E2) and tells you which of the two well-known paths, if either, actually resolved on your domain. Browse the full guide library for the rest of the discovery-file series, including the MCP server card guide linked above.

Frequently asked questions

Is /.well-known/agent-skills/index.json an official standard?

No. The SKILL.md file format has a real, published specification at agentskills.io. The well-known path for advertising one from a website does not — it's a convention several vendors converged on independently, and they haven't converged on the same path. Treat the file format as settled and the discovery path as still moving; serve both known paths rather than betting on one becoming the winner.

I published my skills index, but AIScan's P3 check still shows a fail. What's wrong?

Check which path you served it at. Our P3 probe currently reads only /.well-known/agent-skills/index.json, the newer form. If your file lives at the older /.well-known/skills/index.json path only — the same choice Stripe's own documentation makes — a real, working file still reads as a 404 to this specific check. That's a known limitation on our side, not a defect in your file. Serving the same JSON at both paths is the fix that works regardless of which path any given scanner reads.

My index.json returns HTTP 200, but an agent still can't read it. What's actually wrong?

Check the content-type header first, not just the status code. A 200 with content-type: text/html usually means your host served its app shell instead of the JSON file you dropped — most catch-all routes do this for any unrecognized path. Confirm by requesting a path under the same .well-known/ prefix that has never existed; if it returns an identical body and byte count to your real file, neither is actually being served, and the fix is a routing rule, not a content change.

Do I need to publish my skill's SKILL.md file publicly, or just the index?

Both, if you want an outside agent to use the skill. The index is a pointer, not the payload — Stripe's index entries carry a files array naming each skill's actual SKILL.md and reference files, and an agent has to fetch those separately to get the instructions. An index whose linked files 404 is worse than no index: it tells an agent something exists, then fails the fetch that was supposed to prove it.

What's the difference between the files shape and the archive shape in an index entry?

Stripe's index points at individual files over HTTP — a name, a description, and a files array of relative paths, closer to a directory listing an agent reads file by file. Vercel's points at a single downloadable tarball with a type: "archive" field, a url, and a sha256 digest for integrity — closer to shipping a release artifact. Both are valid uses of the same index concept; pick whichever matches how you already distribute the rest of your skill's files.

Our documentation site is on Docusaurus. Does it publish this file for us automatically?

Not by default, and adoption varies a lot by platform even among sites that could. Across the sites we measured, Docusaurus installs answered 2 of 3, Starlight and GitBook installs answered every one we checked, and hand-rolled documentation stacks answered only 8 of 19. If you hand-rolled yours, remember that most static-site frameworks only serve files placed in a designated public or static-assets directory verbatim — a raw JSON file dropped elsewhere in the source tree gets processed or ignored rather than served as-is, so confirm the file lands at the literal URL after a full build and deploy, not just in your local dev server.

Should our WordPress or Shopify SEO plugin be managing this file?

Not yet, as far as we've found. ThinkRank manages WordPress's robots.txt, robots meta, schema and llms.txt from one screen, and StoreSEO generates a Shopify store's llms.txt from live products and pages, but neither currently has a settings field for a skills index. Until one does, this is a static file you add by hand or through your theme or deploy pipeline, on any platform.

Does a skills index replace llms.txt or a robots.txt entry for AI crawlers?

No — they answer different questions. robots.txt and Content Signals declare what a crawler may fetch; llms.txt is a curated link map into your existing content; a skills index advertises packaged, reusable task instructions an agent can load and run. A site can publish all three, and most sites that need one will eventually need the others too.

Related guides