Deploying the registry
Almost everything the registry writes lives in committed files, so most of the rollout is “merge the catalog and let existing entry points run sync”. No per-developer install campaign: choose the channels that match your org and layer them.
Choosing channels
Section titled “Choosing channels”| Channel | Covers | Dev-machine footprint |
|---|---|---|
| Repo + CI drift gate | Project configs for every dev who pulls | none |
| Claude Code SessionStart hook | Self-healing configs each session | none (npx on demand) |
| OpenCode autosync | OpenCode users at startup | none (plugin already loads) |
| MDM / login script | User-level configs (Claude Desktop, ~/.claude.json) | one scripted command |
CI drift gate
Section titled “CI drift gate”registry sync --check exits 1 when committed configs drift from the
catalog — pair it with the scan gate:
name: MCP registry drift
on: pull_request: paths: - '.ringzero/**' - '**/.mcp.json' - '**/.vscode/mcp.json' - '**/.cursor/mcp.json' - '**/opencode.json' - '**/opencode.jsonc'
jobs: drift: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 22 - run: npx @ringzero/mcp-scan@latest registry validate - run: npx @ringzero/mcp-scan@latest registry sync --checkWhen the catalog changes, run registry sync locally (or let a bot do it)
and commit the updated configs + lock file in the same PR.
Claude Code SessionStart hook
Section titled “Claude Code SessionStart hook”Commit the hook once and every session self-heals its MCP config:
{ "hooks": { "SessionStart": [ { "hooks": [ { "type": "command", "command": "npx -y @ringzero/mcp-scan registry sync --quiet" } ] } ] }}ringzero-mcp init --print-hooks claude-code prints this together with the
usage-auditing hooks. --quiet
prints nothing when clean and a single line when it applied changes, so
session startup stays clean. Repos without a catalog are a silent no-op.
OpenCode autosync
Section titled “OpenCode autosync”If your org already ships
@ringzero/opencode-mcp-guard, the
registry deploys itself: the plugin applies a project-scope sync at startup
whenever a catalog is present.
- Changes to
opencode.jsontake effect on the next OpenCode restart — the plugin says so on stderr when it applied changes. - Failures never block the session (fail-open), conflicts are reported.
- Opt out per environment with
RINGZERO_MCP_REGISTRY_AUTOSYNC=0.
Machine scope via MDM or login scripts
Section titled “Machine scope via MDM or login scripts”User-level configs (Claude Desktop, ~/.claude.json, ~/.cursor/mcp.json,
user OpenCode) are the one surface a repo can’t carry. Ship one command
through whatever your org already runs on login:
# Scheduled task or Intune remediation script — run at logon$env:RINGZERO_MCP_REGISTRY = '\\fileserver\ringzero\mcp-registry.json'npx -y @ringzero/mcp-scan registry sync --machine --quiet# LaunchAgent or Jamf policy — run at loginexport RINGZERO_MCP_REGISTRY=/Library/RingZero/mcp-registry.jsonnpx -y @ringzero/mcp-scan registry sync --machine --quiet# ~/.profile, or a systemd user unitexport RINGZERO_MCP_REGISTRY="$HOME/.config/ringzero/mcp-registry.json"npx -y @ringzero/mcp-scan registry sync --machine --quietRollout playbook
Section titled “Rollout playbook”-
Seed the catalog —
ringzero-mcp registry addthe servers your org already blesses (orregistry pullonce the platform catalog ships — the pulled catalog also carries your org’s control level, so runtime enforcement rides the same file). Merge it with"harnesses"limited to one harness if you want a canary. -
Turn on the CI drift gate so the catalog and configs can’t diverge.
-
Commit the SessionStart hook / plugin entry — devs start receiving servers as they pull and restart sessions.
-
Widen
harnessesto"all", then add the MDM machine-scope job for Claude Desktop coverage. -
Retire a server by flipping
"enabled": false(managed removal everywhere on next sync) before deleting the entry.
Troubleshooting
Section titled “Troubleshooting”- “Registry drift” in CI but not locally — someone edited a managed
entry by hand;
registry statusnames the exact file and server,syncrestores it. - A dev’s own server disappeared? It can’t — sync only ever removes entries recorded in the lock file. If a server vanished, it was a managed entry whose catalog row was removed or disabled.
- Conflict reports — a local entry shadows a catalog name; rename or delete one side.
- Emergency stop —
RINGZERO_MCP_REGISTRY_AUTOSYNC=0(OpenCode), remove the SessionStart hook line, or flip entries to"enabled": falseand let sync clean up.