Skip to content

feat: add Claude marketplace registry support - #150

Merged
Minitour merged 2 commits into
version-2.0from
feat/claude-marketplace-registries
Aug 1, 2026
Merged

feat: add Claude marketplace registry support#150
Minitour merged 2 commits into
version-2.0from
feat/claude-marketplace-registries

Conversation

@Minitour

@Minitour Minitour commented Aug 1, 2026

Copy link
Copy Markdown
Member

Summary

  • Add first-class Claude marketplace registries (claude-marketplace source type) so admins can browse/install plugins from any owner/repo marketplace.json
  • Wire fetch/parse/adapter + installer/loader/API/CLI support, with tests for marketplace parsing and registry routes
  • UI: Add Registry dialog marketplace mode, scrollable registry tabs for many entries, and move project delete into Options danger zone

Test plan

  • Add a Claude marketplace via Registries UI (e.g. a known owner/repo) and confirm it lists plugins
  • Preview a marketplace plugin item without errors; confirm icon resolves (GitHub avatar or favicon)
  • Install a plugin from the marketplace into a project and verify it appears in capabilities
  • Confirm scrollable registry tabs work with many registries
  • Confirm project delete lives under Options → Danger zone
  • Run marketplace and registries route tests

Browse and install plugins from any Claude Code marketplace via owner/repo,
with scrollable registry tabs and project Options danger-zone cleanup.

Co-authored-by: Cursor <cursoragent@cursor.com>
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Add Claude marketplace registries (browse/install from marketplace.json)

✨ Enhancement 🧪 Tests 🕐 40+ Minutes

Grey Divider

AI Description

• Add claude-marketplace registry type to fetch and load Claude marketplace.json catalogs.
• Extend API/CLI/UI flows to preview, install, and browse marketplace plugins.
• Add DB migration + route/parser tests; improve registry tabs and project delete UX.
Diagram

graph TD
  client(["CLI / Web UI"]) --> api["Server registries API"] --> installer[["Registry installer"]] --> managed[("Managed registries dir")]
  installer --> mp[["Claude marketplace module"]] --> ext{{"GitHub/GitLab repo\n(or marketplace.json URL)"}}
  api --> mgr[["RegistryManager/Loader"]] --> db[("SQLite registries table")]
  mgr --> managed

  subgraph Legend
    direction LR
    _c(["Client"]) ~~~ _a["API"] ~~~ _m[["Module"]] ~~~ _d[("Data store")] ~~~ _e{{"External"}}
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Ship a built-in "marketplace adapter" and keep DB type as github/gitlab/url
  • ➕ Avoids adding a new registry type + DB migration complexity
  • ➕ Reuses the existing adapter execution model and preview/trust workflow
  • ➖ Would require executing code to handle a JSON catalog (unnecessary for marketplaces)
  • ➖ Harder to provide marketplace-specific UX (plugin counts, icon strategy, preferred slug)
2. Treat marketplace.json as a normal URL registry and infer behavior from content
  • ➕ No new RegistrySourceType value
  • ➕ Simplifies UI type selection
  • ➖ Ambiguous and brittle detection (adapter TS vs JSON catalog)
  • ➖ Makes security/trust UX confusing (JSON vs executable adapter code)

Recommendation: Keep the PR’s explicit claude-marketplace type. It cleanly separates non-executable JSON catalogs from executable adapters (better security UX), enables marketplace-specific behaviors (preferred slug from catalog name, pluginCount in preview, icon derivation), and keeps loader/installer logic straightforward despite the one-time DB migration.

Files changed (27) +2230 / -186

Enhancement (19) +1693 / -173
registry.tsExpose supported registry source types and marketplace install messaging +12/-1

Expose supported registry source types and marketplace install messaging

• Adds 'REGISTRY_SOURCE_TYPES' including 'claude-marketplace' for CLI validation/help. Adjusts install progress output to show marketplace fetch wording instead of clone wording.

src/cli/commands/registry.ts

index.tsAllow '--type claude-marketplace' for 'capa registry add' +10/-3

Allow '--type claude-marketplace' for 'capa registry add'

• Extends the CLI 'registry add' command help text and validation to accept the new 'claude-marketplace' source type.

src/cli/index.ts

registries-routes.tsSupport 'claude-marketplace' in registry create/patch/preview routes +47/-19

Support 'claude-marketplace' in registry create/patch/preview routes

• Extends type parsing and error messages to include 'claude-marketplace'. Updates create flow to prefer the marketplace.json 'name' as the slug when the caller did not specify one, and updates preview to return 'pluginCount' and use preferred slug when valid.

src/server/registries-routes.ts

adapter.tsImplement in-process adapter for marketplace catalogs +268/-0

Implement in-process adapter for marketplace catalogs

• Adds a 'RegistryAdapter' implementation that exposes marketplace plugins via 'search' and 'view', generates install snippets when possible, and produces markdown previews. Includes icon selection via GitHub avatar or site favicon fallback.

src/shared/registries/claude-marketplace/adapter.ts

fetch.tsFetch marketplace.json from repos or direct URLs with caching and errors +331/-0

Fetch marketplace.json from repos or direct URLs with caching and errors

• Implements parsing of marketplace sources (owner/repo[@ref], GitHub/GitLab URLs, direct marketplace.json URLs), fetching JSON catalogs, and snapshotting repos via the existing cache layer. Returns meta (host/ownerRepo/ref/baseUrl/pluginCount) and a preferred slug derived from marketplace name.

src/shared/registries/claude-marketplace/fetch.ts

index.tsExport Claude marketplace registry public API +35/-0

Export Claude marketplace registry public API

• Adds an index barrel exporting fetch/parse/adapter/path/source helpers and types for the Claude marketplace implementation.

src/shared/registries/claude-marketplace/index.ts

parse.tsParse and validate marketplace.json; normalize plugin sources +146/-0

Parse and validate marketplace.json; normalize plugin sources

• Adds validation and normalization for marketplace.json fields, including metadata.pluginRoot handling and robust source-shape classification. Provides 'marketplaceNameToSlug' sanitization for stable slugs.

src/shared/registries/claude-marketplace/parse.ts

paths.tsManage materialized marketplace.json + meta and load adapter from disk +82/-0

Manage materialized marketplace.json + meta and load adapter from disk

• Defines managed file locations for marketplace.json and marketplace.meta.json and provides a loader that builds the in-process adapter from persisted artifacts.

src/shared/registries/claude-marketplace/paths.ts

sources.tsResolve marketplace plugin sources into capa install coordinates +175/-0

Resolve marketplace plugin sources into capa install coordinates

• Implements GitHub/GitLab URL parsing and translation from marketplace source variants (monorepo-local, repo, url(+path), git-subdir) into installable repo strings. Produces actionable reasons for unsupported source types (npm/pip/unknown/JSON-only origins).

src/shared/registries/claude-marketplace/sources.ts

types.tsDefine Claude marketplace catalog and origin/meta types +100/-0

Define Claude marketplace catalog and origin/meta types

• Introduces types for parsed catalogs, plugin entries, normalized sources, origin context (host/ownerRepo/baseUrl/ref), persisted meta files, and install coordinates.

src/shared/registries/claude-marketplace/types.ts

installer.tsInstall and preview Claude marketplaces alongside adapter registries +81/-9

Install and preview Claude marketplaces alongside adapter registries

• Extends slug derivation and install flow to handle 'claude-marketplace' by fetching and materializing marketplace.json + meta, then validating by building the in-process adapter. Extends 'fetchAdapterSource' to return JSON content plus 'preferredSlug' and 'pluginCount' for preview UX.

src/shared/registries/installer.ts

loader.tsLoad 'claude-marketplace' registries from cached marketplace.json +102/-5

Load 'claude-marketplace' registries from cached marketplace.json

• Adds a marketplace-specific load path that reads cached marketplace artifacts and builds a 'RegistryAdapter' without dynamic-importing code. Integrates caching by mtime + updatedAt and preserves duplicate-registry-id protection.

src/shared/registries/loader.ts

OptionsSection.tsxMove project delete into Options danger zone +28/-1

Move project delete into Options danger zone

• Adds a Danger zone section to Options, wiring delete via hooks with confirmation and navigation on success. Uses project display name for clearer confirmation copy.

web-ui/src/features/projects/components/OptionsSection.tsx

AddRegistryDialog.tsxAdd marketplace add mode with JSON preview and no trust checkbox +127/-43

Add marketplace add mode with JSON preview and no trust checkbox

• Introduces a mode toggle (adapter vs Claude marketplace) and treats marketplaces as JSON catalogs (preview language=json, shows plugin count, no trust checkbox). Ensures preview/trust state resets when mode/type/source changes to prevent installing audited content against a different source.

web-ui/src/features/registries/admin/AddRegistryDialog.tsx

EditRegistryDialog.tsxEnable editing registries to 'claude-marketplace' with marketplace UX +71/-35

Enable editing registries to 'claude-marketplace' with marketplace UX

• Adds 'claude-marketplace' to selectable types and adjusts gating so marketplace changes require preview but not the adapter trust checkbox. Updates preview rendering to JSON for marketplaces and resets preview/trust when type changes.

web-ui/src/features/registries/admin/EditRegistryDialog.tsx

RegistriesTable.tsxLocalize registry type labels for new type +1/-1

Localize registry type labels for new type

• Renders registry type via i18n with a default fallback, enabling readable display for 'claude-marketplace' and existing types.

web-ui/src/features/registries/admin/RegistriesTable.tsx

RegistryTabs.tsxReplace Radix tabs with scrollable registry picker rail +72/-22

Replace Radix tabs with scrollable registry picker rail

• Reworks registry navigation into a horizontal scroller on small screens and a vertically scrollable rail on desktop, including auto-scroll to the active registry. Adds an accessible label and fallback icon rendering when a registry lacks an icon URL.

web-ui/src/features/registries/components/RegistryTabs.tsx

ProjectDetailPage.tsxRemove top-level delete button and reorder sections +4/-33

Remove top-level delete button and reorder sections

• Deletes the header-level project delete button and related navigation hook usage, relying on the new Options danger zone instead. Moves Options below Variables to match the updated page layout.

web-ui/src/pages/ProjectDetailPage.tsx

RegistriesPage.tsxAdjust registries page grid sizing for new tabs layout +1/-1

Adjust registries page grid sizing for new tabs layout

• Tweaks desktop grid column sizing to better accommodate the new registry picker rail and content layout.

web-ui/src/pages/RegistriesPage.tsx

Tests (3) +464 / -0
registries.test.tsTest DB accepts 'claude-marketplace' registry type +10/-0

Test DB accepts 'claude-marketplace' registry type

• Adds a regression test ensuring 'upsertRegistry' accepts 'claude-marketplace' as a valid 'type' value.

src/db/tests/registries.test.ts

registries-routes.test.tsAdd route tests for marketplace preview/install/load/search/view +150/-0

Add route tests for marketplace preview/install/load/search/view

• Adds end-to-end style tests that preview a marketplace.json URL (deriving slug + pluginCount), install it via POST /api/registries, and confirm the manager can load/search it. Adds a fixture test that validates repo-backed marketplaces materialize install snippets for plugins.

src/server/tests/registries-routes.test.ts

claude-marketplace.test.tsAdd unit tests for marketplace parsing, source mapping, and adapter behavior +304/-0

Add unit tests for marketplace parsing, source mapping, and adapter behavior

• Introduces comprehensive tests for parsing marketplace.json, classifying source shapes, deriving install coordinates, favicon/avatar icon logic, and adapter search/view semantics (including unsupported sources).

src/shared/registries/tests/claude-marketplace.test.ts

Documentation (2) +27 / -10
projects.jsonAdd danger zone copy for project options +3/-1

Add danger zone copy for project options

• Adds English strings for the new Options danger zone section and delete description.

web-ui/src/locales/en/projects.json

registries.jsonAdd marketplace strings and clarify adapter trust messaging +24/-9

Add marketplace strings and clarify adapter trust messaging

• Updates empty/settings copy to mention marketplaces, adds Add/Edit dialog marketplace descriptions and placeholders, adds a marketplace type label, and introduces registry list label and preview pluginCount text.

web-ui/src/locales/en/registries.json

Other (3) +46 / -3
schema.tsMigrate registries CHECK constraint to include 'claude-marketplace' +43/-1

Migrate registries CHECK constraint to include 'claude-marketplace'

• Extends the 'registries.type' CHECK constraint to allow 'claude-marketplace'. Adds a migration that rebuilds the table for existing DBs since SQLite cannot alter CHECK constraints in place.

src/db/schema.ts

database.tsExtend RegistrySourceType union with 'claude-marketplace' +1/-1

Extend RegistrySourceType union with 'claude-marketplace'

• Updates the shared 'RegistrySourceType' type to include the new 'claude-marketplace' value across server/shared layers.

src/types/database.ts

api.tsAdd 'claude-marketplace' to UI RegistrySourceType and preview response +2/-1

Add 'claude-marketplace' to UI RegistrySourceType and preview response

• Extends the UI type union with 'claude-marketplace' and adds optional 'pluginCount' to the preview response shape.

web-ui/src/features/registries/api.ts

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Aug 1, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Ambiguous plugin selector ✓ Resolved 🐞 Bug ≡ Correctness
Description
buildRepoString() converts a known plugin subpath into the "owner/repo@name" search form when the
leaf matches the plugin name, which discards the exact location. The plugin install path for "@name"
picks the first matching entry by directory/manifest name, so duplicate names can resolve to the
wrong plugin (traversal-order-dependent).
Code

src/shared/registries/claude-marketplace/sources.ts[R113-124]

+export function buildRepoString(
+	coords: InstallCoords,
+	pluginName: string,
+): string {
+	const { ownerRepo, subpath } = coords;
+	if (!subpath) return ownerRepo;
+	const leaf = subpath.replace(/\/+$/, "").split("/").pop() ?? subpath;
+	if (leaf === pluginName) {
+		return `${ownerRepo}@${pluginName}`;
+	}
+	return `${ownerRepo}::${subpath}`;
+}
Evidence
The new heuristic returns ownerRepo@pluginName when leaf === pluginName, switching from an exact
path to a name-based search. The plugin search resolver selects the first matching discovered plugin
entry by dirName/manifestName without detecting duplicates, so emitting the search form can
resolve incorrectly when duplicates exist.

src/shared/registries/claude-marketplace/sources.ts[113-124]
src/shared/plugin-manifest/detect.ts[283-299]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`buildRepoString()` emits `owner/repo@<pluginName>` when the subpath leaf equals the plugin name. This throws away the exact subpath provided by the marketplace catalog and turns it into a repository-wide search.
Because `@<name>` resolution is a first-match lookup (not uniqueness-checked), this can select the wrong plugin if multiple plugin entries share the same directory basename or manifest name.
### Issue Context
Marketplace catalogs frequently provide exact relative paths (e.g. `./plugins/foo`). We should preserve that exactness in the generated capa install snippet to avoid ambiguous resolution.
### Fix Focus Areas
- src/shared/registries/claude-marketplace/sources.ts[113-124]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Ref slash rejected ✓ Resolved 🐞 Bug ≡ Correctness
Description
parseClaudeMarketplaceSource() throws when an owner/repo@ref ref contains '/', blocking common
branch/tag naming (e.g. feature/foo, release/v1). This prevents users from pinning a marketplace to
valid git refs that the rest of the codebase accepts.
Code

src/shared/registries/claude-marketplace/fetch.ts[R136-140]

+		if (ref.includes("/")) {
+			throw new Error(
+				`Invalid marketplace source "${trimmed}". Ref after "@" must be a branch or tag (no slashes).`,
+			);
+		}
Evidence
The marketplace parser explicitly errors if the ref contains '/'. By contrast, the shared
repo-string parser accepts :version values without any restriction on '/', indicating the system
generally supports slash-containing branch/tag names.

src/shared/registries/claude-marketplace/fetch.ts[124-141]
src/shared/repo-string.ts[115-126]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Claude marketplace sources reject refs containing `/` in the `owner/repo@ref` form. Git refs commonly include slashes, so this unnecessarily blocks valid inputs.
### Issue Context
Other repo/ref parsing in this codebase allows `:version` values without forbidding `/`. The marketplace parser can safely allow `/` after splitting at the last `@`.
### Fix Focus Areas
- src/shared/registries/claude-marketplace/fetch.ts[124-141]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Marketplace cache ignores meta ✓ Resolved 🐞 Bug ☼ Reliability
Description
RegistryLoader caches Claude marketplace adapters using marketplace.json mtime and DB updatedAt
only, but adapter construction also depends on marketplace.meta.json. If meta changes without
marketplace.json/DB timestamp changes, the in-memory adapter can stay stale
(origin/ref/icon/homepage) until process restart.
Code

src/shared/registries/loader.ts[R179-204]

+		const jsonPath = getInstalledMarketplacePath(record.slug);
+		if (!jsonPath) {
+			failures.push({
+				slug: record.slug,
+				error: `No materialized marketplace.json for slug "${record.slug}"; run \`capa registry refresh ${record.slug}\`.`,
+			});
+			return;
+		}
+
+		let mtime: number;
+		try {
+			mtime = statSync(jsonPath).mtimeMs;
+		} catch (err: any) {
+			failures.push({
+				slug: record.slug,
+				error: `Cannot stat ${jsonPath}: ${err?.message ?? err}`,
+			});
+			return;
+		}
+
+		const cached = this.cache.get(record.slug);
+		if (
+			cached &&
+			cached.mtime === mtime &&
+			cached.updatedAt === record.updatedAt
+		) {
Evidence
The loader only stats and keys the cache off marketplace.json, while the marketplace adapter
loader reads marketplace.meta.json to build the origin used in the manifest (homepage/icon) and
plugin install snippet generation. Therefore meta-only changes can be missed by the cache.

src/shared/registries/loader.ts[179-204]
src/shared/registries/claude-marketplace/paths.ts[35-82]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Marketplace adapter cache invalidation only considers `marketplace.json` mtime and the DB row `updatedAt`, but the adapter’s origin/ref/baseUrl comes from `marketplace.meta.json`. This creates a stale-cache risk when meta changes independently.
### Issue Context
`loadClaudeMarketplaceAdapter()` reads both files every time it builds the adapter. The loader should include meta file mtime (or a combined hash) in the cache key.
### Fix Focus Areas
- src/shared/registries/loader.ts[170-245]
- src/shared/registries/claude-marketplace/paths.ts[35-82]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Qodo Logo

Comment thread src/shared/registries/claude-marketplace/sources.ts Outdated
Comment thread src/shared/registries/claude-marketplace/fetch.ts Outdated
Comment thread src/shared/registries/loader.ts
Always emit exact ::subpath install coords, allow slashes in owner/repo@ref,
and invalidate marketplace adapters when marketplace.meta.json changes.

Co-authored-by: Cursor <cursoragent@cursor.com>
@Minitour
Minitour merged commit 08b4f40 into version-2.0 Aug 1, 2026
@Minitour
Minitour deleted the feat/claude-marketplace-registries branch August 1, 2026 16:34
@Minitour Minitour mentioned this pull request Aug 2, 2026
9 tasks
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.

1 participant