import re from typing import Any, Dict _MCP_SERVER_NAME_RE = re.compile(r"[A-Za-z0-9][A-Za-z0-9._-]{0,63}") def _normalize_mcp_servers(raw: Any) -> Dict[str, Dict[str, Any]]: if not isinstance(raw, dict): return {} rows: Dict[str, Dict[str, Any]] = {} for server_name, server_cfg in raw.items(): name = str(server_name or "").strip() if not name or not _MCP_SERVER_NAME_RE.fullmatch(name): continue if not isinstance(server_cfg, dict): continue url = str(server_cfg.get("url") or "").strip() if not url: continue transport_type = str(server_cfg.get("type") or "streamableHttp").strip() if transport_type not in {"streamableHttp", "sse"}: transport_type = "streamableHttp" headers_raw = server_cfg.get("headers") headers: Dict[str, str] = {} if isinstance(headers_raw, dict): for key, value in headers_raw.items(): header_key = str(key or "").strip() if not header_key: continue headers[header_key] = str(value or "").strip() timeout_raw = server_cfg.get("toolTimeout", 60) try: timeout = int(timeout_raw) except Exception: timeout = 60 rows[name] = { "type": transport_type, "url": url, "headers": headers, "toolTimeout": max(1, min(timeout, 600)), } return rows def _merge_mcp_servers_preserving_extras( current_raw: Any, normalized: Dict[str, Dict[str, Any]], ) -> Dict[str, Dict[str, Any]]: current_map = current_raw if isinstance(current_raw, dict) else {} merged: Dict[str, Dict[str, Any]] = {} for name, normalized_cfg in normalized.items(): base = current_map.get(name) base_cfg = dict(base) if isinstance(base, dict) else {} next_cfg = dict(base_cfg) next_cfg.update(normalized_cfg) merged[name] = next_cfg return merged def _sanitize_mcp_servers_in_config_data(config_data: Dict[str, Any]) -> Dict[str, Dict[str, Any]]: if not isinstance(config_data, dict): return {} tools_cfg = config_data.get("tools") if not isinstance(tools_cfg, dict): tools_cfg = {} current_raw = tools_cfg.get("mcpServers") normalized = _normalize_mcp_servers(current_raw) merged = _merge_mcp_servers_preserving_extras(current_raw, normalized) tools_cfg["mcpServers"] = merged config_data["tools"] = tools_cfg return merged