Blog • Claude Code / MCP
Après Claude Code 2.1.318 avec deniedMcpServers URL dans managed settings : logs JSON pour MCP hébergé Anthropic et connectors cloud ?
Le 4 novembre 2026, Anthropic a livré Claude Code 2.1.318. serverUrl dans deniedMcpServers refuse aussi le MCP hébergé Anthropic via upstreamUrl ; les connectors cloud et self-hosted runner comparent connectorUrl d abord, sinon serverName.
Métadonnées serveur session : serverKind, upstreamUrl, connectorUrl, serverName, sessionSurface. Appels tool MCP refusés : decision denied, reason mcp_server_denied, matchedRule, matchedPattern, upstreamUrl. mcp_url_deny_gate reflète schema, deny_by_url, deny_by_connector_url, deny_by_name, allow sans mélanger serverCommand env-only.
Le deny serverCommand est dans allowlist env MCP et serverCommand; la baseline sécurité MCP dans sécurité MCP; les arrêts timeout elicitation non interactifs dans JSON timeout elicitation MCP. Cet article ne couvre que l extension deniedMcpServers URL 2.1.318, JSON settings gérés, métadonnées serveur de session, appel tool MCP, champs audit mcp_server_denied et cinq branches mcp_url_deny_gate.
- deniedMcpServers géré peut mélanger globs serverUrl et serverName ; dès 2.1.318 serverUrl correspond aussi à upstreamUrl du MCP hébergé Anthropic.
- Le JSON métadonnées session exige serverKind, upstreamUrl, connectorUrl, serverName, sessionSurface ; champs manquants ou enum → schema avant match URL ou nom.
- Logs branche deny : decision denied, reason mcp_server_denied, matchedRule, matchedPattern, upstreamUrl ; SIEM agrège par matchedRule sans mélanger deny serverCommand.
- En cloud et self-hosted runner, connectorUrl contre serverUrl d abord, sinon serverName pour deny_by_name ; aucun match → allow.
Gardez ceci : 2.1.318 étend le matching deniedMcpServers URL pour MCP hébergé et connectors cloud plus champs mcp_server_denied ; ne réécrit pas la table deny serverCommand env-only. Ensuite : changelog, JSON gérés, métadonnées, appel MCP, table audit, mcp_url_deny_gate, JSONNote.
Comment 2.1.318 étend deniedMcpServers URL au MCP hébergé et aux connectors cloud
Les notes de version sont dans changelog Claude Code, page tag version v2.1.318. Avant surtout MCP configuré par l utilisateur ; 2.1.318 applique serverUrl à upstreamUrl du MCP hébergé Anthropic ; connectors cloud et self-hosted runner comparent connectorUrl d abord, sinon serverName.
Refus avant l appel tool MCP. Audit : decision denied, reason mcp_server_denied, matchedRule, matchedPattern, upstreamUrl pour attribution SIEM à la règle URL.
Comment écrire serverUrl et serverName dans deniedMcpServers géré
Dans settings gérés, deniedMcpServers accepte globs serverUrl et serverName. Dès 2.1.318 serverUrl correspond à upstreamUrl MCP hébergé Anthropic et connectorUrl cloud.
{
"deniedMcpServers": [
{
"serverUrl": "https://mcp.example.com/v1/*"
},
{
"serverName": "slack-connector"
}
]
}
serverUrl non string ou deniedMcpServers pas tableau → schema d abord, pas allow si forme mauvaise.
Comment upstreamUrl et sessionSurface apparaissent dans le JSON métadonnées serveur
Lors de la résolution MCP, le client écrit métadonnées : serverKind, upstreamUrl, connectorUrl, serverName, sessionSurface.
{
"serverKind": "anthropic_hosted",
"upstreamUrl": "https://mcp.example.com/v1/tools",
"connectorUrl": "https://connect.example.com/slack",
"serverName": "analytics-mcp",
"sessionSurface": "cloud"
}
upstreamUrl et connectorUrl non vides ; sessionSurface dans enum ; serverKind manquant → schema ; pas de règle → allow.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"additionalProperties": false,
"required": ["serverKind", "upstreamUrl", "connectorUrl", "serverName", "sessionSurface"],
"properties": {
"serverKind": { "type": "string", "enum": ["anthropic_hosted", "user_configured", "connector"] },
"upstreamUrl": { "type": "string", "minLength": 1 },
"connectorUrl": { "type": "string", "minLength": 1 },
"serverName": { "type": "string", "minLength": 1 },
"sessionSurface": { "type": "string", "enum": ["cloud", "local", "self_hosted_runner"] }
}
}
Comment le JSON appel tool MCP se compare aux métadonnées sans règle
Si upstreamUrl correspond à serverUrl, JSON tool mcp__analytics-mcp__search ; pas d exécution, decision denied. CALL_OUTSIDE → allow.
{
"tool": "mcp__analytics-mcp__search",
"arguments": {
"query": "quarterly revenue"
}
}
{
"serverKind": "anthropic_hosted",
"upstreamUrl": "https://other.example.com/v1/tools",
"connectorUrl": "https://connect.example.com/other",
"serverName": "other-mcp",
"sessionSurface": "cloud"
}
Comment decision denied et mcp_server_denied apparaissent dans les logs d audit
Si upstreamUrl ou connectorUrl correspond : decision denied, reason mcp_server_denied, matchedRule, matchedPattern, upstreamUrl ; connectorUrl pour cloud. Ne pas mélanger deny serverCommand env-only et URL dans une enum reason.
{
"decision": "denied",
"reason": "mcp_server_denied",
"matchedRule": "serverUrl",
"matchedPattern": "https://mcp.example.com/v1/*",
"upstreamUrl": "https://mcp.example.com/v1/tools"
}
| Signal session ou URL | mcp_url_deny_gate renvoie | Action opérateur |
|---|---|---|
| upstreamUrl correspond à une règle glob serverUrl | deny_by_url | Aligner sur decision denied, matchedRule serverUrl, matchedPattern, upstreamUrl. |
| connectorUrl correspond à serverUrl, pas upstream | deny_by_connector_url | Logger matchedRule serverUrl et garder connectorUrl pour attribution cloud. |
| serverName correspond à une règle de nom deniedMcpServers | deny_by_name | Écrire reason mcp_server_denied et matchedRule serverName. |
| Mauvaise forme JSON métadonnées ou liste deny absente | schema | Corriger settings gérés ou ctx avant branches deny. |
Comment mcp_url_deny_gate choisit entre schema et cinq branches deny
Ordre : CTX_SCHEMA pour serverKind, upstreamUrl, connectorUrl, serverName, sessionSurface ; parcourir deniedMcpServers ; upstreamUrl correspond à serverUrl → deny_by_url ; connectorUrl correspond → deny_by_connector_url ; serverName correspond → deny_by_name ; sinon allow.
Ne fusionnez pas deny_by_url et deny_by_connector_url en un mcp_denied. deny_by_connector_url pour connectors cloud, deny_by_url pour MCP hébergé, deny_by_name pour noms.
def actual_type(data: object) -> str:
if data is None:
return "null"
if isinstance(data, bool):
return "boolean"
if isinstance(data, list):
return "array"
if isinstance(data, int):
return "integer"
if isinstance(data, float):
return "number"
if isinstance(data, str):
return "string"
if isinstance(data, dict):
return "object"
raise TypeError(type(data))
def validate(data: object, schema: dict) -> list[str]:
errors: list[str] = []
if not isinstance(schema, dict):
return errors
types = schema.get("type")
if isinstance(types, str):
types = [types]
actual = actual_type(data)
if types and not any(t == actual or (t == "number" and actual == "integer") for t in types):
errors.append("type")
if "enum" in schema and not any(value == data for value in schema["enum"]):
errors.append("enum")
if isinstance(data, str) and "minLength" in schema and len(data) < schema["minLength"]:
errors.append("minLength")
if isinstance(data, dict) and schema.get("properties"):
for key in schema.get("required") or []:
if key not in data:
errors.append(f"required:{key}")
if schema.get("additionalProperties") is False:
for key in data:
if key not in schema["properties"]:
errors.append(f"additional:{key}")
for key, sub in schema["properties"].items():
if key in data and data[key] is not None:
errors.extend(validate(data[key], sub))
return errors
def url_match(url: str, pattern: str) -> bool:
return fnmatch(url, pattern)
def mcp_url_deny_gate(ctx: dict, deny_doc: dict, schema: dict) -> str:
if validate(ctx, schema):
return "schema"
entries = deny_doc.get("deniedMcpServers")
if not isinstance(entries, list):
return "schema"
upstream = ctx.get("upstreamUrl", "")
connector = ctx.get("connectorUrl", "")
name = ctx.get("serverName", "")
for entry in entries:
if not isinstance(entry, dict):
continue
pat = entry.get("serverUrl")
if isinstance(pat, str):
if upstream and url_match(upstream, pat):
return "deny_by_url"
if connector and url_match(connector, pat):
return "deny_by_connector_url"
rule_name = entry.get("serverName")
if isinstance(rule_name, str) and rule_name and rule_name == name:
return "deny_by_name"
return "allow"
Comment valider settings gérés, métadonnées session et logs d audit dans JSONNote
- 1Fixer la forme deny avec Schema
Ouvrez JSON Schema. Schema deniedMcpServers au premier champ, serverUrl et serverName au second. Erreurs required ou type d abord ; pas deny_by_url ou allow si forme mauvaise.
- 2Format pour les appels tool MCP
Ouvrez JSON Format, collez JSON appel tool MCP. Vérifiez tool et arguments avant deny_by_url ou allow pour mcp_url_deny_gate.
- 3Diff logs allow et denied
Ouvrez JSON Diff. À gauche échantillon allow CALL_OUTSIDE, à droite decision denied et reason mcp_server_denied. matchedPattern surligné est la clé SIEM.
Questions fréquentes
En quoi les règles serverUrl diffèrent du refus MCP configuré seul ?
Avant serverUrl visait surtout le MCP HTTP configuré. Dès 2.1.318 aussi upstreamUrl MCP hébergé Anthropic et connectorUrl puis serverName.
Comment les logs distinguent deny_by_url de deny_by_connector_url ?
deny_by_url : matchedRule serverUrl et upstreamUrl. deny_by_connector_url : connectorUrl correspond, upstream peut manquer, garder connectorUrl et sessionSurface.
Quelle règle passe en premier, serverName ou serverUrl ?
mcp_url_deny_gate parcourt serverUrl d abord : upstreamUrl→deny_by_url, connectorUrl→deny_by_connector_url, puis serverName→deny_by_name. Pas de glob URL dans serverName.
Quel lien entre deny URL et deny serverCommand env-only ?
serverCommand pour stdio et env ne remplace pas le deny upstream serverUrl. JSON séparés ; mcp_url_deny_gate reflète URL et serverName seulement.
Cet article est-il un tutoriel d attaque ou de contournement ?
Non. L article explique seulement les formes JSON de l extension deniedMcpServers URL 2.1.318, champs mcp_server_denied et cinq branches mcp_url_deny_gate. Pas d étapes de contournement via URL ou connector.
Conclusion
Claude Code 2.1.318 étend le matching deniedMcpServers URL et écrit decision denied, reason mcp_server_denied, matchedRule, matchedPattern, upstreamUrl dans l audit. Pilotez mcp_url_deny_gate avec settings gérés, métadonnées, appels MCP et audit denied.
Décision : JSON métadonnées échoue schema → schema ; upstreamUrl correspond serverUrl → deny_by_url ; connectorUrl correspond serverUrl → deny_by_connector_url ; serverName → deny_by_name ; sinon allow ; audit deny aligné sur decision denied, reason mcp_server_denied, matchedRule, matchedPattern, upstreamUrl.
Ensuite : Schema pour deniedMcpServers et métadonnées, Format pour MCP, Diff logs allow et denied, compter séparément deny_by_url, deny_by_connector_url, deny_by_name et allow.