What
allowedMcpServers and deniedMcpServers predicates are no longer matched literally; stdio serverCommand entries and remote serverUrl patterns go through environment-variable expansion first, with a safety analysis that discards allowlist entries whose expansion changes what the pattern means.
Details
- Denylist expansion uses a merged env drawn from global config, user, flag and policy settings as fallbacks; the allowlist path uses the settings-derived env.
- Substituted values are normalised through a placeholder that returns the constant
zzenvsubzz(or a digit-zeroed form for numeric values,::for IPv6-looking ones) so the expanded URL can be re-parsed and compared against the original. - Flagged as unsafe: an injected value that adds wildcard semantics, changes the URL's structure or hostname, or introduces traversal or query/fragment characters.
- Allowlist URL entries with an unsafe expansion are skipped, so they fail closed; denylist entries still apply.
- Missing variables emit a debug warning that the predicate references environment variables not present in the policy expansion env; unsafe expansions log that the expansion was unsafe.
- Neither the placeholder nor the wildcardVars analysis exists in v2.1.218, and the settings schema descriptions were not updated to mention any of it.
Evidence
zzenvsubzz, MCP policy URL predicate expansion was unsafe
Strings lifted out of the shipped bundle, so the claim above can be checked against them.
One source agreesOne thing we can check says the same as this entry.
Anthropic's release notes agree
Changed managed MCP allowlist/denylist ${VAR} entries to resolve from the startup environment and managed-settings env instead of…