MCP Catalog¶
The Bernstein MCP catalog is a community registry of installable Model Context Protocol servers. It shipped in release 1.9 and lives at bernstein mcp catalog (registered as a subgroup of bernstein mcp in cli/main.py:727-729). Browse, search, install, upgrade, and uninstall MCP servers without hand-editing your client config.
The catalog is fetched (network call), validated against reference/mcp-catalog-schema.json, cached locally, and every state-changing call is audited through Bernstein's HMAC-chained audit log.
Every flag below is cited as
cli/commands/mcp_catalog_cmd.py:<line>.
Concepts¶
- Catalog — a JSON document listing MCP servers, their install commands, version pins, and verification status. Schema documented at
reference/mcp-catalog-schema.json(canonical) or below in Schema example. - Entry — one record in the catalog. Has an
id(slug),version_pin(semver),install_command(argv),verified_by_bernstein(bool),signature(optional), andtransports(list of stdio / http / sse). - Cache — local JSON copy of the last-fetched catalog. Path resolved by
default_cache_path(); overridable viaBERNSTEIN_MCP_CATALOG_CACHE_PATH. - User MCP config — the file Bernstein writes installed servers into. Path resolved by
default_user_config_path(); overridable viaBERNSTEIN_MCP_USER_CONFIG_PATH. Edits are bracketed by a "bernstein-managed" block so manual entries elsewhere in the file are preserved. - Audit log — every fetch / install / upgrade / uninstall emits an HMAC-chained event under
.sdd/audit/. Override directory viaBERNSTEIN_MCP_CATALOG_AUDIT_DIR(cli/commands/mcp_catalog_cmd.py:46-51). - Sandbox preview —
installandupgradeexecute the entry'sinstall_commandin a sandbox first, capturing any file changes as a diff. Only after you confirm does Bernstein touch your real user config.
bernstein mcp catalog browse¶
List every entry in the catalog.
Synopsis: bernstein mcp catalog browse [flags]
| Flag | Default | Meaning |
|---|---|---|
--refresh | off | Skip the freshness window; force a fresh fetch. |
(source: cli/commands/mcp_catalog_cmd.py:135-163)
The output is a Rich table with columns ID, Name, Version, Verified, Transports. The Verified column is yes if verified_by_bernstein=true in the catalog entry — i.e. Bernstein's trusted reviewers signed off on this manifest.
If validation fails (an entry has unknown fields, a missing required field, or a malformed signature), the whole catalog fetch is rejected and the cached copy is preserved. You will see Catalog rejected: <reason> and the previous catalog remains usable.
bernstein mcp catalog search <query>¶
Search the catalog by ID, name, or description substring.
Synopsis: bernstein mcp catalog search QUERY [flags]
| Flag | Default | Meaning |
|---|---|---|
QUERY | required | Substring (case-insensitive) matched against id, name, description. |
--refresh | off | Skip the freshness window. |
(source: cli/commands/mcp_catalog_cmd.py:166-181)
Output: one line per match, formatted <id> (<version_pin>) <verified|unverified> - <name>: <description>. Verified entries are green; unverified ones are yellow.
bernstein mcp catalog info <id>¶
Show full details for a single entry.
Synopsis: bernstein mcp catalog info ENTRY_ID [flags]
| Flag | Default | Meaning |
|---|---|---|
ENTRY_ID | required | The slug from browse/search. |
--refresh | off | Skip the freshness window. |
(source: cli/commands/mcp_catalog_cmd.py:184-202)
Prints the entry's name, version pin, description, homepage, repository, transports, verification status, auto-upgrade flag, install command, and signature (if any).
bernstein mcp catalog install <id>¶
Install an MCP server into your user MCP config.
Synopsis: bernstein mcp catalog install ENTRY_ID [flags]
| Flag | Default | Meaning |
|---|---|---|
ENTRY_ID | required | The slug to install. |
--yes | off | Skip the confirmation prompt. |
--refresh | off | Skip the freshness window. |
(source: cli/commands/mcp_catalog_cmd.py:205-243)
Flow:
- Bernstein fetches the catalog (or uses the cached copy if fresh).
- If the entry is unverified, a yellow
WARNINGblock is printed showing the exactinstall_commandthat would execute. - The install command is executed in a sandbox first. Bernstein records every file change, captures stdout/stderr, and renders an
InstallPreview(succeeded / failed / timed-out, duration, file diff list). - If the sandbox preview fails, the install is aborted; your user MCP config is left untouched.
- Otherwise Bernstein prompts:
Apply this install to the user MCP config?. Use--yesto skip the prompt. - On confirmation, Bernstein writes the entry into the bernstein-managed block of the user MCP config.
The audit log records the install with a HMAC-chained event so the trail is tamper-evident.
bernstein mcp catalog install official-github
bernstein mcp catalog install some-experimental-server --yes # skip prompt
bernstein mcp catalog list-installed¶
List every entry currently installed via the catalog.
Synopsis: bernstein mcp catalog list-installed
(source: cli/commands/mcp_catalog_cmd.py:246-276)
Output columns: ID, Pinned (the installed version), Installed at (timestamp), Last upgrade check, In catalog (yes if still listed in the current catalog, no if it was removed upstream).
A no in the In catalog column is a soft warning: the server still works, but you will not get upgrade notifications for it. Investigate before relying on it long-term.
bernstein mcp catalog upgrade [<id>]¶
Re-fetch the catalog and upgrade installed entries.
Synopsis:
| Flag | Default | Meaning |
|---|---|---|
ENTRY_ID | optional | The slug to upgrade. Required unless --all. |
--all | off | Upgrade every installed entry. |
--yes | off | Skip confirmation prompts. |
--refresh | off | Skip the freshness window. |
(source: cli/commands/mcp_catalog_cmd.py:279-309)
For each upgrade, Bernstein:
- Compares the installed
version_pinagainst the catalog's currentversion_pin. - If equal, prints "already on latest" and skips.
- Otherwise runs the same sandbox preview + confirmation flow as
install. - Persists the new version pin into the user MCP config and the audit log.
Skipped upgrades print a skipped_reason (e.g. catalog rejection, sandbox failure).
bernstein mcp catalog uninstall <id>¶
Remove an entry from the bernstein-managed block of the user MCP config.
Synopsis: bernstein mcp catalog uninstall ENTRY_ID
(source: cli/commands/mcp_catalog_cmd.py:312-320)
Errors with <id> is not installed if the entry was never installed via the catalog. Manually-added user MCP entries are not affected — Bernstein only edits its own bracketed block.
bernstein mcp catalog status¶
Show cache freshness, cadence settings, and installed-server count.
Synopsis: bernstein mcp catalog status
(source: cli/commands/mcp_catalog_cmd.py:323-334)
Output keys:
Cache— path to the cached catalog JSON.Last fetch— timestamp of the last successful fetch (ornever).Next due— when the next background check is allowed.Check interval (sec)— how often Bernstein revalidates with the upstream registry. Tuned viaBERNSTEIN_MCP_CATALOG_CHECK_INTERVAL.Installed— count of entries Bernstein has placed in the user MCP config.Cache state— last validation outcome (ok,validation_error: ..., etc.).
Schema example¶
The full schema lives in reference/mcp-catalog-schema.json. A minimal valid entry:
{
"version": 1,
"generated_at": "2026-04-30T12:00:00Z",
"entries": [
{
"id": "official-github",
"name": "GitHub MCP Server",
"description": "Issue, PR, and repo browsing tools.",
"homepage": "https://github.com/modelcontextprotocol/servers/tree/main/src/github",
"repository": "https://github.com/modelcontextprotocol/servers",
"install_command": ["npx", "-y", "@modelcontextprotocol/server-github"],
"version_pin": "0.4.2",
"transports": ["stdio"],
"verified_by_bernstein": true,
"auto_upgrade": false,
"signature": "sha256:..."
}
]
}
Every field is required unless marked otherwise in the schema. additionalProperties is false at the top level and on each entry — the fetch is rejected wholesale if any unknown field is present. This is intentional: catalog drift would otherwise silently install servers with unrecognized capabilities.
Configuration & environment variables¶
All five env vars come from cli/commands/mcp_catalog_cmd.py:46-87.
| Env var | Default | Purpose |
|---|---|---|
BERNSTEIN_MCP_CATALOG_AUDIT_DIR | .sdd/audit/ | Where HMAC-chained audit events are written. |
BERNSTEIN_MCP_CATALOG_CHECK_INTERVAL | DEFAULT_CHECK_INTERVAL_SECONDS (from core/protocols/mcp_catalog) | Minimum seconds between background catalog freshness checks. |
BERNSTEIN_MCP_CATALOG_REVALIDATE_INTERVAL | DEFAULT_REVALIDATE_SECONDS | Minimum seconds before re-fetch of the upstream catalog. |
BERNSTEIN_MCP_CATALOG_CACHE_PATH | default_cache_path() | Where the validated catalog JSON is cached on disk. |
BERNSTEIN_MCP_USER_CONFIG_PATH | default_user_config_path() | The user MCP config file Bernstein writes installed entries into. |
All five accept absolute or ~-expanded paths (where applicable) and parse integer overrides safely (a non-integer value is silently ignored and the default is kept).
Trust model¶
verified_by_bernstein— A boolean on each entry.truemeans a Bernstein-trusted reviewer has audited the upstream server's source and signed off on the install command and version pin combination as of this catalog generation.falseis not a "blocked" mark — it just means you have not had a third party vouch for the server. Unverified entries trigger a yellow warning before install (cli/commands/mcp_catalog_cmd.py:221-226).signature— Optional cryptographic signature over the entry. The schema permits any string; verification is delegated to the catalog service implementation incore/protocols/mcp_catalog.additionalProperties: falseat top level and per-entry — the catalog is rejected wholesale on unknown fields. This blocks silent capability drift.- Sandbox preview — Every install / upgrade runs the install command in a sandbox first. The host config is touched only after you confirm the diff. Failed previews abort without modifying state.
- HMAC-chained audit — Every fetch, install, upgrade, and uninstall is recorded to
.sdd/audit/with a chained HMAC, making after-the-fact tampering detectable. - Bernstein-managed block — Edits to the user MCP config are bracketed; Bernstein only owns its own block. Manually-added user entries elsewhere in the file are preserved.
There is no allow-list on which catalogs can be loaded; the catalog source URL is configured by the runtime, not by the user. If you operate in a high-trust environment, point the cache at an internally-mirrored catalog and treat the public catalog as untrusted by setting BERNSTEIN_MCP_CATALOG_CACHE_PATH and disabling auto-refresh.
See also¶
bernstein mcp— root MCP server command (separate from catalog).integrations/mcp-server-injection.md— theprovide_mcp_serversplugin hook for injecting servers from a plugin (different mechanism, different trust boundary).- Source schema — the canonical JSON Schema.