Your private /deploy shortcut saves you twenty minutes a day and helps exactly one person. Plugins move the same workflow into a parameterized package every team installs in minutes. The full lifecycle — skill, context files, userConfig, MCP wiring, hooks, marketplace.
The three-property filter for deciding which workflows earn a plugin
Full /team-brief walkthrough: personal skill → parameterized plugin → marketplace
plugin.json schema, SKILL.md frontmatter fields, and userConfig for credential prompting
MCP token overhead tradeoffs (a five-server setup costs ~55,000 context tokens upfront)[7]
Pre-ship test surface and the failure modes that kill adoption
Semver bump rules and the two distribution paths
You wrote a slash command that saves you twenty minutes a day. Good. Your teammate is still doing the work by hand. The new hire on the platform team has never heard of the file.
That's the gap between a personal shortcut and organizational leverage. A /deploy skill living in your .claude/ directory is a private dotfile. A packaged plugin — parameterized context files, declared MCP connections, prompted userConfig for credentials, marketplace distribution — runs for every engineer the moment they install it. Same skill. Different topology.
This is the full lifecycle. Spotting a workflow that survives the abstraction. Building the skill. Pulling team-specific values out into context files. Wiring MCP without shipping credentials. Adding hooks for guaranteed execution. Versioning, distribution, and the failure modes that kill adoption before the second team installs it.
Most repeated tasks don't deserve a plugin. The ones that do share three properties.
Three properties separate plugin candidates from workflows you should leave as personal shortcuts. Frequency — someone runs the workflow at least weekly. Variable inputs, stable structure — the data shifts, the skeleton holds. High-context — the work depends on knowledge that lives in heads, not documentation.
Monday standup briefs. Sprint summaries. Incident post-mortems. Onboarding context dumps. Release notes. Each has a predictable skeleton with inputs that change every run. The bad candidates are one-off migrations and anything that requires a fresh judgment call each time. If the human has to think hard mid-flow, the plugin isn't absorbing the task — it's just dressing it up.
There's a concrete disqualifier worth naming: if the output quality depends entirely on institutional memory only two people have, packaging it as a plugin creates a false sense of coverage. The plugin runs but produces garbage for every team that isn't the author's. That's worse than no plugin.
One-off migration scripts
Tasks that change shape every run
Workflows requiring a judgment call at each step
Processes only one person ever runs
Output depends on institutional memory only two people have
Weekly briefs with stable structure and shifting inputs
PR review checklists with team-specific rules
Sprint planning summaries pulled across repos
Onboarding context generation for new hires
Incident post-mortems with standard sections
One real plugin. Six teams. Same code, different context files.
Make this concrete. The org has six teams. Every Monday, each EM writes a brief covering what shipped, what's in progress, what's blocked. They pull from GitHub PRs, Jira tickets, Slack threads — all by hand. Thirty to forty-five minutes per manager per week.
The workflow checks every box. Frequent. Variable inputs, stable output. High-context. Different teams, different repos, same operation. This is what /team-brief looks like rebuilt as a plugin from scratch.
Create .claude/commands/team-brief.md. Write the instructions the way you'd brief a competent colleague: pull merged PRs from the last 7 days, summarize Jira status changes, flag anything blocked over 48 hours, format as a Slack-ready brief. The skill must work end-to-end against your own data before any abstraction. Premature parameterization hides bugs in the prompt.
Your first draft hardcodes the GitHub org, the Jira project key, the Slack channel, the team name. Every value that changes for a different team is a parameter. Every assumption about tooling becomes an MCP connection. The skill itself becomes data-free — pure logic operating on context the user provides.
Each EM fills in their team's specifics: repo names, Jira board IDs, team members, output preferences. The skill reads this at runtime. One skill, N context files, N teams covered. The schema is the contract — get this wrong and every team has to read your source code to install it.
Move everything out of .claude/ into a proper plugin tree. Add the manifest. Configure MCP. Write the SKILL.md with frontmatter so Claude can invoke it on schedule or by slash command. Use userConfig in plugin.json for anything sensitive — tokens go to the system keychain, not settings.json.
Push the plugin to your team's marketplace repo or submit to the official Anthropic marketplace. Other EMs install with one command and configure their context file once. The moment distribution becomes a copy-paste from a thread, drift starts immediately.
Directory structure, manifest schema, SKILL.md frontmatter fields. The shape isn't arbitrary.
treeteam-brief/
├── .claude-plugin/
│ └── plugin.json
├── skills/
│ └── generate-brief/
│ ├── SKILL.md
│ ├── template.md
│ └── examples/
│ └── sample-brief.md
├── context/
│ ├── team-config.template.yaml
│ └── README.md
├── .mcp.json
├── settings.json
└── README.mdThe manifest at .claude-plugin/plugin.json is actually optional — the runtime auto-discovers components in default locations and derives the plugin name from the directory name. You need the manifest when you want to declare metadata, custom component paths, user configuration prompts, or plugin dependencies. Once you're distributing to a marketplace, treat it as required.
Two fields in plugin.json that most plugin authors miss:
userConfig lets you declare values that Claude Code prompts the installer for at enable time — API tokens, endpoints, team-specific identifiers. Sensitive fields (marked "sensitive": true) go to the system keychain rather than settings.json, so team members never have tokens stored in plaintext config files. Non-sensitive values are available in skill content as ${user_config.KEY} and exported as CLAUDE_PLUGIN_OPTION_<KEY> to all plugin subprocesses.[8]
defaultEnabled (v2.1.154+) ships the plugin disabled by default. Use it for plugins that hit external APIs the installer might not have access to yet.
The SKILL.md frontmatter controls how the runtime discovers and invokes the skill. The allowed-tools field is the most consequential: it's a least-privilege declaration that scopes exactly what tools the skill can call. Overly broad grants (Bash(*)) let the skill do anything — which breaks the security boundary that makes plugin distribution safe. Narrow grants that name specific binaries give team members a verifiable surface area.[8]
Conflate the two and you'll either hardcode credentials or make installation impossible.
This is where most plugins die quietly. The distinction that matters:
Context files are per-team configuration the skill reads at runtime — repo names, Jira board IDs, team members, output preferences. This is domain knowledge only the team owner has. Nobody else can supply it.
userConfig (in plugin.json) handles anything that varies per installation rather than per team — API tokens, instance URLs, personal credentials. The runtime prompts for these at enable time. Sensitive fields go to the system keychain. Non-sensitive fields land in settings.json and are available as ${user_config.KEY} in skill content and MCP server configs.
Settings control behavior — "use bullet points," "include velocity." Settings have defaults. Context cannot.
We shipped the wrong version first. The initial team-brief plugin had the GitHub org baked into SKILL.md. Three days in, an engineer from another org tried to install it, got opaque errors, and walked away. The fix was twenty minutes of code. The reputational damage took weeks to undo. People don't return to a tool that failed them on first contact.
The plugin declares what it needs. The user's machine handles auth. But every MCP server has a context price.
MCP servers give the plugin authenticated access to GitHub, Jira, Slack, databases — without embedding credentials in the plugin itself. The .mcp.json at the plugin root declares which MCP servers the plugin requires. When someone installs, Claude Code connects using credentials already configured on their machine.[1] The plugin never sees a token.
But MCP has a cost that plugin authors routinely underestimate: every server injects its full tool schema into the context of every message, regardless of whether those tools are used in that session. A five-server setup can burn around 55,000 tokens of context overhead before the conversation starts — roughly a third of the available window.[7] Skills, by contrast, use progressive disclosure: only names and descriptions load at session start (~30–50 tokens per skill), with the full SKILL.md body loading only when Claude decides to invoke it.
The practical implication: declare only the MCP servers the plugin actually needs per invocation, not every server the broader plugin ecosystem might want. If your brief plugin only needs GitHub and Jira, don't bundle a Slack MCP server that most teams won't use. Each additional server is a fixed context tax paid on every message.
Another consideration: the MCP specification updated in November 2025 to formalize OAuth 2.1 as the authentication standard for remote servers.[9] If you're pointing your plugin at a remote MCP endpoint (rather than a local process), make sure that server has auth enabled. Analysis of over 5,200 open-source MCP implementations found 53% still rely on long-lived static secrets.[9]
| Need | Reach for | Why |
|---|---|---|
| Authenticated read/write to an external API | MCP server | Token isolation, structured tool schema, protocol-level auth |
| Repeatable procedure over local files or shell tools | Skill (SKILL.md) | Zero per-session token overhead until invoked; no server to start |
| Guaranteed execution at a lifecycle event | Hook | Runs unconditionally at PostToolUse, Stop, SessionEnd, etc. |
| External service + repeated procedure | MCP + skill together | MCP handles auth; skill orchestrates the steps |
| Per-team credentials or instance URLs | userConfig in plugin.json | Prompted at enable time; sensitive fields go to system keychain |
Skills wait to be invoked. Hooks fire unconditionally at lifecycle events.
Skills are invoked — by the user typing a slash command or by Claude deciding the skill is relevant based on its description. Hooks are different: they fire unconditionally at declared lifecycle events, regardless of what's happening in the conversation.
For the team-brief plugin, a hook that auto-posts a draft brief every Monday morning is more reliable than remembering to invoke the skill. Here's what that looks like in hooks/hooks.json:
Local loading, a second team's context, and the failure modes that matter.
Push a plugin to a marketplace before testing it locally and you'll be debugging in front of an audience. The --plugin-dir flag loads your plugin directly from disk — no install, no marketplace round-trip, fast iteration.
A plugin that only works with the author's config is a fancy script. The second team is where the abstraction is proven or broken.
When a required field is absent, the skill must tell the user what's missing and where to add it. Silent failure is the cheapest way to lose adoption.
A weekly brief that runs to two thousand words defeats the purpose. Validate the output constraint, not just the structure.
The empty case is where most prompts fail. The plugin must handle a quiet week with clean output, not an exception trace.
Strict mode treats unrecognized field warnings as errors. It catches misspelled manifest fields that the runtime silently ignores but the marketplace rejects.
Marketplace mechanics and the semver rules that decide whether updates break installs.
Once the plugin is solid, you have two distribution paths. For internal teams, the simplest move is a team marketplace — a Git repository that serves as a plugin registry. Add your plugin directory; anyone on the team installs it directly.
For broader distribution, submit to the official Anthropic marketplace via claude.ai/settings/plugins/submit or platform.claude.com/plugins/submit.
Plugin installation has four scopes, and which one you choose determines who sees the plugin and what trust gates apply:[8]
user scope (~/.claude/settings.json): personal, available in every project on that machineproject scope (.claude/settings.json): checked into the repo, reaches every collaborator who clones it — but MCP servers go through a per-server approval dialog and background monitors are disabledlocal scope (.claude/settings.local.json): project-specific, gitignoredmanaged scope: read-only, org-controlled, update-onlyEither way, versioning is enforcement. Use semantic versioning in plugin.json. Omit the version field and Claude Code falls back to the git commit SHA, treating every commit as a new version — which breaks pinned installs. Get semver wrong and every release breaks installs that were working yesterday.
| Change | Bump | Example |
|---|---|---|
| Fix a typo in the brief template | Patch (1.2.0 → 1.2.1) | Template formatting correction |
| Add an optional metrics section | Minor (1.2.0 → 1.3.0) | New preferences.include_velocity field in context YAML |
| Rename a context file field | Major (1.2.0 → 2.0.0) | sources.github.org → sources.github.organization |
| Add a new required MCP server | Major (1.2.0 → 2.0.0) | Slack MCP now required for thread scanning |
| Add a new userConfig field with a default | Minor (1.2.0 → 1.3.0) | New optional brief_style preference |
| Add a required userConfig field (breaks existing installs) | Major (1.2.0 → 2.0.0) | New required slack_token with no default |
Patterns from plugins running in production.
One plugin per workflow, not one per team — context files carry the differences
Ship example context files for common shapes (monorepo, microservices, single-app)
Pin MCP server versions in .mcp.json so upstream releases don't break installs
Use the SKILL.md description aggressively — it's what triggers automatic invocation
Keep supporting files (templates, examples) small and on-purpose
Use userConfig for any value that differs per install; context files for values that differ per team
Hardcoding team names, repo lists, or project keys inside the skill
Putting credentials in context files instead of userConfig (tokens in plaintext YAML)
Skipping the template file and expecting users to reverse-engineer the schema
Building one plugin that tries to cover five workflows
Adding every available MCP server — each one costs context tokens per message whether used or not[7]
Forgetting that project-scope plugins disable background monitors and require MCP approval per server
The pattern transfers. Not every workflow earns it.
The /team-brief example is one application of the architecture. Once you've built one plugin with parameterized context files and userConfig for credentials, the same shape applies everywhere. Below are workflows teams have packaged using this approach: a SKILL.md with explicit instructions, context files carrying team-specific knowledge, MCP connections for external data, userConfig for install-time secrets.
The honest counterpoint: not every workflow earns a plugin. Tasks that run once a quarter, or that require a fresh judgment call at every step, generate maintenance overhead that exceeds the savings. The format works when the structure is stable and the variable is the input data — not the decision-making.
| Plugin | What it does | Context file fields |
|---|---|---|
| /incident-retro | Generates post-incident reviews from PagerDuty + Slack data | Service map, escalation policies, SLA thresholds |
| /release-notes | Compiles user-facing release notes from merged PRs | Product areas, audience segments, changelog format |
| /onboard-engineer | Builds onboarding context docs for new team members | Tech stack, key repos, team rituals, contact list |
| /sprint-health | Weekly sprint health dashboard from Jira data | Velocity targets, definition of done, risk thresholds |
| /pr-review | Runs team-specific code review against your standards | Style guide, security rules, test coverage minimums |
Don't build the whole package on day one. Start with a personal skill in .claude/commands/ that solves your own problem. Run it for a week. Watch what you keep hardcoding and what shifts between runs. The variable parts are the schema for your context file. The sensitive parts are the schema for your userConfig.
Once the skill works for you, spend an hour converting it: create the directory, write the manifest, move the skill into skills/. Test with --plugin-dir. Run claude plugin validate --strict. Hand it to one teammate and watch where they get stuck — that's where your template comments need to be sharper.
The gap between a personal shortcut and a team-wide plugin is smaller than it looks. The skill itself is most of the work. Packaging is mechanical. The investment that pays back is context file design and getting userConfig right. Do both correctly and a new team installs the plugin and runs in five minutes instead of building their own from scratch.
Can I use a plugin across multiple projects without reinstalling?
Yes. Plugins installed at user scope (~/.claude/settings.json) are available in every project on that machine. For project-specific plugins, install at project scope — they get checked into the repo and reach every collaborator who clones it. The tradeoff: project-scope plugins require per-server MCP approval and can't run background monitors. Choose user scope for personal productivity tools; project scope for team-wide tooling you want enforced across the codebase.
How do I update a plugin that team members already have installed?
Bump the version in plugin.json and push to the marketplace repository. Team members update via the plugin manager or /plugin update. Major bumps must include migration notes in the README — what changed, how to update existing context files. If you renamed a field (sources.github.org → sources.github.organization), ship a one-line migration sed command in the README. If you added a required userConfig field, they'll be prompted at next enable. Friction kills upgrades — make migrations obvious.
What happens if two plugins define a skill with the same name?
Plugin skills are namespaced by plugin name (/team-brief:generate-brief vs /sprint-tools:generate-brief), so cross-plugin collisions are impossible. Within a single plugin, every skill needs a unique name. The runtime resolves the namespace; the author owns the local uniqueness.
Can context files reference environment variables?
Context files are YAML the skill reads — there's no automatic interpolation. For dynamic values, declare them in userConfig instead; they're available in skill content as ${userconfig.KEY} and exported to plugin subprocesses as CLAUDEPLUGINOPTION. For truly sensitive values (tokens, keys), always use userConfig with sensitive: true — the runtime stores those in the system keychain rather than settings.json.
How many MCP servers should my plugin bundle?
As few as necessary. Every MCP server injects its full tool schema into every message's context — a five-server setup costs roughly 55,000 tokens of overhead per conversation before any work starts.[7] Anthropic's Tool Search feature (ENABLETOOLSEARCH) can reduce this by deferring schema loading until a tool is actually needed, but it's better to start lean. If your plugin only needs GitHub and Jira, don't bundle Slack. Users who need Slack can add it themselves.
What's the difference between a plugin hook and a plugin skill?
Skills wait to be invoked — either by the user typing a slash command or by Claude deciding the skill matches the current task. Hooks fire unconditionally at declared lifecycle events (SessionStart, PostToolUse, Stop, etc.) regardless of what's in the conversation. Use skills for on-demand procedures. Use hooks for things that must run reliably at specific moments — linting after every Write, posting a reminder at session start, logging tool usage.
When production agents fail, teams default to prompt tuning regardless of structural root cause. This MAST-based triage protocol gives engineering leaders three speed-ordered checks — 30 seconds, 5 minutes, 20 minutes — each routing to a different structural owner before anyone changes a line.
MAST's 14 agent failure modes cluster into 3 structural categories, each preventable at a different pre-production stage. This playbook maps them to 12 deployment gate questions with pass criteria and named ownership.
Why frontier model defaults bloat inference bills, and the per-task quality SLO framework that makes model selection explicit, testable, and owned — instead of inherited from prototype defaults.