feat(#4815): implement MCP registry provider backend plugin - #4871
Conversation
|
Important This PR includes changes that affect public-facing API. Please ensure you are adding/updating documentation for new features or behavior. Changed Packages
|
|
🤖 Finished Review · ✅ Success · Started 6:21 PM UTC · Completed 6:48 PM UTC Commit: Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $8.49 |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #4871 +/- ##
==========================================
+ Coverage 63.81% 63.97% +0.16%
==========================================
Files 2698 2705 +7
Lines 107951 108462 +511
Branches 30264 30362 +98
==========================================
+ Hits 68887 69388 +501
- Misses 38506 38529 +23
+ Partials 558 545 -13
*This pull request uses carry forward flags. Click here to find out more. Continue to review full report in Codecov by Harness.
🚀 New features to boost your workflow:
|
|
Risk Assessment: moderate (2/5) DetailsA large but bot-authored PR introducing an entirely new isolated plugin: the high change size and multiple dependency file changes are substantially offset by all-new-file isolation, zero protected-path and security-sensitive exposure, and a well-scoped, well-triaged linked issue with detailed acceptance criteria. Previous runRisk Assessment: moderate (2/5) DetailsLarge but well-tested net-new plugin addition with strong test coverage (~3100 lines). Package rename introduces breaking-change risk. No protected-path or critical security sensitivity concerns beyond SSRF defense patterns. Moderate composite risk driven by breaking API change and SSRF gap. Previous run (2)Risk Assessment: moderate (2/5) DetailsLarger new plugin addition (56 files, 5036 lines, 3 dependency files) compared to prior snapshot, but same dimension scores apply — net-new area, no protected paths, no security concerns, no CI changes, bot author, well-bounded issue with resolved prerequisites — yielding an unchanged moderate composite risk. Previous run (3)Risk Assessment: moderate (2/5) DetailsLarge new plugin addition (32 files, 3743 lines, 3 dependency files changed) in a well-scoped, net-new area with no security concerns, no CI changes, and a bot author on an explicitly bounded issue, yielding a moderate composite risk. Previous run (4)Risk Assessment: moderate (2/5) DetailsSignals are unchanged from the prior assessment -- bot-authored, all-additive new backend plugin with large line count and two dependency files raises Tier 1 to 2.25, but solid test coverage (ratio 0.21), net-new files with no modification of shared code, and full acceptance-criteria coverage keep Tier 2 and Tier 3 at moderate (2), yielding a composite score of 2. Previous run (5)Risk Assessment: moderate (2/5) DetailsA large-line-count, all-additive new backend plugin with dependency additions raises Tier 1, but bot authorship, solid test coverage (ratio 0.24), net-new files with no modification of existing code, and full acceptance-criteria coverage yield a composite moderate score of 2. |
ReviewFindingsHigh
Medium
Low
Next steps:
Previous runReviewNew MCP Registry provider plugin for the RHDH catalog: an FindingsHigh
Medium
Low
Risk Assessment
ProvenancePrior review provenance: Next steps:
Previous run (2)ReviewFindingsHigh
Medium
Low
Next steps:
Previous run (3)ReviewFindingsMedium
Low
Next steps:
Previous run (4)ReviewFindingsHigh
Medium
Low
Next steps:
Previous run (5)ReviewFindingsMedium
Low
Next steps:
|
|
/fs-fix |
|
🤖 Finished Fix · ✅ Success · Started 7:30 PM UTC · Completed 7:48 PM UTC Commit: Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $3.05 |
- Wrap new URL(endpoint) in try/catch throwing McpRegistryClientError in client.ts (error handling gap) - Add safeGetOptionalString helper and use for all config string reads in config.ts (error-handling idioms) - Add URL scheme validation (http/https only) for baseUrl in config.ts (SSRF/input validation) - Add pageLimit >= 1 and pageSize >= 1 bounds checks in config.ts (edge case/input validation) - Update spec.md to document in-memory last-good index semantics (spec-implementation divergence) - Remove redundant first provider setup in degraded-retention test (test adequacy) - Document annotation key dependency in last-good index rebuild comment in provider.ts (architectural coherence) - Import and use McpServerMappingDefaults type instead of inline type in provider.ts (API shape patterns) - Update README.md to reference existing provider instead of future (stale-doc) - Add @visibility backend annotation to schedule field in config.d.ts (config schema visibility) Addresses #4871 Assisted-by: Claude Opus 4.6
🔧 Fix agent — iteration 1 (human-triggered)Addressed all 10 review findings: 3 medium (URL error handling in client, safeGetOptionalString in config, spec-implementation divergence for in-memory last-good index) and 7 low (pageLimit/pageSize bounds, URL scheme validation, test cleanup, architectural coherence documentation, McpServerMappingDefaults import, stale README, config visibility annotation). All 43 tests pass, lint clean, no secrets detected. Fixed (10):
Tests: passedNext steps:
|
|
/fs-review |
|
🤖 Finished Review · ✅ Success · Started 8:39 PM UTC · Completed 9:03 PM UTC Commit: Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $8.47 |
|
/fs-fix address review comments |
|
🤖 Finished Fix · ✅ Success · Started 9:21 PM UTC · Completed 9:39 PM UTC Commit: Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $3.70 |
- Wrap getOptionalConfig() in try-catch for scalar TypeError (config.ts) - Move entry.server access inside try block for null safety (provider.ts) - Exclude degraded entities from lastGoodIndex to prevent perpetual retention - Truncate error response body to 256 chars to avoid data exposure (client.ts) - Refactor while(true) to while(hasMorePages) to remove eslint-disable - Re-export McpRegistryEntityProvider and McpRegistryProviderConfig (index.ts) - Add @public release tags and regenerate API report - Update design.md D6 to describe in-memory index populated at end of sync - Update tasks.md 4.1/5.2 to reflect end-of-sync index behavior - Update audit.md timestamp to 2026-09-18 - Update proposal.md consumer reference from "future" to actual plugin name - Add test: empty registry commits full mutation with 0 entities - Add test: applyMutation throw does not update lastGoodIndex - Add test: degraded entities excluded from lastGoodIndex on subsequent syncs Addresses #4871 Assisted-by: Claude Opus 4.6
🔧 Fix agent — iteration 2 (human-triggered)Addressed all 13 review findings: 12 fixed (error handling gaps, perpetual degraded retention, stale docs, test coverage, data exposure, eslint-disable, API exports), 1 disagreed (fromConfig() factory pattern has no workspace precedent). Added 3 new tests (empty registry, applyMutation throw, degraded exclusion). All 46 tests pass, lint clean, tsc clean, API report regenerated. Fixed (12):
Disagreed (1):
Tests: passed Decision points
Next steps:
|
…ce id Move provider options to catalog.providers.mcpRegistry.mcpRegistry so the top-level key is a map of instances, ignoring extra ids with a warning. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Prefer the underlying cause message for network failures and avoid embedding Error constructor names like TypeError in operator-facing logs. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: Michael Valdron <mvaldron@redhat.com>
Type providersConfig as JsonObject and drop an unused import so yarn tsc:full passes in CI. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Document nested config, remotesOnly, hostAllowList, maxEntries, and the mapping package rename for consumers. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Extract the volume mount string before JSON.stringify to address SonarCloud feedback. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Log a defense-in-depth SSRF warning via the config warn sink when the provider starts without a hostname allowlist. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Pass an optional lifecycle override through to the mapping so operators can align ingested mcp-server entities with other ai-integrations defaults. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Split prerequisite, package install, and module registration so the ai-model dependency is clear up front. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Keep last-good entities outside the maxEntries window in the mutation with degraded sync status, and re-index them until they are refreshed. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com>
… testing secret redaction Signed-off-by: Michael Valdron <mvaldron@redhat.com>
Sort McpRegistryProviderConfig members to match API Extractor output. Assisted-by: grok-4.6 Co-authored-by: Cursor <cursoragent@cursor.com> Signed-off-by: Michael Valdron <mvaldron@redhat.com>
Align assertSingleRegistryConfig error keys with KNOWN_MCP_REGISTRY_KEYS, use the short Backstage moduleId, document internal test seams, cover the startCursor===endCursor soft-stop edge case, and note pagination reset before applyMutation. Assisted-by: grok-4.6 Co-authored-by: Cursor <cursoragent@cursor.com> Signed-off-by: Michael Valdron <mvaldron@redhat.com>
Drop branch-local edits under openspec/changes for mcp-registry-provider and mcp-registry-server-mapping so this PR no longer changes those specs. Assisted-by: grok-4.6 Co-authored-by: Cursor <cursoragent@cursor.com> Signed-off-by: Michael Valdron <mvaldron@redhat.com>
When enabled, list requests include ?version=latest so the registry returns only the latest version of each server; default remains unset. Assisted-by: grok-4.6 Co-authored-by: Cursor <cursoragent@cursor.com> Signed-off-by: Michael Valdron <mvaldron@redhat.com>
michael-valdron
left a comment
There was a problem hiding this comment.
@gabemontero @johnmcollier This lgtm to proceed with peer review 👍
how did your testing against the public MCP registry go @michael-valdron ? |
@gabemontero Seems good now, I was able to ingest large batch (4800 entries with limits) using https://registry.modelcontextprotocol.io/ and all of them on https://staging.registry.modelcontextprotocol.io/ and local. https://registry.modelcontextprotocol.io/ has 5000+ entries so good for trying out the quota limits on the provider though I did most of my testing with the other two. You can test it out by replacing For testing the backend I recommend starting from the plugin directory |
Add a guide for pointing the MCP Registry provider at production or staging official registries, and cross-link it from workspace and plugin READMEs plus the local deploy doc. Assisted-by: grok-4.6 Co-authored-by: Cursor <cursoragent@cursor.com> Signed-off-by: Michael Valdron <mvaldron@redhat.com>
Signed-off-by: Michael Valdron <mvaldron@redhat.com>
|
…plugin (redhat-developer#4871) * feat(redhat-developer#4815): implement MCP registry provider backend plugin Add catalog-backend-module-mcp-registry-provider, a Backstage catalog backend module that ingests MCP servers from one configured MCP Registry into the RHDH catalog as mcp-server API entities. Implementation includes: - Config reading at catalog.providers.mcpRegistry (single object) with baseUrl (required), baseName, apiVersion (default v1), defaultOwner, pageLimit (default 10), pageSize, and schedule (default 30m/3m) - Registry client with full cursor pagination, page cap safeguard, repeated-cursor detection, and typed error handling - EntityProvider with full-mutation semantics (catalog converges to registry state), per-entry failure isolation with last-good retention (D6), and sync status annotation (ok/degraded per D8) - Provider attribution: locationKey mcp-registry-provider, backstage.io/managed-by-location url:<normalizedBaseUrl> - Delegates entirely to mcp-registry-server-mapping-common for the server.json to entity transform and annotation projection - Keyed multi-registry map rejected at startup with actionable error - 43 unit tests covering config, client, provider, and module Closes redhat-developer#4815 Assisted-by: Claude Opus 4.6 * fix: address review feedback on PR redhat-developer#4871 - Wrap new URL(endpoint) in try/catch throwing McpRegistryClientError in client.ts (error handling gap) - Add safeGetOptionalString helper and use for all config string reads in config.ts (error-handling idioms) - Add URL scheme validation (http/https only) for baseUrl in config.ts (SSRF/input validation) - Add pageLimit >= 1 and pageSize >= 1 bounds checks in config.ts (edge case/input validation) - Update spec.md to document in-memory last-good index semantics (spec-implementation divergence) - Remove redundant first provider setup in degraded-retention test (test adequacy) - Document annotation key dependency in last-good index rebuild comment in provider.ts (architectural coherence) - Import and use McpServerMappingDefaults type instead of inline type in provider.ts (API shape patterns) - Update README.md to reference existing provider instead of future (stale-doc) - Add @visibility backend annotation to schedule field in config.d.ts (config schema visibility) Addresses redhat-developer#4871 Assisted-by: Claude Opus 4.6 * fix: address review feedback on PR redhat-developer#4871 - Wrap getOptionalConfig() in try-catch for scalar TypeError (config.ts) - Move entry.server access inside try block for null safety (provider.ts) - Exclude degraded entities from lastGoodIndex to prevent perpetual retention - Truncate error response body to 256 chars to avoid data exposure (client.ts) - Refactor while(true) to while(hasMorePages) to remove eslint-disable - Re-export McpRegistryEntityProvider and McpRegistryProviderConfig (index.ts) - Add @public release tags and regenerate API report - Update design.md D6 to describe in-memory index populated at end of sync - Update tasks.md 4.1/5.2 to reflect end-of-sync index behavior - Update audit.md timestamp to 2026-09-18 - Update proposal.md consumer reference from "future" to actual plugin name - Add test: empty registry commits full mutation with 0 entities - Add test: applyMutation throw does not update lastGoodIndex - Add test: degraded entities excluded from lastGoodIndex on subsequent syncs Addresses redhat-developer#4871 Assisted-by: Claude Opus 4.6 * fix(redhat-developer#4815): address review comments for package.json Signed-off-by: Michael Valdron <mvaldron@redhat.com> * fix(redhat-developer#4815): expand more scripts in package.json files Signed-off-by: Michael Valdron <mvaldron@redhat.com> * fix(redhat-developer#4815): add standalone dev entry for mcp-registry-provider Enable yarn start for the catalog module by adding a local backend entrypoint and the catalog/backend-defaults start dependencies. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com> * fix(redhat-developer#4815): add staging MCP Registry provider config Point local plugin and workspace app configs at the staging registry so the catalog provider can sync when started standalone or from the workspace. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com> * fix(redhat-developer#4815): list ingested mcp-server APIs in the catalog Start the refresh after the catalog connection exists, stamp the origin location annotation, and load the ai-model catalog module so Backstage accepts spec.type mcp-server entities that omit spec.definition. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com> * fix(redhat-developer#4815): record supertest in the workspace lockfile Keep the lockfile aligned with the integration test devDependencies. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com> * fix(redhat-developer#4815): address SonarCloud feedback on mcp-registry-provider Split high-complexity provider, config, and client paths into helpers, replace trailing-slash regex stripping with a linear util, and add focused unit coverage for the extracted pieces. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com> * docs(redhat-developer#4815): add mapping-common changeset for provider link Record a patch release note for documenting consumption by the MCP Registry provider and listing it in pluginPackages. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com> * fix(redhat-developer#4815): address review feedback on PR redhat-developer#4871 - Move fetchApi test seam out of public constructor into an internal options bag; run() marked @internal — both removed from public API surface (report.api.md) - Add TSDoc to all McpRegistryProviderConfig fields - Change rebuildLastGoodIndex filter from === 'degraded' to !== 'ok' for forward-compatible spec alignment - Add configurable maxEntries cap (default 5000) to abort sync when accumulated entries exceed the threshold, guarding against oversized registry pages - Rename provider.ts to McpRegistryEntityProvider.ts to match workspace naming conventions (class-based filenames) - Standardize package.json scripts: lint:check/lint:fix to lint to match sibling catalog-backend-module-* plugins - Mark all tasks in tasks.md as completed - Update audit.md timestamp to 2026-09-19 - Update stale future ingestion references in mcp-registry-server-mapping design.md to reference the now-implemented provider - Add maxEntries config field to config.d.ts with @visibility backend - Add tests for maxEntries config parsing and client enforcement - Regenerate report.api.md Addresses redhat-developer#4871 Assisted-by: Claude Opus 4.6 * fix(redhat-developer#4815): restore lint:check/lint:fix/tsc/prettier scripts in package.json Bring back lint:check, lint:fix, tsc, prettier:check, and prettier:fix scripts in both catalog-backend-module-mcp-registry-provider and mcp-registry-server-mapping-common package.json files, while keeping the standard lint script for workspace compatibility with other plugins. Addresses redhat-developer#4871 Assisted-by: Claude Opus 4.6 * fix(redhat-developer#4815): align package.json scripts with workspace convention Remove lint:check, lint:fix, tsc, prettier:check, and prettier:fix scripts from both mcp-registry-provider and mcp-registry-server- mapping-common package.json files to match the standard scripts block used by all other catalog-backend-module-* plugins in the ai-integrations workspace. Update mcp-registry-server-mapping audit.md timestamp to reflect the proposal.md and design.md changes made in this PR. Addresses redhat-developer#4871 Assisted-by: Claude Opus 4.6 * revert: undo package.json script removal from b255a04 Revert commit b255a04 which removed lint:check, lint:fix, tsc, prettier:check, and prettier:fix scripts from both package.json files. Restores audit.md timestamp to its pre-b255a04 value. Addresses redhat-developer#4871 Assisted-by: Claude Opus 4.6 * fix(redhat-developer#4815): add optional hostAllowList config for SSRF defense-in-depth Add an optional hostAllowList config field under catalog.providers.mcpRegistry that restricts outbound requests to explicitly permitted hostnames. When configured: - Config parsing validates that the baseUrl hostname is in the list - The registry client validates the endpoint hostname at runtime Hostnames are normalized to lowercase for case-insensitive matching. When omitted, all hosts are allowed (backward-compatible). Addresses redhat-developer#4871 Assisted-by: Claude Opus 4.6 * fix(redhat-developer#4815): add recent config fields to app-config.yaml files Signed-off-by: Michael Valdron <mvaldron@redhat.com> * fix(redhat-developer#4815): use registry baseUrl as the placeholder remote Servers with no valid remotes need a catalog remote that points at the configured MCP Registry, before falling back to websiteUrl. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com> * docs(redhat-developer#4815): add non-remote instruction to README Signed-off-by: Michael Valdron <mvaldron@redhat.com> * docs(redhat-developer#4815): document maxEntries in the provider README Surface the existing sync entry cap in the configuration example and options table so operators can find it alongside the other settings. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com> * fix(redhat-developer#4815): resume pageLimit and soft-stop at maxEntries Large registries can span multiple sync ticks via a saved resume cursor, and hitting maxEntries now commits the buffer with an end cursor instead of aborting the run. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com> * fix(redhat-developer#4815): add remotesOnly to skip non-remote MCP servers Operators can opt in to ingest only servers with a native remote so package-only and placeholder-remote entries never enter the catalog. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com> * refactor(redhat-developer#4815): rename mapping package to catalog-mcp-registry-server-mapping Align the mapping library directory, package name, and pluginId with workspace conventions, and set the provider module pluginId to catalog. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com> * fix(redhat-developer#4815): snapshot seenCursors on error and validate response.url Snapshot seenCursors before calling fetchRegistryServers and restore the snapshot on McpRegistryClientError so a failed sync does not carry partially mutated cursor history into the next retry. Validate response.url against the hostAllowList after each fetch completes in fetchRegistryPage, preventing SSRF via HTTP redirects to disallowed hosts. Addresses redhat-developer#4871 Assisted-by: Claude Opus 4.6 (anthropic) * fix(redhat-developer#4815): address review feedback on PR redhat-developer#4871 - Treat explicit empty hostAllowList as 'deny all' by returning an empty array instead of undefined (readOptionalHostAllowList -> readHostAllowList) - Move createMockLogger, createDefaultConfig, and mockFetchForResponses into src/testUtils.ts and import them in each test file - Align host-validation naming: validateHostAgainstAllowList -> validateHostAllowList (config.ts), validateUrlHostAllowList -> validateHostAllowList (client.ts) - Add 'yarn add' install command to README before backend.add code - Reorder formatMappingFailureMessage fragments for natural grammar: 'Failed to map MCP Registry server entry (version "1.0.0"): boom' Addresses redhat-developer#4871 Assisted-by: Claude Opus 4.6 * fix(redhat-developer#4815): make defaulted provider config fields optional Align McpRegistryProviderConfig with config parsing so apiVersion, pageLimit, maxEntries, and remotesOnly can be omitted on direct construction; the provider resolves the same defaults at runtime. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com> * fix(redhat-developer#4815): follow redirects manually with Location validation Use redirect: manual and validate each Location against http(s) and hostAllowList before following, so SSRF via redirect cannot reach a disallowed host. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com> * refactor(redhat-developer#4815): rename client host allowlist guard Rename validateHostAllowList to assertRequestHostAllowed in the client so the runtime request guard is distinct from config-time validation. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com> * docs(redhat-developer#4815): use bare hostnames in hostAllowList examples Align app-config examples with runtime hostname matching so operators do not copy full URLs that would never pass the allowlist. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com> * refactor(redhat-developer#4815): extract provider helpers to providerUtils Move hasNativeRemote, buildLastGoodKey, readServerIdentity, and formatMappingFailureMessage out of the entity provider, and split their unit tests into providerUtils.test.ts. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com> * refactor(redhat-developer#4815): return seenCursors instead of mutating options Treat FetchServersOptions.seenCursors as read-only input and return the updated set on FetchServersResult so callers replace state explicitly. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com> * refactor(redhat-developer#4815): lower fetchRegistryServers cognitive complexity Split pagination into applyMaxEntriesSoftStop, advanceAfterResolvedCursor, and isAtEndCursor helpers so SonarCloud cognitive complexity stays within the allowed limit. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com> * refactor(redhat-developer#4815): extract McpRegistryEntityProviderOptions Give the provider constructor options a named public interface so the API surface is clearer and taskRunner is documented for callers. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com> * fix(redhat-developer#4815): fail closed when response.url is missing When hostAllowList is configured, require response.url on every fetch and validate it against the allowlist so SSRF checks cannot be bypassed by an opaque response. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com> * chore(redhat-developer#4815): add local MCP Registry deploy tooling Provide yarn start/stop scripts, Podman/Docker compose helpers with custom seed mounts, example seed fixtures, and docs for developing the mcp-registry-provider against a local registry without ko. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com> * chore(redhat-developer#4815): wire mcp-registry-provider into workspace backend Register the catalog MCP registry provider in the local backend so yarn dev loads it with the rest of the catalog stack. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com> * chore(redhat-developer#4815): make app-config changes - Replace live staging MCP Registry URL with MCP_REGISTRY_URL env var (default: localhost:8080) - Comment baseName out and make it library default (mcp.registry) - Replace guest defaultOwner with OWNER env var (default: default-owner) to be consistent with workspace Signed-off-by: Michael Valdron <mvaldron@redhat.com> * chore(redhat-developer#4815): remove unused app-config from catalog-backend-module-mcp-registry-provider plugin directory Signed-off-by: Michael Valdron <mvaldron@redhat.com> * fix(redhat-developer#4815): wait for MCP Registry readiness before returning Block start-mcp-registry until the HTTP API responds so yarn dev does not race seed import and fail with fetch failed on the first sync. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com> * chore(redhat-developer#4815): rename hack/ to scripts/ for workspace consistency Align local MCP Registry tooling with the scripts/ convention used by other workspaces and update yarn/docs references. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com> * chore(redhat-developer#4815): move MCP registry examples and type docs to workspace Relocate server-json fixtures under examples/mcp-registry and server-json-types.md under docs/, and update mapping package links. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com> * fix(redhat-developer#4815): harden MCP Registry scripts for SonarCloud Resolve binaries from fixed directories and keep checkout/temp files under ~/.cache instead of world-writable /tmp. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com> * chore(redhat-developer#4815): pin MCP Registry checkout and image via env vars Make clone URL, revision, path, image name, and image tag independently configurable, defaulting the checkout and image to the 1.8.1 release. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com> * chore(redhat-developer#4815): make MCP Registry readiness API version configurable Expose MCP_REGISTRY_API_VERSION for the readiness probe path, defaulting to v0.1. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com> * chore(redhat-developer#4815): rename local MCP Registry scripts for clarity Rename deploy/undeploy scripts and yarn targets to include "local" so their purpose is clearer. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com> * chore(redhat-developer#4815): add example mcp-server API entities from registry seed Provide catalog YAML mirroring provider output for the sample seed data and wire it as a local file location for review. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com> * chore(redhat-developer#4815): nest MCP Registry provider config under reserved instance id Move provider options to catalog.providers.mcpRegistry.mcpRegistry so the top-level key is a map of instances, ignoring extra ids with a warning. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com> * fix(redhat-developer#4815): clarify MCP Registry fetch error messages Prefer the underlying cause message for network failures and avoid embedding Error constructor names like TypeError in operator-facing logs. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com> * chore(redhat-developer#4815): add 'example' tag to mcp-server examples Signed-off-by: Michael Valdron <mvaldron@redhat.com> * fix(redhat-developer#4815): satisfy tsc for nested mcpRegistry config tests Type providersConfig as JsonObject and drop an unused import so yarn tsc:full passes in CI. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com> * chore(redhat-developer#4815): expand MCP Registry changesets for provider features Document nested config, remotesOnly, hostAllowList, maxEntries, and the mapping package rename for consumers. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com> * fix(redhat-developer#4815): avoid nested template literals in deploy script Extract the volume mount string before JSON.stringify to address SonarCloud feedback. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com> * fix(redhat-developer#4815): warn when hostAllowList is unset at startup Log a defense-in-depth SSRF warning via the config warn sink when the provider starts without a hostname allowlist. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com> * feat(redhat-developer#4815): add defaultLifecycle to MCP Registry provider config Pass an optional lifecycle override through to the mapping so operators can align ingested mcp-server entities with other ai-integrations defaults. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com> * docs(redhat-developer#4815): reorganize MCP Registry provider installation section Split prerequisite, package install, and module registration so the ai-model dependency is clear up front. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com> * fix(redhat-developer#4815): retain degraded MCP servers across soft-stop syncs Keep last-good entities outside the maxEntries window in the mutation with degraded sync status, and re-index them until they are refreshed. Assisted-by: grok-4.6 Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com> * chore(redhat-developer#4815): add secret value to seed.json entry and server.json for testing secret redaction Signed-off-by: Michael Valdron <mvaldron@redhat.com> * chore(redhat-developer#4815): regenerate MCP Registry provider API report Sort McpRegistryProviderConfig members to match API Extractor output. Assisted-by: grok-4.6 Co-authored-by: Cursor <cursoragent@cursor.com> Signed-off-by: Michael Valdron <mvaldron@redhat.com> * fix(redhat-developer#4815): address remaining PR redhat-developer#4871 review feedback Align assertSingleRegistryConfig error keys with KNOWN_MCP_REGISTRY_KEYS, use the short Backstage moduleId, document internal test seams, cover the startCursor===endCursor soft-stop edge case, and note pagination reset before applyMutation. Assisted-by: grok-4.6 Co-authored-by: Cursor <cursoragent@cursor.com> Signed-off-by: Michael Valdron <mvaldron@redhat.com> * chore(redhat-developer#4815): restore mcp-registry openspec docs to main Drop branch-local edits under openspec/changes for mcp-registry-provider and mcp-registry-server-mapping so this PR no longer changes those specs. Assisted-by: grok-4.6 Co-authored-by: Cursor <cursoragent@cursor.com> Signed-off-by: Michael Valdron <mvaldron@redhat.com> * feat(redhat-developer#4815): add latestVersion MCP Registry list query option When enabled, list requests include ?version=latest so the registry returns only the latest version of each server; default remains unset. Assisted-by: grok-4.6 Co-authored-by: Cursor <cursoragent@cursor.com> Signed-off-by: Michael Valdron <mvaldron@redhat.com> * docs(redhat-developer#4815): document using official MCP registries with the provider Add a guide for pointing the MCP Registry provider at production or staging official registries, and cross-link it from workspace and plugin READMEs plus the local deploy doc. Assisted-by: grok-4.6 Co-authored-by: Cursor <cursoragent@cursor.com> Signed-off-by: Michael Valdron <mvaldron@redhat.com> * docs(redhat-developer#4815): self-revision on wording of official live production note Signed-off-by: Michael Valdron <mvaldron@redhat.com> --------- Signed-off-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: fullsend-code <278716306+fullsend-ai-coder[bot]@users.noreply.github.com> Co-authored-by: Michael Valdron <mvaldron@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com>



Summary
catalog-backend-module-mcp-registry-provider) that ingests MCP servers from one configured MCP Registry into the RHDH catalog asmcp-serverAPI entitiescatalog.providers.mcpRegistry(single object):baseUrl(required),baseName,apiVersion(defaultv1),schedule(default 30m/3m),pageLimit(default 10 pages/sync),pageSize,defaultOwner; inert when absent; keyed multi-registry maps rejected at startupredhat.com/rhdh-mcp-registry-sync-statusannotation (ok/degradedper D8)mcp-registry-server-mapping-commonfor theserver.json→ entity transform and annotation projectionTesting
yarn tsc)backstage-cli package lint)yarn prettier:check)✔️ Checklist
Closes #4815
Post-script verification
agent/4815-mcp-registry-provider)5fc4c610086737e81d15fc9e612ba2663d534f18..HEAD)