Skip to content
Specification
On this page
  1. Abstract
  2. Status of this document
  3. 1. Introduction
  4. 1.1. Goals
  5. 1.2. Non-goals
  6. 2. Conventions and terminology
  7. 3. Relationship to upstream specifications
  8. 4. Inputs
  9. 4.1. Input A: a skills directory
  10. 4.2. Input B: a Claude Code marketplace
  11. 4.3. Generator parameters
  12. 4.4. Input validation
  13. 5. Entry points
  14. 5.1. Fixed paths
  15. 5.2. The llms.txt section
  16. 6. The base namespace
  17. 6.1. Layout
  18. 6.2. Configuration
  19. 7. Catalog and entry documents
  20. 7.1. Canonical JSON
  21. 7.2. The catalog
  22. 7.3. Plugin items
  23. 7.4. Skill items
  24. 7.5. Host support levels
  25. 7.6. Entry documents
  26. 7.7. External entries
  27. 8. Upstream documents
  28. 8.1. AI Catalog
  29. 8.2. Agent Skills discovery index
  30. 8.3. Claude Code marketplace
  31. 9. Head discovery links
  32. 10. Archives and digests
  33. 10.1. Three archive shapes
  34. 10.2. Canonical encoding
  35. 10.3. Members, paths and modes
  36. 10.4. SHA256SUMS
  37. 11. Versioning, the lock and yank
  38. 11.1. Immutable versioned paths
  39. 11.2. The lock
  40. 11.3. Retained versions
  41. 11.4. Removal, yank and deprecation
  42. 12. Install prompts
  43. 12.1. Purpose and rules
  44. 12.2. Claude Code prompt
  45. 12.3. Agent Skills prompt
  46. 13. Security considerations
  47. 13.1. Everything published is public
  48. 13.2. Reference closure
  49. 13.3. Code that runs on install or use
  50. 13.4. Integrity
  51. 13.5. Archive extraction
  52. 13.6. Install prompts
  53. 13.7. Well-known URIs
  54. 13.8. Threats considered and residual risk
  55. 14. Privacy considerations
  56. 15. IANA considerations
  57. 16. Conformance
  58. 16.1. Conformance classes
  59. 16.2. Conformance suite
  60. 16.3. Schemas and prose
  61. 17. Implementation notes (informative)
  62. Annex A. Per-host packaging (informative)
  63. A.1. Host tree
  64. A.2. Field mapping
  65. A.3. Hook events
  66. A.4. Host archives
  67. A.5. Local install prompts
  68. Annex B. Example (informative)
  69. Annex C. References
  70. Annex D. Changes
Page actions

Static Agent Marketplace

Abstract

This document is a profile. It says how a static website, such as one served by GitHub Pages, publishes a set of agent plugins and skills so that people can browse them and agent hosts can install them. It invents no new file format and no new well-known URI. It composes four existing documents, AI Catalog, Agent Skills discovery, the Claude Code plugin marketplace and llms.txt, and adds what they leave open: a fixed place for the remaining files, one normalized catalog, a canonical archive encoding, immutable versioned paths, discovery links in HTML, and install prompts.

Status of this document

This is an Editor’s Draft of version 0.1.0, dated in the header and changed in place. It is published so that the reference implementation and other implementers can test against it; cite a release candidate or a release instead once one exists. Sections marked informative are not requirements. Where an upstream specification and this profile disagree, the upstream specification wins (section 3).

1. Introduction

Agent plugins and skills are usually distributed from a git repository. Many publishers instead want a plain website: the source can stay private, the site is cheap to host, and people need a page to read before they install anything. No standard covers publishing a plugin marketplace at a web URL. Several conventions cover parts of it, and each host reads a different part.

This profile gives one answer for a static site. A generator reads the publisher’s existing plugin or skill tree, writes one normalized catalog, and derives every published file from that catalog. The result can be served by any static host that preserves paths and bytes.

1.1. Goals

  • Publish one source tree so that Claude Code and Agent Skills clients install from the site URL, and other hosts install from downloaded archives.
  • Let people, agents and tools find every machine-readable file from any page of the site.
  • Make every archive reproducible and every published version immutable.
  • Keep the source repository and its history private; the published site is the only public surface.

1.2. Non-goals

  • A new plugin or skill format. Inputs are the formats publishers already use (section 4).
  • A search service or server API. Everything here is a static file.
  • Trust in the publisher. Digests detect changed bytes; they do not say who wrote them (section 13).

2. Conventions and terminology

The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “NOT RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.

The Abstract, the Status of this document, the Introduction, examples and every section or annex marked “(informative)” are informative. Everything else, tables included, is normative. The text blocks of sections 5.2, 12.2 and 12.3 and the elements of section 9 are templates and are normative; the host names under example.com in them are examples.

The terms below are defined in the project glossary, which is the only place a term is defined; this list is generated from it. A term in italics, such as catalog, refers to the glossary entry.

  • Archive: A ZIP archive in the canonical encoding of section 10.2. Its digest identifies one version of one plugin or skill.
  • Base namespace: The configurable path, by default /.agent-marketplace/, that holds every published file other than the entry points. Written <base> in paths; it starts and ends with a slash.
  • Canonical JSON: The single JSON serialization every generated JSON file uses: sorted keys, two-space indentation and a final newline (section 7.1).
  • Catalog: The document <base>catalog.json, from which every other published file is derived (section 7.2).
  • Client: Software that reads the published files of a site to list, download or install packages, such as a host or a skills command-line tool. A conforming client meets section 16.1.
  • Component (from Claude Code plugin reference): One of skills, agents, commands, hooks or Model Context Protocol (MCP) servers inside a plugin.
  • Conformance case: A directory of the conformance suite holding case.json, input/, expected/ and SHA256SUMS (section 16.2).
  • Digest (from Agent Skills discovery 0.2.0): The lowercase hexadecimal SHA-256 of an archive’s bytes, written sha256:<hex> where an upstream format requires that form.
  • Entry document: The file <base>entries/<p>.json or <base>entries/skills/<s>.json: one catalog item with $schema, kind and specVersion added (section 7.6).
  • Entry point: A published file that a client finds by a fixed path rather than by a link (section 5).
  • External entry: An input B marketplace entry whose source is remote. It is listed in the catalog but never fetched or packaged (section 7.7).
  • Generator: Software that reads an input tree and parameters and writes the published files. A conforming generator meets section 16.1.
  • Host: An application that installs plugins or skills. This profile covers Claude Code, Codex, Grok Build, Antigravity and Gemini CLI, plus clients of Agent Skills discovery.
  • Host package: An archive or tree in a host-specific layout, produced only when requested (Annex A).
  • Install prompt: Generated text a user can paste into their own agent to install one plugin or skill with the host’s own commands (section 12).
  • Lock: The record, kept in the source repository and never published, of every archive published under a versioned path and its digest (section 11.2).
  • Plugin (from Claude Code plugin reference): A directory in the Claude Code plugin layout: an optional .claude-plugin/plugin.json plus component folders such as skills/, agents/, commands/ and hooks/.
  • Plugin archive: The archive of a whole plugin, with the plugin root at the top, used as a Claude Code archive source (section 10.1).
  • Site: The static website the files are published on. Its origin is the scheme, host and port of siteUrl.
  • Skill (from Agent Skills): A directory holding a SKILL.md file and any supporting files.
  • Skill archive (from Agent Skills discovery 0.2.0): The archive of one skill with SKILL.md at the top, as Agent Skills discovery 0.2.0 requires (section 10.1).
  • Support level: One of native, partial or none, saying how much of a plugin works on a host (section 7.5).
  • Well-known URI (from RFC 8615): A URI whose path starts with /.well-known/, reserved for names registered with IANA.
  • Yank: To withdraw one published version: it leaves every published document and its archives stop being served, and the lock keeps the reason (section 11.4).

In paths, <base> is the base namespace, <p> a plugin name, <s> a skill name and <v> a version. URLs written as https://plugins.example.com/... are examples.

3. Relationship to upstream specifications

This profile composes the following documents. Each is cited at the version shown.

Document Version used What this profile takes from it
AI Catalog draft, specVersion 1.0 /.well-known/ai-catalog.json, entry fields, media types, urn:air identifiers
Agent Skills discovery 0.2.0 /.well-known/agent-skills/index.json, archive entries, sha256: digests
Agent Skills as published SKILL.md front matter, name and description limits
Claude Code plugin marketplace Claude Code 2.1.224 or later marketplace.json, archive plugin sources, plugin layout
llms.txt proposal /llms.txt structure
RFC 8615   rules for /.well-known/ names

Rules for this relationship:

  1. Upstream wins. If an upstream document and this profile conflict, the upstream document is authoritative for the files it defines, and the conflict is a defect in this profile that a later version corrects.
  2. No restatement. This profile restates upstream rules only where it narrows them (for example, absolute URLs where upstream also allows relative ones). Narrowing never produces a file that upstream forbids; a rule of this profile that would is a defect in this profile.
  3. Shrinking scope. When an upstream document adopts something this profile defines, a later version of this profile replaces its own text with a reference to the upstream text.
  4. Extension points only. Where this profile adds data to an upstream document, it uses that document’s extension mechanism (for AI Catalog, the extensions member; section 8.1).

4. Inputs

A generator reads exactly one of two input layouts, selected by the source parameter. Both are layouts publishers already use; this profile defines no new manifest.

4.1. Input A: a skills directory

The input root holds skills/<s>/SKILL.md for one or more skills, in the Agent Skills layout. Each skill becomes one plugin with the same name and the single skill <s>.

  • The plugin version is the version parameter (section 4.3). A generator MAY read it from a package manifest instead, but the value MUST be the same for every skill of the input.
  • The plugin description is the skill’s description.
  • The plugin license is the skill’s license front matter field, or else the license parameter.
  • The generator synthesizes a plugin manifest .claude-plugin/plugin.json whose content is the canonical JSON (section 7.1) of an object with exactly the members description, license, name and version.
  • The plugin directory is the synthesized manifest plus every file of skills/<s>/ at skills/<s>/ inside the plugin.

Input A has no agents, commands, hooks or MCP servers. A publisher who needs them uses input B.

4.2. Input B: a Claude Code marketplace

The input root holds .claude-plugin/marketplace.json in the Claude Code format, with plugins in subdirectories.

  • The marketplace name, owner and metadata.description become the catalog’s marketplace.name, marketplace.owner and marketplace.description.
  • An entry whose source is a relative path (./...) is a local plugin. The generator reads and packages it.
  • An entry whose source is an object of type github, url, git-subdir or npm is an external plugin. The generator lists it under external in the catalog (section 7.7) and MUST NOT fetch or package it.
  • Entries of any other source type MUST be rejected (source-type).
  • For a local plugin, version is taken from .claude-plugin/plugin.json, else from the entry. If both are present and differ, the input MUST be rejected (version-mismatch). If neither is present, the input MUST be rejected (version-missing).
  • The display fields description, license, displayName, author, homepage, repository and keywords are taken from the entry when present, else from plugin.json. category and tags are taken from the entry.
  • Entry component fields (commands, agents, skills, hooks, outputStyles, themes) and strict are copied unchanged into the URL-mode marketplace entry (section 8.3). They do not change the plugin directory.

Components of a local plugin are found by the standard layout, relative to the plugin directory:

Component Found at Recorded as
skills skills/<s>/SKILL.md skill names, sorted
agents agents/*.md paths, sorted
commands commands/**/*.md paths, sorted
hooks hooks/hooks.json one item per handler (section 7.3)
MCP servers .mcp.json one item per server, sorted by name

4.3. Generator parameters

Parameter Required Meaning
source yes skills (input A) or marketplace (input B)
siteUrl yes The site origin: https:// plus host and optional port, no path, no trailing slash
base yes The base namespace path, starting and ending with /. Default /.agent-marketplace/
pages yes The path of human-readable pages, starting and ending with /. Default /plugins/
marketplace input A Object with name, owner ({"name": ...}) and optional description
version input A The version of every plugin (section 4.1)
license no Input A license when a skill has none
lock no The path of the lock (section 11.2) relative to the input root, when the generator is told where it is
hostPackages yes Annex A packages to produce: any of codex, antigravity, gemini, bundle. Can be empty

How an implementation receives these parameters (a configuration file, command-line flags) is out of scope. The conformance suite passes them in case.json (section 16.2).

The entry points of section 5 are defined at the origin root. A site whose content is served under a path prefix, such as a GitHub project site at https://owner.github.io/repo/, cannot publish them and does not conform. Generators SHOULD warn about this and SHOULD recommend a custom domain.

4.4. Input validation

A generator MUST check the input before writing any file, and MUST NOT publish anything when a check fails. Each check has a rule identifier. A generator SHOULD report the identifiers of every failed check; the conformance suite compares them (section 16.2).

Rule Requirement
name-format Every plugin, skill and marketplace name matches ^[a-z0-9]+(-[a-z0-9]+)*$ and is at most 64 characters. This is the intersection of what the supported hosts accept.
name-directory A skill’s name equals its directory name. A local plugin’s entry name equals the name in its plugin.json when that file exists.
name-duplicate No two plugins share a name, and no two skills share a name across the whole input.
name-reserved The marketplace name, and the URL-mode name derived from it (section 8.3), is not reserved by Claude Code (for example, names starting with claudeai).
description-length Every plugin and skill description is 1 to 1,024 characters.
license-missing Every plugin has a license, as an SPDX license expression.
version-format Every version is a Semantic Versioning 2.0.0 version without build metadata, at most 64 characters.
version-missing Every local plugin has a version (section 4.2).
version-mismatch Entry and plugin.json versions agree (section 4.2).
source-type Every input B entry has a supported source type (section 4.2).
path-escape Every component path and every relative source stays inside its plugin directory and contains no .. segment.
symlink The input contains no symbolic links inside a plugin or skill directory.
reference-closure Every relative link or image target in a Markdown file resolves to an existing file inside the same skill directory, for files in a skill, or inside the same plugin directory, for other files (section 13.2).
portable-path Every file path inside a plugin meets section 10.3.
case-collision No two paths in one plugin differ only in letter case.
file-size No file exceeds 25 MiB, and no archive exceeds 100 MiB.
lock-violation No published version changes content (section 11.2).
frontmatter Every SKILL.md starts with YAML front matter holding name and description.

SKILL.md files longer than 500 lines SHOULD produce a warning, not a failure.

5. Entry points

5.1. Fixed paths

A conforming site publishes these files at these paths, relative to the origin. They are the only files a client is expected to find without a link.

Path Defined by Content Required
/.well-known/ai-catalog.json AI Catalog section 8.1 yes
/.well-known/agent-skills/index.json Agent Skills discovery 0.2.0 section 8.2 when the catalog has at least one skill
/.claude-plugin/marketplace.json Claude Code convention section 8.3 yes
/llms.txt llms.txt section 5.2 yes

Under /.well-known/agent-skills/, the generator MUST write only the index and the skill archives of section 10.1, at /.well-known/agent-skills/<s>/<v>/<s>.zip. Any other file there could collide with a skill name.

A generator MUST NOT create any other name under /.well-known/. RFC 8615 requires new well-known names to be registered, and this profile registers none (section 15).

5.2. The llms.txt section

The generator adds one section to /llms.txt. If the site already has an /llms.txt, the section is appended to it and the rest of the file is kept unchanged. If the site has none, the generator writes # and the marketplace name as the first line, a blank line, and then the section.

The section is the following lines, each ended by a line feed (LF), with values substituted. The agentSkillsIndex line is present only when that entry point exists. One plugin line follows per plugin, sorted by name.

## Agent marketplace

- [AI catalog](<aiCatalog>): every package on this site, in AI Catalog format
- [Agent skills index](<agentSkillsIndex>): skills for Agent Skills discovery clients
- [Claude Code marketplace](<claudeMarketplace>): add with /plugin marketplace add
- [Catalog](<catalog>): versions, files, digests and install prompts
- [Checksums](<sha256sums>): SHA-256 of every archive
- [<p> <v>](<entryUrl>): <description>

The values in angle brackets are the members of the same name in the catalog’s entryPoints and plugin items.

6. The base namespace

6.1. Layout

Every published file other than the entry points lives under <base>, by default /.agent-marketplace/. Clients reach these files only through links from the entry points, so the path carries no meaning of its own.

Path Content Section
<base>catalog.json the catalog 7.2
<base>SHA256SUMS digests of every archive 10.4
<base>entries/<p>.json one plugin entry 7.6
<base>entries/skills/<s>.json one skill entry 7.6
<base>dist/plugins/<p>/<v>/<p>.zip plugin archive 10.1
<base>dist/skills/<s>/<v>/<s>.claude-ai.zip Claude app upload archive 10.1
<base>tree/ host tree (only with hostPackages) Annex A
<base>dist/<v>/market.zip and <base>dist/plugins/<p>/<v>/<p>.<host>.zip host packages (only with hostPackages) Annex A

The default name starts with a dot so that it cannot collide with paths of human-written pages, in the same way as /.claude-plugin/. It is not a well-known URI and is not registered.

6.2. Configuration

A publisher can choose another base. The generator MUST refuse a base that is, or is inside, /.well-known/ or /.claude-plugin/, and MUST refuse to overwrite a file of the site that it did not generate. A client MUST NOT assume the default; it follows the links in the entry points.

7. Catalog and entry documents

7.1. Canonical JSON

Every JSON file this profile defines or generates, including the upstream documents of section 8 and the manifest of section 4.1, MUST be serialized as canonical JSON, a subset of JSON:

  • UTF-8 without a byte order mark.
  • Object members sorted by key, comparing keys as sequences of Unicode code points.
  • Two spaces of indentation per level; one member or array item per line; ": " between key and value; empty arrays as [] and empty objects as {}.
  • Strings escape only ", \, and control characters; \b, \f, \n, \r and \t use their short forms, other control characters use \u00XX with lowercase hex. Non-ASCII characters and / are written as is.
  • Numbers are integers without exponent or leading zeros.
  • The file ends with a single LF.

This is the output of Python’s json.dumps(value, indent=2, sort_keys=True, ensure_ascii=False) followed by a newline. Canonical JSON makes the files of two conforming generators byte-identical for the same input, which the conformance suite relies on.

All URLs inside generated JSON MUST be absolute https URLs built from siteUrl. Some hosts do not resolve relative URLs.

7.2. The catalog

<base>catalog.json is the single source of every other output. Its schema is catalog.schema.json, a JSON Schema 2020-12 document like every schema of this profile.

Member Content
$schema https://static-agent-marketplace.org/schemas/draft/catalog.schema.json
specVersion 0.1.0-draft
generator OPTIONAL. name, version and optional url of the implementation. Omitted in conformance runs
marketplace name, owner, optional description, and url: siteUrl plus /
entryPoints absolute URLs: aiCatalog, agentSkillsIndex (when there are skills), claudeMarketplace, llmsTxt, catalog, sha256sums
plugins plugin items (section 7.3), sorted by name
skills skill items (section 7.4), sorted by name
external external entries (section 7.7), sorted by name
bundle the Annex A bundle package, present only when produced

A client MUST compare $schema as an opaque string. It need not fetch it.

7.3. Plugin items

Member Content
name, version, description, license from the input (section 4)
displayName, author, homepage, repository, keywords, category, tags present only when the input has them
lifecycle present only for deprecated plugins (section 11.4)
components skills, agents, commands, hooks and mcpServers, each an array, empty when absent
hosts support level per host (section 7.5)
files every member of the plugin archive: path, bytes, sha256, mode (0644 or 0755), sorted by path
packages claudeArchive (section 10.1), plus Annex A packages when produced. Each package has url, sha256 and bytes
install prompts, with at least claude (section 12)
pageUrl siteUrl + pages + <p>/
entryUrl the URL of the plugin’s entry document

Each hook item is one handler of hooks/hooks.json, visiting events in sorted order, then matcher groups and handlers in file order. It has event, type, and when present in the input matcher, command, url and timeout.

Each MCP server item has name, env (the sorted names of its environment variables) and, when present, command or url. A generator MUST NOT copy environment variable values, headers or arguments that could hold credentials.

7.4. Skill items

Member Content
name, description from the skill’s SKILL.md
version, license from the plugin that holds the skill
plugin the name of that plugin
files every member of the skill archive, as in section 7.3
packages skillArchive and claudeAiUpload (section 10.1)
install prompts, with at least agent-skills (section 12)
pageUrl siteUrl + pages + <p>/skills/<s>/
entryUrl the URL of the skill’s entry document

7.5. Host support levels

hosts gives each host one of native (every component works), partial (some components do not work on that host) or none (no component works). A component is unsupported on a host when it appears in that host’s row below. The level is native if the plugin has no unsupported component, none if it has components and all of them are unsupported, and partial otherwise.

Host Unsupported components
Claude Code none
Grok Build none (it reads Claude Code plugins)
Codex agents, commands, hooks
Antigravity commands, hooks
Gemini CLI agents; hooks whose event is not one of PreToolUse, PostToolUse, SessionStart, SessionEnd, UserPromptSubmit, Stop, PreCompact, Notification

These rows summarize the host formats as of this draft and change with them; Annex A gives the detail.

7.6. Entry documents

For every plugin item, the generator writes <base>entries/<p>.json; for every skill item, <base>entries/skills/<s>.json. An entry document is the item with three members added: $schema (https://static-agent-marketplace.org/schemas/draft/entry.schema.json), kind (plugin or skill) and specVersion. Nothing else is added or removed. Its schema is entry.schema.json.

An entry document is the machine-readable form of the page that describes the same package, and the page links it (section 9).

7.7. External entries

An external entry has name, source copied from the input, and description and version when the input has them. The generator lists it so that the catalog is complete, but it produces no archive, file list, entry document or install prompt for it, and does not list it in the upstream documents of section 8.

8. Upstream documents

8.1. AI Catalog

/.well-known/ai-catalog.json is an AI Catalog document with specVersion 1.0, a host with displayName set to the owner name, and these entries:

Entry identifier type url Other members
each skill urn:air:<host>:skill:<s> application/agent-skills+zip the skill archive description, digest, version
each plugin urn:air:<host>:plugin:<p> application/zip the plugin archive description, digest, version, profile extension with role claude-plugin-archive
the catalog urn:air:<host>:catalog:<marketplace> application/json <base>catalog.json description, profile extension with role catalog and specVersion
the Claude marketplace urn:air:<host>:claude-marketplace:<name> application/json /.claude-plugin/marketplace.json description, profile extension with role claude-marketplace

<host> is the host name of siteUrl. digest is sha256: followed by the lowercase hex digest of the archive. Entries are sorted by identifier, then version.

The profile extension is the extensions member https://static-agent-marketplace.org/extensions/profile, an object whose role says what the entry points at. The descriptions of the catalog and Claude marketplace entries are Static Agent Marketplace catalog with versions, files and digests. and Claude Code plugin marketplace with archive sources.

A Claude Code plugin archive is not an Agent Plugins bundle, so it is not typed application/agent-plugins+zip. When Annex A packages are produced, a generator MAY add Agent Plugins entries for them.

8.2. Agent Skills discovery index

/.well-known/agent-skills/index.json follows Agent Skills discovery 0.2.0: $schema is https://schemas.agentskills.io/discovery/0.2.0/schema.json, and skills has one item per skill, sorted by name, with name, description, type archive, url (the absolute URL of the skill archive) and digest.

8.3. Claude Code marketplace

/.claude-plugin/marketplace.json is a Claude Code marketplace for URL mode, in which Claude Code downloads only this file. Every plugin therefore uses an archive source.

  • name is the marketplace name followed by -pages, so that it never collides with the git-mode marketplace of the same publisher in a user’s settings.
  • owner is the catalog owner; metadata.description is the marketplace description when there is one.
  • plugins has one item per plugin, sorted by name, with name, version, source {"source": "archive", "url": ..., "sha256": ...}, the display fields of section 7.3 that the plugin has, and the entry fields copied under section 4.2.

Claude Code 2.1.224 or later is needed for archive sources. Claude Code checks sha256 before installing.

Every HTML page of the site SHOULD carry these elements in its head, in this order, with absolute href values. A link whose target was not generated is omitted.

<link rel="alternate" type="text/plain" title="llms.txt" href="https://plugins.example.com/llms.txt">
<link rel="alternate" type="application/ai-catalog+json" title="AI catalog" href="https://plugins.example.com/.well-known/ai-catalog.json">
<link rel="alternate" type="application/json" title="Agent skills" href="https://plugins.example.com/.well-known/agent-skills/index.json">
<link rel="alternate" type="application/json" title="Claude plugin marketplace" href="https://plugins.example.com/.claude-plugin/marketplace.json">

A page that describes one plugin or one skill SHOULD also carry a link to its entry document, with title <p> entry or <s> skill entry:

<link rel="alternate" type="application/json" title="safe-shell entry" href="https://plugins.example.com/.agent-marketplace/entries/safe-shell.json">

These links use rel="alternate", the relation llms.txt uses for a page’s Markdown version (type="text/markdown"). They sit next to that link as siblings that differ only in type and title. Attribute values are HTML-escaped. Every page is a valid place to start discovery, which matters because a static host such as GitHub Pages cannot add HTTP Link headers.

AI Catalog also defines a rel="ai-catalog" link relation. A page MAY carry it in addition.

A page about a package SHOULD also describe it with a schema.org SoftwareApplication object in JSON-LD whose softwareVersion and downloadUrl match the entry. rel="alternate" is not used for archives, which are not another representation of the page.

10. Archives and digests

10.1. Three archive shapes

Hosts expect different layouts, so each package is published in the shape its reader expects.

Archive Path Top level of the archive Read by
Plugin archive <base>dist/plugins/<p>/<v>/<p>.zip the plugin root (.claude-plugin/, skills/, …) Claude Code archive source
Skill archive /.well-known/agent-skills/<s>/<v>/<s>.zip the skill root (SKILL.md, …) Agent Skills discovery clients
Claude app upload <base>dist/skills/<s>/<v>/<s>.claude-ai.zip one folder <s>/ holding the skill manual upload to the Claude apps

The members of a plugin archive are the files of the plugin directory (section 4). The members of a skill archive are the files of skills/<s>/ with that prefix removed. The members of a Claude app upload archive are the skill archive members with the prefix <s>/ added.

10.2. Canonical encoding

Every archive MUST be encoded exactly as follows, so that any two implementations produce the same bytes from the same members.

  1. ZIP format as described by the PKWARE APPNOTE, without ZIP64, encryption, data descriptors, extra fields, comments or directory entries.
  2. Members ordered by path, comparing UTF-8 bytes.
  3. Every member stored without compression (method 0).
  4. Every local header: version needed 20, flags 0, method 0, modification time 0x0000 and date 0x0021 (1980-01-01 00:00:00), CRC-32, compressed and uncompressed size equal, name length, extra length 0.
  5. Every central directory header: version made by 0x0314 (Unix, 2.0), version needed 20, flags 0, method 0, the same time, date, CRC-32 and sizes, extra, comment and disk number 0, internal attributes 0, external attributes equal to the Unix mode 0100644 or 0100755 shifted left by 16 bits, and the offset of the local header.
  6. One end of central directory record with disk numbers 0, both entry counts equal, the central directory size and offset, and no comment.

Stored entries cost size but remove every dependence on a compression library, which would otherwise make archives differ between implementations (see Reproducible builds: archives). Text-heavy plugins are usually small; section 4.4 caps archives at 100 MiB.

10.3. Members, paths and modes

  • Paths use / and are relative to the archive root. Each segment matches ^[A-Za-z0-9._-]+$ and is not . or ..; the whole path is at most 240 characters. Segments MUST NOT be Windows device names (CON, PRN, AUX, NUL, COM1 to COM9, LPT1 to LPT9, with or without an extension).
  • Only regular files are members. Empty directories are not represented.
  • The mode is 0755 when the input file is executable (git mode 100755) and 0644 otherwise.
  • Member bytes are the input bytes unchanged, with the single exception of the manifest synthesized for input A.

10.4. SHA256SUMS

<base>SHA256SUMS lists every archive the site publishes, including retained earlier versions (section 11.3), one per line: the lowercase hex SHA-256, two spaces, and the path relative to the origin root without a leading slash. Lines are sorted by path, comparing UTF-8 bytes, and each ends with LF. This is the format GNU sha256sum reads, so a user who mirrors the site can check it with sha256sum -c SHA256SUMS from the mirror root.

The same digest appears in the catalog (sha256), in the AI Catalog and Agent Skills index (digest), and in the Claude Code marketplace (sha256). A generator MUST write the same value in every place.

11. Versioning, the lock and yank

11.1. Immutable versioned paths

Every archive path contains the package version. Once an archive has been published at a path, the bytes at that path MUST NOT change. A content change needs a new version. The catalog and the other JSON documents can change on every publication; archives cannot.

Static hosts cache responses (GitHub Pages sends max-age=600). A client can therefore see an older catalog for a few minutes; because archive paths are immutable, every URL that older catalog names still serves the bytes it describes.

11.2. The lock

The generator keeps a lock: a record of every archive it has published, with its digest. The lock lives in the source repository, is updated by the generator and is never published. Its format is lock.schema.json. The file name is implementation-defined; agent-marketplace.lock.json is RECOMMENDED. When the lock parameter is given, the generator reads the lock from that path.

Before publishing, the generator compares every archive it is about to publish with the lock. If an archive path is already in the lock with a different digest, the generator MUST stop (lock-violation). After a successful publication it records new archives in the lock. Canonical archives (section 10.2) make this check meaningful: the same input always gives the same digest.

11.3. Retained versions

Some static hosts replace the whole site on each deployment. To keep earlier versions available, a generator SHOULD rebuild the archives of the last few versions of each package (RECOMMENDED: 3) from the source revision of that version, and MUST check each rebuilt archive against the lock. Retained archives are listed in SHA256SUMS; the catalog and the upstream documents list only the current version.

11.4. Removal, yank and deprecation

  • Removal. A package that leaves the input disappears from every published document. The lock keeps its releases with removed: true.
  • Yank. A publisher withdraws one version, for example after a security problem, by adding yanked with a reason to its lock release. A yanked version is removed from every published document and its archives are no longer served. If the yanked version is the newest, the newest non-yanked version becomes current. A page about the package SHOULD show the reason.
  • Deprecation. A plugin whose plugin.json sets metadata.lifecycle to deprecated stays published with lifecycle {"status": "deprecated"} in its item, plus replacedBy and note when metadata.replacedBy and metadata.lifecycleNote are present. Install prompts for a deprecated plugin SHOULD say so.

Removing a version is always allowed; changing one is not. Rolling back to an earlier source revision therefore works.

12. Install prompts

12.1. Purpose and rules

An install prompt is text a user can paste into their own agent so that the agent installs one package. It is an alternative to typing commands. Install prompts are stored in install.prompts of each item (install-prompt.schema.json).

  • The generator writes prompts from the catalog by the templates below; they are never written by hand, so their commands, URLs, versions and digests always match the catalog.
  • Prompts use only the host’s own install commands. They MUST NOT ask the agent to download and run a script, to pipe a download into a shell, or to run any command other than those listed.
  • When a package can run code (hooks, MCP servers, or skill files under scripts/), the prompt MUST ask the agent to show what will run and wait for the user’s approval, and requiresApproval is true.
  • Prompts are in English whatever the page language; a page MAY add a one-line explanation in its own language.

The prompt object has host, text (lines joined by LF, no trailing LF), commands (the commands in text, in order), sha256 (of the archive installed) and requiresApproval.

12.2. Claude Code prompt

For each plugin, install.prompts.claude has commands /plugin marketplace add <claudeMarketplace> and /plugin install <p>@<name>, where <name> is the marketplace name of section 8.3, and this text:

Install the "<p>" plugin, version <v>, for Claude Code.
1. Run: /plugin marketplace add <claudeMarketplace>
2. Run: /plugin install <p>@<name>
3. Before enabling it, show me its hooks (<events>) and MCP servers (<servers>) and wait for my approval.
The plugin archive sha256 is <sha256>. Stop if Claude Code reports a different digest.
Do not run any other download or shell command.

Line 4 is present only when the plugin has hooks or MCP servers. <events> is the sorted, distinct hook event names and <servers> the sorted server names, each joined by , , or none when empty.

12.3. Agent Skills prompt

For each skill, install.prompts.agent-skills has the single command npx skills add <siteUrl> and this text:

Install the "<s>" skill, version <v>, with the skills CLI.
1. Run: npx skills add <siteUrl>
2. When it lists the skills on that site, select only "<s>".
3. Before using it, show me the files in its scripts folder and wait for my approval.
The skill archive sha256 is <sha256>. Stop if the CLI reports a digest mismatch.
Do not run any other download or shell command.

Line 4 is present only when the skill has files under scripts/.

Prompts for hosts that install from a downloaded archive are defined in Annex A.5, and exist only when the matching host packages are produced.

13. Security considerations

13.1. Everything published is public

A static site is public even when its source repository is private. Every file in every archive, including scripts and hook commands, is readable by anyone. Generators SHOULD take an explicit allow list of what to publish rather than publish the whole repository, and SHOULD scan generated files for secrets and for names the publisher has marked as internal before publishing.

13.2. Reference closure

A skill or plugin can link to files outside its directory, for example internal documents. Publishing those files automatically would leak them; publishing the link alone would break it. The reference-closure rule (section 4.4) therefore makes such a link an error. The generator MUST NOT resolve it by copying the target.

13.3. Code that runs on install or use

Hooks, MCP servers and skill scripts run on the user’s machine. A page about a package MUST NOT execute any of them. It SHOULD show, for each hook, the event, matcher, command, timeout and whether the host can block on it, and SHOULD let the user read the script files it calls. Hosts differ: Antigravity ignores a failing hook, Grok Build blocks only on PreToolUse with a 5 second default timeout, and Codex runs plugin hooks only for manually installed desktop plugins. Pages SHOULD state such differences. Install prompts require explicit approval for these packages (section 12.1).

13.4. Integrity

Every archive is pinned by SHA-256 in several documents (section 10.4). Claude Code and Agent Skills clients verify the digest before installing. For hosts that install from a downloaded archive, the user or the agent has to compare the digest with the catalog or SHA256SUMS; install prompts say so.

A digest proves that bytes are unchanged, not who produced them. A site that is compromised can change archives and digests together. Publishers who need provenance can add build attestations for archives; they are outside this profile.

13.5. Archive extraction

A client that extracts an archive MUST reject members with absolute paths, .. segments or links. It SHOULD cap the extracted size. Canonical archives never contain them, so a member that does is a sign of tampering.

13.6. Install prompts

An install prompt is input to an agent, and a hostile page could try to inject instructions through it. Users are advised to take prompts only from the site that publishes the package. An agent that receives a prompt is expected to treat its text as a request from the user, not as authority, and to refuse commands outside the host’s install commands; this profile does not define agents, so this is advice, not a requirement. The fixed templates of section 12 keep legitimate prompts short and checkable.

13.7. Well-known URIs

Whoever can write to /.well-known/ of an origin can speak for it. On shared hosts, publishers are advised to make sure no other party can write files to the site.

13.8. Threats considered and residual risk

This section adds no requirement. It records which attacks the profile addresses and which it leaves open, as RFC 3552 asks of a security considerations section.

  • Eavesdropping and modification in transit. Every URL in generated JSON is an absolute https URL (section 7.1), so the profile relies on TLS between the site and the client. It signs nothing itself.
  • A compromised site or source repository. Out of scope: whoever controls the site can change archives and every digest together (section 13.4).
  • Replay of older documents. A cache, a mirror or an attacker that serves an older catalog or marketplace file can lead a client to an older version. Its archive digest still matches, so the client installs genuine but older bytes, which can include a yanked version when its archive is still served from elsewhere. The profile has no freshness or rollback protection beyond the short cache lifetime of section 11.1.
  • Malicious content. A package can contain hooks, MCP servers and scripts that act against the user. The profile does not judge content; it makes that code visible on pages and in prompts and asks for approval (sections 12.1 and 13.3).
  • Prompt injection. Install prompts are text for an agent (section 13.6); a hostile page can imitate them. The fixed templates make a legitimate prompt recognizable but do not authenticate it.
  • Denial of service. Input validation caps file and archive sizes (section 4.4), and clients cap extraction (section 13.5). The size of the catalog and the number of packages are not bounded.

14. Privacy considerations

Published files contain only what the input contains plus digests and URLs. Generators MUST NOT add download counters, tracking parameters or analytics to generated files or URLs.

The rest of this section adds no requirement; it follows the questions of RFC 6973.

  • Data published. Catalogs, entry documents and marketplace files copy the input’s names, descriptions and display fields, including author, homepage and repository. An author object can hold a person’s name and email address, which every visitor and crawler can then read. Publishers are advised to review these fields before publishing.
  • Observers. The static host and any content delivery network in front of it see every request for the entry points and archives, with the client’s IP address and user agent. The profile cannot hide that a client fetched a given package; it only keeps the published files free of identifiers of their own.
  • Identifiers. Generated files carry no per-user or per-download identifier. The optional generator member names the implementation and its version.
  • Retention. The lock, which records every published version and the reason for each yank, stays in the source repository and is never published (section 11.2).

15. IANA considerations

This document has no IANA actions. The section is kept, as RFC 8126 recommends, so that readers see that nothing is requested.

It uses /.well-known/ai-catalog.json and /.well-known/agent-skills/, which are defined, and proposed for registration, by their own specifications. It defines no media type and no link relation; the media types in sections 8.1 and 9 are those of the upstream documents.

16. Conformance

16.1. Conformance classes

  • A conforming generator, given an input and parameters, either writes exactly the files of sections 5 to 10 with the content those sections define, byte for byte, or rejects the input under section 4.4 and writes nothing.
  • A conforming site serves the files a conforming generator wrote, at their paths and with their bytes. It SHOULD serve .json as application/json and .zip as application/zip, and SHOULD allow cross-origin reads. The requirements on pages in sections 9, 11.4, 12.1 and 13.3 apply to the HTML pages of a conforming site.
  • A conforming client meets the requirements on clients in sections 6.2, 7.2 and 13.5. This profile places no other requirement on how a client lists or installs packages; the hosts’ own documents govern that.

Annex A outputs are OPTIONAL. A generator that produces them still conforms when hostPackages is empty and it produces none.

16.2. Conformance suite

The suite lives in conformance/<version>/ of the specification repository. Each case is a directory:

  • case.json: identifier, description, the sections it exercises, level (required or optional), expect (accept or reject), rules for reject cases, and the generator parameters (case.schema.json).
  • input/: the input tree.
  • expected/: for accept cases, every file the generator writes, at its path relative to the origin root, plus _fragments/ with the llms.txt section (llms-section.md) and the head links (head-site.html, head-plugin-<p>.html, head-skill-<s>.html). For reject cases, only rejection.json with the expected rule identifiers.
  • SHA256SUMS: the digest of every file under expected/.

An implementation passes an accept case when it writes exactly the files under expected/, outside _fragments/, with identical bytes, and produces the fragments when asked. It passes a reject case when it writes nothing and reports at least the listed rules. A conformance claim names the suite version and passes every required case.

The suite tests generators only. A case whose parameters name a lock holds that lock in input/; a case without it has no lock, whatever file name an implementation uses by default (section 11.2).

16.3. Schemas and prose

The JSON Schemas of a version describe the structure of the documents that version defines. Where a schema and the prose differ, the prose governs. A schema is never stricter than the prose: a document that meets the prose validates against its schema, and a schema that rejects such a document is a defect in the schema. A schema can be looser, because some rules, such as sort order, digests and canonical form, are not expressed in JSON Schema.

17. Implementation notes (informative)

  • Both build paths give the same site. On GitHub Pages a site can be built by the classic branch build (the github-pages gem, Jekyll 3.10, safe mode) or by a GitHub Actions workflow. A generator that runs before Jekyll, and writes its output into the site source, works with both: the classic path commits the output, the Actions path writes it just before the build. An implementation is expected to give byte-identical sites through both paths, and to check this in its own CI. This is a condition on implementations, not on the format.
  • Jekyll skips paths that start with a dot. Sites need include: [.well-known, .claude-plugin, .agent-marketplace] (and .agents, .antigravity-plugin for Annex A trees). Actions deployments with actions/upload-pages-artifact also need include-hidden-files: true, or the dot directories are dropped silently. Do not add .nojekyll, which turns off the classic build.
  • Files with YAML front matter, such as SKILL.md, are rendered by Jekyll. A generator has to make sure the published bytes are the input bytes, for example by serving them from a path Jekyll copies unchanged, and is advised to check the built site.
  • GitHub Pages limits a site to 1 GB and a deployment to 10 minutes. A generator can warn well before the size limit and keep the number of retained versions small.
  • The reference implementation is jekyll-theme-modootoday.

Annex A. Per-host packaging (informative)

This annex describes the optional host packages for hosts that cannot install from a URL. Host formats change often; the host documentation is authoritative: Agent Plugins 1.0, Codex plugins, Antigravity plugins, Gemini CLI extensions and Grok Build plugins.

A.1. Host tree

With any hostPackages, the generator writes <base>tree/, a marketplace tree in which each plugin carries every host’s manifest side by side:

.claude-plugin/marketplace.json      relative sources (Claude Code, Grok Build, Codex fallback)
.agents/plugins/marketplace.json     Codex; local sources only
plugins/<p>/
  .claude-plugin/plugin.json         input
  plugin.json                        Agent Plugins 1.0 (Codex)
  .antigravity-plugin/plugin.json    Antigravity; takes precedence over plugin.json
  gemini-extension.json              Gemini CLI
  skills/  agents/                   input, shared
  commands/*.md                      input (Claude Code)
  commands/*.toml                    Gemini CLI
  hooks/hooks.json                   input (Claude Code event names)
  hooks.json                         Antigravity
  .mcp.json                          input (Claude Code)
  mcp.json                           Agent Plugins
  mcp_config.json                    Antigravity

Codex skips github and archive sources, so the tree’s marketplace files use relative sources only. The URL-mode marketplace of section 8.3 is a different file with a different name.

A.2. Field mapping

Input (.claude-plugin/plugin.json) Agent Plugins / Codex Antigravity Gemini CLI
name name name name
displayName extensions["com.openai"].interface.displayName displayName not carried
version, description same same same
author, homepage, repository, license, keywords same same not carried
skills/ skills/ skills/ skills/
agents/*.md not carried agents/ agents/*.md (preview)
commands/*.md not carried not carried converted to commands/*.toml
hooks/hooks.json extensions["com.openai"].hooks root hooks.json hooks/hooks.json, renamed events, timeouts in ms
.mcp.json mcp.json mcp_config.json, servers named <p>_<server> mcpServers in the manifest

A.3. Hook events

Claude Code Codex Antigravity Gemini CLI Grok Build
PreToolUse PreToolUse PreToolUse BeforeTool PreToolUse
PostToolUse PostToolUse PostToolUse AfterTool PostToolUse
SessionStart SessionStart SessionStart SessionStart SessionStart
SessionEnd SessionEnd not carried SessionEnd SessionEnd
UserPromptSubmit UserPromptSubmit not verified BeforeAgent UserPromptSubmit
Stop Stop Stop AfterAgent Stop
PreCompact PreCompact not carried PreCompress PreCompact
Notification not carried not carried Notification Notification

Events without a counterpart are left out of that host’s package, and the page says the hook does not run there.

A.4. Host archives

Archive Path Content
Antigravity <base>dist/plugins/<p>/<v>/<p>.antigravity.zip the plugin root with the Antigravity files
Gemini CLI <base>dist/plugins/<p>/<v>/<p>.gemini.zip the plugin root with the Gemini CLI files
Bundle <base>dist/<v>/market.zip all of <base>tree/, where <v> is the catalog publication version

They use the encoding of section 10.2 and are listed in SHA256SUMS, in packages (antigravityArchive, geminiArchive) and in the catalog bundle.

A.5. Local install prompts

For a host that installs from a local path, the prompt asks the agent to download the archive, compare its SHA-256 with the catalog value and stop on a mismatch, extract it, and run the host’s own install command: codex plugin marketplace add ./market for Codex, agy plugin install ./<p> for Antigravity, gemini extensions install ./<p> for Gemini CLI, and --plugin-dir ./market/plugins/<p> for Grok Build. Exact templates will be defined once each path has been tested against the host.

Annex B. Example (informative)

A minimal catalog for one skill-only plugin; the conformance suite has complete, byte-exact examples.

{
  "$schema": "https://static-agent-marketplace.org/schemas/draft/catalog.schema.json",
  "entryPoints": {
    "aiCatalog": "https://plugins.example.com/.well-known/ai-catalog.json",
    "catalog": "https://plugins.example.com/.agent-marketplace/catalog.json",
    "claudeMarketplace": "https://plugins.example.com/.claude-plugin/marketplace.json",
    "llmsTxt": "https://plugins.example.com/llms.txt",
    "sha256sums": "https://plugins.example.com/.agent-marketplace/SHA256SUMS"
  },
  "external": [],
  "marketplace": {
    "name": "example-market",
    "owner": {
      "name": "Example Publisher"
    },
    "url": "https://plugins.example.com/"
  },
  "plugins": [],
  "skills": [],
  "specVersion": "0.1.0-draft"
}

Annex C. References

Normative:

Informative:

Annex D. Changes

  • 0.1.0-draft: first editor’s draft.

Edit this page