How the Marketplace Manifest Works in Claude Plugins: Architecture and Validation

The Claude plugins marketplace manifest system uses a two-tier validation architecture where a central marketplace.json enumerates plugins and a resolution function either discovers existing plugin.json files or synthesizes minimal manifests for skills-only plugins, enabling uniform validation via the claude plugin validate CLI command.

The marketplace manifest system in the anthropics/claude-plugins-community repository orchestrates how external tools are discovered, indexed, and validated. This mechanism relies on a hierarchical manifest structure that balances strict validation requirements with flexibility for lightweight, skills-only integrations.

The Two-Tier Manifest Architecture

The marketplace manifest system operates at two distinct levels: a centralized marketplace registry and individual plugin declarations. This separation allows the community repository to index hundreds of plugins while delegating capability definitions to individual repositories.

Marketplace-Level Definition (.claude-plugin/marketplace.json)

The top-level marketplace.json file serves as the source of truth for the Claude Marketplace UI and CI validation workflows. Each entry contains the plugin's metadata and a source block that pins the plugin code to a specific URL and SHA commit hash.

According to the source code at /.claude-plugin/marketplace.json#L14-L20, a typical entry follows this structure:

{
  "name": "0x",
  "source": {
    "source": "url",
    "url": "https://github.com/0xProject/0x-ai.git",
    "sha": "0167bbb..."
  }
}

This centralized registry enables the marketplace to display plugin information without cloning every repository, while the SHA pinning ensures reproducible builds and security.

Per-Plugin Manifest Resolution

Individual plugins declare their capabilities through plugin manifest files located in one of two canonical paths. The validator function resolve_external_manifest in /.github/actions/validate-plugins/lib/common.sh#L54-L59 checks for:

  1. .claude-plugin/plugin.json (preferred location)
  2. plugin.json at the repository root

If neither file exists, the system evaluates whether the plugin is strict or skills-only.

Resolving Manifests with resolve_external_manifest

The core logic for manifest discovery and synthesis resides in the resolve_external_manifest bash function. This utility determines whether to use an existing manifest or generate a synthetic one based on the plugin's strict flag.

Standard Discovery Logic

When validating a plugin, the function first probes the filesystem for standard manifest locations. As implemented in /.github/actions/validate-plugins/lib/common.sh#L152-L165, the function returns exit code 0 and prints the file path when it locates a valid plugin.json:

resolve_external_manifest() {
  local target=$1 name=$2 strict=${3:-true}
  
  if [[ -f "$target/.claude-plugin/plugin.json" ]]; then
    printf '%s' "$target/.claude-plugin/plugin.json"
    return 0
  fi
  
  if [[ -f "$target/plugin.json" ]]; then
    printf '%s' "$target/plugin.json"
    return 0
  fi
  
  # Skills-only logic follows...

}

Synthetic Manifest Generation for Skills-Only Plugins

Some plugins are intentionally skills-only, shipping no plugin.json file. Their marketplace entries set strict:false. For these entries, the marketplace synthesizes a minimal manifest on-the-fly so that claude plugin validate can still execute.

As documented in /.github/actions/validate-plugins/lib/common.sh#L60-L64, when strict:false is detected and no manifest file exists, the function:

  1. Creates a temporary directory structure
  2. Writes a minimal JSON file containing only the plugin name: { "name": "<plugin-name>" }
  3. Returns exit code 2 to indicate synthesis occurred

This synthesis mechanism ensures uniform validation across all marketplace entries, regardless of whether the original repository contains a full manifest.

CI Validation Flow

The validation pipeline executes in /.github/actions/validate-plugins/scripts/30-validate-cli-external.sh#L11-L27, which orchestrates the end-to-end verification process:

  1. Clone: Retrieves the external plugin repository at the pinned SHA specified in marketplace.json
  2. Resolve: Invokes resolve_external_manifest to locate or synthesize the manifest
  3. Validate: Executes claude plugin validate against the resolved path

The script handles three distinct exit states from resolve_external_manifest:

  • Exit code 0: Manifest found at standard location, proceed with validation
  • Exit code 2: Synthetic manifest created for skills-only plugin, proceed with validation
  • Exit code 1: No manifest found and strict:true, mark as failing

Working with the Manifest System

When developing plugins for the Claude marketplace, you can replicate the CI validation logic locally using the same bash patterns found in the community repository.

Validating Plugins Locally

The following bash snippet mirrors the production validation workflow, handling both strict plugins and skills-only integrations:

#!/bin/bash

resolve_external_manifest() {
  local target=$1 name=$2 strict=${3:-true}
  
  if [[ -f "$target/.claude-plugin/plugin.json" ]]; then
    printf '%s' "$target/.claude-plugin/plugin.json"
    return 0
  fi
  
  if [[ -f "$target/plugin.json" ]]; then
    printf '%s' "$target/plugin.json"
    return 0
  fi
  
  if [[ "$strict" == "false" ]]; then
    mkdir -p "$target/.claude-plugin"
    jq -n --arg name "$name" '{name: $name}' > "$target/.claude-plugin/plugin.json"
    printf '%s' "$target/.claude-plugin/plugin.json"
    return 2
  fi
  
  return 1
}

# Usage example for a skills-only plugin:

manifest_path=$(resolve_external_manifest "/tmp/my-plugin" "my-skill" "false")
exit_code=$?

if [[ $exit_code -eq 0 ]]; then
  echo "Found existing manifest at: $manifest_path"
elif [[ $exit_code -eq 2 ]]; then
  echo "Created synthetic manifest at: $manifest_path"
fi

# Validate using Claude CLI:

claude plugin validate "$manifest_path"

This implementation matches the production logic in /.github/actions/validate-plugins/lib/common.sh, ensuring your local testing aligns with CI requirements.

Key Files in the Repository

Summary

  • The marketplace manifest system uses a centralized marketplace.json to index plugins while delegating capability definitions to individual repositories.
  • The resolve_external_manifest function in common.sh implements a three-tier resolution strategy: check .claude-plugin/plugin.json, check root plugin.json, or synthesize a minimal manifest for skills-only plugins.
  • Exit code 2 from resolve_external_manifest indicates synthetic manifest generation, allowing skills-only plugins to pass validation without shipping a full plugin.json.
  • All marketplace entries, whether strict or skills-only, undergo uniform validation via claude plugin validate against either real or synthesized manifests.
  • SHA pinning in the marketplace manifest ensures reproducible builds and security validation across the plugin ecosystem.

Frequently Asked Questions

What is the difference between a strict plugin and a skills-only plugin?

A strict plugin ships with a full plugin.json manifest file containing detailed capability definitions, tool schemas, and metadata. A skills-only plugin intentionally omits this file and relies on the marketplace to generate a synthetic manifest containing only the plugin name. According to the source code, skills-only entries must set strict:false in marketplace.json to trigger the synthesis logic in resolve_external_manifest.

Where should I place the plugin.json file in my plugin repository?

The validator checks two canonical locations in order of preference: first .claude-plugin/plugin.json (the recommended location), then plugin.json at the repository root. As implemented in /.github/actions/validate-plugins/lib/common.sh#L54-L59, the function returns the path unchanged upon finding either file, with the .claude-plugin/ subdirectory taking precedence.

How does the CI system handle plugins that don't have a manifest file?

When the validator encounters a plugin without a manifest, it checks the strict flag from the marketplace entry. If strict:false, the resolve_external_manifest function creates a temporary directory and writes a minimal JSON file containing only the plugin name, then returns exit code 2 to signal synthesis. The validation script at /.github/actions/validate-plugins/scripts/30-validate-cli-external.sh#L11-L27 recognizes this code and proceeds with claude plugin validate using the temporary file.

Why does the marketplace use SHA pinning in the source definition?

The marketplace.json file specifies each plugin's code location using both a URL and a SHA commit hash. This pinning ensures that the CI validation pipeline and end users load exactly the same code version, preventing supply chain attacks where a repository owner might modify code after listing. The validation script clones the repository at the specific SHA before attempting manifest resolution or validation.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →