[BUG] marketplace.json plugins[].skills filter ignored when entries share source root and ship no plugin.json (anthropic-agent-skills repro)

Resolved 💬 3 comments Opened Apr 26, 2026 by kreitter Closed Apr 29, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported (closest matches: #43297 — different bug, cross-marketplace conflation; #41890 / #50486 / #50488 — namespacing/aliasing ergonomics; none describe the shared-source pattern below)
  • [x] This is a single bug report
  • [x] I am using the latest version of Claude Code (2.1.119)

What's Wrong?

When a marketplace.json declares multiple plugin entries that share the same source: "./" (i.e. point at the marketplace root) and use the per-entry skills: array to express disjoint skill subsets, **all installed plugins load all skills from the shared skills/ directory**, not the declared subset.

This is observable today on the official anthropic-agent-skills marketplace (anthropics/skills). Its marketplace.json declares three plugins from a shared source:

// .claude-plugin/marketplace.json (excerpt)
"plugins": [
  { "name": "document-skills", "source": "./", "skills": ["./skills/xlsx", "./skills/docx", "./skills/pptx", "./skills/pdf"] },           // intended: 4
  { "name": "example-skills",  "source": "./", "skills": ["./skills/algorithmic-art", "./skills/brand-guidelines", /* ... 12 total */] },  // intended: 12
  { "name": "claude-api",      "source": "./", "skills": ["./skills/claude-api"] }                                                          // intended: 1
]

Observed: each installed plugin registers all 17 skills under its own namespace, not its declared subset.

| Plugin | marketplace.json skills: (intended) | Skills actually registered |
|---|---|---|
| document-skills@anthropic-agent-skills | 4 (xlsx, docx, pptx, pdf) | 17 |
| claude-api@anthropic-agent-skills | 1 (claude-api) | 17 |

Both /document-skills:claude-api and /claude-api:claude-api exist, with byte-identical SKILL.md content (verified via sha256).

What Should Happen?

The marketplace.json plugins[].skills array should act as a load-time filter, mirroring the per-plugin plugin.json skills field which is documented in plugins-reference.md as:

skills | string\|array | Custom skill directories containing <name>/SKILL.md (replaces default skills/)

For document-skills, only xlsx, docx, pptx, pdf should register as /document-skills:* skills. /document-skills:claude-api should not exist.

If the current behavior is by design, then the docs at plugin-marketplaces.md should explicitly state that marketplace.json plugins[].skills is metadata-only (catalog hints, not a runtime filter). Today the field's description ("Custom paths to skill directories containing <name>/SKILL.md") parallels the plugin.json field but omits the "replaces default" parenthetical, leaving the semantics ambiguous.

Steps to Reproduce

  1. Add the marketplace and install both plugins:

``
/plugin marketplace add anthropics/skills
/plugin install document-skills@anthropic-agent-skills
/plugin install claude-api@anthropic-agent-skills
``

  1. List skills under each namespace:

``
/skills | grep -E '^/(document-skills|claude-api):'
`
Expected: 4 entries under
document-skills:, 1 under claude-api:`. Observed: 17 under each.

  1. Sha256-verify the duplication is at the file level, not just the namespace:

``bash
for s in algorithmic-art brand-guidelines canvas-design claude-api doc-coauthoring \
docx frontend-design internal-comms mcp-builder pdf pptx skill-creator \
slack-gif-creator theme-factory web-artifacts-builder webapp-testing xlsx; do
a=$(shasum -a 256 ~/.claude/plugins/cache/anthropic-agent-skills/document-skills/*/skills/$s/SKILL.md | awk '{print $1}')
b=$(shasum -a 256 ~/.claude/plugins/cache/anthropic-agent-skills/claude-api/*/skills/$s/SKILL.md | awk '{print $1}')
[ "$a" = "$b" ] && echo "IDENTICAL $s" || echo "DIVERGENT $s"
done
``
Result: all 17 byte-identical.

  1. Inspect marketplace.json to confirm the declared subset is not what loaded:

``
cat ~/.claude/plugins/cache/anthropic-agent-skills/document-skills/*/.claude-plugin/marketplace.json
``

Root Cause Hypothesis

Per plugins-reference.md "Plugin manifest schema":

The manifest is optional. If omitted, Claude Code auto-discovers components in default locations and derives the plugin name from the directory name.

None of the three plugins in anthropic-agent-skills ship a per-plugin .claude-plugin/plugin.json — only the marketplace's catalog marketplace.json exists. So at install/load time, each plugin appears manifest-less and falls through to auto-discovery, which scans the entire shared skills/ directory.

Two possible fixes:

  • (a) Runtime fix — when constructing a plugin's component set, give precedence to marketplace.json plugins[].skills (and commands, agents, hooks, etc.) when present, the same way per-plugin plugin.json overrides default auto-discovery. This is the more defensible interpretation given the parallel field names.
  • (b) Docs fix + upstream restructure — declare marketplace.json plugins[].skills is metadata-only; reorganize anthropics/skills so each plugin has its own source subdirectory (./document-skills/, ./example-skills/, ./claude-api/) with per-plugin plugin.json files.

Either way addresses the user-visible symptom. (a) is preferable because it preserves the existing marketplace structure and matches the authoring intent expressed in the catalog.

Impact

  • Users who install both document-skills and claude-api from the official Anthropic marketplace get 17 skills loaded twice, doubling their contribution to the in-context skills budget (per skills.md, default 8000 chars or 1% of context window — easy to truncate with ~120 plugin-shipped skills typical of a power-user setup).
  • Surprising namespacing: /document-skills:claude-api exists but is not what document-skills was supposed to ship.
  • Cross-marketplace impact: any third-party marketplace using the shared-source pattern (legitimately, e.g. for monorepo authoring convenience) inherits the same surprise.
  • claude doctor does not surface this kind of in-namespace duplication today; the bug is silent until the user runs sha256 diffs.

Environment

  • Claude Code version: 2.1.119
  • OS: macOS Tahoe (Darwin 25.4.0, arm64)
  • Node: managed via asdf
  • Auth: --dangerously-skip-permissions mode under op run env wrapper
  • Affected marketplace: anthropic-agent-skills (https://github.com/anthropics/skills) at commit 5128e1865d67
  • Affected plugins: document-skills@anthropic-agent-skills, claude-api@anthropic-agent-skills

Related issues

  • #43297 — Marketplace plugin skill silently mapped to official plugin skill (cross-marketplace conflation; same family, different surface)
  • #41890 / #50486 / #50488 — plugin skill namespacing and autocomplete-alias ergonomics
  • #48750 — plugin skill shadows built-in /review via unqualified resolution

Related upstream

If the resolution path is (b), file a paired issue against anthropics/skills to restructure the marketplace into per-plugin source subdirs.

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗