Skip to content

Split monolithic plugin into product-axis plugins#51

Open
CarterPape wants to merge 7 commits into
cloudflare:mainfrom
CarterPape:split-into-product-plugins
Open

Split monolithic plugin into product-axis plugins#51
CarterPape wants to merge 7 commits into
cloudflare:mainfrom
CarterPape:split-into-product-plugins

Conversation

@CarterPape
Copy link
Copy Markdown

@CarterPape CarterPape commented May 9, 2026

Summary

Splits the monolithic cloudflare plugin into 9 plugins (8 product plugins + 1 umbrella) per the request in #50. Existing users keep working transparently — on Claude Code via the umbrella's plugin dependencies, on Cursor via an inverted-symlink layout that keeps the root cloudflare plugin functional. Users who want narrower scope and lower per-session token usage can install only the per-product plugins they need.

What changed

The marketplace ships these plugins (canonical content under plugins/<plugin-name>/):

Plugin Skills MCP servers Commands Depends on
cloudflare-core cloudflare cloudflare-api, cloudflare-docs
cloudflare-workers wrangler, workers-best-practices cloudflare-bindings, cloudflare-builds cloudflare-core
cloudflare-observability cloudflare-observability cloudflare-workers
cloudflare-durable-objects durable-objects cloudflare-workers
cloudflare-agents agents-sdk /build-agent, /build-mcp cloudflare-workers
cloudflare-sandbox sandbox-sdk cloudflare-workers
cloudflare-email cloudflare-email-service cloudflare-core
cloudflare-web-perf web-perf
cloudflare all 8 above (umbrella)

Each MCP server lives in exactly one plugin. Other plugins reach those MCPs transitively via the dependency tree. (Verified empirically: Claude Code silently dedupes duplicate MCP server names across plugins, so duplicating servers across plugins is unsafe.)

No skill, command, or rule content has changed — all files are pure git renames into plugins/<plugin-name>/. The only content additions are the new manifests.

Cursor monolith continuity (inverted-symlink layout)

Cursor's plugin system has no plugin-to-plugin dependency mechanism (verified against the Cursor docs and the cursor/plugins repo — none of the 11 official plugins declare dependencies, and the Plugins Reference doesn't list a dependencies field). The Claude umbrella pattern doesn't translate.

To keep existing cloudflare@cloudflare Cursor installs working without forcing a migration, the repo root keeps a thin "Cursor monolith" plugin layout that resolves into the per-product plugins via symlinks:

  • skills/<X>../plugins/<product>/skills/<X> (8 skill symlinks)
  • commands/<X>.md../plugins/<product>/commands/<X>.md (2 command symlinks)
  • rules/workers.mdc../plugins/cloudflare-workers/rules/workers.mdc (1 rule symlink)
  • .cursor-plugin/plugin.json — the Cursor monolith manifest (unchanged from upstream)
  • .cursor-plugin/marketplace.json — re-adds cloudflare./ alongside the 8 per-product entries
  • .mcp.json — hand-merged from the 3 product .mcp.json files (5 servers, identical to upstream pre-PR)

Symlinks point inside the Cursor monolith plugin's source dir (./), satisfying the empirically-verified "internal symlinks are preserved through git clone" constraint. Claude Code never scans the repo root, so the Claude umbrella plugin (deps-only at plugins/cloudflare/) is unaffected — content is loaded once via the per-product plugins, not twice.

The root .mcp.json is hand-maintained: when a product's .mcp.json changes, the root file needs to be updated to match. The README's "Repository layout" section documents this for contributors.

Migration: Claude Code (smooth)

The new cloudflare umbrella plugin depends on all 8 product plugins. Existing users running /plugin install cloudflare@cloudflare get the same content as before, transparently — Claude Code auto-installs the 8 dependencies. No user action required.

Users who want lower per-session token usage can uninstall the umbrella and install only the product plugins they need:

/plugin uninstall cloudflare@cloudflare
/plugin install cloudflare-workers@cloudflare

Installing cloudflare-workers automatically pulls in cloudflare-core via the dep graph.

Migration: Cursor (smooth)

Existing Cursor users with the cloudflare monolith plugin keep working — same skills, same MCP servers, same install path. No user action required.

Users who want narrower scope can install per-product Cursor plugins from the same marketplace. Note that without dependency resolution, each Cursor plugin ships only its own MCP servers — installing only cloudflare-workers gives cloudflare-bindings and cloudflare-builds but not cloudflare-api/cloudflare-docs/cloudflare-observability. Users who want the full MCP surface should keep the cloudflare umbrella.

Verification

  • All 9 plugin manifests pass claude plugin validate.
  • End-to-end tested locally:
    • claude plugin install cloudflare@cloudflare → installs umbrella + auto-installs 8 dependencies.
    • claude plugin install cloudflare-workers@cloudflare (without the umbrella) → installs only cloudflare-workers + cloudflare-core (transitive).
    • All 5 MCP servers connect with proper plugin namespacing (plugin:cloudflare-core:cloudflare-api, etc.).
  • Cursor install pipeline preserves internal symlinks: empirically verified that a git clone --depth 1 of this branch produces a working tree where the 11 root symlinks are intact (mode-120000), and Cursor's runtime loader follows them when scanning <plugin-root>/skills/*/.
  • Git rename detection auto-merges upstream edits to moved files even with the co-located symlink ADD at the original parent path (verified on scratch repos before authoring this restructure).

Open questions for maintainers

  1. .mcp.json vs mcp.json for Cursor. The repo uses .mcp.json (Claude Code convention). Cursor's plugin docs document mcp.json (no leading dot) as the auto-discovery filename. Today the repo uses .mcp.json and apparently works for both ecosystems, but if Cursor's auto-discovery actually needs mcp.json for plugin-scoped configs, please let me know and I'll either rename the files or add an explicit mcpServers: "./.mcp.json" to each Cursor manifest.
  2. Per-plugin logos. The Cursor monolith plugin.json references logo.svg at the repo root, which still works (the file is present). Per-product Cursor plugins don't reference a logo because plugin source paths can't reach files outside the plugin's own directory after install. Happy to duplicate logo.svg into each plugin if branding consistency matters.
  3. Hand-merged root .mcp.json. The 5 server entries are duplicated between the root file and the 3 product .mcp.json files. Tolerable as long as MCP server adds/removes are infrequent. A future PR could add a CI check that asserts the union matches the root if drift becomes a concern.
  4. PR scope. Both Claude Code and Cursor manifests update in lockstep here, since moving files out of the repo root affects both ecosystems simultaneously. If you'd prefer them in separate PRs anyway, I can split.

Test plan

  • Maintainer reviews the partition (which skills/commands/MCPs go in which plugin).
  • Maintainer confirms .mcp.json filename works for Cursor plugin auto-discovery (or asks for mcp.json).
  • Smoke-test on Claude Code: install umbrella, confirm everything loads as before.
  • Smoke-test on Claude Code: install only cloudflare-workers, confirm transitive cloudflare-core install and that observability MCP is NOT loaded.
  • Smoke-test on Cursor: install the cloudflare umbrella, confirm all 8 skills + 5 MCP servers load (matches pre-PR behavior).
  • Smoke-test on Cursor: install one or more per-product plugins, confirm narrower scope works as expected.

Closes #50

🤖 Generated with Claude Code

CarterPape and others added 7 commits May 8, 2026 16:49
Adds plugins/<plugin-name>/ skeletons for the 9-plugin partition (8
product plugins + 1 umbrella). Updates both marketplace.json files to
list the new plugins.

The Claude side (.claude-plugin/marketplace.json) declares all 9
plugins, with the umbrella `cloudflare` plugin depending on the 8
product plugins so existing `/plugin install cloudflare@cloudflare`
users keep working transparently.

The Cursor side (.cursor-plugin/marketplace.json) declares only the 8
product plugins. Cursor's plugin system has no dependency mechanism, so
the umbrella plugin doesn't translate; existing Cursor users will need
to install the per-product plugins individually.

Skill content, command files, rules, and MCP configs are still at the
repo root and unchanged. Subsequent commits move them into the new
plugin subtrees.

Removes the now-stale root-level plugin.json files; multi-plugin
marketplaces only need marketplace.json at the root.

Refs cloudflare#50

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
All 8 skill directories relocate from skills/ at repo root into
plugins/<plugin-name>/skills/. Skill content is unchanged; this is
purely a packaging move so each skill ships under the appropriate
product plugin:

  skills/cloudflare              -> plugins/cloudflare-core
  skills/wrangler                -> plugins/cloudflare-workers
  skills/workers-best-practices  -> plugins/cloudflare-workers
  skills/durable-objects         -> plugins/cloudflare-durable-objects
  skills/agents-sdk              -> plugins/cloudflare-agents
  skills/sandbox-sdk             -> plugins/cloudflare-sandbox
  skills/cloudflare-email-service -> plugins/cloudflare-email
  skills/web-perf                -> plugins/cloudflare-web-perf

Refs cloudflare#50

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Both slash commands directly invoke the agents-sdk skill, so they
relocate alongside it under plugins/cloudflare-agents/commands/.

Refs cloudflare#50

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Workers-scoped Cursor rule (alwaysApply with .ts/.js/wrangler glob
matchers) relocates into plugins/cloudflare-workers/rules/. Cursor
discovers rules at <plugin-root>/rules/ the same way it discovers
skills at <plugin-root>/skills/.

Refs cloudflare#50

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The 5 MCP servers previously declared at the repo root are now split
across the three plugins that own them:

  cloudflare-api, cloudflare-docs       -> cloudflare-core
  cloudflare-bindings, cloudflare-builds -> cloudflare-workers
  cloudflare-observability               -> cloudflare-observability

The per-plugin .mcp.json files were added in the scaffolding commit;
this removes the now-redundant root file.

Refs cloudflare#50

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Documents the 9-plugin Claude / 8-plugin Cursor partition, both
install paths (umbrella for full experience, per-product for narrower
scope), and which plugin owns each skill, MCP server, and command.

The ``/plugin install cloudflare@cloudflare`` command continues to
work and gives users the same content as before via the umbrella
plugin's dependencies. Users who want lower per-session token usage
can install only the product plugins they need.

Cursor users get a brief migration note since the monolithic
``cloudflare`` Cursor plugin entry has been removed (Cursor's plugin
system has no dependency mechanism, so the umbrella pattern doesn't
translate).

Refs cloudflare#50

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The split-into-product-plugins refactor removed the root cloudflare
Cursor plugin, which would orphan existing cloudflare@cloudflare Cursor
installs (no skills, no MCP servers — silent breakage). Restore the
root cloudflare plugin while keeping the canonical content under
plugins/<product>/ by using inverted symlinks:

- skills/<X>, commands/<X>, rules/<X>: 11 symlinks pointing into
  plugins/<product>/. The Cursor cloudflare plugin (rooted at ./)
  resolves content through these. Claude never scans the repo root,
  so the Claude umbrella plugin is unaffected.
- .cursor-plugin/plugin.json: restored verbatim from upstream.
- .mcp.json: hand-merged from the 3 product .mcp.json files
  (5 servers, identical content + ordering to upstream pre-PR).
- .cursor-plugin/marketplace.json: re-add cloudflare → ./ entry
  alongside the 8 per-product entries.
- README: document the inverted-symlink layout, the hand-maintained
  root .mcp.json, and the Cursor monolith install option.

Empirically validated: Cursor's git-clone-based install pipeline
preserves internal symlinks (Cursor 3.3.27 against a throwaway repo
on 2026-05-09), and git's rename detection auto-merges upstream
edits to moved files even with a co-located symlink ADD at the
original parent path (verified on two scratch repos at /tmp).

Rationale and architecture: pape-docs/0003 (local-only, not in PR
diff). Closes the gap pape-docs/0002 flagged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Split the cloudflare plugin into smaller plugins along product axes

1 participant