How the Claude Plugins Validation Action Works: A Deep Dive into the CI Pipeline

The Claude plugins validation action is a composite GitHub Action in the anthropics/claude-plugins-community repository that enforces a three-layer defense-in-depth pipeline: change detection to scope validation, eleven hard-coded policy invariants (I1-I11) for security and consistency, and schema validation via the official claude-code CLI to ensure every plugin meets JSON Schema requirements before merging.

Every plugin submitted to the Claude marketplace undergoes rigorous automated checks through the Validate‑Plugins GitHub Action. Located in the anthropics/claude-plugins-community repository, this composite action serves as the canonical CI gate, combining structural diffing, policy enforcement, and schema validation to prevent malformed or unsafe plugins from reaching production.

Three-Layer Validation Architecture

The validation action implements a defense-in-depth strategy through three distinct layers:

  • Change Detection – Identifies which marketplace entries, external plugins, and in-repo plugin folders were modified by the PR, ensuring validation effort is scoped only to relevant changes.
  • Policy Invariants – Enforces eleven hard-coded "I-rules" (I1-I11) that validate naming conventions, sorting, description limits, URL safety, SHA presence, and hidden Unicode detection via 11-validate-invariants.sh.
  • Schema Validation – Uses the official claude-code CLI (@anthropic-ai/claude-code) to execute claude plugin validate against the assembled marketplace.json and individual plugin sources.

All layers are orchestrated by the composite action definition in .github/actions/validate-plugins/action.yml and invoked from .github/workflows/validate-plugins.yml.

Step-by-Step Execution Flow

Environment Setup and CLI Installation

The action begins by configuring the runner environment. It installs jq, Node.js 20, and the claude-code CLI package. The CLI installation includes a resilient retry loop (lines 77-89 in action.yml) to ensure the native binary is present even on flaky runners, guaranteeing that schema validation tools are available before proceeding.

Change Detection (00-detect-changes.sh)

The script computes three JSON arrays representing modified artifacts:

  • entries – Changed marketplace plugin names
  • external – Changed entries where source is an external repository object
  • folders – Changed in-repo plugin directories

Outputs are written to $VALIDATE_TMP/changes.json and exported as GitHub step outputs (changed-entries, changed-external, changed-folders). If the base ref cannot be fetched, the script gracefully degrades by treating all entries as changed, ensuring no validation gaps occur.

Policy Invariant Enforcement (11-validate-invariants.sh)

This layer loads the assembled marketplace from $VALIDATE_TMP/marketplace.json and validates against eleven invariants:

  • I1-I2 – Enforce alphabetical order and uniqueness of plugin names
  • I3-I11 – Validate description length constraints, detect hidden Unicode characters, enforce name shape requirements, verify source URL safety protocols, check SHA completeness, validate vendored-source existence, and ensure shell-character safety

Invariants can be configured as WARN (via the warn-invariants input) or ERROR. When scope-errors-to-changed is set to true, any per-entry error on a plugin not touched by the PR is automatically downgraded to a warning, preventing legacy defects from blocking unrelated changes.

CLI Schema Validation of Marketplace (20-validate-cli-marketplace.sh)

The action executes claude plugin validate $MARKETPLACE_PATH using the installed CLI. Schema violations are reported as GitHub annotation errors. With the fail-on-warnings input set, warnings can be escalated to hard failures.

External Plugin Validation (30-validate-cli-external.sh)

For each changed external entry, the action:

  1. Clones the referenced repository (respecting the allowed-hosts whitelist)
  2. Checks out the specific commit identified by source.sha
  3. Runs claude plugin validate against the repository's plugin.json

A configurable per-plugin timeout (external-timeout-secs) guards against hung clone operations on slow or unresponsive hosts.

Local Folder and Auxiliary Validation

Reporting and Outputs (90-report.sh)

The final aggregation step emits a markdown summary to the path specified by report-path and sets the action output result to either pass or **fail. All intermediate outputs—including changed-entries, changed-external, and changed-folders`—are available to downstream workflow jobs for artifact upload or additional gating logic.

Configuration and Key Inputs

The workflow invokes the action with several security-critical inputs:

- uses: ./.github/actions/validate-plugins
  with:
    marketplace-path: .claude-plugin/marketplace.json
    skip-local-folders: "true"
    scope-errors-to-changed: "true"
    warn-invariants: "false"
    fail-on-warnings: "false"
    external-timeout-secs: "300"
    allowed-hosts: "github.com,gitlab.com"
  • marketplace-path – Location of the assembled marketplace JSON
  • skip-local-folders – Disables validation of in-repo plugins (useful for repositories hosting only external entries)
  • scope-errors-to-changed – Downgrades errors on untouched plugins to warnings, preventing unrelated legacy issues from blocking PRs
  • external-timeout-secs – Per-plugin clone timeout for external repositories
  • allowed-hosts – Comma-separated whitelist of approved git hosts for external plugin sources

Example Workflow Configuration

To implement the validation action in your repository workflow:

name: Validate Plugins
on:
  pull_request:
    paths:
      - '.claude-plugin/**'
      - '.github/actions/**'
  push:
    branches: [main]
    paths:
      - '.claude-plugin/**'
      - '.github/actions/**'

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0  # Required for change detection

      - uses: ./.github/actions/validate-plugins
        id: validate
        with:
          marketplace-path: .claude-plugin/marketplace.json
          scope-errors-to-changed: "true"
          skip-local-folders: "true"

      - name: Upload Report
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: validation-report
          path: ${{ steps.validate.outputs.report-path }}

For local development testing outside of CI, you can run the CLI validator manually:


# Install the official CLI

npm install -g @anthropic-ai/claude-code@latest

# Validate the marketplace file

claude plugin validate .claude-plugin/marketplace.json

# Validate a single vendored plugin

claude plugin validate path/to/plugin/.claude-plugin

Summary

  • The Validate-Plugins action provides three layers of security: change detection, policy invariants (I1-I11), and JSON Schema validation via the official CLI.
  • Validation scripts are located in .github/actions/validate-plugins/scripts/ and cover everything from Unicode detection to external repository cloning with SHA verification.
  • Key configuration options like scope-errors-to-changed and warn-invariants allow maintainers to balance strictness with pragmatic legacy support.
  • All validation outputs—including changed-entries arrays and markdown reports—are exposed as GitHub Action outputs for downstream automation.

Frequently Asked Questions

What are the I1-I11 policy invariants?

The policy invariants are hard-coded security and consistency checks enforced by 11-validate-invariants.sh. I1-I2 ensure alphabetical ordering and name uniqueness, while I3-I11 enforce description length limits, detect hidden Unicode characters, validate URL safety protocols, verify SHA completeness for external sources, and check for shell-safe characters. These invariants prevent common marketplace pollution vectors and ensure consistent metadata quality.

How does the action handle validation of external plugins from other repositories?

The action clones external repositories referenced in the marketplace JSON, validates the clone against an allowed-hosts whitelist, checks out the exact commit specified by the source.sha field, and runs claude plugin validate against the remote plugin.json. A configurable external-timeout-secs parameter prevents CI hangs from slow git operations.

Can the validation action be run locally for testing?

While the composite action itself requires a GitHub Actions runner, the underlying validation logic can be executed locally by installing the @anthropic-ai/claude-code CLI via npm and running claude plugin validate against your marketplace file or individual plugin directories. This allows developers to catch schema errors before submitting pull requests.

What happens when a legacy validation error exists in an unchanged plugin?

When scope-errors-to-changed is set to true, the action automatically downgrades any ERROR-level finding on plugins not modified by the current PR to WARNING status. This prevents historical validation failures from blocking unrelated contributions while still surfacing issues in the CI logs for eventual remediation.

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 →