Complete field-by-field reference for techpack.yaml. For a tutorial-style introduction, see Creating Tech Packs.
| Field | Type | Required | Description |
|---|---|---|---|
schemaVersion |
Integer |
Yes | Must be 1 |
identifier |
String |
Yes | Unique pack ID. Lowercase alphanumeric + hyphens, e.g. my-pack |
displayName |
String |
Yes | Human-readable name shown in CLI output |
description |
String |
Yes | One-line description of what the pack provides |
version |
String |
Yes | Semantic version, e.g. "1.0.0" |
minMCSVersion |
String |
No | Minimum mcs version required, e.g. "2.1.0" |
peerDependencies |
[PeerDependency] |
No | Other packs this pack requires |
components |
[Component] |
No | Installable components (see below) |
templates |
[Template] |
No | CLAUDE.local.md section contributions |
prompts |
[Prompt] |
No | Interactive prompts for mcs sync |
configureProject |
ConfigureProject |
No | Script to run after project configuration |
supplementaryDoctorChecks |
[DoctorCheck] |
No | Pack-level health checks |
hookContributions |
[HookContribution] |
No | Legacy hook fragments (prefer hook: components) |
gitignoreEntries |
[String] |
No | Legacy gitignore entries (prefer gitignore: component) |
peerDependencies:
- pack: other-pack
minVersion: "1.0.0"| Field | Type | Required | Description |
|---|---|---|---|
pack |
String |
Yes | Pack identifier |
minVersion |
String |
Yes | Minimum version required |
Components are defined in the components array. Each component represents something mcs can install, verify, and uninstall.
These fields are available on every component, regardless of which shorthand key is used:
| Field | Type | Required | Description |
|---|---|---|---|
id |
String |
Yes | Unique identifier. Short form auto-prefixed with <pack>. |
description |
String |
Yes | One-line description |
displayName |
String |
No | Display name (defaults to id) |
dependencies |
[String] |
No | Component IDs this depends on. Short form auto-prefixed |
isRequired |
Boolean |
No | If true, cannot be deselected in --customize mode |
hookEvent |
String |
No | Claude Code event for hook components |
doctorChecks |
[DoctorCheck] |
No | Custom health checks (see Doctor Checks) |
Use one of these keys to define a component's install action. Each key infers the component type automatically (except shell:).
- id: node
description: JavaScript runtime
brew: node| Field | Type | Description |
|---|---|---|
brew |
String |
Homebrew package name |
Infers: type: brewPackage, installAction: brewInstall
# Stdio transport
- id: my-server
description: Code analysis
mcp:
command: npx
args: ["-y", "my-server@latest"]
env:
API_KEY: "value"
scope: local
# HTTP transport
- id: remote-server
description: Cloud server
mcp:
url: https://example.com/mcp| Field | Type | Required | Description |
|---|---|---|---|
name |
String |
No | Server name (defaults to component id) |
command |
String |
Stdio only | Command to run (e.g. npx, uvx) |
args |
[String] |
No | Command arguments |
env |
{String: String} |
No | Environment variables |
url |
String |
HTTP only | Server URL |
scope |
String |
No | local (default), project, or user |
Transport is inferred: if url is present, HTTP; otherwise stdio.
Infers: type: mcpServer, installAction: mcpServer
- id: my-plugin
description: Helpful plugin
plugin: "my-plugin@my-org"| Field | Type | Description |
|---|---|---|
plugin |
String |
Plugin full name (name@org or name@user/repo) |
Infers: type: plugin, installAction: plugin
- id: session-hook
description: Session start hook
hookEvent: SessionStart
hook:
source: hooks/session_start.sh
destination: session_start.sh| Field | Type | Required | Description |
|---|---|---|---|
source |
String |
Yes | Path to script in the pack repo |
destination |
String |
Yes | Filename in <project>/.claude/hooks/ |
Use with hookEvent to register the hook in settings.local.json.
Infers: type: hookFile, installAction: copyPackFile(fileType: hook)
- id: pr-command
description: Create pull requests
command:
source: commands/pr.md
destination: pr.md| Field | Type | Required | Description |
|---|---|---|---|
source |
String |
Yes | Path to command file in the pack repo |
destination |
String |
Yes | Filename in <project>/.claude/commands/ |
Infers: type: command, installAction: copyPackFile(fileType: command)
- id: my-skill
description: Domain knowledge
skill:
source: skills/my-skill
destination: my-skill| Field | Type | Required | Description |
|---|---|---|---|
source |
String |
Yes | Path to skill directory in the pack repo |
destination |
String |
Yes | Directory name in <project>/.claude/skills/ |
Infers: type: skill, installAction: copyPackFile(fileType: skill)
- id: settings
description: Claude Code configuration
isRequired: true
settingsFile: config/settings.json| Field | Type | Description |
|---|---|---|
settingsFile |
String |
Path to settings JSON file in the pack repo |
The settings file is deep-merged into <project>/.claude/settings.local.json.
Infers: type: configuration, installAction: settingsFile
- id: gitignore
description: Global gitignore
isRequired: true
gitignore:
- .claude/memories
- .claude/settings.local.json| Field | Type | Description |
|---|---|---|
gitignore |
[String] |
Patterns to add to the global gitignore |
Infers: type: configuration, installAction: gitignoreEntries
- id: homebrew
description: macOS package manager
type: brewPackage # Required — shell: doesn't infer type
shell: '/bin/bash -c "$(curl -fsSL https://brew.sh)"'| Field | Type | Description |
|---|---|---|
shell |
String |
Shell command to execute |
Does not infer type — you must provide type: explicitly. This is because a shell command could install anything (a brew package, a skill, a tool).
No auto-derived doctor check — add doctorChecks if verification is needed.
The explicit form with type + installAction is always supported:
- id: my-pack.node
displayName: Node.js
description: JavaScript runtime
type: brewPackage
installAction:
type: brewInstall
package: nodetype |
Fields | Description |
|---|---|---|
mcpServer |
name, command, args, env, transport, url, scope |
Register MCP server |
plugin |
name |
Install Claude Code plugin |
brewInstall |
package |
Install Homebrew package |
shellCommand |
command |
Run shell command |
gitignoreEntries |
entries |
Add to global gitignore |
settingsMerge |
(none) | Merge settings (internal) |
settingsFile |
source |
Merge settings from file |
copyPackFile |
source, destination, fileType |
Copy file from pack |
fileType values: skill, hook, command, generic
Templates contribute sections to CLAUDE.local.md during mcs sync.
templates:
- sectionIdentifier: my-pack.instructions
contentFile: templates/instructions.md
placeholders:
- __PROJECT__
- __BRANCH_PREFIX__| Field | Type | Required | Description |
|---|---|---|---|
sectionIdentifier |
String |
Yes | Unique section ID. Must start with pack identifier |
contentFile |
String |
Yes | Path to markdown file in the pack repo |
placeholders |
[String] |
No | __PLACEHOLDER__ tokens used in the template |
__REPO_NAME__ is always available — it resolves to the git repository name of the project being configured.
Templates are wrapped in HTML comment markers in CLAUDE.local.md:
<!-- mcs:begin my-pack.instructions v1.0.0 -->
(template content here)
<!-- mcs:end my-pack.instructions -->Content outside markers is preserved. Re-running mcs sync updates only the managed sections.
Prompts gather values from the user during mcs sync.
prompts:
- key: PROJECT
type: fileDetect
label: "Xcode project"
detectPattern:
- "*.xcodeproj"
- "*.xcworkspace"| Field | Type | Required | Description |
|---|---|---|---|
key |
String |
Yes | Unique key. Becomes __KEY__ placeholder and MCS_RESOLVED_KEY env var |
type |
String |
Yes | One of: fileDetect, input, select, script |
label |
String |
No | Human-readable prompt label |
default |
String |
No | Default value for input type |
detectPattern |
String or [String] |
fileDetect |
Glob pattern(s) to match files |
options |
[{value, label}] |
select |
Choices for select prompts |
scriptCommand |
String |
script |
Shell command whose stdout becomes the value |
| Type | Behavior |
|---|---|
fileDetect |
Scans the project directory for files matching the glob pattern(s). If one match is found, it's used automatically. If multiple, the user picks one. |
input |
Free-text input with optional default value. |
select |
Choose from a predefined list of options. |
script |
Runs a shell command and uses its stdout as the value. |
Doctor checks verify pack health. They can be defined at two levels:
- Per-component —
doctorChecksfield on a component - Pack-level —
supplementaryDoctorChecksat the top level
- type: shellScript
name: Xcode CLI Tools
section: Prerequisites
command: "xcode-select -p >/dev/null 2>&1"
fixCommand: "xcode-select --install"
isOptional: false| Field | Type | Required | Description |
|---|---|---|---|
type |
String |
Yes | Check type (see table below) |
name |
String |
Yes | Display name in doctor output |
section |
String |
No | Grouping label in output |
fixCommand |
String |
No | Shell command for mcs doctor --fix |
fixScript |
String |
No | Path to fix script (for complex fixes) |
scope |
String |
No | global or project |
isOptional |
Boolean |
No | If true, failure is a warning, not an error |
| Type | Required Fields | Description |
|---|---|---|
commandExists |
command |
Is a CLI command available on PATH? |
fileExists |
path |
Does a file exist? |
directoryExists |
path |
Does a directory exist? |
fileContains |
path, pattern |
Does a file match a regex pattern? |
fileNotContains |
path, pattern |
Does a file NOT match a regex pattern? |
shellScript |
command |
Run a command. Exit codes: 0=pass, 1=fail, 2=warn, 3=skip |
hookEventExists |
event |
Is a hook event registered in settings? |
settingsKeyEquals |
keyPath, expectedValue |
Does a settings JSON key equal a specific value? |
Most components get free doctor checks from their install action — no need to define them manually:
| Shorthand | Auto-derived check |
|---|---|
brew: node |
commandExists for node |
mcp: {command: npx, ...} |
MCP server registered in ~/.claude.json |
plugin: "name@org" |
Plugin enabled in settings |
hook: {source, dest} |
File exists at destination |
skill: {source, dest} |
Directory exists at destination |
command: {source, dest} |
File exists at destination |
settingsFile: path |
Always re-applied (convergent) |
gitignore: [...] |
Always re-applied (convergent) |
shell: "..." |
None — add doctorChecks manually |
configureProject:
script: scripts/configure.sh| Field | Type | Required | Description |
|---|---|---|---|
script |
String |
Yes | Path to shell script in the pack repo |
The script receives:
| Variable | Description |
|---|---|
MCS_PROJECT_PATH |
Absolute path to the project root |
MCS_RESOLVED_<KEY> |
Resolved prompt values (uppercased key) |
hookContributions:
- hookName: session_start
fragmentFile: hooks/fragment.sh
position: after| Field | Type | Required | Description |
|---|---|---|---|
hookName |
String |
Yes | Hook name (e.g. session_start, pre_tool_use) |
fragmentFile |
String |
Yes | Path to script file in the pack repo |
position |
String |
No | before or after |
Prefer
hook:components overhookContributions. Hook components are tracked per-project and support convergence (add/remove on re-configure). Hook contributions are a legacy mechanism.
The engine validates manifests on load. These rules are enforced:
schemaVersionmust be1identifiermust be non-empty, lowercase alphanumeric with hyphens, not starting with a hyphen- Component IDs must be unique and prefixed with the pack identifier (or use short form for auto-prefixing)
- Template
sectionIdentifiermust start with the pack identifier - Prompt
keyvalues must be unique - Doctor check required fields must be present and non-empty
A minimal but realistic pack:
schemaVersion: 1
identifier: web-dev
displayName: Web Development
description: Node.js development environment for Claude Code
version: "1.0.0"
prompts:
- key: FRAMEWORK
type: select
label: "Framework"
options:
- value: next
label: Next.js
- value: remix
label: Remix
components:
- id: node
description: JavaScript runtime
brew: node
- id: prettier-server
description: Code formatting MCP server
dependencies: [node]
mcp:
command: npx
args: ["-y", "prettier-mcp-server@latest"]
- id: pr-review
description: PR review toolkit
plugin: "pr-review-toolkit@claude-plugins-official"
- id: session-hook
description: Shows npm outdated on session start
hookEvent: SessionStart
hook:
source: hooks/session_start.sh
destination: session_start.sh
- id: settings
description: Plan mode and thinking
isRequired: true
settingsFile: config/settings.json
- id: gitignore
description: Gitignore entries
isRequired: true
gitignore:
- .claude/memories
- .claude/settings.local.json
templates:
- sectionIdentifier: web-dev.instructions
placeholders: [__FRAMEWORK__]
contentFile: templates/instructions.md