Static Agent Marketplace
- Version: 0.1.0-draft
- Date: 2026-11-04
- Status: Editor’s Draft, the first maturity stage of GOVERNANCE.md. Not stable; anything can change before 1.0.0.
- Editors: Choi Won (@myso-kr)
- This version: static-agent-marketplace.org/spec/draft/
- Latest release: none yet
- Previous version: none
- Schemas: static-agent-marketplace.org/schemas/
- Repository: static-agent-marketplace/static-agent-marketplace-spec
- Feedback: GitHub issues
- License: Copyright 2026 Static Agent Marketplace contributors. Licensed under the Apache License 2.0.
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/andSHA256SUMS(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>.jsonor<base>entries/skills/<s>.json: one catalog item with$schema,kindandspecVersionadded (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.jsonplus component folders such asskills/,agents/,commands/andhooks/. - 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.mdfile and any supporting files. - Skill archive (from Agent Skills discovery 0.2.0): The archive of one skill with
SKILL.mdat 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:
- 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.
- 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.
- 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.
- Extension points only. Where this profile adds data to an upstream document, it uses that document’s extension mechanism (for AI Catalog, the
extensionsmember; 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
versionparameter (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
licensefront matter field, or else thelicenseparameter. - The generator synthesizes a plugin manifest
.claude-plugin/plugin.jsonwhose content is the canonical JSON (section 7.1) of an object with exactly the membersdescription,license,nameandversion. - The plugin directory is the synthesized manifest plus every file of
skills/<s>/atskills/<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,ownerandmetadata.descriptionbecome the catalog’smarketplace.name,marketplace.ownerandmarketplace.description. - An entry whose
sourceis a relative path (./...) is a local plugin. The generator reads and packages it. - An entry whose
sourceis an object of typegithub,url,git-subdirornpmis an external plugin. The generator lists it underexternalin 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,
versionis 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,repositoryandkeywordsare taken from the entry when present, else fromplugin.json.categoryandtagsare taken from the entry. - Entry component fields (
commands,agents,skills,hooks,outputStyles,themes) andstrictare 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,\rand\tuse their short forms, other control characters use\u00XXwith 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.
nameis 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.owneris the catalog owner;metadata.descriptionis the marketplace description when there is one.pluginshas one item per plugin, sorted by name, withname,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.
9. Head discovery links
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.
- ZIP format as described by the PKWARE APPNOTE, without ZIP64, encryption, data descriptors, extra fields, comments or directory entries.
- Members ordered by path, comparing UTF-8 bytes.
- Every member stored without compression (method 0).
- 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.
- 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
0100644or0100755shifted left by 16 bits, and the offset of the local header. - 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,COM1toCOM9,LPT1toLPT9, with or without an extension). - Only regular files are members. Empty directories are not represented.
- The mode is
0755when the input file is executable (git mode100755) and0644otherwise. - 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
yankedwith 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.jsonsetsmetadata.lifecycletodeprecatedstays published withlifecycle{"status": "deprecated"}in its item, plusreplacedByandnotewhenmetadata.replacedByandmetadata.lifecycleNoteare 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, andrequiresApprovalistrue. - 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
httpsURL (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,homepageandrepository. Anauthorobject 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
generatormember 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
.jsonasapplication/jsonand.zipasapplication/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(requiredoroptional),expect(acceptorreject),rulesfor reject cases, and the generatorparameters(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, onlyrejection.jsonwith the expected rule identifiers.SHA256SUMS: the digest of every file underexpected/.
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-pagesgem, 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-pluginfor Annex A trees). Actions deployments withactions/upload-pages-artifactalso needinclude-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:
- [AGENT-SKILLS] Agent Skills specification, as published.
- [AGENT-SKILLS-DISCOVERY] Agent Skills discovery via well-known URIs, version 0.2.0.
- [AI-CATALOG] AI Catalog, draft,
specVersion1.0. - [APPNOTE] PKWARE APPNOTE, the ZIP format.
- [CLAUDE-CODE-MARKETPLACE] Claude Code plugin marketplace reference, Claude Code 2.1.224 or later.
- [FIPS180-4] NIST, Secure Hash Standard (SHS), FIPS PUB 180-4, August 2015.
- [HTML] WHATWG, HTML Living Standard.
- [JSON-LD] W3C, JSON-LD 1.1, Recommendation, July 2020.
- [JSON-SCHEMA] JSON Schema 2020-12, core and validation vocabularies.
- [LLMS-TXT] llms.txt, proposal.
- [RFC2119] S. Bradner, Key words for use in RFCs to Indicate Requirement Levels, BCP 14, RFC 2119, March 1997.
- [RFC3629] F. Yergeau, UTF-8, a transformation format of ISO 10646, STD 63, RFC 3629, November 2003.
- [RFC3986] T. Berners-Lee, R. Fielding and L. Masinter, Uniform Resource Identifier (URI): Generic Syntax, STD 66, RFC 3986, January 2005.
- [RFC8174] B. Leiba, Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words, BCP 14, RFC 8174, May 2017.
- [RFC8259] T. Bray, The JavaScript Object Notation (JSON) Data Interchange Format, STD 90, RFC 8259, December 2017.
- [RFC8615] M. Nottingham, Well-Known Uniform Resource Identifiers (URIs), RFC 8615, May 2019.
- [SCHEMA-ORG] schema.org
SoftwareApplication. - [SEMVER] Semantic Versioning 2.0.0.
- [SPDX] SPDX license expressions, SPDX Specification 3.0.1, Annex B.
Informative:
- [AGENT-PLUGINS] Agent Plugins 1.0.
- [ANTIGRAVITY] Antigravity plugins.
- [CODEX] Codex plugins.
- [GEMINI-CLI] Gemini CLI extensions.
- [GH-PAGES] GitHub Pages limits.
- [GNU-SHA256SUM] GNU
sha256sum, GNU Coreutils manual. - [GROK-BUILD] Grok Build plugins.
- [REFERENCE-IMPLEMENTATION] jekyll-theme-modootoday, the reference implementation.
- [REPRO-ARCHIVES] Reproducible builds: archives.
- [RFC3552] E. Rescorla and B. Korver, Guidelines for Writing RFC Text on Security Considerations, BCP 72, RFC 3552, July 2003.
- [RFC6973] A. Cooper et al., Privacy Considerations for Internet Protocols, RFC 6973, July 2013.
- [RFC8126] M. Cotton, B. Leiba and T. Narten, Guidelines for Writing an IANA Considerations Section in RFCs, BCP 26, RFC 8126, June 2017.
Annex D. Changes
- 0.1.0-draft: first editor’s draft.