Skip to content

MCP registries and gateways: allowlisting servers for an organization

An organization allowlists MCP servers in four layers: vet each server against the Official MCP Registry and the vendor’s repository, record approved servers in one reviewed catalog, enforce the catalog in each agent’s managed configuration, and route calls through a gateway where audit logs are needed. The Official MCP Registry is a preview index, not a vetted list.

Your security team approved GitHub and Sentry MCP in the spring. Six months later, claude mcp list on a random laptop shows eleven servers, including a figma entry that points at a package from an unrelated publisher, and a Codex user has a Postgres server nobody reviewed. Nobody broke a rule, because there was no rule a tool could enforce, and nobody can say which servers ran against production data last week.

This page is for the tech lead who owns the approved list and the CTO who needs to show auditors that it holds. It assumes you have read securing MCP servers, which covers scanning and read-only modes for a single server. The per-tool policy files that carry this list sit in enforcing one policy across every coding agent.

What an organization MCP allowlist gives you

Section titled “What an organization MCP allowlist gives you”
  • A repeatable vetting routine that uses the Official MCP Registry for identity and the vendor’s repository for trust, with a prompt that runs it.
  • A decision table for choosing between a fixed server set, an approved catalog and a denylist.
  • One catalog file that renders into Claude Code allowedMcpServers, Codex requirements.toml and a Cursor review checklist.
  • A gateway route for teams that need one entry point, secret scanning and call logs.
  • Mechanical checks that prove the allowlist holds on every machine, and a list of what breaks during rollout.

What is the Official MCP Registry, and what does it not do?

Section titled “What is the Official MCP Registry, and what does it not do?”

The Official MCP Registry is a public index of MCP server metadata: name, version, packages and remote URLs. Its own README calls it “an app store for MCP servers”. It launched in preview on 2025-09-08 and its API has been frozen at v0.1 since 2025-10-24; general availability has not been announced (checked 2026-09-26). On 2026-09-26 it listed 36,176 servers at their latest version, counted through the registry API by this site’s research.

Three properties decide how you use it:

PropertyWhat it means for your allowlist
Namespace verification only. io.github.<user>/… names require the publisher to authenticate as that GitHub user; com.example/… names require DNS or HTTP proof of the domain.A name like io.github.getsentry/sentry-mcp proves who published the entry. It does not prove the server is safe or the one you meant.
Minimal moderation. The registry’s moderation policy says it does not remove “low-quality or buggy servers” or “servers with security vulnerabilities”, and that “consumers should assume minimal-to-no moderation”.Registry presence is never an approval. Several */github, */figma and */sentry entries come from publishers unrelated to those vendors.
Versions can lag packages. On 2026-09-26 Serena’s registry entry was 1.5.3 while PyPI had 1.7.0, and Next.js DevTools was 0.3.6 against npm 0.4.0.Pin versions from the package registry you install from, not from the MCP Registry.

The registry expects aggregators downstream of it: services that copy its data “on a regular but infrequent basis (e.g., once per hour)” and add value such as ratings or security scanning. An aggregator that also serves the registry’s OpenAPI is a subregistry. That is the model for a private registry: your own filtered, reviewed copy, not a live dependency on the public index.

Which allowlist pattern fits your organization?

Section titled “Which allowlist pattern fits your organization?”

Choose the pattern before you write any file. The stricter patterns cost developers flexibility, so match them to risk, not to habit.

PatternWhat developers can doChoose it whenClaude Code mechanism
Fixed setUse exactly the servers you deploy; add nothingRegulated code, contractors, CI runnersmanaged-mcp.json
Approved catalogAdd any server on the list; everything else is blockedMost product teamsallowedMcpServers + allowManagedMcpServersOnly: true
Provided plus ownGet your remote servers automatically and keep their ownEarly rollout, low-risk repositoriesmanagedMcpServers
Denylist onlyAdd anything except known-bad serversOnly as a stopgap while you build the catalogdeniedMcpServers

The rest of this page builds the approved catalog, because it is the pattern most organizations end up on and the other three are subsets of its steps.

How do you vet a server against the registry?

Section titled “How do you vet a server against the registry?”
  1. Search the registry, then keep only the vendor’s namespace. The registry API returns every entry that matches the product name, with its packages and remote URLs; the select line keeps the vendor’s namespace:

    Terminal window
    # terminal; needs curl and jq
    curl -s "https://registry.modelcontextprotocol.io/v0.1/servers?search=sentry&version=latest" \
    | jq '.servers[]
    | select(.server.name | startswith("io.github.getsentry/"))
    | {name: .server.name, version: .server.version,
    repo: .server.repository.url,
    packages: [.server.packages[]?.identifier],
    remotes: [.server.remotes[]?.url]}'

    On 2026-09-26 the vendor’s entry was io.github.getsentry/sentry-mcp 0.42.0, with the npm package @sentry/mcp-server and the remote https://mcp.sentry.dev/mcp. Remove the select line to see every other result for sentry: each one is a candidate for your denylist, not your allowlist.

  2. Confirm the identity outside the registry. The repository.url must belong to the vendor’s GitHub organization, and the vendor’s own README must name the same package or URL. Check the package itself with npm view <package> name version repository.url deprecated or https://pypi.org/pypi/<package>/json.

  3. Prefer the remote URL when the vendor offers one. A vendor-hosted HTTP server with OAuth runs no code on the laptop, and URL patterns are easier to enforce than exact commands. Record the read-only variant where one exists, such as GitHub’s https://api.githubcopilot.com/mcp/readonly.

  4. Scan and classify. Run the scan from securing MCP servers and mark the server read-only or write-capable. Write-capable servers that touch production need a named security sign-off.

  5. Record the decision in the catalog (next section), with the evidence: registry name, repository, pinned version, transport, read-only switch and approver.

Build the approved catalog as your private registry

Section titled “Build the approved catalog as your private registry”

For most organizations, the private registry is a reviewed file in a policy repository, not a server. It is the single source for every tool’s configuration, and a pull request is the approval record.

# mcp-catalog/servers.yaml — one entry per approved server
- name: github
registry_name: io.github.github/github-mcp-server
repository: https://github.com/github/github-mcp-server
transport: http
url: https://api.githubcopilot.com/mcp/readonly
version: remote # vendor-hosted; nothing to pin
read_only: true
evidence: "registry entry + vendor README checked 2026-09-26"
approver: "@acme/platform-security"
- name: sentry
registry_name: io.github.getsentry/sentry-mcp
repository: https://github.com/getsentry/sentry-mcp
transport: http
url: https://mcp.sentry.dev/mcp
version: 0.42.0 # registry entry and npm @sentry/mcp-server, 2026-09-26
read_only: false # write tools listed in the approving pull request
evidence: "registry entry + npm view @sentry/mcp-server, 2026-09-26"
approver: "@acme/platform-security"
- name: playwright
registry_name: io.github.microsoft/playwright-mcp
repository: https://github.com/microsoft/playwright-mcp
transport: stdio
command: [npx, "@playwright/mcp@0.0.82"]
version: 0.0.82
read_only: true
evidence: "npm view @playwright/mcp version, 2026-09-26"
approver: "@acme/frontend-leads"

Give developers the exact install command next to each entry. Each line below matches the Claude Code allowlist and the Codex identity in the next section: the URLs fit their patterns, and the commands match argument by argument.

Terminal window
# terminal; Claude Code
claude mcp add --transport http github https://api.githubcopilot.com/mcp/readonly
claude mcp add --transport http sentry https://mcp.sentry.dev/mcp
claude mcp add playwright -- npx @playwright/mcp@0.0.82
# terminal; Codex
codex mcp add github --url https://api.githubcopilot.com/mcp/readonly
codex mcp add sentry --url https://mcp.sentry.dev/mcp
codex mcp add playwright -- npx @playwright/mcp@0.0.82

In Codex, the requirement key must match the name the developer uses, so name is part of the contract, not a label.

Two other catalog forms are worth knowing:

  • A plugin marketplace. Claude Code has no built-in MCP registry that users browse. Anthropic’s managed-MCP documentation suggests distributing approved servers as plugins in a team plugin marketplace so developers install them from /plugin.
  • A subregistry. If you run many internal servers, host an aggregator that copies the public registry hourly with updated_since, filters it to your approved names and adds your own servers. Publishing your own servers to the public registry is covered in building your own MCP server.

Every tool enforces the list at a different point, with different matching rules. Render all three files from servers.yaml, and gate them in CI with the parity check from enforcing one policy across every coding agent.

Put the allowlist in a managed settings source: server-managed settings from the claude.ai admin console, a deployed managed-settings.json, an MDM profile or a registry policy. allowManagedMcpServersOnly: true stops users from widening the list in their own settings; denylists still merge from every scope.

{
"allowManagedMcpServersOnly": true,
"allowedMcpServers": [
{ "serverUrl": "https://api.githubcopilot.com/mcp/readonly" },
{ "serverUrl": "https://mcp.sentry.dev/*" },
{ "serverCommand": ["npx", "@playwright/mcp@0.0.82"] }
],
"deniedMcpServers": [
{ "serverUrl": "https://*.untrusted.example.com/*" }
]
}

The matching rules decide whether the list is real:

  • Once the list has one serverUrl entry, every remote server must match a URL pattern; once it has one serverCommand entry, every stdio server must match a command exactly, argument by argument. ["npx", "-y", "server"] does not match ["npx", "server"].
  • A serverName entry is not a security control, because users choose the name.
  • A denylist match always wins.
  • To push remote servers to everyone without taking exclusive control, list them under managedMcpServers (v2.1.259 or later, per the Claude Code managed-MCP documentation and changelog, checked 2026-09-26). Entries must be https:// HTTP or SSE servers with no command, and they load without an allowlist entry.
  • For a fixed set, deploy managed-mcp.json to /Library/Application Support/ClaudeCode/ (macOS), /etc/claude-code/ (Linux and WSL) or C:\Program Files\ClaudeCode\ (Windows). It cannot be delivered through server-managed settings.

A gateway is a process that sits between the agent and the MCP servers: the agent connects to one entry, and the gateway starts or proxies the servers behind it. Add one when you need at least one of these:

  • One entry point for several tools. One profile serves Claude Code, Cursor and other clients, so the tool-level allowlist shrinks to one gateway entry.
  • Isolation. Stdio servers run in containers instead of in the developer’s session.
  • Central controls. Secret scanning on arguments and responses, signature checks on server images, and call logs in one place.

Docker MCP Gateway is the verified option on this site. As of 2026-09-26, docker/mcp-gateway had 1.6k GitHub stars and its catalog repository docker/mcp-registry had 558 stars and 1.5k forks from server submissions (GitHub). Its gateway run defaults matter for audit:

FlagDefaultWhat it does
--log-callsonLogs each tool call with the tool name and argument shape only; raw argument values are not logged
--block-secretsonScans tool-call arguments and text responses for secret-like values before and after execution
--verify-signatureson for Docker MCP imagesRequires images referenced by digest and verifies them before pull or run
--block-networkoffRestricts the servers’ network access further
Terminal window
# terminal; vendor lines from the docker/mcp-gateway README
docker mcp catalog pull mcp/docker-mcp-catalog
docker mcp profile create --name dev-tools \
--server catalog://mcp/docker-mcp-catalog/github
docker mcp client connect claude-code --profile dev-tools --global

Then allowlist the gateway command itself, exactly as the client runs it, so no other stdio server gets through:

{ "serverCommand": ["docker", "mcp", "gateway", "run", "--profile", "dev-tools"] }

This entry is an example. Read the command that docker mcp client connect actually wrote (claude mcp list, then claude mcp get <name>) and copy that into the allowlist, because the entry must match argument by argument.

Outside Docker Desktop, run docker mcp feature enable profiles first. Profiles can be pushed to and pulled from an OCI registry, which is how one reviewed profile reaches every machine.

Two other gateway routes appear in the research for this site but were not tested here: the LiteLLM proxy lists an MCP gateway alongside its model gateway, and a Claude apps gateway policy is one of the managed sources Claude Code reads managedMcpServers from. Verify either against its own documentation before you rely on it.

A gateway also changes context cost: one entry can expose every tool in its profile. Keep profiles per team, measure with /context in Claude Code before and after you connect it, and use the techniques in reducing MCP token cost when the tool list grows.

Enforcement says what may run; audit says what did. Collect both signals in one place.

  • Claude Code telemetry. With OpenTelemetry export configured, set OTEL_LOG_TOOL_DETAILS=1 so tool events include MCP server and tool names. Aggregate them by server to see real usage per team.
  • Gateway logs. Ship the gateway’s call log to the same collector and alert on calls to tools outside the profile you expect.
  • Upstream audit logs. For write-capable servers, the service’s own audit log is the record of what changed.

How do you prove the allowlist holds on every machine?

Section titled “How do you prove the allowlist holds on every machine?”

Nobody reads every tool call. Check the configuration mechanically on a canary machine per platform, then on a sample of the fleet each release.

  1. Negative test in Claude Code. With managed-mcp.json deployed, claude mcp add --transport http test https://example.com/mcp must fail with Cannot add MCP server: enterprise MCP configuration is active and has exclusive control over MCP servers. Under an allowlist, adding an unlisted server must fail with not allowed by enterprise policy. The URL does not need to exist; the policy check rejects the command first.

  2. Inventory check. claude mcp list shows only catalog servers. If a user’s own server still appears under managed-mcp.json, Claude Code is not reading the file: check the path and its directory permissions. /status shows managedMcpServers entries it dropped and why.

  3. Name-mismatch test in Codex. Register an approved URL under a wrong name and confirm Codex disables it. This proves the requirements file is loaded, not only that the right servers happen to be configured.

  4. Gateway check. Call one tool through the gateway and confirm the call appears in the collector with the team and tool name.

  5. Sign-off. The tech lead owns the catalog and approves additions; the security owner signs off every write-capable server with production reach; the platform team owns deployment and the checks above. Record each release’s results in the policy repository.

The measure to report upward is enforcement coverage: the share of sampled machines where the negative tests pass, per tool. Anything below 100% is a deployment bug, not a policy question.

What breaks when you roll out an MCP allowlist?

Section titled “What breaks when you roll out an MCP allowlist?”

Developers lose a server with no message. Claude Code removes blocked servers from /mcp silently. Recovery: publish the catalog and the removal list before rollout, and point people at the negative-test message so they recognise policy.

Codex disables an approved server. The name differs from the requirement key, or the arguments differ (npx -y … against an identity without -y). Recovery: publish the exact codex mcp add line per catalog entry and render requirements from the same file.

CI jobs fail at startup. A job that passes --mcp-config on a machine with managed-mcp.json exits with You cannot dynamically configure MCP servers when an enterprise MCP config is present. Recovery: give CI runners their own managed file that contains the servers the job needs.

A managed server from settings never loads. managedMcpServers rejects http:// URLs, command entries and ${VAR} references, and Claude Code before v2.1.259 ignores the key. Recovery: read /status, fix the entry, and set a minimum client version in your rollout.

Users widened the list anyway. allowManagedMcpServersOnly is missing, so user-level allowlists merged in. Recovery: add the flag. With more than one managed source, reading the lock across sources needs v2.1.273 or later, according to the Claude Code managed-MCP documentation (checked 2026-09-26).

A gateway profile change shipped without review. Someone added a server to the shared profile, and every client picked it up. Recovery: keep profiles in the policy repository, push them to the OCI registry from CI only, and treat a profile diff like an allowlist diff.