Skip to main content

Creating Plugins

A step-by-step guide to creating a new harness-kit plugin.

1. Create the Plugin Directory

plugins/<plugin-name>/
├── .claude-plugin/
│ └── plugin.json
├── scripts/ ← optional, for bundled utilities
└── skills/
└── <skill-name>/
├── SKILL.md
└── README.md

Plugins can contain more than skills — agents, hooks, MCP servers, LSP servers, scripts. Everything inside the plugin directory ships together. Reference bundled files via ${CLAUDE_SKILL_DIR} in SKILL.md.

2. Write the Plugin Manifest

Create plugins/<plugin-name>/.claude-plugin/plugin.json:

{
"name": "<plugin-name>",
"description": "One sentence describing what this plugin does.",
"version": "0.1.0"
}

2a. Declare Environment Requirements

If your plugin depends on environment variables (API keys, tokens, service credentials), declare them in plugin.json under requires.env:

{
"name": "<plugin-name>",
"description": "One sentence describing what this plugin does.",
"version": "0.1.0",
"requires": {
"env": [
{
"name": "MY_API_KEY",
"description": "API key for My Service — used to authenticate requests",
"required": false,
"sensitive": true,
"when": "When the plugin calls My Service"
}
]
}
}

Each entry needs a name (the env var) and description (one sentence: what it's for). Set required: true only if the plugin can't function at all without it — prefer required: false with graceful degradation in SKILL.md for optional integrations.

See Secrets & Configuration for the full field reference and security guidelines.

3. Register in the Marketplace

Add an entry to .claude-plugin/marketplace.json under plugins:

{
"name": "<plugin-name>",
"source": "./<plugin-name>",
"description": "One sentence describing what this plugin does.",
"version": "0.1.0",
"author": { "name": "siracusa5" },
"license": "Apache-2.0"
}

source is relative to pluginRoot (./plugins), so "./research" resolves to ./plugins/research.

4. Write the SKILL.md

The SKILL.md is what Claude Code reads at runtime. Quality here is the primary review criterion.

Frontmatter (required)

---
name: my-skill
description: Use when user invokes /my-skill with [argument types]. [Behavior.] Do NOT use for [anti-patterns].
dependencies: python>=3.10, pandas>=1.5.0
disable-model-invocation: true
user-invocable: true
---

Full frontmatter field reference:

FieldRequiredConstraintPurpose
nameyes64 chars max, lowercase, hyphenatedSlash command name
descriptionyes1,024 chars max, no < or >Activation trigger — Claude reads this to decide when to load the skill
dependenciesnoComma-separated package specsRuntime dependencies (e.g., python>=3.10)
disable-model-invocationnobooleantrue = only user can invoke, not auto-triggered by Claude
user-invocablenobooleanfalse = Claude-only background knowledge, not a slash command
contextnoforkRuns skill in isolated subagent with no conversation history
agentnoExplore, PlanSpecialized subagent execution environment

Description best practices:

The description is Claude's routing signal — it's loaded for every installed skill on every turn. Write it like a trigger rule, not a summary.

  • Start with "Use when" and name the invocation pattern explicitly
  • Include specific phrases a user would type: "review my code", "explain this function"
  • Include negative triggers to prevent false activation: "Do NOT use for quick factual questions — use /explain instead"
  • Format: [What it does] + [When to use it] + [Key triggers] + [Anti-patterns]
  • No XML angle brackets (< or >); max 1,024 characters

Good:

Use when user invokes /research with a URL, GitHub repo, YouTube video, or local file. Processes sources into indexed research. Do NOT use for quick factual questions — use /explain instead.

Too vague:

Use this skill to do research.

Spec reference: This frontmatter format follows the Agent Skills open standard. See also: How to Create Custom Skills.

Size guidelines

  • Keep SKILL.md under 500 lines / 5,000 words
  • Move reference tables, tag taxonomies, and lookup data to a references/ subdirectory
  • Reference bundled files via ${CLAUDE_SKILL_DIR}/references/filename.md
  • Put critical instructions at the top — Claude may deprioritize buried content
skills/
└── my-skill/
├── SKILL.md
└── references/
├── tag-taxonomy.md
└── quick-reference.md

Required sections

SectionRequired when
## Overview with core principlesAlways
## Workflow with numbered stepsAlways (even 1-step skills)
## Common Mistakes tableAny skill with 3+ steps or file writes
## Scope ControlsAny skill that touches files
## Argument Types tableAny parameterized skill

Optional sections

Common optional sections (not in the required table) that carry real behavioral signal:

SectionWhen to include
## When to UseSkills with multiple trigger patterns or disambiguation needs
## Invocation ExamplesParameterized skills with varied input types
## Adaptive BehaviorSkills that change workflow based on input analysis
## Red Flags / ## Anti-patternsComplex skills with common misuse patterns

Argument substitution

Use $ARGUMENTS to pass the full argument string from the slash command invocation into SKILL.md content. Positional arguments are $0, $1, etc.

## Workflow

The user invoked: /my-skill $ARGUMENTS

### Step 1: Parse Input
Argument 1: $1
Argument 2: $2

Invoked as /my-skill readme src/auth/$ARGUMENTS = readme src/auth/, $1 = readme, $2 = src/auth/.

Dynamic context injection

Use `!command` syntax to run a shell command and inject its output into the skill before Claude reads it:

Current git diff:
`!git diff main...HEAD`

Review the changes above.

The shell command runs at skill load time. Output is injected inline. Use this to pre-populate skills with live context — current branch, recent commits, environment info.

Workflow conventions

  • Number all steps. Label mandatory order: ## Workflow (MANDATORY — follow in order)
  • Each step: one action, concrete command where applicable.
  • Use Bash blocks for exact commands Claude should run.
  • If a step requires stopping to verify: **STOP HERE until file is written.**

Common Mistakes table

## Common Mistakes

| Mistake | Fix |
|---------|-----|
| Did X wrong | Do Y instead |

Two columns, two sentences max per row.

What NOT to add

  • Don't repeat the README in the SKILL.md — README is for humans, SKILL.md is for the model.
  • Don't describe philosophy or background. State the workflow.
  • Don't add sections not listed above unless they carry real behavioral signal.

5. Write the README.md

The README is human-facing documentation. Cover:

  • What the plugin does (prose overview)
  • Requirements
  • Usage examples (copy-paste ready)
  • Output structure
  • Design notes (rationale)

6. Add to Root README

Under the ## Plugins table in the repo root README.md, add a row linking to your plugin's README.

7. Test It

# From within Claude Code:
/plugin marketplace add ./ # add local marketplace
/plugin install <plugin-name>@harness-kit

Versioning

Versions in plugin.json and marketplace.json must always match.

  • Patch (0.1.0 → 0.1.1): Bug fixes, typo corrections, documentation. No behavior change.
  • Minor (0.1.0 → 0.2.0): New features, new capabilities. Existing behavior unchanged.
  • Major (0.x → 1.0): Breaking changes — renamed commands, removed features, changed output structure.

Release Checklist

  1. Bump version in both plugin.json and marketplace.json (must match)
  2. Commit: chore: bump <plugin> to vX.Y.Z
  3. Create release: gh release create vX.Y.Z --generate-notes

CI Validation

Two checks run on every PR:

CheckWhat it validates
JSON manifests validmarketplace.json and all plugin.json files parse without error
Version alignmentversion in plugin.json and marketplace.json must match exactly

Both must pass before merge.