Skip to content

feat(ai-integrations): add 'mcp-registry-provider' openspec files - #4667

Open
michael-valdron wants to merge 7 commits into
mainfrom
mcp-registry-provider-openspecs
Open

feat(ai-integrations): add 'mcp-registry-provider' openspec files#4667
michael-valdron wants to merge 7 commits into
mainfrom
mcp-registry-provider-openspecs

Conversation

@michael-valdron

Copy link
Copy Markdown
Member

Hey, I just made a Pull Request!

Contributes openspecs for MCP Registry Provider plugin implementation.

https://redhat.atlassian.net/browse/RHIDP-15658

✔️ Checklist

  • A changeset describing the change and affected packages. (more info)
  • Added or Updated documentation
  • Tests for new functionality and regression tests for bug fixes
  • Screenshots attached (for UI changes)

Signed-off-by: Michael Valdron <mvaldron@redhat.com>
Assisted-by: Claude Opus 4.8
Signed-off-by: Michael Valdron <mvaldron@redhat.com>
Assisted-by: Claude Opus 4.8
@fullsend-ai-review

fullsend-ai-review Bot commented Sep 9, 2026

Copy link
Copy Markdown

🤖 Review · ❌ Terminated · Started 6:35 PM UTC · Ended 6:37 PM UTC

Commit: 70a32fb · View workflow run →

@codecov

codecov Bot commented Sep 9, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 62.50%. Comparing base (ffe511f) to head (5ffcbcc).
⚠️ Report is 57 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #4667   +/-   ##
=======================================
  Coverage   62.50%   62.50%           
=======================================
  Files        2621     2621           
  Lines      104902   104902           
  Branches    29463    29463           
=======================================
  Hits        65568    65568           
  Misses      37519    37519           
  Partials     1815     1815           
Flag Coverage Δ *Carryforward flag
adoption-insights 84.77% <ø> (ø) Carriedforward from 8db4881
ai-integrations 78.80% <ø> (ø)
app-defaults 53.07% <ø> (ø) Carriedforward from 8db4881
augment 46.67% <ø> (ø) Carriedforward from 8db4881
boost 82.94% <ø> (ø) Carriedforward from 8db4881
bulk-import 73.12% <ø> (ø) Carriedforward from 8db4881
cost-management 13.35% <ø> (ø) Carriedforward from 8db4881
dcm 73.47% <ø> (ø) Carriedforward from 8db4881
e2e-adoption-insights 60.00% <ø> (ø) Carriedforward from 8db4881
e2e-extensions 62.31% <ø> (ø) Carriedforward from 8db4881
e2e-global-header 50.35% <ø> (ø) Carriedforward from 8db4881
e2e-homepage 61.11% <ø> (ø) Carriedforward from 8db4881
e2e-intelligent-assistant 46.74% <ø> (ø) Carriedforward from 8db4881
e2e-orchestrator 49.52% <ø> (ø) Carriedforward from 8db4881
e2e-orchestrator-plugin 49.51% <ø> (ø) Carriedforward from 8db4881
e2e-quickstart 55.21% <ø> (ø) Carriedforward from 8db4881
e2e-scorecard 50.16% <ø> (ø) Carriedforward from 8db4881
e2e-theme 16.36% <ø> (ø) Carriedforward from 8db4881
extensions 57.37% <ø> (ø) Carriedforward from 8db4881
global-floating-action-button 71.18% <ø> (ø) Carriedforward from 8db4881
global-header 68.09% <ø> (ø) Carriedforward from 8db4881
homepage 48.48% <ø> (ø) Carriedforward from 8db4881
install-dynamic-plugins 71.94% <ø> (ø) Carriedforward from 8db4881
intelligent-assistant 76.51% <ø> (ø) Carriedforward from 8db4881
konflux 91.98% <ø> (ø) Carriedforward from 8db4881
lightspeed 69.02% <ø> (ø) Carriedforward from 8db4881
mcp-integrations 84.46% <ø> (ø) Carriedforward from 8db4881
orchestrator 71.13% <ø> (ø) Carriedforward from 8db4881
quickstart 63.74% <ø> (ø) Carriedforward from 8db4881
sandbox 79.56% <ø> (ø) Carriedforward from 8db4881
scorecard 87.98% <ø> (ø) Carriedforward from 8db4881
theme 87.91% <ø> (ø) Carriedforward from 8db4881
translations 5.12% <ø> (ø) Carriedforward from 8db4881
x2a 77.18% <ø> (ø) Carriedforward from 8db4881

*This pull request uses carry forward flags. Click here to find out more.


Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update ffe511f...5ffcbcc. Read the comment docs.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@fullsend-ai-review

Copy link
Copy Markdown

🤖 Finished Review · ❌ Failure (validation failed after 2 iteration(s)) · Started 6:35 PM UTC · Completed 6:37 PM UTC

Commit: 70a32fb · View workflow run →

Runtime: claude · Model: opus → claude-opus-5

@michael-valdron

Copy link
Copy Markdown
Member Author

/fs-fix Apply the following fix from previous review comment: Change schema: rhdh-spec-driven to schema: spec-driven in both .openspec.yaml files to match the established convention

@fullsend-ai-coder

fullsend-ai-coder Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

🤖 Finished Fix · ❌ Failure (validation failed after 2 iteration(s)) · Started 6:43 PM UTC · Completed 6:46 PM UTC

Commit: 70a32fb · View workflow run →

Runtime: claude · Model: opus → claude-opus-5

@michael-valdron

Copy link
Copy Markdown
Member Author

/fs-fix Apply the following fix from previous review comment: Change schema: rhdh-spec-driven to schema: spec-driven in both .openspec.yaml files to match the established convention

@fullsend-ai-coder

fullsend-ai-coder Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

🤖 Finished Fix · ✅ Success · Started 3:38 PM UTC · Completed 3:42 PM UTC

Commit: 70a32fb · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Cost: $0.76

Change schema value from rhdh-spec-driven to spec-driven in both
.openspec.yaml files to match the established convention used by
all other openspec changes in the repository.

Addresses review feedback on #4667

Assisted-by: Claude Opus 4.6
@fullsend-ai-review

fullsend-ai-review Bot commented Sep 10, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 3:44 PM UTC · Completed 3:57 PM UTC

Commit: ffac784 · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Cost: $5.03

@fullsend-ai-review

fullsend-ai-review Bot commented Sep 10, 2026

Copy link
Copy Markdown

Review

Findings

Low

  • [scope-title-mismatch] — The PR title mentions only mcp-registry-provider but the change adds two complete openspec change sets: mcp-registry-provider (7 files) and mcp-registry-server-mapping (8 files). Consider updating the title to reflect both.

  • [missing-authorization] — Non-trivial change (15 new specification files) references Jira RHIDP-15658 but has no linked GitHub issue. Authorized scope cannot be verified through the GitHub API.

  • [Internal consistency] workspaces/ai-integrations/openspec/changes/mcp-registry-provider/audit.md:34 — The audit.md reports zero findings (0 CRITICAL, 0 WARNING, 0 SUGGESTION), but the sibling journal.jsonl records the audit found 1 WARNING (schedule type name drift) and 1 SUGGESTION (cursor safeguard wording). The mcp-registry-server-mapping audit.md properly documents its resolved findings for comparison.

Previous run

Review

Verdict: Approve — no blocking findings.

This draft PR adds OpenSpec design documentation for two related changes:

  1. mcp-registry-server-mapping — a pure, deterministic transform from MCP Registry server.json documents to Backstage mcp-server API entities, including direct field mapping, annotation projection for unmapped attributes, secret redaction, and identity derivation.
  2. mcp-registry-provider — a Backstage catalog-backend-module entity provider that fetches servers from MCP Registries via cursor pagination and commits them to the catalog using the mapping transform.

All 15 files are specification/design documents (proposals, design docs, specs, tasks, audits, and journals) under workspaces/ai-integrations/openspec/changes/. No code, no runtime impact.

Observations

Well-designed aspects:

  • The two-change decomposition (pure mapping vs. runtime provider) is clean and follows good separation of concerns.
  • The mcp-registry-server-mapping design carefully targets the upstream McpServerApiEntity shape (spec.remotes[], no spec.definition), with explicit references to the Backstage PR #34016 and canonical example.
  • Secret redaction (D9) is well-thought-out — uniformly pruning default/value of isSecret: true inputs across all input locations, not just env vars.
  • The cursor pagination requirement (D4) correctly addresses the gap left by the reference proxy prototype (which only fetched the first page).
  • The SCM-aware repository URL combination algorithm (D10) handles GitHub/GitLab/Bitbucket/Azure DevOps templates correctly using HEAD instead of inventing a branch name.
  • The failure isolation model (D6 in the provider) — skip bad entries, fail bad runs atomically — is robust and prevents catalog flapping from partial full mutations.

Low-severity notes (non-blocking):

  1. PR title scope: The title mentions only mcp-registry-provider but the PR also adds the mcp-registry-server-mapping openspec (8 files). Consider updating the title to reflect both changes, e.g., feat(ai-integrations): add mcp-registry-server-mapping and mcp-registry-provider openspec files.

  2. New artifacts (audit.md, journal.jsonl): These are new to the openspec structure — no other existing change has them. They add useful traceability. If they represent a new convention, consider documenting it (or adding them retroactively to other changes for consistency). This is purely a conventions observation, not a problem.

  3. apiVersion default (v1) vs. live registry (v0/v0.1): This discrepancy is thoroughly documented as a risk and open question in both the design and proposal. The decision to default to v1 and let operators override is reasonable for forward compatibility, though it means first-time users will hit a connection error if they don't configure apiVersion. The clear error messaging specified in D7 and the config validation (task 2.3) should mitigate this.

  4. McpServerApiEntity empty remotes[] assumption (D8): The spec assumes the upstream schema accepts an empty remotes array (no minItems: 1). The dependency note in D8 correctly flags this as a revisit point if upstream tightens the constraint. Worth verifying during implementation.

Checklist Assessment

All PR checklist items are unchecked, which is appropriate for a draft/WIP PR containing only specification documents — no changeset, tests, or screenshots are needed for documentation-only openspec additions.

Previous run (2)

Review

Findings

Medium

  • [algorithm-logic-consistency] workspaces/ai-integrations/openspec/changes/mcp-registry-server-mapping/specs/mcp-registry-server-mapping/spec.md:75 — The spec requires combining repository.url with repository.subfolder to produce a backstage.io/source-location annotation in url: format, with an example embedding the branch name main. However, constructing this URL requires knowledge of the repository's default branch, which is not present in the server.json schema. The algorithm for combining these values is never specified, and different Git hosting platforms use different URL formats for referencing subdirectories. The same underspecification appears in design D10, the proposal, and tasks.
    Remediation: Define the URL combination algorithm explicitly. Options: (a) specify that repository.url is expected to already include the tree/branch path when a subfolder is relevant, (b) define a simple URL-path-join behavior and document that the resulting URL may not be directly browsable, or (c) add a repository.branch field as an optional input.

  • [scope-mismatch] — The PR title names only mcp-registry-provider, but the diff introduces openspec documents for two distinct changes: mcp-registry-provider (7 files) and mcp-registry-server-mapping (8 files). The server-mapping change is a prerequisite of the provider change and defines a separate capability (the pure server.json to entity transform). The title omits the server-mapping change entirely.
    Remediation: Update the PR title to reflect both changes, or split into two PRs if independent review is preferred.

Low

  • [internal-consistency] workspaces/ai-integrations/openspec/changes/mcp-registry-provider/audit.md:23 — The provider's journal.jsonl records that the audit found "1 WARNING (schedule type name drift), 1 SUGGESTION (cursor safeguard wording)", but audit.md shows zero findings across all categories with no documentation of initially-found and later-resolved issues. The sibling mcp-registry-server-mapping/audit.md demonstrates the expected pattern by documenting its pass-1 and pass-2 findings as "all resolved" with details.

  • [technical-accuracy] workspaces/ai-integrations/openspec/changes/mcp-registry-provider/design.md:60 — Design D1, the proposal, and the spec use the Backstage runtime type name SchedulerServiceTaskScheduleDefinition for the config-level schedule field. In a config.d.ts declaration, the proper type is SchedulerServiceTaskScheduleDefinitionConfig (the serializable config variant). The tasks.md (task 2.1) correctly specifies the config variant, creating an inconsistency across the documents.

  • [structural-divergence] workspaces/ai-integrations/openspec/changes/mcp-registry-provider/journal.jsonl — Both new openspec changes introduce journal.jsonl and audit.md files that do not exist in any of the existing openspec change directories. If these are intentional additions to the openspec convention, consider documenting the expectation for future contributors.

  • [naming-convention] workspaces/ai-integrations/openspec/changes/mcp-registry-provider/design.md — Decision heading format in both new design files uses colons (e.g., ### D1: Configuration under ...) whereas the majority of existing design files use em-dashes (e.g., ### D1 --- Dedicated kind ...). Applies to both mcp-registry-provider/design.md (7 headings) and mcp-registry-server-mapping/design.md (10 headings).

  • [documentation-comment-format] workspaces/ai-integrations/openspec/changes/mcp-registry-provider/design.md — Both new design files use bullet-point format for the Risks / Trade-offs section, whereas all existing design files with this section use a two-column Markdown table (| Risk | Mitigation |). Applies to both mcp-registry-provider/design.md and mcp-registry-server-mapping/design.md.

  • [missing-authorization] — No linked GitHub issue exists for this PR. The PR body links to JIRA RHIDP-15658, which is not accessible for scope verification. The author is a repository MEMBER, and the PR is a draft with the do-not-merge/work-in-progress label, which mitigates the risk.

Previous run (3)

Review — comment

PR: #4667 — feat(ai-integrations): add 'mcp-registry-provider' openspec files
Status: Draft · do-not-merge/work-in-progress

This PR adds 15 new specification/design files across two well-structured openspec change sets (mcp-registry-provider and mcp-registry-server-mapping). The two changes have a clean dependency relationship — the provider consumes the mapping — with no scope overlap. The specs are thorough, internally consistent, and follow the established openspec conventions in the workspace. The design decisions are well-reasoned with clear rationale and alternatives considered.

Three medium-severity findings identified during review, all related to spec accuracy that could affect implementation correctness:

Medium

1. backstage.io/source-location annotation missing required url: prefix format · correctness
workspaces/ai-integrations/openspec/changes/mcp-registry-server-mapping/specs/mcp-registry-server-mapping/spec.md

The spec, design (D10), and proposal describe emitting repository.url as a backstage.io/source-location annotation but do not specify the required Backstage location-ref format url:<url>. In this codebase, backstage.io/source-location values consistently use the url: prefix (e.g., url:oci://quay.io/org/skills:latest in collectOciErrors.ts), and Backstage's parseLocationRef() splits on the first colon to extract {type, target}. Without the url: prefix, a raw URL would be parsed as type='https', target='//host/...', breaking source-location resolution in Backstage tooling.

Remediation: Update the spec, design D10, and proposal to state that the backstage.io/source-location value MUST use url:<url> format.


2. metadata.name collision disambiguation cannot work in a pure single-document transform · correctness
workspaces/ai-integrations/openspec/changes/mcp-registry-server-mapping/specs/mcp-registry-server-mapping/spec.md

The spec states that when two distinct (name, version) pairs sanitize to the same <name>__<version>, the mapping appends a stable hash suffix. However, the mapping is defined as a pure function of a single server.json document — it cannot detect collisions with other documents. The spec needs to clarify whether: (a) the hash suffix is always applied when sanitization alters the input (preemptively preventing collisions since different canonical values produce different hashes), or (b) collision detection is delegated to the provider layer.

Remediation: Clarify the hash-suffix application rule. The simplest correct approach: always append a stable hash suffix derived from the pre-sanitization canonical values when sanitization changes any character or the combined length exceeds 63 characters.


3. D9 secret redaction does not prune choices leaf for isSecret: true inputs · security
workspaces/ai-integrations/openspec/changes/mcp-registry-server-mapping/design.md

D9 explicitly lists choices among the non-secret sibling leaves that "continue to project" for isSecret: true inputs. If a secret input's choices array enumerates valid secret values (e.g., allowed API keys), those values would be published into searchable, plaintext catalog annotations despite the input being marked secret.

Remediation: Extend D9 to also prune the choices leaf when isSecret: true, or document an explicit rationale for why enumerating possible secret values in annotations is acceptable.


Low

  • Journal vs. audit discrepancy (mcp-registry-provider/journal.jsonl): The journal records "1 WARNING, 1 SUGGESTION" from the audit, but audit.md shows all zeros with "None" under each heading. Unlike the sibling mcp-registry-server-mapping/audit.md, which documents resolved findings, the provider audit doesn't show the disposition of these findings.

  • Degenerate sanitization edge case (mcp-registry-server-mapping spec): The metadata.name derivation doesn't address what happens when sanitization of name or version produces an empty or invalid result (e.g., a name consisting entirely of characters illegal in Backstage names).

  • New artifact types (audit.md, journal.jsonl): These files don't exist in any of the 7 existing ai-integrations openspec change directories. They introduce new convention without documented rationale or schema. Consider documenting the journal.jsonl schema/purpose or confirming with maintainers.

  • PR title omits the second change set (mcp-registry-server-mapping). Consider updating to reflect both.

  • Upstream References heading in mcp-registry-server-mapping/proposal.md is not present in any existing proposal.md or in the sibling mcp-registry-provider/proposal.md. Consider folding into Canonical Touchpoints or Impact for consistency.

  • JIRA-only authorization: The PR links to JIRA RHIDP-15658 but has no GitHub issue. Reviewers without JIRA access cannot verify scope authorization. Consider adding a scope summary to the PR body.

  • Type naming: Proposal/design describe the operator-facing config schedule as SchedulerServiceTaskScheduleDefinition (runtime type) rather than SchedulerServiceTaskScheduleDefinitionConfig (config-time type, correctly used in tasks.md task 2.1).

Previous run (4)

Review

Findings

Medium

  • [api-contract] workspaces/ai-integrations/openspec/changes/mcp-registry-server-mapping/specs/mcp-registry-server-mapping/spec.md:95 — The spec states that repository.url (combined with repository.subfolder) is emitted as a backstage.io/source-location annotation but never specifies the required Backstage location-ref format url:<repository-url>. The existing codebase enforces this prefix — tests in catalog-backend-module-ai-resource-extensions explicitly reject bare URLs without the url: prefix. An implementer following these specs would emit the raw URL string, which Backstage source-location tooling would not recognize.
    Remediation: Specify that the annotation value SHALL use the format url:<repository-url>. Propagate to design.md D10 and proposal.md.

  • [naming-conventions] workspaces/ai-integrations/openspec/changes/mcp-registry-provider/design.md:109 — Decision body paragraphs use inline prose instead of the labeled structure (**Choice**:, **Alternatives considered**:, **Rationale**:) used consistently by all existing design.md files in this workspace. The same deviation applies to all decisions in both new design.md files.
    Remediation: Restructure each decision body to use the established label pattern.

Low

  • [scope-title-mismatch] — PR title mentions only mcp-registry-provider but the PR contributes two distinct openspec change sets: mcp-registry-provider (7 files) and mcp-registry-server-mapping (8 files). The server-mapping set is substantial and not reflected in the title.

  • [code-organization] workspaces/ai-integrations/openspec/changes/mcp-registry-provider — Both new change directories introduce audit.md and journal.jsonl files not present in existing openspec changes. This appears to be openspec tooling evolution rather than arbitrary additions.

  • [internal-consistency] workspaces/ai-integrations/openspec/changes/mcp-registry-provider/audit.md — audit.md reports zero findings but journal.jsonl records "1 WARNING (schedule type name drift), 1 SUGGESTION (cursor safeguard wording)." The sibling mcp-registry-server-mapping/audit.md correctly documents its resolved findings. Additionally, the schedule type name inconsistency persists: design.md/proposal.md/spec.md reference SchedulerServiceTaskScheduleDefinition while tasks.md 2.1 correctly uses SchedulerServiceTaskScheduleDefinitionConfig.

  • [internal-consistency] workspaces/ai-integrations/openspec/changes/mcp-registry-provider/design.md:83 — Cursor pagination termination described as "stop when it is absent/empty", omitting the null case that spec.md correctly includes ("absent, null, or empty").

  • [naming-conventions] workspaces/ai-integrations/openspec/changes/mcp-registry-provider/design.md:109 — Decision headings use colon separator (### D1: ...) where existing design.md files predominantly use em dash (### D1 — ...).

  • [code-organization] workspaces/ai-integrations/openspec/changes/mcp-registry-provider/design.md:56 — Canonical Touchpoints section uses a PRDs/ADRs/Long-lived-specs list with "None" values; existing design.md files use direct Jira-reference bullet points.

Previous run (5)

Review — comment

Summary

This PR contributes OpenSpec design/specification documents for two related features in the ai-integrations workspace:

  1. mcp-registry-server-mapping — a deterministic transform contract from MCP Registry server.json to Backstage mcp-server API entities
  2. mcp-registry-provider — a scheduled catalog entity provider that ingests servers from MCP Registries

All 15 files are new documentation/specification artifacts (proposals, designs, task lists, specs, audits, journals). No production code is changed. The PR is appropriately marked as a draft with the do-not-merge/work-in-progress label.

The specifications are thorough and well-structured, with good separation of concerns between the mapping contract and the provider runtime. Design decisions are well-reasoned with alternatives considered and rejected. Cross-document consistency is strong overall. Several spec underspecifications and convention deviations are noted below for resolution before implementation begins.

Findings

Medium

1. backstage.io/source-location annotation format not specifiedmcp-registry-server-mapping/design.md, D10

D10 specifies that repository.url is emitted as a backstage.io/source-location annotation, but does not specify the required Backstage url:<actual-url> location-ref format. In this repository, backstage.io/source-location consistently uses the url: prefix (e.g., url:oci://... in collectOciErrors.ts). The upstream parseLocationRef from @backstage/catalog-model parses type:target form, so a bare URL without the url: prefix would be parsed incorrectly — the URL scheme (e.g., https) would become the location type instead of url. An implementer following this spec literally could emit a bare repository URL, causing source-location tooling to fail.

Remediation: Specify that the backstage.io/source-location value MUST use the format url:<repository-url>.

2. metadata.name collision disambiguation incompatible with pure-function constraintmcp-registry-server-mapping/specs/mcp-registry-server-mapping/spec.md

The collision disambiguation scenario states that when two distinct (name, version) pairs sanitize to the same <name>__<version>, a hash suffix is appended. However, the mapping is specified as a pure function of a single server.json document — it has no knowledge of other documents. For example, name='a/b' sanitizes / to - producing a-b__c, which collides with name='a-b', version='c' needing no sanitization. A single-document function cannot detect this.

Remediation: Either (a) always append the hash suffix when sanitization modifies the input (preemptive, self-contained), or (b) explicitly state that cross-document collision is delegated to the provider.

3. Empty registry edge case not addressedmcp-registry-provider/specs/mcp-registry-provider/spec.md

The provider spec covers multi-page, single-page, and error scenarios, but not the empty-registry case. An HTTP 200 with { "servers": [], "metadata": { "count": 0 } } would produce a valid full mutation with zero entities, pruning ALL previously-ingested entities. This is indistinguishable from a registry bug that temporarily empties its listing. D6 preserves state on transport errors, but an empty 200 is not classified as an error.

Remediation: Add a scenario for the empty-registry case: document it as expected behavior, or add a safeguard (e.g., log a warning, or treat zero-server responses as suspicious).

4. PR title understates scope

The title says "add 'mcp-registry-provider' openspec files" but the PR contributes specs for two features: mcp-registry-server-mapping (the transform contract) AND mcp-registry-provider (the entity provider). The mapping spec tree is roughly half the diff.

Remediation: Update title to reflect both features, e.g., feat(ai-integrations): add mcp-registry-server-mapping and mcp-registry-provider openspec files.

5. Missing H1 title headingsproposal.md, design.md, tasks.md in both feature directories

Six of seven existing sibling openspec change directories use # Proposal: <Name>, # Design: <Name>, # Tasks: <Name> as their H1 heading. All six files in this PR start at H2 level, skipping the H1 title. This deviates from the dominant workspace convention.

Remediation: Add H1 headings to match the established pattern (e.g., # Proposal: MCP Registry Provider).

Low

6. metadata.name sanitization rules not specifiedmcp-registry-server-mapping/specs/mcp-registry-server-mapping/spec.md

The worked example shows io.github.user/weather sanitized to io.github.user-weather, implying /-, but the spec does not document explicit replacement rules. Different strategies would produce different names, breaking determinism across implementations.

7. Dot-in-key-name ambiguitymcp-registry-server-mapping/specs/mcp-registry-annotation-projection/spec.md

JSON keys in server.json can themselves contain dots. Under dot-path encoding, the annotation key becomes ambiguous (e.g., _meta.io.modelcontextprotocol.registry.field vs a deep nesting). Values are correct but path reconstruction is lossy.

8. Secret redaction depends on upstream isSecret flagmcp-registry-server-mapping/design.md, D9

No defense-in-depth mechanism for unmarked secrets. A credential-bearing input without isSecret: true would be projected into a searchable plaintext annotation.

9. Novel file typesaudit.md, journal.jsonl in both directories

Neither file type exists in the 7 existing ai-integrations openspec changes, introducing new conventions without precedent. If intentional, document the expected file set.

10. .openspec.yaml missing status field

Six of seven existing siblings include status: draft. Both new files omit it.

Dimensions reviewed

Dimension Result
Correctness ⚠ Medium — spec underspecifications (source-location format, collision strategy, empty registry)
Security ✅ Clean — secret design is sound, no injection patterns, no hardcoded secrets
Intent & coherence ⚠ Medium — PR title understates scope; Jira authorization is established practice
Style & conventions ⚠ Medium — missing H1 headings deviate from sibling convention
Documentation currency ✅ N/A — spec-only PR, no code changes
Cross-repo contracts ✅ N/A — no exported interfaces modified

Labels: PR adds MCP Registry integration specs; documentation label applies to spec-only changes

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot added requires-manual-review Review requires human judgment documentation Improvements or additions to documentation mcp-integrations labels Sep 10, 2026
@michael-valdron

Copy link
Copy Markdown
Member Author

/fs-fix

  • Add status: draft to the openspecs: Six of seven existing siblings include status: draft. Both new files omit it.
  • Apply: Add H1 headings to match the established pattern (e.g., # Proposal: MCP Registry Provider).

@fullsend-ai-coder

fullsend-ai-coder Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

🤖 Finished Fix · ✅ Success · Started 4:16 PM UTC · Completed 4:23 PM UTC

Commit: ffac784 · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Cost: $1.96

Add status: draft to both .openspec.yaml files to match the
convention used by six of seven existing sibling changes. Add H1
headings to proposal.md, design.md, tasks.md, and audit.md in both
mcp-registry-provider and mcp-registry-server-mapping to match the
established heading pattern (e.g., # Proposal: Title).

Addresses review feedback on #4667

Assisted-by: Claude Opus 4.6
@fullsend-ai-review

fullsend-ai-review Bot commented Sep 10, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 4:25 PM UTC · Completed 4:45 PM UTC

Commit: 09d181b · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Cost: $6.45

@fullsend-ai-review fullsend-ai-review Bot added requires-manual-review Review requires human judgment and removed requires-manual-review Review requires human judgment labels Sep 10, 2026
@michael-valdron

Copy link
Copy Markdown
Member Author

/fs-fix Restructure each decision body under workspaces/ai-integrations/openspec/changes/mcp-registry-server-mapping/design.md and workspaces/ai-integrations/openspec/changes/mcp-registry-provider/design.md to use the established label pattern.

@fullsend-ai-coder

fullsend-ai-coder Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

🤖 Finished Fix · ✅ Success · Started 7:59 PM UTC · Completed 8:14 PM UTC

Commit: 09d181b · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Cost: $2.52

… pattern

Restructure each decision body in mcp-registry-server-mapping/design.md
and mcp-registry-provider/design.md to use the established label pattern
(Choice/Alternatives considered/Rationale) matching the format used by
other design.md files in the openspec/changes directory.

Addresses review feedback on #4667

Assisted-by: Claude Opus 4.6
@fullsend-ai-review

fullsend-ai-review Bot commented Sep 10, 2026

Copy link
Copy Markdown

🤖 Review · ❌ Terminated · Started 8:15 PM UTC · Ended 8:37 PM UTC

Commit: c9b9171 · View workflow run →

@michael-valdron
michael-valdron force-pushed the mcp-registry-provider-openspecs branch from 775bfaf to 5ffcbcc Compare September 11, 2026 17:29
@fullsend-ai-review

fullsend-ai-review Bot commented Sep 11, 2026

Copy link
Copy Markdown

🤖 Review · ❌ Terminated · Started 5:30 PM UTC · Ended 5:35 PM UTC

Commit: 5ffcbcc · View workflow run →

@sonarqubecloud

Copy link
Copy Markdown

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot added ready-for-merge All reviewers approved — ready to merge and removed requires-manual-review Review requires human judgment labels Sep 11, 2026
@fullsend-ai-review

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 5:30 PM UTC · Completed 5:35 PM UTC

Commit: 5ffcbcc · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Cost: $1.12

@rhdh-qodo-merge

Copy link
Copy Markdown

PR Summary by Qodo

Specify MCP registry mapping and catalog provider

✨ Enhancement 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Defines deterministic, secret-safe conversion from registry server records into Backstage
 entities.
• Specifies scheduled multi-registry ingestion with pagination, atomic synchronization, and failure
 isolation.
• Documents architecture, acceptance scenarios, implementation tasks, risks, and completed
 specification audits.
Diagram

graph TD
  R[("MCP Registry")] --> P["Catalog Provider"] --> M["Server Mapping"] --> E["MCP Entities"] --> C[("Backstage Catalog")]
  CFG["Provider Config"] --> P
  S["Scheduler"] --> P
Loading
High-Level Assessment

The proposed separation between a pure mapping contract and a scheduled catalog provider is the strongest approach. It prevents mapping drift, keeps ingestion concerns isolated, supports independent registry schedules and pruning scopes, and avoids the incomplete semantics of a proxy, inline mapping, or partial mutation strategy.

Files changed (15) +1015 / -0

Enhancement (2) +130 / -0
proposal.mdPropose an MCP registry catalog provider +53/-0

Propose an MCP registry catalog provider

• Explains the need for scheduled registry ingestion and scopes a multi-instance Backstage entity provider. It establishes cursor pagination, full-mutation reconciliation, mapping delegation, and resilient synchronization behavior.

workspaces/ai-integrations/openspec/changes/mcp-registry-provider/proposal.md

proposal.mdPropose the canonical MCP server mapping contract +77/-0

Propose the canonical MCP server mapping contract

• Proposes a deterministic server.json-to-mcp-server transformation aligned with Backstage's dedicated entity schema. It covers native mappings, lossless annotation projection, secret redaction, valid key construction, supplied defaults, and upstream dependencies.

workspaces/ai-integrations/openspec/changes/mcp-registry-server-mapping/proposal.md

Documentation (11) +879 / -0
audit.mdRecord the provider specification audit +31/-0

Record the provider specification audit

• Documents a clean audit across entity propagation, semantics, conventions, ownership, coherence, and security categories.

workspaces/ai-integrations/openspec/changes/mcp-registry-provider/audit.md

design.mdDesign scheduled MCP registry catalog ingestion +132/-0

Design scheduled MCP registry catalog ingestion

• Defines provider configuration, module registration, scheduling, cursor pagination, mapping delegation, atomic full mutations, and failure isolation. It also documents operational risks, deployment behavior, and deferred concerns such as authentication and cross-registry deduplication.

workspaces/ai-integrations/openspec/changes/mcp-registry-provider/design.md

journal.jsonlCapture provider specification authoring history +13/-0

Capture provider specification authoring history

• Records proposal creation, key configuration and pagination decisions, generated artifacts, and audit activity.

workspaces/ai-integrations/openspec/changes/mcp-registry-provider/journal.jsonl

spec.mdSpecify MCP registry provider requirements +129/-0

Specify MCP registry provider requirements

• Defines acceptance requirements and scenarios for configuration, scheduling, endpoint construction, pagination, mapping, catalog mutations, and error handling. It requires failed registry runs to preserve the last successful catalog state.

workspaces/ai-integrations/openspec/changes/mcp-registry-provider/specs/mcp-registry-provider/spec.md

tasks.mdPlan registry provider implementation and verification +51/-0

Plan registry provider implementation and verification

• Breaks implementation into packaging, configuration, registry client, scheduling, mapping integration, and end-to-end verification work. The plan includes unit, integration, schema-conformance, and reconciliation tests.

workspaces/ai-integrations/openspec/changes/mcp-registry-provider/tasks.md

audit.mdRecord the server mapping specification audit +47/-0

Record the server mapping specification audit

• Documents a clean final audit and the resolution of earlier consistency findings. It also clarifies ownership of MCP annotations and the standard Backstage source-location annotation.

workspaces/ai-integrations/openspec/changes/mcp-registry-server-mapping/audit.md

design.mdDesign deterministic server-to-entity mapping +144/-0

Design deterministic server-to-entity mapping

• Defines entity shape, annotation projection, identity derivation, defaults, determinism, schema-drift handling, secret redaction, and SCM-aware source links. It captures alternatives, rationale, risks, and compatibility assumptions for each decision.

workspaces/ai-integrations/openspec/changes/mcp-registry-server-mapping/design.md

journal.jsonlCapture mapping specification decision history +25/-0

Capture mapping specification decision history

• Records the mapping proposal lifecycle, audits, revisions, and major decisions. Later entries document secret-safe metadata behavior and the SCM-aware repository URL algorithm using HEAD instead of an invented branch.

workspaces/ai-integrations/openspec/changes/mcp-registry-server-mapping/journal.jsonl

spec.mdSpecify fallback annotation projection and redaction +113/-0

Specify fallback annotation projection and redaction

• Defines dot-path projection of unmapped scalar values into catalog-valid annotations with deterministic sanitization and collision handling. It requires redaction of secret defaults and values while preserving other metadata and round-trip fidelity.

workspaces/ai-integrations/openspec/changes/mcp-registry-server-mapping/specs/mcp-registry-annotation-projection/spec.md

spec.mdSpecify direct MCP server entity mapping +155/-0

Specify direct MCP server entity mapping

• Defines the complete native-field mapping, version-unique identity, defaults, remotes handling, and deterministic output contract. It also specifies SCM-specific repository subfolder URLs for GitHub, GitLab, Bitbucket, Azure DevOps, and unknown hosts.

workspaces/ai-integrations/openspec/changes/mcp-registry-server-mapping/specs/mcp-registry-server-mapping/spec.md

tasks.mdPlan mapping implementation and conformance testing +39/-0

Plan mapping implementation and conformance testing

• Organizes schema pinning, direct mapping, annotation projection, secret redaction, and fixture-based verification tasks. It requires upstream schema validation, deterministic output tests, round-trip checks, and SCM URL coverage.

workspaces/ai-integrations/openspec/changes/mcp-registry-server-mapping/tasks.md

Other (2) +6 / -0
.openspec.yamlConfigure the registry provider OpenSpec change +3/-0

Configure the registry provider OpenSpec change

• Declares the change as a draft using the repository-standard spec-driven schema and records its creation date.

workspaces/ai-integrations/openspec/changes/mcp-registry-provider/.openspec.yaml

.openspec.yamlConfigure the server mapping OpenSpec change +3/-0

Configure the server mapping OpenSpec change

• Declares the mapping change as a draft using the standard spec-driven schema and records its creation date.

workspaces/ai-integrations/openspec/changes/mcp-registry-server-mapping/.openspec.yaml

@fullsend-ai-review

fullsend-ai-review Bot commented Sep 11, 2026

Copy link
Copy Markdown

🤖 Review · ❌ Terminated · Started 5:43 PM UTC · Ended 6:02 PM UTC

Commit: 5ffcbcc · View workflow run →

@rhdh-qodo-merge

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (6) 📘 Rule violations (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Malformed updates delete valid entries 🐞 Bug ☼ Reliability
Description
The malformed-entry scenario skips a rejected server and still commits a full mutation containing
only successfully mapped entities. If that server existed in the preceding sync, its omission
reaches full-mutation pruning and removes its last valid catalog entity.
Code

workspaces/ai-integrations/openspec/changes/mcp-registry-provider/specs/mcp-registry-provider/spec.md[R123-124]

+- **WHEN** one accumulated server entry fails mapping while the others succeed
+- **THEN** the provider logs the failing entry, skips it, and commits a full mutation containing the successfully mapped entities
Relevance

●●● Strong

Omitting failed entries from a full mutation can prune valid prior entities; atomic preservation is
required.

PR-#3833

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new spec states both that omitted entities are removed by a full mutation and that malformed
entries are omitted while such a mutation is committed. The existing model catalog provider
demonstrates that a full mutation is built solely from the current entity list and submitted with a
common location key.

workspaces/ai-integrations/openspec/changes/mcp-registry-provider/specs/mcp-registry-provider/spec.md[103-110]
workspaces/ai-integrations/openspec/changes/mcp-registry-provider/specs/mcp-registry-provider/spec.md[117-124]
workspaces/ai-integrations/plugins/catalog-backend-module-model-catalog/src/providers/ModelCatalogResourceEntityProvider.ts[193-204]
workspaces/ai-integrations/plugins/catalog-backend-module-model-catalog/src/providers/ModelCatalogResourceEntityProvider.ts[221-228]

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

## Issue description
Skipping a malformed entry and then committing a full mutation removes any last-known-good entity for that server because omission from a full mutation means deletion.

## Fix Focus Areas
- workspaces/ai-integrations/openspec/changes/mcp-registry-provider/specs/mcp-registry-provider/spec.md[117-129]
- workspaces/ai-integrations/openspec/changes/mcp-registry-provider/design.md[97-103]
- workspaces/ai-integrations/openspec/changes/mcp-registry-provider/tasks.md[41-43]

## Recommended Fix
Treat any mapping rejection as a failed atomic sync and emit no mutation, matching the existing registry-level failure rule. Alternatively, explicitly design persistent last-known-good retention and include retained entities in the full mutation, with regression coverage for an entry that changes from valid to malformed.

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


2. Distinct servers can share one identity 🐞 Bug ≡ Correctness
Description
The identity requirement concatenates sanitized name and version values with __, then
conditionally requests a hash only when two inputs collide. Because the transform receives one
document at a time, it cannot detect another document's collision, and distinct tuples such as
foo__bar plus 1 and foo plus bar__1 produce the same identity.
Code

workspaces/ai-integrations/openspec/changes/mcp-registry-server-mapping/specs/mcp-registry-server-mapping/spec.md[R60-63]

+#### Scenario: Over-length or colliding names remain unique
+
+- **WHEN** two distinct (name, version) pairs sanitize to the same `<name>__<version>`, or the combined value exceeds the 63-character limit after sanitization
+- **THEN** the mapping produces a deterministic, unique `metadata.name` by truncating and appending a stable hash suffix derived from the canonical name and version
Relevance

●●● Strong

Delimiter-based sanitized identities can collide across distinct tuples; deterministic tuple hashing
is a local correctness fix.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The mapping requires a pure one-document transform, permits underscores in both sanitized
components, and nevertheless requires hashes only when separate inputs collide. Its un-hashed worked
example confirms there is no unconditional tuple discriminator.

workspaces/ai-integrations/openspec/changes/mcp-registry-server-mapping/specs/mcp-registry-server-mapping/spec.md[46-63]
workspaces/ai-integrations/openspec/changes/mcp-registry-server-mapping/specs/mcp-registry-server-mapping/spec.md[148-155]
workspaces/ai-integrations/openspec/changes/mcp-registry-server-mapping/design.md[69-75]

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

## Issue description
The pure per-document transform cannot conditionally detect collisions with other documents, and the delimiter-based identity is not injective for valid name and version values.

## Fix Focus Areas
- workspaces/ai-integrations/openspec/changes/mcp-registry-server-mapping/specs/mcp-registry-server-mapping/spec.md[46-63]
- workspaces/ai-integrations/openspec/changes/mcp-registry-server-mapping/spec.md[148-155]
- workspaces/ai-integrations/openspec/changes/mcp-registry-server-mapping/design.md[69-75]
- workspaces/ai-integrations/openspec/changes/mcp-registry-server-mapping/tasks.md[16-21]

## Recommended Fix
Define an identity algorithm whose result depends only on the current canonical name and version and is collision-resistant without registry-wide detection. For example, append a stable hash of the unmodified tuple whenever sanitization, truncation, or delimiter ambiguity applies, and add fixtures covering lossy sanitization and delimiter collisions.

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


3. Default registry setup cannot sync 🐞 Bug ≡ Correctness
Description
apiVersion defaults to v1 even though the design states that the current registry serves /v0
or /v0.1. When an operator supplies only the required baseUrl, every scheduled request targets
the acknowledged incompatible endpoint until they discover and configure an override.
Code

workspaces/ai-integrations/openspec/changes/mcp-registry-provider/specs/mcp-registry-provider/spec.md[15]

+The provider SHALL read its configuration from `catalog.providers.mcpRegistry`, treated as a keyed map where each key is a caller-chosen instance `<id>` and each value configures one registry. For each instance the provider SHALL read `baseUrl` (**required**), `apiVersion` (optional, defaulting to the constant `v1`), `schedule` (optional, a standard `SchedulerServiceTaskScheduleDefinition`), and `defaultOwner` (optional, a Backstage entity reference). The provider SHALL register one independent provider instance per configured `<id>`. When `catalog.providers.mcpRegistry` is absent, the provider SHALL register nothing and SHALL NOT error (the module is inert unless configured).
Relevance

●● Moderate

The mismatch is explicitly documented as an intentional forward-looking default, making acceptance
depend on product preference.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The provider spec makes apiVersion optional and defaults it to v1, while the design explicitly
records that the live registry uses /v0 or /v0.1 and that operators must override the default.

workspaces/ai-integrations/openspec/changes/mcp-registry-provider/specs/mcp-registry-provider/spec.md[13-20]
workspaces/ai-integrations/openspec/changes/mcp-registry-provider/design.md[105-115]
workspaces/ai-integrations/openspec/changes/mcp-registry-provider/proposal.md[47-50]

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

## Issue description
The optional API version defaults to `v1`, while the design explicitly states that current registry endpoints use `v0` or `v0.1`, making minimal valid configuration fail to synchronize.

## Fix Focus Areas
- workspaces/ai-integrations/openspec/changes/mcp-registry-provider/specs/mcp-registry-provider/spec.md[13-20]
- workspaces/ai-integrations/openspec/changes/mcp-registry-provider/design.md[105-115]
- workspaces/ai-integrations/openspec/changes/mcp-registry-provider/proposal.md[10-15]
- workspaces/ai-integrations/openspec/changes/mcp-registry-provider/tasks.md[17-20]

## Recommended Fix
Choose the currently supported stable registry version as the default and update every scenario, task, and risk statement consistently. If no stable default can be guaranteed, make `apiVersion` required and fail startup with an actionable configuration error instead of supplying a known-incompatible value.

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



Remediation recommended

4. Rollback leaves stale catalog entries 🐞 Bug ≡ Correctness
Description
The migration plan claims that removing the module or configuration causes its entities to be pruned
during the next reconciliation. An absent configuration registers no provider, so nothing emits the
empty full mutation needed to remove the previously managed entities.
Code

workspaces/ai-integrations/openspec/changes/mcp-registry-provider/design.md[125]

+Not applicable to existing data — this is additive and introduces no migration of prior state. Deployment: publish the backend module package, add it to the backend via `backend.add(...)`, and configure at least one `catalog.providers.mcpRegistry.<id>` with a `baseUrl`. Rollback: remove the module registration (or the config block); ingested entities are pruned on the next catalog reconciliation because they are provider-managed via `locationKey`. The provider is inert when unconfigured, so shipping the package without config is a safe no-op.
Relevance

●●● Strong

Rollback cannot trigger provider reconciliation after registration removal, leaving location-managed
entities stale.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The provider spec requires absent configuration to register nothing, while pruning is defined as the
effect of an emitted full mutation. The existing provider likewise performs pruning only through an
explicit applyMutation call, so removing registration cannot initiate cleanup.

workspaces/ai-integrations/openspec/changes/mcp-registry-provider/specs/mcp-registry-provider/spec.md[32-35]
workspaces/ai-integrations/openspec/changes/mcp-registry-provider/specs/mcp-registry-provider/spec.md[103-110]
workspaces/ai-integrations/plugins/catalog-backend-module-model-catalog/src/providers/ModelCatalogResourceEntityProvider.ts[221-228]

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

## Issue description
Disabling the provider cannot trigger the promised pruning because no provider remains to submit a final empty full mutation.

## Fix Focus Areas
- workspaces/ai-integrations/openspec/changes/mcp-registry-provider/design.md[123-125]
- workspaces/ai-integrations/openspec/changes/mcp-registry-provider/specs/mcp-registry-provider/spec.md[32-35]
- workspaces/ai-integrations/openspec/changes/mcp-registry-provider/tasks.md[45-50]

## Recommended Fix
Replace the automatic-pruning claim with a concrete decommissioning procedure, such as running a supported cleanup command or temporarily configuring a disabled instance that submits one empty full mutation before module removal. Add that cleanup behavior and its verification to the implementation tasks and operator documentation.

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


5. Opaque cursors can request wrong pages 🐞 Bug ≡ Correctness
Description
The pagination contract says to place an opaque cursor verbatim into ?cursor=<value> without
requiring query-component encoding. A cursor containing reserved characters such as &, +, #,
or % can be split or decoded differently, sending the subsequent request to the wrong page.
Code

workspaces/ai-integrations/openspec/changes/mcp-registry-provider/specs/mcp-registry-provider/spec.md[R65-68]

+#### Scenario: Opaque cursor is passed unchanged
+
+- **WHEN** a response's `metadata.nextCursor` is an opaque token
+- **THEN** the provider passes that token verbatim as the `cursor` query parameter without parsing or altering it
Relevance

●●● Strong

Repository history accepts fixes for incomplete URL escaping that can route requests incorrectly.

PR-#3833

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The spec and design repeatedly show direct ?cursor=<value> construction and call for verbatim
passthrough, while the implementation tasks contain no URL-encoding requirement or
reserved-character test. A past accepted review in this workspace found the same class of URL bug
when reserved characters were not fully encoded.

workspaces/ai-integrations/openspec/changes/mcp-registry-provider/specs/mcp-registry-provider/spec.md[51-68]
workspaces/ai-integrations/openspec/changes/mcp-registry-provider/design.md[81-87]
workspaces/ai-integrations/openspec/changes/mcp-registry-provider/tasks.md[22-29]
PR-#3833

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

## Issue description
Opaque cursor values need query-component encoding; raw interpolation can change their value or split them into additional URL components.

## Fix Focus Areas
- workspaces/ai-integrations/openspec/changes/mcp-registry-provider/specs/mcp-registry-provider/spec.md[51-68]
- workspaces/ai-integrations/openspec/changes/mcp-registry-provider/design.md[81-87]
- workspaces/ai-integrations/openspec/changes/mcp-registry-provider/tasks.md[22-29]

## Recommended Fix
Require construction through `URL` and `URLSearchParams` or equivalent query-value encoding while preserving the decoded cursor value exactly. Add a pagination test using a cursor containing `+`, `&`, `=`, `%`, and `#` and assert that the server receives the original token as one `cursor` parameter.

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


6. Repository URLs cannot round-trip 🐞 Bug ≡ Correctness
Description
The repository combination algorithm strips a trailing slash and .git suffix before emitting the
URL, while generic projection excludes the original repository.url. Inputs that differ only by
those removed characters therefore become indistinguishable, violating the stated recovery guarantee
for every non-redacted scalar.
Code

workspaces/ai-integrations/openspec/changes/mcp-registry-server-mapping/specs/mcp-registry-server-mapping/spec.md[R71-73]

+1. **Normalize the base URL.** Let `base` be `repository.url` with any trailing `/` removed and with a trailing `.git` suffix removed when present.
+2. **Absent subfolder.** When `repository.subfolder` is unset or empty after trimming whitespace, the combined URL is `base`. No tree/src/browse segment is inserted.
+3. **Normalize the subfolder.** Let `subfolder` be `repository.subfolder` with leading and trailing `/` characters removed. The mapping SHALL treat it as `/`-separated relative path segments and SHALL NOT resolve `.` or `..` segments.
Relevance

●● Moderate

Normalization intentionally canonicalizes repository URLs, but conflicts with the broad scalar
round-trip guarantee.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The direct mapping explicitly removes .git and trailing slashes, the projection spec forbids
projecting repository.url, and the same projection spec requires every non-redacted scalar to be
recoverable.

workspaces/ai-integrations/openspec/changes/mcp-registry-server-mapping/specs/mcp-registry-server-mapping/spec.md[67-80]
workspaces/ai-integrations/openspec/changes/mcp-registry-server-mapping/specs/mcp-registry-annotation-projection/spec.md[20-23]
workspaces/ai-integrations/openspec/changes/mcp-registry-server-mapping/specs/mcp-registry-annotation-projection/spec.md[96-103]

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

## Issue description
Repository URL normalization discards source characters even though the projection rules exclude the raw field and promise that every non-redacted scalar remains recoverable.

## Fix Focus Areas
- workspaces/ai-integrations/openspec/changes/mcp-registry-server-mapping/specs/mcp-registry-server-mapping/spec.md[65-82]
- workspaces/ai-integrations/openspec/changes/mcp-registry-server-mapping/specs/mcp-registry-annotation-projection/spec.md[20-23]
- workspaces/ai-integrations/openspec/changes/mcp-registry-server-mapping/specs/mcp-registry-annotation-projection/spec.md[96-103]
- workspaces/ai-integrations/openspec/changes/mcp-registry-server-mapping/tasks.md[25-30]

## Recommended Fix
Preserve the unmodified input in a dedicated `modelcontextprotocol.io/repository.url` annotation while continuing to use the normalized value for links and source location. Update the native-field exclusion rule, mapping table tasks, and round-trip fixtures to cover `.git` and trailing-slash inputs.

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


Grey Divider

Context sources
⚠️ Tickets: not configured — ticket URL found in PR but could not be fetched — check ticket provider credentials
✅ Compliance rules (platform): 14 rules
✅ Cross-repo context — repo relationships
Review mode: ⚖️ Balanced: This adds detailed runtime behavior contracts for registry ingestion, pagination, catalog mutations, mapping, and secret handling across two interconnected capabilities, so a careful single-pass review is warranted.

Grey Divider

Tip of the day
💡 Did you know, you can switch off images and animations for a plain-text comment

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

@rhdh-qodo-merge rhdh-qodo-merge Bot added the enhancement New feature or request label Sep 11, 2026
@rhdh-qodo-merge

Copy link
Copy Markdown

Important

The /generate_labels command by Qodo is sunsetting on the 1st of October 2026 and will no longer be available. We recommend switching to the latest Qodo review capabilities. Learn more

@fullsend-ai-review fullsend-ai-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: The following review comments could not be posted on the diff (GitHub returned 422) and are included here instead:

  • workspaces/ai-integrations/openspec/changes/mcp-registry-provider/audit.md (file-level): Line 34 · [low] Internal consistency

The audit.md reports zero findings (0 CRITICAL, 0 WARNING, 0 SUGGESTION), but the sibling journal.jsonl records the audit found 1 WARNING (schedule type name drift) and 1 SUGGESTION (cursor safeguard wording). The mcp-registry-server-mapping audit.md properly documents its resolved findings for comparison.

@fullsend-ai-review fullsend-ai-review Bot added ready-for-merge All reviewers approved — ready to merge and removed ready-for-merge All reviewers approved — ready to merge labels Sep 11, 2026
@fullsend-ai-review

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 5:43 PM UTC · Completed 6:02 PM UTC

Commit: 5ffcbcc · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Cost: $6.30

@johnmcollier johnmcollier left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, this aligns largely with what I would expect from an entity provider ingesting from an MCP registry. One question that stands out to me:

How do we want to handle collisions between multiple MCP registries? What if multiple MCP registries emit the same catalog entity identity? We might want to consider some kind of namespacing per-mcp registry?

@michael-valdron

Copy link
Copy Markdown
Member Author

Overall, this aligns largely with what I would expect from an entity provider ingesting from an MCP registry. One question that stands out to me:

How do we want to handle collisions between multiple MCP registries? What if multiple MCP registries emit the same catalog entity identity? We might want to consider some kind of namespacing per-mcp registry?

@johnmcollier Good point, supporting multiple MCP Registries would make sense. Though not listed as out of scope, the current design from the feature only considers one MCP Registry at a time:

baseUrl: #The URL of the MCP Registry.

@michael-valdron

Copy link
Copy Markdown
Member Author

Overall, this aligns largely with what I would expect from an entity provider ingesting from an MCP registry. One question that stands out to me:
How do we want to handle collisions between multiple MCP registries? What if multiple MCP registries emit the same catalog entity identity? We might want to consider some kind of namespacing per-mcp registry?

@johnmcollier Good point, supporting multiple MCP Registries would make sense. Though not listed as out of scope, the current design from the feature only considers one MCP Registry at a time:

baseUrl: #The URL of the MCP Registry.

That being said I could revise a bit at least to have the consideration that multiple registries may be supported down the road to avoid future catalog breakages.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation enhancement New feature or request mcp-integrations ready-for-merge All reviewers approved — ready to merge workspace/ai-integrations

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants