Skip to main content
The facet manifest (facet.json) is the source of truth for a facet’s identity, the text assets it contains, the MCP server declarations it contributes, and the supplementary files it ships. The names it declares are authored names: they fix every archive path and integrity value, and a consuming project may materialize an asset or a server under a different name without changing any of that. This page defines every field in the manifest schema and is the canonical reference for the facet name grammar, the asset name grammar, and the server declaration grammar.
The facet manifest is distinct from the project manifest (facets.json), which declares which facets a consuming project installs. Throughout this page, “the manifest” means the facet manifest.
For conceptual guides to each asset type, see Skills, Commands, Agents, and MCP servers.

Example

facet.json

Schema

Every top-level field of facet.json. The name and version fields MUST be present; a manifest missing either MUST be rejected. A facet MUST also declare at least one asset, which is a text asset (skills, agents, commands) or an MCP server (servers), or else a composed facet (facets). Consumers MUST tolerate unrecognized top-level fields rather than reject them. The one exception is inside an MCP server declaration, which is a closed object.

Fields

string
required
Facet identity: an unscoped name (cowsay) or a scoped name (@scope/name). Validated, never normalized. See Facet name grammar. This is the facet’s own name; the names of the assets it contains follow the separate asset name grammar.
string
required
The facet version. The schema requires a string; a registry MAY require semver, and the public registry does.
string
Human-readable description.
string
Author name or identifier.
boolean
Privacy declaration. true declares private publish intent; false or omission is public-by-default. See Privacy.
map of string → skill descriptor
Skill name → skill descriptor. A skill descriptor MAY declare a files array of companion files shipped inside the skill directory.
map of string → agent descriptor
Agent name → agent descriptor.
map of string → command descriptor
Command name → command descriptor.
array of string
Top-level supplementary files: exact repo-relative paths to non-asset files that ship in the archive but never materialize, such as README.md or LICENSE. Exact paths only, with no globs. Every path MUST satisfy the portable path grammar and MUST NOT resolve under skills/ (skill companions are declared in their skill’s own files).
array of facets entries
References to other facets (name@version strings or selective entries). Schema-validated only: build does not resolve the references, and facet add rejects sources that declare a non-empty facets array.
map of string → MCP server declaration
Server name → concrete MCP server declaration: a standard-input server ({ "type": "stdio", … }) or a Streamable HTTP server ({ "type": "http", … }). Declarations are materialized into each selected adapter’s native project configuration at install time, after explicit approval. Server names use the asset name grammar and occupy their own namespace.

Privacy

The optional private field declares the author’s publish-visibility intent:
  • private: true declares that the facet is private.
  • private: false, or omitting the field, declares the facet is public (public-by-default).
private is a recognized schema field, not an unknown extension. It MUST be a boolean: a non-boolean value (string, number, object, array, or null) MUST be rejected rather than coerced. Validation MUST NOT inject private: false into a manifest that omits the field, so tooling sees exactly what the author wrote. The private declaration is part of manifest content: it is embedded verbatim in the built .facet artifact and travels to the registry at publish time (see Publish). It expresses author intent; registry-side authorization and visibility enforcement are the registry’s responsibility, not the CLI’s.

Facet name grammar

A facet identity is one of exactly two forms:
  • Unscoped: a single slug, e.g. cowsay.
  • Scoped: @<scope>/<slug>, where both the scope and the base name are slugs, e.g. @julian/cowsay.
Every slug component (the unscoped name, a scope, and a scoped base name) MUST satisfy the same grammar:
  • It MUST be at least 2 and at most 64 characters long.
  • It MUST start with a lowercase ASCII letter (a-z).
  • It MUST end with a lowercase ASCII letter or ASCII digit.
  • It MUST contain only lowercase ASCII letters, ASCII digits, and hyphens (-).
  • It MUST NOT contain consecutive hyphens.
Names are validated, never normalized. Uppercase letters, non-ASCII characters, underscores, dots, spaces, and other characters outside the grammar are rejected rather than rewritten. Valid: ab, cowsay, admin-tester, apple-b34r, @julian/cowsay, @acme/deploy-tools. Invalid: The following scoped shapes are rejected: a bare scope (@scope), a missing scope (@/name), a missing name (@scope/), extra path depth (@scope/name/extra), and the legacy un-prefixed form (scope/name).

Asset names

Asset names, covering skills, commands, and agents, follow the Agent Skills name grammar, interpreted here as a single ASCII segment: 1–64 characters of lowercase ASCII letters, digits, and hyphens, not starting or ending with a hyphen and with no consecutive hyphens. Unlike the earlier format, a current-format asset name is a single segment: it MUST NOT contain /, so slash-namespaced names like viper-plans/planning are no longer valid (use viper-planning). Skills and commands share one namespace: within a facet, a skill and a command MUST NOT use the same name. Agents are a separate namespace and may reuse a skill or command name. MCP server names use this same grammar and are a third, separate namespace: a server and a skill MAY both be named review. The manifest is validated against this rule within a single facet. The same rule is applied again across every facet in a project at install time, over the names text assets are actually materialized under. See Namespaces. A companion file’s path may contain / for directory depth (references/api.md), but those separators are part of the file path, not the skill name, which is still a single segment.
Legacy 0.1 archives retain their earlier multi-segment naming during the compatibility window. New manifests use the single-segment grammar above. A legacy manifest MUST NOT declare servers at all: MCP declarations are a current-format feature, and a legacy archive carrying any servers member is rejected rather than installed.
The facet manifest MUST NOT be modified by a build, publish, or install. The project manifest follows different rules: an install writes it.

Text assets

Text assets are the asset types that materialize as files: skills, agents, and commands. The other asset type, an MCP server declaration, materializes into tool-owned configuration instead. Each text asset is declared under its type’s map, and its name maps to a file at a conventional path. The file’s content is the asset’s prompt, resolved by convention at build time rather than referenced by a manifest field.
  • Skill: skills/<name>/SKILL.md. Each skill lives in its own directory, per the Agent Skills convention, and the main file MUST be named SKILL.md. A skill directory MAY also contain declared companion files at any safe depth.
  • Agent: agents/<name>.md, whose content is the agent’s system prompt.
  • Command: commands/<name>.md, whose content is the command’s prompt.
A primary asset file (a SKILL.md, agent, or command file) MAY carry author-supplied YAML front matter. It ships verbatim in the archive and is reconciled with the manifest only at install time: materialization merges the manifest’s name and description, plus any per-adapter extras, on top of whatever the author wrote. The manifest always wins, so a facet cannot override its own asset identity through front matter.
The name written at install time is the asset’s effective name, the one the consuming project chose. description stays as the publisher wrote it. See Two identities.
An asset descriptor’s adapters block is validated at build time against each installed adapter’s schema: unknown adapters produce a warning, and invalid metadata for an installed adapter is a build error.

MCP server declarations

A facet can declare Model Context Protocol servers its text assets expect to be available. A declaration is portable connection information, not a package reference: it says how a tool should launch or reach a server, and nothing about where that server comes from. Facets never resolves, downloads, versions, or runs a declared server. A server declaration is an asset, and the only one that materializes into a file the facet does not own. At install time each selected adapter translates the portable declaration into its own tool’s native project configuration, after the user approves it.
facet.json

Declaration types

A declaration is exactly one of two shapes, selected by its required type tag. A field from one shape MUST NOT appear in the other.
standard-input server
A server the tool launches as a subprocess.
Streamable HTTP server
A server the tool connects to over Streamable HTTP.

Closed objects

A server declaration MUST reject any member its selected shape does not define. This is the one exception to the manifest’s general tolerate-unknown-fields rule. These fields change what runs. If one consumer ignored an unrecognized headers, cwd, or shell member while another honored it, both would report successful validation while configuring materially different behavior. A new portable field requires a schema revision, not a tolerated extension. Unrecognized fields outside the declaration objects, including at the top level of the manifest, are still tolerated and preserved.

Canonical declaration fingerprint

A declaration has a canonical fingerprint: the identity used to decide whether two facets declare the same server and whether a declaration has already been approved. The encoding is normative, so independent implementations agree:
  • It is computed from the declaration’s meaning, not its spelling, and is tagged with a version so it can evolve.
  • Argument order is preserved, because reordering arguments changes what runs.
  • Environment keys are sorted, because their order does not.
  • An omitted args or env is equal to an empty one.
  • It is independent of both the authored and effective server name.
Because the fingerprint carries no command, URL, or environment data, a consumer can prove a declaration was approved without storing the declaration itself.

Environment name grammar

An env key MUST start with an ASCII letter or underscore and continue with ASCII letters, digits, or underscores. Names are validated, never normalized: PATH, _HOME, and API_KEY_2 are valid; 2FA, MY-VAR, and my.var are rejected.
env values are literal strings that ship inside the published archive. Never put a secret, token, or credential in one. The portable schema has no headers, credentials, OAuth, or substitution grammar: authentication belongs to the target tool.

What a declaration is not

  • It is not a package reference. There is no registry, version range, or OCI image for a server. The earlier speculative version-string and { image } reference forms are rejected in every manifest format.
  • It is not a separately published artifact. There is no standalone server manifest; a declaration travels inside facet.json. See Publish.
  • It is not an archive entry. Declarations add no file to the .facet archive; the embedded manifest is their integrity-protected representation, so changing a declaration changes the facet’s integrity.

Supplementary files

Beyond its assets, a facet can declare supplementary files: non-asset files that ship inside the archive and are integrity-protected, but are not independently installable assets. They have no asset type, no adapter metadata, and no lockfile asset tuple. There are two declaration sites, and every supplementary archive entry MUST be derivable from one of them (there is no auto-discovery):
  • Top-level files: repo-relative paths at the tree root, declared in the manifest’s files array. These ship but never materialize, so they are not written to disk at install time.
  • Per-skill files: paths relative to a skill’s directory, declared in that skill descriptor’s files array. A companion materializes atomically with its skill and cannot list SKILL.md itself.
Supplementary files are declared as exact paths only, with no globs, and each path MUST satisfy the portable path grammar. A top-level path MUST NOT resolve under skills/, so a skill’s files have exactly one declaration site, and MUST NOT be the root facet.json, which the archive always carries. Supplementary bytes are shipped verbatim: binary and empty files are valid, and front matter is never parsed or stripped. README.md and the extensionless README are ordinary top-level files declarations. There is no README-specific manifest field. Authoring support is described in Create a Facet and facet edit.
A CLI older than the first 0.2-producing release tolerates the files fields but omits their bytes when it builds, producing a declared-but-empty facet. Build with a current release so declared files actually ship.