How to Build a Plugin for Anthropic Claude Cowork (and Claude Code)
A practical guide to building an Anthropic Claude plugin: package Skills, commands, agents, hooks, MCP servers, and other Claude components, then measure them with Telvine.
This guide walks through how to build a plugin for Anthropic Claude Cowork (the same packaging approach applies to Claude Code). Cowork is an agent harness - the runtime that runs plugin capabilities on real work. Your plugin is the app; a Skill is one capability inside it. Claude plugins can also include commands, agents, hooks, MCP servers, output styles, LSP servers, settings, user config, dependencies, scripts, bins, themes, and monitors.
If you're new to the concept, start with what an agent plugin is.
Step 1 — Define the plugin product
For Telvine-backed distribution and metrics, start with the plugin users install:
npm i -g @telvine/cli@0.1.0
telvine login
telvine publish ./my-plugin
The Telvine product record should hold the plugin id, owner, version, component inventory, harness adapters, and telemetry policy. The Claude package is an adapter for that product, not the only source of truth.
Step 2 — Write the SKILL.md
A Cowork Skill is a directory with a SKILL.md. The frontmatter tells Cowork when to trigger it:
---
name: month-end-close
description: >
Use this skill when the user asks to "run the month-end close",
"reconcile the bank accounts", or "prepare the close pack".
Reconciles accounts, reviews accruals, and produces a close pack.
---
# Month-end close
## Steps
1. Confirm the period and locate the data.
2. Reconcile each bank account; list exceptions.
3. Produce a close pack. Propose journals — never post them.
See the SKILL.md template for a fuller scaffold.
Step 3 — Package it as a plugin
In Claude, Skills are distributed inside plugins - a folder with a .claude-plugin/plugin.json manifest plus the component directories your plugin needs:
my-plugin/
├── .claude-plugin/
│ └── plugin.json
├── skills/
│ └── month-end-close/
│ └── SKILL.md
├── commands/
│ └── close-status.md
├── agents/
│ └── controller-reviewer.md
├── hooks/
│ └── hooks.json
├── output-styles/
│ └── finance-summary.md
├── monitors/
│ └── close-health.json
├── bin/
│ └── close-check
├── settings.json
├── .mcp.json
└── .lsp.json
{
"name": "my-plugin",
"version": "0.1.0",
"description": "What this plugin does.",
"skills": "./skills/",
"commands": ["./commands/close-status.md"],
"agents": ["./agents/controller-reviewer.md"],
"hooks": "./hooks/hooks.json",
"mcpServers": "./.mcp.json",
"outputStyles": "./output-styles/",
"userConfig": {
"api_token": {
"type": "string",
"title": "API token",
"description": "API authentication token",
"sensitive": true
}
}
}
Claude's manifest reference also includes lspServers, userConfig,
channels, dependencies, and experimental themes and monitors. Claude
also documents root package locations such as bin/, scripts/, and
settings.json. Register each shipped component in Telvine, even when it is
inventory-only.
Step 4 — Install and trigger it
Install the plugin in Cowork, then describe the task the way a real user would — you don't click a "run" button. If your description is good, Cowork loads the Skill automatically:
"Run the month-end close for May and flag any exceptions."
If it doesn't trigger, the fix is almost always the description: add the exact phrases users say.
Step 5 — Connect data and tools
Cowork Skills can use connected tools, MCP servers, hooks, commands, and bundled scripts. Pull data from a connected source when one is available; otherwise have the Skill ask for the files it needs. Keep deterministic steps in scripts/ or bin/.
Step 6 — Measure it
Once people are using your plugin, you'll want to know what's happening inside it: which capability is invoked, which directory fails validation, how long hooks or connectors take, and whether your latest version is better than the last. Wrap each Skill's telemetry with Telvine - invocations, errors, and feedback, with no user content collected - and emit plugin.component.* events for observable non-Skill components.
Register Claude inventory with plugin-relative paths and manifest fields:
{
"component_type": "hook",
"name": "close-complete",
"telemetry_mode": "component_events",
"component_path": "hooks/hooks.json",
"component_directory": "hooks",
"manifest_field": "hooks",
"source": "default_directory"
}
agents also goes in the component inventory. If the manifest uses a custom
path like "agents": "./custom/agents/reviewer.md", resolve it into an
individual component:
{
"component_type": "agent",
"name": "reviewer",
"telemetry_mode": "component_events",
"component_path": "custom/agents/reviewer.md",
"component_directory": "agents",
"manifest_field": "agents",
"source": "manifest_path"
}
For packaging, scan, validation, and resolution work, use
component_type: "directory" so Telvine can report reliability by folder:
{
"event_type": "plugin.component.invoked",
"plugin_id": "plg_abc12345",
"version": "0.1.0",
"installation_id": "inst_xyz12345",
"occurred_at": "2026-05-27T12:34:56.789Z",
"idempotency_key": "01HX0000000000000000000000",
"runtime": "claude-code",
"properties": {
"component_type": "directory",
"component_name": "hooks",
"operation": "scanned",
"component_path": "hooks/hooks.json",
"component_directory": "hooks",
"manifest_field": "hooks",
"outcome": "completed"
}
}
Never emit prompts, file contents, connector payloads, tool arguments, model outputs, absolute local paths, or user file paths. component_path must be plugin-relative package metadata only.
This closes the loop: build → ship → measure → improve.
Frequently asked questions
Is a Claude Cowork Skill different from a Claude Code Skill?
They share the SKILL.md format. Cowork targets non-developer knowledge work; Claude Code targets the terminal. A well-written Skill often works in both.
Does every Claude directory emit telemetry? No. Telvine records every declared directory in inventory, but runtime telemetry only exists where the host or your wrapper can reliably emit it.
How do I distribute a Skill to my team?
Package it as a plugin and share the .plugin file (or a marketplace). Teammates install it once and it triggers automatically.
Why isn't my Skill triggering? The description is too vague or doesn't match how users phrase the request. Iterate on it first.