Skip to content

feat(#4795): implement MCP registry annotation projection - #4859

Merged
michael-valdron merged 23 commits into
mainfrom
agent/4795-annotation-projection
Sep 18, 2026
Merged

michael-valdron merged 23 commits into
mainfrom
agent/4795-annotation-projection

Conversation

@fullsend-ai-coder

Copy link
Copy Markdown
Contributor

Summary

Implement annotation projection for the MCP registry → Backstage catalog mapping (part 2 of 2; part 1 was direct field mapping in #4813).

The projection walks every scalar leaf in server.json that is not consumed by direct mapping and emits it as a modelcontextprotocol.io/<dot.path> annotation with:

  • D3 key sanitization: per-segment lowercase/illegal-char replacement, boundary normalization, ≤63-char truncation with FNV-1a hash suffix, and collision disambiguation
  • D9 secret redaction: prunes default/value/choices from isSecret inputs
  • D11 URL gating: refuses non-http/https scheme URLs from projection
  • D12 null/empty omission: null→omit, empty []/{}→omit, but false/0/""→project as strings

Exports projectAnnotations from the common library package.

Test plan

  • 65 new unit tests covering all design decisions (D3, D9, D11, D12)
  • All 140 existing tests still pass (205 total)
  • TypeScript compilation passes
  • ESLint passes
  • Prettier passes
  • API reports generated and verified
  • Secret scan passed

Closes #4795

Post-script verification

  • Branch is not main/master (agent/4795-annotation-projection)
  • Secret scan passed (gitleaks — e760451223d9ae9997cc1cab890e710194954e1e..HEAD)
  • PR body secret scan passed (gitleaks — no-git)

Add annotation projection for the MCP registry to Backstage catalog
mapping. This is the second half of the server.json transform (the
first was direct field mapping in #4813).

The projection walks every scalar leaf in server.json that is not
consumed by direct mapping and emits it as a
modelcontextprotocol.io/<dot.path> annotation with:

- D3 key sanitization: per-segment lowercase/illegal-char replacement,
  boundary normalization, 63-char truncation with FNV-1a hash suffix,
  and collision disambiguation
- D9 secret redaction: prunes default/value/choices from isSecret inputs
- D11 URL gating: refuses non-http/https scheme URLs from projection
- D12 null/empty omission: null omit, empty arrays/objects omit, but
  false/0/empty-string project as strings

Exports projectAnnotations from the common library package.
Includes 65 tests covering all design decisions.

Assisted-by: Claude Opus 4.6
@rhdh-gh-app

rhdh-gh-app Bot commented Sep 17, 2026

Copy link
Copy Markdown

Important

This PR includes changes that affect public-facing API. Please ensure you are adding/updating documentation for new features or behavior.

Changed Packages

Package Name Package Path Changeset Bump Current Version
@red-hat-developer-hub/backstage-plugin-mcp-registry-server-mapping-common workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common minor v0.2.0

@fullsend-ai-review

fullsend-ai-review Bot commented Sep 17, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 5:38 PM UTC · Completed 5:59 PM UTC

Commit: 4c786c8 · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $7.26

@codecov

codecov Bot commented Sep 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.78505% with 48 lines in your changes missing coverage. Please review.
✅ Project coverage is 63.69%. Comparing base (e760451) to head (ce3fd2d).
⚠️ Report is 12 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4859      +/-   ##
==========================================
+ Coverage   63.59%   63.69%   +0.09%     
==========================================
  Files        2685     2687       +2     
  Lines      107076   107495     +419     
  Branches    29974    30083     +109     
==========================================
+ Hits        68099    68468     +369     
- Misses      37166    37216      +50     
  Partials     1811     1811              
Flag Coverage Δ *Carryforward flag
adoption-insights 84.77% <ø> (ø) Carriedforward from fde889d
ai-integrations 83.80% <88.78%> (+1.16%) ⬆️
app-defaults 54.82% <ø> (ø) Carriedforward from fde889d
augment 46.67% <ø> (ø) Carriedforward from fde889d
boost 84.97% <ø> (ø) Carriedforward from fde889d
bulk-import 73.12% <ø> (ø) Carriedforward from fde889d
cost-management 13.53% <ø> (ø) Carriedforward from fde889d
dcm 73.47% <ø> (ø) Carriedforward from fde889d
e2e-adoption-insights 60.00% <ø> (ø) Carriedforward from fde889d
e2e-extensions 62.31% <ø> (ø) Carriedforward from fde889d
e2e-global-header 51.82% <ø> (ø) Carriedforward from fde889d
e2e-homepage 61.11% <ø> (ø) Carriedforward from fde889d
e2e-intelligent-assistant 46.01% <ø> (ø) Carriedforward from fde889d
e2e-orchestrator 49.49% <ø> (ø) Carriedforward from fde889d
e2e-orchestrator-plugin 49.48% <ø> (ø) Carriedforward from fde889d
e2e-quickstart 55.21% <ø> (ø) Carriedforward from fde889d
e2e-scorecard 50.00% <ø> (ø) Carriedforward from fde889d
e2e-theme 16.36% <ø> (ø) Carriedforward from fde889d
extensions 58.30% <ø> (ø) Carriedforward from fde889d
global-floating-action-button 71.18% <ø> (ø) Carriedforward from fde889d
global-header 67.76% <ø> (ø) Carriedforward from fde889d
homepage 55.05% <ø> (ø) Carriedforward from fde889d
install-dynamic-plugins 71.77% <ø> (ø) Carriedforward from fde889d
intelligent-assistant 78.04% <ø> (ø) Carriedforward from fde889d
konflux 91.98% <ø> (ø) Carriedforward from fde889d
lightspeed 69.02% <ø> (ø) Carriedforward from fde889d
mcp-integrations 84.46% <ø> (ø) Carriedforward from fde889d
orchestrator 77.69% <ø> (ø) Carriedforward from fde889d
quickstart 63.74% <ø> (ø) Carriedforward from fde889d
sandbox 79.56% <ø> (ø) Carriedforward from fde889d
scorecard 88.71% <ø> (ø) Carriedforward from fde889d
theme 87.91% <ø> (ø) Carriedforward from fde889d
translations 5.12% <ø> (ø) Carriedforward from fde889d
x2a 78.44% <ø> (ø) Carriedforward from fde889d

*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 e760451...ce3fd2d. 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 fullsend-ai-review Bot added the risk/low PR risk: low label Sep 17, 2026
@fullsend-ai-review

fullsend-ai-review Bot commented Sep 17, 2026

Copy link
Copy Markdown

Risk Assessment: moderate (2/5)

Details

Tier 1 signals are unchanged from the prior assessment (line count grew from 3155 to 4329 but remains in the ≥2000 bucket, all other signals identical); Tier 2 confirms zero regression history on brand-new files; Tier 3 confirms detailed acceptance criteria and safe rollback posture — composite of ~1.55 rounds to 2 and re-review anchoring preserves the prior moderate score.

Previous run

Risk Assessment: moderate (2/5)

Details

Large volume of new code (3155 lines) with complex annotation-projection logic in a shared library warrants standard review, but bot authorship, zero regression history, no CI or dependency changes, well-specified acceptance criteria, and no security-sensitive surface keep the risk at moderate.

Previous run (2)

Risk Assessment: moderate (2/5)

Details

Large volume of new code (2574 lines) with complex domain logic in a shared library warrants standard review, but bot authorship, zero regression history, no CI or dependency changes, and adequate test coverage keep the risk at moderate.

Previous run (3)

Risk Assessment: moderate (2/5)

Details

Tier 1 signals are unchanged from the prior assessment; large raw volume (2411 lines, large blast radius) is the sole upward driver but the bulk is new test code for a purely additive, well-scoped feature with no security, dependency, CI, or protected-path exposure, preserving the prior moderate score of 2.

Previous run (4)

Risk Assessment: moderate (2/5)

Details

Although raw change volume is large (2411 lines, large blast radius), the bulk is comprehensive new tests (1847 lines) for a well-scoped additive feature with no security, dependency, CI, or protected-path signals, yielding a moderate composite score.

Previous run (5)

Risk Assessment: low (1/5)

Details

Prior score of 1 is preserved: signals are unchanged - bot-authored, additive-only feature on newly created files, zero security or CI concerns, comprehensive test coverage (1,585 test lines vs 351 implementation lines), and fully triaged acceptance criteria; the large blast-radius and high line-count are artifacts of the test-heavy addition, not of modifications to existing shared code.

Previous run (6)

Risk Assessment: low (1/5)

Details

Bot-authored PR adding a well-scoped new feature (annotation projection for MCP registry mapping, part 2 of 2) with comprehensive test coverage (1,875 test lines for a 47-line implementation), no security or CI concerns, clean git history on newly created files, and explicitly defined acceptance criteria in the linked issue; the large blast-radius metric is an artifact of the test-heavy change, not of modifications to existing shared code.

@fullsend-ai-review

fullsend-ai-review Bot commented Sep 17, 2026

Copy link
Copy Markdown

Review

Findings

Critical

  • [breaking-api] types.tsMcpServerDocument.$schema changed from optional to required. Every public entry point calls assertServerJsonSchema(doc) which throws TypeError when $schema is absent or its URL basename is not server.schema.json. Downstream consumers constructing McpServerDocument without $schema will fail at compile time and runtime.
    Remediation: Semver major bump required. Document migration: every McpServerDocument must include $schema.

  • [breaking-api] types.tsMcpServerDocument index signature [key: string]: unknown removed. assertServerJsonSchema throws TypeError for any key not in the closed SERVER_DETAIL_KEYS set. The previous design explicitly enabled unknown fields for annotation projection pass-through.
    Remediation: Semver major bump required. Document that McpServerDocument is now a closed schema.

High

  • [breaking-api] types.tsMcpServerRepository.source changed from optional to required. assertServerJsonSchema throws when source is absent from repository.
    Remediation: Revert to optional or bump semver major.

  • [breaking-api] mapServerToEntity.tsmapRemotes changed from silently skipping invalid remote types to throwing TypeError via assertServerJsonSchema. Callers with type values other than 'streamable-http' or 'sse' now receive a runtime error.
    Remediation: Semver major bump.

  • [breaking-api] types.tsMcpRegistryRemote changed from open interface to discriminated union. Type discriminant restricted to 'streamable-http' | 'sse'. headers narrowed from unknown[] to McpKeyValueInput[]. variables narrowed from unknown to Record<string, McpInput>.
    Remediation: Semver major bump.

  • [stale-doc] mapping-reference.md, design.md — D4/D5 Caller Defaults tables missing placeholderRemoteUrl. D8 states placeholder URL sourced exclusively from websiteUrl, but implementation now tries placeholderRemoteUrl first.
    Remediation: Add placeholderRemoteUrl to D5 tables and update D8 to document placeholderRemoteUrl as primary, websiteUrl as fallback.

Medium

  • [breaking-api] types.tsMcpRegistryIcon.mimeType narrowed from string to closed union of 5 MIME types. McpRegistryPackage.transport changed from unknown to McpLocalTransport; environmentVariables, packageArguments, runtimeArguments narrowed to typed arrays. Runtime validator throws for non-conforming input.
    Remediation: Keep types open if upstream allows other values, or bump major.

  • [missing-version-bump] .changeset/mcp-registry-annotation-projection.md — Classifies as minor despite multiple non-backward-compatible interface changes.
    Remediation: Change to major.

  • [architectural-conflict] util.ts — Closed-schema validation (assertServerJsonSchema throws on unknown fields) contradicts previous design that enabled unknown field pass-through for annotation projection. Any new upstream schema field causes runtime failures.
    Remediation: Consider warning rather than throwing for unknown fields.

  • [error-handling-gap] annotationProjection.tsassertServerJsonSchema validates _meta as a plain object only, but projection walker calls requireBooleanProperty on nested isSecret keys. Non-boolean isSecret in _meta passes validation but throws during projection.
    Remediation: Deeply validate _meta during assertServerJsonSchema, or treat non-boolean isSecret as false.

  • [stale-doc] spec.md — Scenario for remote-less server failure and $schema validation precondition are stale or missing. Annotation projection spec example uses mimeType (camelCase) but D3 lowercases to mimetype.
    Remediation: Update scenarios, add $schema requirement, and fix casing.

  • [error-handling-idiom] util.tsrequireBooleanProperty throws Error instead of TypeError and omits the established 'MCP Registry server.json requires...' message prefix used by all other validators.
    Remediation: Use TypeError with established prefix.

Low

  • [scope-creep] mapServerToEntity.tsplaceholderRemoteUrl and strict type validation extend beyond annotation projection scope of issue feat(ai-integrations): implement MCP registry server mapping common library plugin - 2 / 2 (mcp-registry-annotation-projection) #4795 (changeset description explicitly mentions these features).

  • [api-contract] mapServerToEntity.ts — Error message unconditionally references placeholderRemoteUrl even when the parameter was not supplied.

  • [doc-style] urlPolicy.tsparseAbsoluteUrl documented as "Exported for unit testing only" but used in production by annotationProjection.ts.

  • [pattern-inconsistency] annotationProjection.tssortAnnotationEntries uses triple-comparison sort while all other sorts use localeCompare.

  • [performance] mapServerToEntity.tsassertServerJsonSchema runs 5 times per mapServerToEntity call.

  • [edge-case] annotationProjection.ts — Counter-suffix disambiguation for truncated keys is order-dependent.

  • [stale-doc] mapping-reference.mdremotes[].type row doesn't describe that invalid type now throws.


Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR
Previous run

Review

Findings

Medium

  • [breaking-api] workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/mapServerToEntity.ts:34assertServerJsonSchema(doc) is called at the entry of every public function (validateRequiredFields, mapRemotes, buildLinks, trackConsumedRemotePaths, mapServerToEntity), throwing TypeError when doc.$schema is absent, empty, not a valid URL, or has a basename other than server.schema.json. However, McpServerDocument.$schema is declared as optional ($schema?: string) in types.ts and report.api.md. Callers who passed documents without $schema — which the TypeScript type explicitly permits — now receive an unexpected TypeError. The library is at version 0.2.0 with minimal external consumers, but the type-vs-runtime mismatch should be resolved.
    Remediation: Either (a) change $schema from optional to required in types.ts and update report.api.md so the type contract matches runtime enforcement, or (b) treat the $schema guard as advisory and keep validation non-throwing for callers who omit $schema.

  • [code-organization] workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/util.test.ts:24makeMinimalDoc factory function and SERVER_SCHEMA_URI constant are duplicated identically across util.test.ts, annotationProjection.test.ts, and mapServerToEntity.test.ts. AGENTS.md states: "When test helpers like entity factory functions are needed by multiple test files, extract them into a shared testUtils.ts in the same src/ directory."
    Remediation: Extract SERVER_SCHEMA_URI and makeMinimalDoc into src/testUtils.ts and import from there in all three test files.

  • [stale-doc] workspaces/ai-integrations/openspec/changes/mcp-registry-server-mapping/design.md:151 — D8 design choice says the placeholder spec.remotes URL is sourced from websiteUrl and the mapping fails when websiteUrl is absent or fails D11. This PR adds placeholderRemoteUrl as a caller override tried before websiteUrl, changing the failure condition. Lines 151 and 153 are now misleading about D8 behavior.
    Remediation: Update D8 in design.md to document that placeholderRemoteUrl (via McpServerMappingDefaults) is tried first, and the mapping fails only when both the caller override and websiteUrl are absent or fail D11.

  • [missing-doc] workspaces/ai-integrations/openspec/changes/mcp-registry-server-mapping/mapping-reference.md:96 — Caller Defaults table lists only prefix, owner, and lifecycle as McpServerMappingDefaults options. This PR adds placeholderRemoteUrl as a fourth caller-configurable option but the table does not document it.
    Remediation: Add a row for placeholderRemoteUrl to the Caller Defaults table, describing it as the optional D8 placeholder remote URL tried before websiteUrl.

Low

  • [logic-error] workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/annotationProjection.ts:246 — The D9 isSecret check in collectScalarsFromObject is applied to every object during the recursive walk, not just Input schema positions. The spec states this redaction applies "uniformly" which makes broad scope intentional, but when an arbitrary object (e.g., vendor extension in _meta) contains isSecret with a non-boolean value, requireBooleanProperty throws a TypeError, causing the entire projectAnnotations call to fail.
    Remediation: Make the isSecret check defensive for non-boolean values (treat as false with a warning instead of throwing) to improve robustness against unexpected _meta content.

  • [scope-creep] workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/mapServerToEntity.ts:88mapRemotes receives a new optional placeholderRemoteUrl parameter extending D8's fallback chain. This is a backward-compatible additive change naturally coupled with annotation projection delivery, but no explicit authorization trail links this direct-mapping extension to an approved issue.

  • [breaking-api] workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/mapServerToEntity.ts:139 — The error message thrown by mapRemotes when no valid remote source is found has changed wording to include placeholderRemoteUrl. The library is at 0.2.0 with minimal external consumers.

  • [documentation comment format] workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/annotationProjection.ts:296 — The @internal tag format is inconsistent within the file: first six exported-for-testing functions use a description paragraph followed by @internal on its own line; remaining six inline the text after the tag keyword.
    Remediation: Use the two-part block pattern consistently.

  • [redundant-validation] workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/mapServerToEntity.ts:269 — A single call to mapServerToEntity triggers assertServerJsonSchema five times (once directly, then via each sub-function). The document is immutable between calls; four assertions are redundant overhead.

  • [edge-case] workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/annotationProjection.ts:252 — When a non-Input object has isSecret: true (possible in _meta extension data), the code silently redacts default/value/choices siblings. No test covers this non-Input redaction scenario to confirm whether the behavior is desired.

  • [documentation comment format] workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/util.ts:31assertServerJsonSchema is exported but carries no API tag (@public or @internal), unlike its sibling requireBooleanProperty which has @internal.
    Remediation: Add @internal tag to the JSDoc for assertServerJsonSchema.

  • [naming-convention] workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/annotationProjection.ts:476sortAnnotationEntries uses an explicit three-way comparator (if (a < b) return -1; ...) while mapServerToEntity.ts uses localeCompare for an equivalent annotation key sort.
    Remediation: Use localeCompare consistently to match the existing idiom.

  • [documentation comment format] workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/annotationProjection.ts:162ScalarWalkContext type alias has no JSDoc comment while adjacent ScalarWalkPath (line 160) has a descriptive comment.
    Remediation: Add a single-line JSDoc comment.

  • [spec-coherence] workspaces/ai-integrations/openspec/changes/mcp-registry-server-mapping/specs/mcp-registry-annotation-projection/spec.md:33 — Spec scenario corrects example annotation key from icons.0.mimeType to icons.0.mimetype (lowercase), aligning the example with the documented D3 lowercase normalization requirement.


Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR
Previous run (2)

Review

Findings

Medium

Low

  • [Code organization / duplication] workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/annotationProjection.ts:109 — isRefusedUrl shares the WHATWG URL parse + protocol check pattern with urlPolicy.ts::isAllowedUrl. However, the functions have fundamentally different semantics: isRefusedUrl returns false for non-URL strings (allowing projection of package identifiers, descriptions, etc.), while !isAllowedUrl would return true for them, incorrectly refusing legitimate scalar values. The current code is correct; the duplication is a 5-line pattern with inverted three-state semantics.
    Remediation: If consolidation is desired, extract a shared URL-parsing primitive (e.g., parseAbsoluteUrl(value: string): URL | null) into urlPolicy.ts and have both isAllowedUrl and isRefusedUrl call it. Note that the current code is functionally correct as-is.

  • [Missing documentation] workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/README.md — The plugin has no README.md. This PR adds projectAnnotations as a new @public API function exported from the package. The gap predates this PR (from feat(#4813): add MCP registry server mapping common library plugin - mcp-registry-server-mapping #4823), and task 4.5 in tasks.md already tracks user/consumer documentation.
    Remediation: Create a README.md for the plugin in a follow-up PR (task 4.5 already tracks this).

  • [secret redaction maintenance risk] workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/annotationProjection.ts:137 — SECRET_REDACTED_FIELDS is a hardcoded allowlist of field names ('default', 'value', 'choices') that are suppressed for isSecret:true Input objects. If the upstream MCP Registry Input schema adds new secret-bearing fields, those would leak into projected annotations until manually added here. The code documents this sync requirement in a comment.
    Remediation: Consider extracting the redacted field list to a named constant that references the upstream schema version, or adding a build-time check.

  • [determinism] workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/annotationProjection.ts:462 — sortAnnotationEntries uses localeCompare without an explicit locale argument. The JSDoc claims deterministic output, but localeCompare() without a locale uses the implementation's default locale. For the ASCII-subset annotation keys, collation differences are unlikely but the determinism guarantee would be unconditional with a fixed-locale comparison.
    Remediation: Replace a.localeCompare(b) with a.localeCompare(b, 'en') or a < b ? -1 : a > b ? 1 : 0.

  • [Documentation comment format] workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/annotationProjection.ts:161 — buildChildWalkPath and shouldSkipSecretRedactedField use '@internal Exported for unit testing only.' on one line, while the established pattern in the same file places descriptive text in the body and @internal alone on its own line.
    Remediation: Move the explanatory text into the description block above the @internal tag.

  • [Documentation comment format] workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/util.ts:17 — formatObjectDotPath has no JSDoc comment, contrary to the established pattern where every function in this package has at minimum a one-line JSDoc.
    Remediation: Add a one-line JSDoc comment.

  • [API shape patterns / visibility tags] workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/util.ts:24 — requireBooleanProperty is exported but lacks @internal tag. Every exported symbol in this package that is not re-exported from index.ts uses @internal.
    Remediation: Add @internal to the JSDoc block for requireBooleanProperty.


Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR
Previous run (3)

Review

Findings

Medium

  • [Documentation comment accuracy] workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/annotationProjection.ts:187 — The JSDoc for the private function collectScalarLeaf contains @internal Exported for unit testing only., but the function is NOT exported — there is no export keyword. The comment is factually incorrect and will mislead future readers into thinking this function has a testing-visible contract that does not exist.
    Remediation: Replace the JSDoc body with a plain description that does not claim the function is exported.

  • [audit-summary-accuracy] workspaces/ai-integrations/openspec/changes/mcp-registry-server-mapping/audit.md:15 — The audit.md summary table shows G: 3 SUGGESTIONS, but the detailed SUGGESTION section lists four G-category findings (spec.md:9, tasks.md:17, design.md:99, tasks.md:10). The count in the summary row is understated by one.
    Remediation: Update the G row in the summary table from 3 to 4 in the SUGGESTION column.

Low

  • [missing-documentation] workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/README.md — The mcp-registry-server-mapping-common plugin has no README.md file, yet this PR adds projectAnnotations as a new @public API export. Other plugins in the same workspace have README files. (Pre-existing gap, not a regression introduced by this change.)
    Remediation: Create README.md documenting the package purpose, projectAnnotations function signature, usage example, and annotation key conventions.

  • [edge case] workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/annotationProjection.ts:383uniquifyAnnotationKey appends a counter suffix (e.g. -2, -3) to the full annotation key when an FNV-1a hash collision occurs, but does not check whether the resulting name segment exceeds the 63-character Backstage limit. Probability is extremely low (requires FNV-1a 32-bit collision on two keys from the same document).
    Remediation: After computing the counter-suffixed candidate, re-truncate the name segment to 63 characters if it exceeds the limit.

  • [data exposure / secret redaction] workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/annotationProjection.ts:235 — D9 secret redaction uses strict boolean equality (obj.isSecret === true) on untyped JSON input (Record<string, unknown>). If upstream data arrives with a truthy non-boolean isSecret value (e.g., string "true"), secret fields will NOT be redacted. Practically unlikely given the MCP Registry schema guarantees boolean typing.
    Remediation: Consider a truthy check or explicit coercion to prevent secret leakage from loosely-typed sources.

  • [test accuracy] workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/annotationProjection.test.ts:70 — The test helper makeMinimalConsumed unconditionally adds backstage.io/source-location and modelcontextprotocol.io/repository.url to reservedAnnotationKeys whenever doc.repository?.url is defined. The real buildLinks only adds those keys when computeRepositoryUrl returns non-undefined (requires passing D11). No existing test exercises this gap.

  • [API surface accuracy] workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/annotationProjection.ts:26ANNOTATION_PREFIX ('modelcontextprotocol.io/') is declared as a module-private const and is not exported. The PR description states it is a new public export, but it does not appear in report.api.md or index.ts.
    Remediation: Either export ANNOTATION_PREFIX as export const with a @public tag and add it to index.ts and report.api.md, or correct the PR description.

  • [API surface accuracy] workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/annotationProjection.ts — The PR metadata references a ProjectionResult type as a new public export, but no such type exists in the changed files or report.api.md. projectAnnotations returns Record<string, string>.
    Remediation: Update the PR description to reflect Record<string, string> as the return type.

  • [Internal symbol leakage] workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/identity.ts — Three previously private symbols (MAX_NAME_LENGTH, normalizeBoundaries, fnv1a32) are promoted to exported with @internal. Consumers could access them via deep import paths if package.json exports field is not restrictive.
    Remediation: Verify package.json has restrictive exports field that prevents consumers from reaching src/identity.ts directly.


Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR
Previous run (4)

Review

Reason: stale-head

The review agent reviewed commit 03647c4a40646908495a69e2f276fa6f63e14c37 but the PR HEAD is now b2c8575cb5ef85125fc5c44672727969593b699a. This review was discarded to avoid approving unreviewed code.

Previous run (5)

Review

Findings

Medium

  • [stale-doc] workspaces/ai-integrations/openspec/changes/mcp-registry-server-mapping/audit.md:3 — The change-root audit.md was not updated in this PR, but spec.md was modified (annotation key casing corrected from mimeType to mimetype). Per the workspace's AGENTS.md rule, any spec.md modification requires audit.md to be updated.
    Remediation: Update the 'Last audited' timestamp in audit.md and re-run the audit.

Low

  • [documentation-comment-format] workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/annotationProjection.ts:39 — The @internal JSDoc tag uses trailing inline text (@internal Exported for unit testing only.), while the PR's own edits to identity.ts use @internal alone on its own line.
    Remediation: Move the explanatory text to the description paragraph and leave @internal alone on its own line.

  • [logic-gap] workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/annotationProjection.ts:314 — When a projected key needs both disambiguation and truncation-hashing, the disambiguation branch is skipped (if (needsDisambiguation && !item.needsTruncationHash)). If a truncation-hashed key matched a reserved key exactly, the projection would emit a conflicting key. Not reachable today since reserved keys are all short.

  • [secret-redaction-scope] workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/annotationProjection.ts:125 — D9 secret redaction uses a static field set (default, value, choices). Future upstream schema additions of secret-bearing fields would require updating this set.

  • [architectural-coherence] workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/annotationProjection.ts:24MAX_NAME_LENGTH = 63 is independently defined in both identity.ts and annotationProjection.ts. Since annotationProjection.ts already imports from identity.ts, a single exported constant could prevent drift.
    Remediation: Export MAX_NAME_LENGTH from identity.ts and import it in annotationProjection.ts.

  • [code-organization] workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/annotationProjection.ts:26 — Block-comment section dividers (/* ---- */) are new to this file and absent from all other source files in the package.
    Remediation: Use descriptive block comments without horizontal dashes, matching existing files.

  • [stale-doc] workspaces/ai-integrations/openspec/changes/mcp-registry-server-mapping/tasks.md:27 — Tasks 3.1–3.6 and 4.3, 4.4, 4.6, 4.7 are implemented in this PR but remain marked as unchecked in tasks.md.
    Remediation: Mark these tasks as [x] in tasks.md.

  • [missing-doc] workspaces/ai-integrations/openspec/changes/mcp-registry-server-mapping/tasks.md:40 — Task 4.5 (user/consumer documentation for annotation projection) is not addressed. No README or user-facing docs were added.
    Remediation: Add a README covering annotation projection or open a follow-up issue for task 4.5.

  • [implicit-behavioral-contract] workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/annotationProjection.ts — The public projectAnnotations return type is Record<string, string> but annotation keys encode a non-obvious lowercasing rule. Callers constructing expected key literals must account for this.
    Remediation: Add a JSDoc @remarks block documenting that object-key path segments are normalized to lowercase.


Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR
Previous run (6)

Review

Findings

Medium

  • [Code organization] workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/annotationProjection.ts:29 — The FNV-1a 32-bit hash implementation (FNV1A_32_OFFSET_BASIS, FNV1A_32_PRIME, fnv1a32) and normalizeBoundaries function are duplicated from identity.ts. The established pattern in this package is to centralize shared hash and normalization utilities in identity.ts; repository.ts and urlPolicy.ts each import from identity.ts rather than re-implementing. The duplication creates a maintenance hazard: an algorithm fix in identity.ts would not propagate to annotationProjection.ts.
    Remediation: Export normalizeBoundaries and fnv1a32 from identity.ts with @internal tags, then import them in annotationProjection.ts. Alternatively, extract them into a dedicated internal module (e.g., src/hashUtils.ts) imported by both files.

  • [stale scenario example] workspaces/ai-integrations/openspec/changes/mcp-registry-server-mapping/specs/mcp-registry-annotation-projection/spec.md:18 — The scenario THEN clause states the annotation key is modelcontextprotocol.io/icons.0.mimeType, preserving camelCase. The implementation lowercases all object-key segments via sanitizeSegment, so the actual key produced is modelcontextprotocol.io/icons.0.mimetype. The test confirms lowercase output. The mapping-reference.md correctly shows lowercase, and the spec's own requirement text mandates lowercasing — contradicting its own scenario example.
    Remediation: Change modelcontextprotocol.io/icons.0.mimeType to modelcontextprotocol.io/icons.0.mimetype in the THEN clause of the "Unmapped scalar becomes an annotation" scenario.

Low

  • [Code organization] workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/annotationProjection.ts:77 — Internal helpers (computeAnnotationHashSuffix, buildBaseNameSegment, buildHashedNameSegment, isRefusedUrl) are exported with @internal tags for test imports, diverging from the package's established pattern where private helpers (e.g., normalizeBoundaries, fnv1a32 in identity.ts) are unexported and tested only through public APIs. See also: [Internal exports accessible via module path] finding at this location.
    Remediation: Either test these helpers indirectly through projectAnnotations, or add a comment declaring the export-for-testing intent.

  • [Internal exports accessible via module path] workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/annotationProjection.ts:77@internal-tagged exports are correctly excluded from report.api.md and index.ts, but remain reachable via direct dist-path imports if the package ships individual compiled files. See also: [Code organization] finding at this location.

  • [Implicit input-format contract] workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/annotationProjection.ts:295consumedPaths is matched via exact-string Set lookup against dot-joined, zero-based-index paths (e.g., remotes.0.type), but the JSDoc does not specify this format. A consumer using bracket notation or 1-based indices would silently fail to exclude consumed paths.
    Remediation: Extend the @param consumedPaths JSDoc to specify the path format (dot-separated, zero-based decimal indices, no brackets) and add an @example.

  • [edge case / silent data loss] workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/annotationProjection.ts:345 — When collision disambiguation is needed but the item already has a truncation hash, the code skips further disambiguation. A FNV-1a 32-bit hash collision (~1/4 billion per pair) would cause silent data loss via Map overwrite with no diagnostic.
    Remediation: Consider logging a warning when a Map key is about to be overwritten, or adding a secondary disambiguation step.


Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR

fullsend-ai-review[bot]

This comment was marked as outdated.

@michael-valdron

Copy link
Copy Markdown
Member

/fs-fix

@fullsend-ai-coder

fullsend-ai-coder Bot commented Sep 17, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ✅ Success · Started 6:02 PM UTC · Completed 6:19 PM UTC

Commit: 4c786c8 · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $3.64

- Deduplicate fnv1a32 and normalizeBoundaries from annotationProjection.ts
  by exporting them as @internal from identity.ts and importing
- Fix stale spec scenario: camelCase mimeType to lowercase mimetype
- Add export-for-testing-only comments on @internal helpers
- Extend consumedPaths/reservedAnnotationKeys JSDoc with format spec
- Add hash-collision guard in disambiguation to prevent silent data loss

Addresses #4859

Assisted-by: Claude Opus 4.6
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@fullsend-ai-coder

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 1 (human-triggered)

Addressed all 6 review findings: deduplicated FNV-1a hash and normalizeBoundaries into identity.ts (medium), fixed stale camelCase mimeType in spec scenario (medium), added export-for-testing intent comments on @internal helpers (low, two related findings), extended consumedPaths JSDoc with format specification (low), and added hash-collision guard in disambiguation to prevent silent data loss (low)

Fixed (6):

  1. FNV-1a hash and normalizeBoundaries duplicated from identity.ts (workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/annotationProjection.ts): Removed duplicate fnv1a32, FNV1A_32_OFFSET_BASIS, FNV1A_32_PRIME, and normalizeBoundaries from annotationProjection.ts; exported fnv1a32 and normalizeBoundaries as @internal from identity.ts; updated import in annotationProjection.ts
  2. Stale scenario example: camelCase mimeType in THEN clause contradicts lowercasing implementation (workspaces/ai-integrations/openspec/changes/mcp-registry-server-mapping/specs/mcp-registry-annotation-projection/spec.md): Changed modelcontextprotocol.io/icons.0.mimeType to modelcontextprotocol.io/icons.0.mimetype in the THEN clause of the 'Unmapped scalar becomes an annotation' scenario
  3. Internal helpers exported with @internal tags diverge from unexported-helper pattern (workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/annotationProjection.ts): Added block comment to section header declaring helpers are exported for direct unit-testing only (excluded from public API surface); updated @internal JSDoc tags to include 'Exported for unit testing only' on all four helpers
  4. Internal exports accessible via module path (workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/annotationProjection.ts): Addressed together with the previous finding: the section-header comment clarifies that these exports are not re-exported in index.ts and are omitted from report.api.md
  5. Implicit input-format contract for consumedPaths parameter (workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/annotationProjection.ts): Extended @PARAM consumedPaths JSDoc to specify dot-separated format with zero-based decimal indices (no brackets) and added an @example; also clarified reservedAnnotationKeys includes the full modelcontextprotocol.io/ prefix
  6. Silent data loss on FNV-1a 32-bit hash collision in disambiguation (workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/annotationProjection.ts): Added a keyOwners Map to track which source dotPath owns each final key; when a hash collision is detected (same final key from a different source path), a counter suffix is appended to produce a distinct key instead of silently overwriting

Tests: passed

Decision points
  • How to handle FNV-1a hash collision in disambiguation (alternatives: Add a logger warning on collision, Add secondary counter-based disambiguation; rationale: A logger would require adding a dependency for an extremely rare edge case; a counter suffix is self-contained and preserves all data without external dependencies)

Next steps:

  • /fs-review — request a re-review of the changes
  • /fs-fix <your instruction> — run another fix pass with specific guidance
  • Push commits directly — review re-runs automatically on push
    Updated by fullsend fix agent

@michael-valdron

Copy link
Copy Markdown
Member

/fs-review

@fullsend-ai-review

fullsend-ai-review Bot commented Sep 17, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 6:28 PM UTC · Completed 6:50 PM UTC

Commit: b26c23b · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $8.14

fullsend-ai-review[bot]

This comment was marked as outdated.

@michael-valdron

Copy link
Copy Markdown
Member

/fs-fix

@fullsend-ai-coder

fullsend-ai-coder Bot commented Sep 17, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ✅ Success · Started 6:53 PM UTC · Completed 7:11 PM UTC

Commit: b26c23b · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $3.62

- Export MAX_NAME_LENGTH from identity.ts and import in
  annotationProjection.ts to eliminate duplication
- Fix @internal JSDoc tags to use standalone format matching identity.ts
- Fix logic gap: disambiguation now applies regardless of truncation
  hash state, preventing potential reserved-key conflicts
- Add @remarks documenting lowercase normalization of annotation keys
- Add sync-dependency comment on SECRET_REDACTED_FIELDS
- Remove horizontal-dash section dividers not used in other files
- Update audit.md timestamp to reflect spec.md changes
- Mark tasks 3.1-3.6, 4.3, 4.4, 4.6, 4.7 as completed in tasks.md

Addresses #4859

Assisted-by: claude-code
@fullsend-ai-coder

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 2 (human-triggered)

Addressed all 9 review findings: fixed 8 items (stale audit timestamp, JSDoc format, disambiguation logic gap, secret-redaction sync comment, MAX_NAME_LENGTH deduplication, section divider style, task completion status, and lowercase normalization documentation) and recorded 1 reasoned disagreement (task 4.5 docs are out of scope for this PR). All 205 tests pass, TypeScript compilation clean, ESLint and Prettier pass.

Fixed (8):

  1. [stale-doc] audit.md not updated after spec.md modification (workspaces/ai-integrations/openspec/changes/mcp-registry-server-mapping/audit.md): Updated 'Last audited' timestamp from 2026-09-15 to 2026-09-17 to reflect spec.md changes in this PR
  2. [documentation-comment-format] @internal JSDoc tag uses trailing inline text (workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/annotationProjection.ts): Moved explanatory text to description paragraph and placed @internal alone on its own line for all four exported internal helpers, matching the pattern in identity.ts
  3. [logic-gap] Disambiguation branch skipped when truncation-hashing is also needed (workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/annotationProjection.ts): Removed the !item.needsTruncationHash guard from the disambiguation condition. Now disambiguation applies regardless of truncation state, since buildHashedNameSegment derives the hash from the full source path segments and handles truncation correctly
  4. [secret-redaction-scope] D9 secret redaction uses a static field set (workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/annotationProjection.ts): Added a JSDoc comment on SECRET_REDACTED_FIELDS noting the sync dependency with the upstream MCP Registry Input schema
  5. [architectural-coherence] MAX_NAME_LENGTH duplicated in identity.ts and annotationProjection.ts (workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/identity.ts): Exported MAX_NAME_LENGTH from identity.ts with @internal tag and imported it in annotationProjection.ts, eliminating the duplicate constant
  6. [code-organization] Block-comment section dividers with horizontal dashes (workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/annotationProjection.ts): Replaced all horizontal-dash section dividers with descriptive JSDoc block comments, matching the style used in other source files in the package
  7. [stale-doc] Tasks 3.1-3.6 and 4.3, 4.4, 4.6, 4.7 remain unchecked in tasks.md (workspaces/ai-integrations/openspec/changes/mcp-registry-server-mapping/tasks.md): Marked tasks 3.1-3.6, 4.3, 4.4, 4.6, and 4.7 as completed with [x] checkboxes
  8. [implicit-behavioral-contract] projectAnnotations return type does not document lowercasing rule (workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/annotationProjection.ts): Added a @remarks JSDoc block documenting that object-key path segments are normalized to lowercase and that array indices are unchanged decimal numerals

Disagreed (1):

  1. [missing-doc] Task 4.5 user/consumer documentation not addressed: Task 4.5 (user/consumer documentation) is out of scope for this PR which focuses on annotation projection implementation. The task remains unchecked in tasks.md and should be addressed in a follow-up PR

Tests: passed

Decision points
  • Task 4.5 documentation is out of scope (alternatives: Add README documentation for annotation projection in this PR, Leave task 4.5 for a follow-up PR; rationale: This PR implements the annotation projection feature (tasks 3.x and 4.x tests). Adding user documentation is a separate deliverable that should be its own PR to keep changes focused)

Next steps:

  • /fs-review — request a re-review of the changes
  • /fs-fix <your instruction> — run another fix pass with specific guidance
  • Push commits directly — review re-runs automatically on push
    Updated by fullsend fix agent

Callers can pass placeholderRemoteUrl to mapRemotes and
mapServerToEntity defaults; it is D11-checked like websiteUrl.

Assisted-by: grok-4.6
Signed-off-by: Michael Valdron <mvaldron@redhat.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@michael-valdron

Copy link
Copy Markdown
Member

/fs-review

@fullsend-ai-review

fullsend-ai-review Bot commented Sep 18, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 1:24 AM UTC · Completed 1:49 AM UTC

Commit: b8a4988 · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $9.30

Signed-off-by: Michael Valdron <mvaldron@redhat.com>
fullsend-ai-review[bot]

This comment was marked as outdated.

fullsend-ai-review[bot]

This comment was marked as outdated.

michael-valdron and others added 5 commits September 17, 2026 22:17
Replace the open index signature with schema-aligned ServerDetail
types and expand assertServerJsonSchema to reject unknown or mistyped
fields.

Assisted-by: grok-4.6
Signed-off-by: Michael Valdron <mvaldron@redhat.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…atures

Assisted-by: grok-4.6
Signed-off-by: Michael Valdron <mvaldron@redhat.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…anch state

Assisted-by: grok-4.6
Signed-off-by: Michael Valdron <mvaldron@redhat.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
… top

Assisted-by: grok-4.6
Signed-off-by: Michael Valdron <mvaldron@redhat.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Split assertServerJsonSchema to reduce cognitive complexity and cover
the extracted helpers with unit tests.

Assisted-by: grok-4.6
Signed-off-by: Michael Valdron <mvaldron@redhat.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@michael-valdron

Copy link
Copy Markdown
Member

/fs-review

@fullsend-ai-review

fullsend-ai-review Bot commented Sep 18, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 2:43 AM UTC · Completed 3:12 AM UTC

Commit: 3e5c9fc · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $11.77

@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.

See the review comment for full details.

@michael-valdron

Copy link
Copy Markdown
Member

/fs-fix Address the following: Conditionally include placeholderRemoteUrl in the error message only when the parameter was supplied (#4859 (comment)); Remove the 'Exported for unit testing only.' sentence from the JSDoc, keep @internal (#4859 (comment)).

@fullsend-ai-coder

fullsend-ai-coder Bot commented Sep 18, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ✅ Success · Started 3:24 AM UTC · Completed 3:41 AM UTC

Commit: 3e5c9fc · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $2.78

Conditionally include placeholderRemoteUrl in mapRemotes error message
only when the parameter was supplied. Remove misleading 'Exported for
unit testing only.' from parseAbsoluteUrl JSDoc since it is used in
production code by annotationProjection.ts.

Addresses #4859

Assisted-by: claude-code/claude-sonnet-4-20250514
@fullsend-ai-coder

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 4 (human-triggered)

Addressed both review findings: (1) made the mapRemotes error message conditionally reference placeholderRemoteUrl only when the parameter was supplied, and (2) removed the misleading 'Exported for unit testing only.' sentence from parseAbsoluteUrl JSDoc in urlPolicy.ts since the function is used in production code by annotationProjection.ts.

Fixed (2):

  1. Error message unconditionally references placeholderRemoteUrl even when the optional parameter was not supplied (workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/mapServerToEntity.ts): Conditionally include placeholderRemoteUrl in the error message only when the parameter was supplied, using a ternary to select between 'placeholderRemoteUrl or websiteUrl' and just 'websiteUrl'
  2. parseAbsoluteUrl documented as 'Exported for unit testing only' but is imported and used in production code by annotationProjection.ts (workspaces/ai-integrations/plugins/mcp-registry-server-mapping-common/src/urlPolicy.ts): Removed the 'Exported for unit testing only.' sentence from the JSDoc while keeping the @internal tag

Tests: passed

Next steps:

  • /fs-review — request a re-review of the changes
  • /fs-fix <your instruction> — run another fix pass with specific guidance
  • Push commits directly — review re-runs automatically on push
    Updated by fullsend fix agent

@michael-valdron michael-valdron 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.

Current changes lgtm.

@gabemontero @johnmcollier PTAL when you can.

@gabemontero gabemontero left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I like these refs to the spec via the @ see @ link usage .... on small suggestion - maybe the README.md or a new markdown could somehow contain a cross reference table to all these different locations (i.e. links to the line of code where you have the use of see / link) so we have a centralized view of all the aspects of the mcp registry spec

otherwise seems straight forward; pending any feedback from @johnmcollier go ahead and get this merged @michael-valdron and move onto the next step

michael-valdron and others added 3 commits September 18, 2026 11:06
Assisted-by: grok-4.6
Signed-off-by: Michael Valdron <mvaldron@redhat.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Point package docs, JSDoc, and tests at the tagged draft schema instead
of main so the mapping contract tracks a stable registry revision.

Assisted-by: grok-4.6
Signed-off-by: Michael Valdron <mvaldron@redhat.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Add field-level type tables for the MCP Registry v1.8.1 server.json
entities and link them from the package README.

Assisted-by: grok-4.6
Signed-off-by: Michael Valdron <mvaldron@redhat.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@sonarqubecloud

Copy link
Copy Markdown

@michael-valdron

Copy link
Copy Markdown
Member

I like these refs to the spec via the @ see @ link usage .... on small suggestion - maybe the README.md or a new markdown could somehow contain a cross reference table to all these different locations (i.e. links to the line of code where you have the use of see / link) so we have a centralized view of all the aspects of the mcp registry spec

otherwise seems straight forward; pending any feedback from @johnmcollier go ahead and get this merged @michael-valdron and move onto the next step

@gabemontero Addressed your feedback, I added a separate document highlighting the breakdowns of the server.json types with links to the source locations with an inclusion reference to it in the README (forgot to push that commit) ce3fd2d.

Just need @johnmcollier's complete review, any feedback addressed there and we're good to go I think.

@michael-valdron
michael-valdron merged commit 5fc4c61 into main Sep 18, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-for-review Triggers review agent dispatch risk/moderate PR risk: moderate workspace/ai-integrations

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(ai-integrations): implement MCP registry server mapping common library plugin - 2 / 2 (mcp-registry-annotation-projection)

3 participants