Skip to content

Add 'apm config' surface for persistent MCP registry URL (item 4b from #810) #818

@danielmeppiel

Description

@danielmeppiel

Background

PR #810 ships apm install --mcp NAME ... for declaratively adding MCP servers to apm.yml, with MCP_REGISTRY_URL as the env-var-based registry override. PR #810 is also adding apm install --mcp --registry <url> (item 4a from the same review) for one-shot installs against a non-default registry.

This issue tracks the third (and final) layer of the precedence chain: persistent, cross-platform configuration.

Problem

Env vars are awkward on Windows (no .bashrc-style equivalent for terminal profiles; setting them per-shell is cumbersome). Enterprise teams that consistently use a private MCP registry need a way to set this once, persistently, in a way apm itself manages.

Proposed solution

Add apm config set mcp-registry-url <url> (and apm config get / apm config unset).

Final precedence chain (after this issue lands)

  1. apm install --mcp --registry <url> (CLI flag — already in PR feat(install): add --mcp flag for declaratively adding MCP servers to apm.yml #810)
  2. MCP_REGISTRY_URL env var (already shipped)
  3. apm config get mcp-registry-url (this issue)
  4. Default: https://api.mcp.github.com

The first non-empty / non-null value wins.

Design surface to resolve

  • Config namespace: does apm config already exist? If not, this issue includes designing the surface (storage location, file format, scoping — global vs project). If it exists, just add the new key.
  • Storage location: ~/.config/apm/config.yaml (XDG) on Linux/macOS, %APPDATA%/apm/config.yaml on Windows.
  • Schema: Single key mcp.registry-url (or mcp_registry_url) — needs decision, with room to grow (other mcp.* keys later).
  • Validation on set: same URL allowlist as --mcp --registry (http/https only; reject file://, ws://, etc.). Reuse _ALLOWED_URL_SCHEMES from models/dependency/mcp.py.
  • Display: apm config get mcp-registry-url prints the value or "(unset)" — never crashes.
  • Diagnostic: when an apm install --mcp or apm mcp search resolves the registry from this layer (not env, not flag), emit a single-line diagnostic naming the source: Registry: https://corp.mcp.example.com (from apm config). Mirrors the existing env-var diagnostic.

Acceptance criteria

  • apm config set mcp-registry-url https://corp.mcp.example.com writes to user config
  • apm config get mcp-registry-url returns the stored value
  • apm config unset mcp-registry-url removes it
  • Invalid URL schemes (file://, ws://, javascript:) rejected at set time
  • apm install --mcp NAME (no --registry, no env) uses the configured value
  • CLI --registry flag overrides config; env var overrides config; config overrides default
  • One-line diagnostic when the config-layer URL is in effect
  • Documented in docs/src/content/docs/guides/mcp-servers.md with the full precedence chain
  • CHANGELOG entry under Added with the issue + PR number suffix

Out of scope

  • Project-scoped (vs user-scoped) config — single user-level value is enough for v1.
  • Migration tooling for existing MCP_REGISTRY_URL users — env keeps working at higher precedence; no migration needed.
  • Auth/credentials per registry — separate concern, file separately if needed.

Panel context

This issue was distilled from a multi-expert review (supply-chain security, devx-ux, cli-logging, python-architecture) of PR #810. When this is implemented, invoke the APM Review Panel skill (.github/skills/apm-review-panel/).

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/cliCLI command surface, flags, help text (cross-cutting).area/docs-sitedocs/src/content (Starlight), README, doc generation.area/mcp-configMCP server configuration depth, transports, variable resolution.enhancementDeprecated: use type/feature. Kept for issue history; will be removed in milestone 0.10.0.priority/lowAccepted but not time-sensitivestatus/acceptedDirection approved, safe to start work.status/triagedInitial agentic triage complete; pending maintainer ratification (silence = approval).theme/portabilityOne manifest, every target. Multi-target deploy, marketplace, packaging, install.type/featureNew capability, new flag, new primitive.

    Type

    No type

    Projects

    Status

    Todo

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions