Skip to content

fix(mcp): suggest an explicit Official Registry retry on catalog miss - #2803

Open
vedanth bora (VedanthB) wants to merge 1 commit into
microsoft:mainfrom
VedanthB:fix/2478-official-registry-hint
Open

fix(mcp): suggest an explicit Official Registry retry on catalog miss#2803
vedanth bora (VedanthB) wants to merge 1 commit into
microsoft:mainfrom
VedanthB:fix/2478-official-registry-hint

Conversation

@VedanthB

@VedanthB vedanth bora (VedanthB) commented Sep 4, 2026

Copy link
Copy Markdown

fix(mcp): suggest an explicit Official Registry retry on catalog miss

TL;DR

Direct apm install --mcp NAME failures against the built-in GitHub MCP catalog now show a literal command that retries the same canonical server name against the Official MCP Registry. The retry remains an explicit user decision: APM does not switch registries or make a second request automatically. Manifest installs, registry overrides, and unsafe names retain the existing generic guidance.

Note

This implements the accepted hint-only option from #2478; automatic cross-registry fallback remains out of scope.

Fixes #2478

Problem (WHY)

  • A valid server can exist in the Official MCP Registry while being absent from APM's built-in GitHub catalog, leaving a direct install at Server(s) not found in registry with no concrete recovery step.
  • Automatically trying a second registry would silently change the trust authority selected for the install.
  • A recovery command is only useful if it is safe to paste and remains one literal line in narrow terminals.
  • [!] Manifest-driven installs and explicit registry selections must not inherit advice that assumes the built-in default catalog.

The issue's triage panel accepted an exact retry hint as the bounded first step because it improves first-install DevX without changing registry semantics or policy.

Approach (WHAT)

# Fix Principle Source
1 Gate the hint to direct --mcp prevalidation against the canonical default source. DevX (pragmatic as npm) #2478
2 Reuse the registry client's canonical server-name validator before rendering any pasted command. Secure by default src/apm_cli/registry/client.py
3 Render the retry without Rich hard wrapping while preserving default rendering everywhere else. DevX (pragmatic as npm) src/apm_cli/utils/console.py
4 Execute the printed command through Click and assert the two registry boundaries independently. Governed by policy tests/unit/install/test_mcp_registry_config_layer.py

Implementation (HOW)

File Intent and scope
src/apm_cli/integration/mcp_integrator_install.py Adds an internal, default-off context flag. Only direct MCP prevalidation enables it; manifest installation leaves it disabled. A hint is emitted only for the default registry source and an entirely canonical missing-name batch.
src/apm_cli/registry/client.py Promotes the existing server-name rule into one fullmatch validator used by both URL construction and retry rendering. Adds the existing flag source to registry URL diagnostics so the printed --registry retry follows the normal CLI path.
src/apm_cli/utils/console.py Threads Rich's soft_wrap option through the info renderer, defaulting to the existing behavior.
src/apm_cli/core/command_logger.py Exposes the keyword-only, default-off rendering option at the command logger boundary.
src/apm_cli/core/null_logger.py Keeps the null logger facade signature-compatible with CommandLogger.
tests/unit/install/test_mcp_registry_config_layer.py Covers direct and manifest paths, exact output cardinality, unsafe inputs, every override source, redaction, deterministic batches, and a real Click round trip.
docs/src/content/docs/reference/cli/install.md Documents the explicit retry and states that APM never switches registries automatically.
CHANGELOG.md Records the direct-install behavior under Unreleased.

Diagrams

Legend: dashed nodes are the new recovery boundary; the first branch to inspect is whether the request is a direct default-catalog miss.

flowchart LR
    subgraph Resolve[Resolve]
        A["apm install --mcp NAME"]
        B[prevalidate_registry_dependencies]
        C[_validate_registry_servers]
        M["manifest apm install"]
    end
    subgraph Decide[Decide]
        D{"direct hint enabled, default source, canonical names"}
    end
    subgraph Render[Render]
        E["Try command with Official Registry"]
        F[generic search guidance]
    end
    subgraph UserChoice[User choice]
        G["run copied command"]
        H["Official Registry request"]
    end
    A --> B
    B --> C
    M --> C
    C --> D
    D -->|all true| E
    D -->|otherwise| F
    E --> G
    G --> H
    classDef new stroke-dasharray: 5 5;
    class D,E new;
Loading

Trade-offs

  • Explicit retry instead of automatic fallback. Chose a copyable command; rejected a hidden second lookup because changing registry authority must remain user-controlled.
  • Batch-wide safety instead of partial hints. Chose to suppress all retry commands if any missing name is non-canonical; rejected selectively printing a subset because that creates ambiguous recovery output.
  • Context flag instead of registry inference alone. Chose a default-off internal flag plus the registry source; rejected source-only inference because manifest installs can also use the public default.
  • Targeted soft wrapping instead of a global console change. Chose one keyword-only logger option used only for this command; rejected changing all CLI rendering.

Benefits

  1. A direct default-catalog miss yields exactly one actionable retry per canonical missing server name.
  2. The initial failed command contacts only api.mcp.github.com; the copied retry contacts only registry.modelcontextprotocol.io.
  3. Five registry override sources (explicit, flag, env, config, and unknown) are regression-tested to suppress the hint.
  4. Four hostile or malformed name shapes are regression-tested and never cross into a pasted command.

Validation

uv run pytest tests/unit/install/test_mcp_registry_config_layer.py -q:

................                                                         [100%]
16 passed in 1.65s
Full unit suite after rebasing onto current `main` (21,383 collected items)

uv run pytest tests/unit -n auto --dist worksteal:

===== 21358 passed, 4 skipped, 21 xfailed, 21 warnings in 65.82s (0:01:05) =====
Additional repository gates
Related registry and integrator slice: 263 passed
Logger and output slice: 162 passed
Quality suite: 54 passed
Spec conformance suite: 196 passed, 2 skipped
Ruff check and format: passed
Pylint duplicate-code gate: passed
Architecture boundaries: passed
Authentication signal checks: passed
git diff --check: passed

The lifecycle smoke suite passed 131 tests and skipped one. Its one remaining failure is environment-only: the Mac mini does not have PowerShell for test_claude_project_hook_runs_from_external_cwd; the failure is outside the changed paths.

Scenario Evidence

# Scenario (user promise) Principle(s) Test(s) proving it Type
1 A direct install missing from the default catalog tells me exactly how to retry against the Official Registry. DevX (pragmatic as npm) tests/unit/install/test_mcp_registry_config_layer.py::test_direct_default_miss_prints_round_trippable_official_retry (regression-trap for #2478) integration
2 The failed first attempt does not silently contact a second registry, and the copied retry contacts only the registry I selected. Secure by default, Governed by policy tests/unit/install/test_mcp_registry_config_layer.py::test_direct_default_miss_prints_round_trippable_official_retry integration
3 A manifest install keeps generic recovery guidance rather than suggesting an unrelated registry. Portability by manifest, Vendor-neutral tests/unit/install/test_mcp_registry_config_layer.py::test_manifest_default_registry_miss_keeps_generic_guidance integration
4 A registry override remains the sole trust authority after a miss and its credentials never appear in diagnostics. Secure by default, Governed by policy tests/unit/install/test_mcp_registry_config_layer.py::test_registry_override_miss_does_not_suggest_another_registry unit
5 Unsafe server names never appear inside a shell command I might paste. Secure by default tests/unit/install/test_mcp_registry_config_layer.py::test_official_retry_rejects_unsafe_server_names unit
6 Multiple safe misses produce one deterministic retry per exact name. DevX (pragmatic as npm) tests/unit/install/test_mcp_registry_config_layer.py::test_default_registry_miss_suggests_ordered_exact_name_retries unit

How to test

  • Run the focused registry-layer test file; expect all 16 tests to pass.
  • Run apm install --mcp ado-mcp with no registry override against a catalog where it is absent; expect one literal Try: line and no Official Registry request.
  • Run the printed command; expect the lookup to use only https://registry.modelcontextprotocol.io.
  • Repeat with MCP_REGISTRY_URL or --registry set; expect the selected registry to remain authoritative and no Official Registry hint.
  • Run a manifest-driven miss; expect the existing apm mcp search guidance and no Try: line.

Pull request checklist

Type of change

  • Bug fix
  • New feature
  • Documentation
  • Maintenance / refactor

Testing

  • Tested locally
  • All existing unit tests pass
  • Added tests for new functionality

Spec conformance (OpenAPM v0.1)

  • Spec edit
  • Manifest edit
  • Spec test edit
  • Conformance statements regenerated
  • N/A -- this PR changes CLI recovery guidance, not an OpenAPM v0.1 normative requirement.

AI-assisted review disclosure: GPT-5.6 Sol and Claude Opus were used as independent reviewers; the contributor remains responsible for the change.

Copilot AI 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.

🟡 Changes recommended

The new CHANGELOG entry does not follow the file’s existing “end with PR number” formatting pattern, so it should be adjusted for consistency/traceability.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR improves the UX of direct apm install --mcp NAME when the default GitHub MCP catalog misses an exact server name by printing a safe, copyable retry command that explicitly targets the Official MCP Registry (without any automatic cross-registry fallback).

Changes:

  • Add an optional, direct-install-only hint path that prints Try: apm install --mcp "NAME" --registry https://registry.modelcontextprotocol.io on default-catalog misses for validated server names.
  • Thread a soft_wrap rendering option through the console/info logger boundary to keep the retry command as a single literal line.
  • Add unit coverage for direct vs manifest installs, override suppression, unsafe name suppression, and Click round-trip behavior; update docs + changelog.
File summaries
File Description
tests/unit/install/test_mcp_registry_config_layer.py Adds regression tests for default-catalog misses, official-retry rendering, override suppression, and safety/determinism.
src/apm_cli/utils/console.py Adds soft_wrap support to Rich-backed echo/info rendering.
src/apm_cli/registry/client.py Introduces OFFICIAL_MCP_REGISTRY_URL and centralizes server-name validation via is_valid_mcp_server_name; improves hint text for flag source.
src/apm_cli/integration/mcp_integrator_install.py Emits the explicit official-registry retry hint only for direct default-registry misses with validated names.
src/apm_cli/core/null_logger.py Keeps logger facade signature-compatible while supporting soft_wrap info rendering.
src/apm_cli/core/command_logger.py Exposes keyword-only soft_wrap option for info messages.
docs/src/content/docs/reference/cli/install.md Documents the explicit retry behavior and clarifies no automatic registry switching.
CHANGELOG.md Adds an Unreleased entry describing the new direct-install retry hint behavior.
Review details
  • Files reviewed: 8/8 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread CHANGELOG.md Outdated
@VedanthB
vedanth bora (VedanthB) force-pushed the fix/2478-official-registry-hint branch from 4d89e20 to 2e4b6cf Compare September 5, 2026 07:06
@VedanthB
vedanth bora (VedanthB) force-pushed the fix/2478-official-registry-hint branch from 2e4b6cf to 23bf784 Compare September 5, 2026 07:12
@VedanthB

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE] Offer Official MCP Registry fallback when GitHub catalog misses an exact server name

2 participants