Skip to main content
Once Enforce hooks are installed, the Enforce policy governs which unmanaged tool sources are allowed and which built-in client tools are blocked. This page explains each Enforce policy setting.

Where to configure it

Admins edit the policy under SettingsTool governanceEnforce. Editing requires the Manage org settings capability. The Enforce editor has four groups — Allowed remote MCP URLs, Allowed local (STDIO) packages, Allowed built-in MCP servers, and Blocked built-in tools. The page saves Protect denylists and Enforce settings together; unchanged groups are preserved exactly as fetched.
All source rules are fail-closed: anything Enforce cannot positively match to a Runlayer-managed source, an allowlist entry, or a permitted built-in is blocked. Widening what runs always means adding an allowlist entry, never removing a block.

Allowed remote MCP URLs

Enforce blocks remote (HTTP/S) MCP servers that do not point at your Runlayer instance. Add allowed remote MCP URLs to permit specific external endpoints. You can configure up to 50 entries. Each entry is a base URL. A tool call’s MCP URL is allowed when:
  • The scheme, host, and port match the entry (default ports 443 / 80 for HTTPS / HTTP are treated the same as omitting the port).
  • If the entry has no path (or only /), any path on that origin is allowed.
  • If the entry includes a path (for example https://partner.example.com/mcp), the MCP URL’s path must match that prefix with a path boundary/mcp and /mcp/stream match; /mcpbackup does not.

Allowed local (STDIO) packages

Enforce also blocks local (stdio) MCP servers — those launched by a command such as npx ... or uvx ... — unless the launched package is on your stdio package allowlist. This lets you permit specific published MCP packages without permitting arbitrary local command execution. The editor accepts up to 100 entries.
Local commands are trivially spoofable, so Enforce never allowlists a raw command string. Instead it extracts a canonical package identity from the command and matches that against the list. A command that does not resolve to a launcher-backed package identity is denied.

Package identity

Each entry is a canonical package identity: an ecosystem (npm or PyPI), a name, and an optional version pin.
  • Ecosystem + name must match. Names are normalized before comparison (see Name normalization).
  • Version is optional. An entry with no version matches any version; an entry pinned to a version matches only that version. A versionless launch command can never satisfy a pinned entry.

Recognized launchers

Only ecosystem launchers resolve to a package identity. Every other program — including raw binary paths (/usr/local/bin/server), interpreters (node build/index.js, python -m server), and unknown launchers — has no package identity and is blocked.
Other package runners — pnpm dlx, bunx, yarn dlx — and container launchers like docker run are not recognized, so servers launched through them cannot be allowlisted and are always blocked in Enforce. Relaunch the server through npx or uvx (or run it as a Runlayer-managed server) to allow it.

Which argument is the package

Enforce matches the package the launcher actually installs and runs — normally the first argument after the launcher’s own flags. If an explicit package flag is present (npx --package foo bar, uvx --from foo bar), that flag’s value is the package. Commands Enforce cannot resolve to a single package are blocked (see Always blocked).

Version pinning

Leave the version blank on an entry to allow any version.

Name normalization

Names are normalized so stored entries match what the parser produces at hook time:
  • npm — lowercased. The leading @ of a scoped name is part of the name, not a version separator.
  • PyPIPEP 503 canonical form: lowercased, with runs of -, _, and . collapsed to a single -. So mcp_server_fetch, MCP.Server.Fetch, and mcp-server-fetch are all the same package.

Always blocked

These never resolve to a single package identity and are always denied, regardless of the allowlist:
  • Arbitrary-command launchersnpx --call / -c run an arbitrary command string, not a package.
  • Unrecognized launcher flags before the command — Enforce cannot safely tell which argument is the package, so it fails closed. Put npm config in .npmrc or environment variables rather than inline flags.
  • Shell injection — commands with hidden shell operators, subshells, or injection patterns.
  • Unparseable or empty commands.

Worked examples

What name-based allowlisting covers

The allowlist identifies packages by name and, optionally, version. It does not verify package contents, so continue using your existing dependency and supply-chain controls alongside Enforce.

Promote a discovered package (one-click)

When Detect discovers a local stdio MCP whose command resolves to a package identity, you can Add to allowlist in one click from the sighting’s detail drawer — Enforce appends the parsed identity to the stdio allowlist. This requires the Manage org settings capability. The same one-click Add to allowlist action is available for discovered remote MCP URLs.

Blocked built-in tools

Beyond MCP sources, Enforce can deny built-in client tools by behavior type. Toggle a type on to block every built-in tool of that type across supported clients: These are behavior-level switches — each blocks a whole category of built-in tool. There is no per-path or per-command rule layer; for content-based decisions on the calls that remain allowed, use your scanner configuration. Types you do not toggle on are allowed by default, and a built-in tool whose name does not map to one of these governed types is also allowed by default — it is not covered by this control, though scanner decisions still apply to it.
This is part of local tool lifecycle enforcement, so it requires Full session scanning APIs and the target Hook client enabled under SettingsAgent session monitoring. Shadow MCP source blocking (the remote-URL, stdio, and built-in dimensions) stays active without full session scanning. See Enforcement paths.

Built-in MCP servers

Some clients ship non-configurable, built-in MCP servers that never appear in any config file — for example GitHub Copilot CLI’s github-mcp-server, playwright, fetch, time, and computer-use. The client resolves these internally, so the hook identifies them by (client, built-in server name) rather than by URL or command. Enforce ships a safe default allowlist of well-known built-ins per client, so these keep working out of the box: In the Allowed built-in MCP servers group:
  • Keep Use Runlayer safe defaults on (the default) to trust the built-ins above.
  • Turn it off to define a custom list — seeded from the defaults so you can prune or extend it — that replaces the defaults entirely. Each row is a (client, built-in server name) pair.
  • Clear the custom list to block all built-in MCP servers.
A missing client or built-in server name never matches, so an unidentifiable built-in is blocked like any other unmatched source.
Claude.ai account connectors are allowed through. A connector the user has attached to their Claude account on claude.ai has no URL or local command on the device — Claude Code reports it by connector source and name. Enforce allows these connector calls rather than blocking them; govern them through your Claude.ai workspace controls.

Protect policy

Configure allow-unless-denied MCP source governance for Protect

Enforce

How Enforce intercepts calls, deployment, and troubleshooting

Detect

Discover shadow servers and promote them to the allowlist

Policies

Configure access control policies for managed MCPs

Deploy AI Watch

Install the package and turn on Enforce