Loads configuration and credentials from various sources into environment variables. Uses per-variable precedence - each variable is loaded from the highest priority source. Priority order (highest to lowest): 1. Runtime environment variables (shell/CI) 2. Local `.codegraphconte
(cli_context_flag: Optional[str] = None)
| 295 | # ============================================================================ |
| 296 | |
| 297 | def _load_credentials(cli_context_flag: Optional[str] = None): |
| 298 | """ |
| 299 | Loads configuration and credentials from various sources into environment variables. |
| 300 | Uses per-variable precedence - each variable is loaded from the highest priority source. |
| 301 | Priority order (highest to lowest): |
| 302 | 1. Runtime environment variables (shell/CI) |
| 303 | 2. Local `.codegraphcontext/.env` and `.env` in the current project directory (per-repo mode only) |
| 304 | 3. Global `~/.codegraphcontext/.env` (user defaults, including `cgc config set`) |
| 305 | 4. Local `mcp.json` env vars (project defaults) |
| 306 | |
| 307 | Duplicate loading is skipped when the local file resolves to the same path as the global file. |
| 308 | Arbitrary parent directory `.env` files are not loaded—ensuring isolation. |
| 309 | """ |
| 310 | from dotenv import dotenv_values |
| 311 | from codegraphcontext.cli.config_manager import ( |
| 312 | ensure_config_dir, |
| 313 | codegraphcontext_dotenv_at_cwd, |
| 314 | normalize_config_path, |
| 315 | ) |
| 316 | |
| 317 | # Ensure config directory exists (lazy initialization) |
| 318 | ensure_config_dir() |
| 319 | |
| 320 | # Snapshot runtime environment BEFORE merging config files. |
| 321 | # These values must remain highest priority. |
| 322 | runtime_env = dict(os.environ) |
| 323 | |
| 324 | # Collect all config sources in precedence order (lowest to highest) |
| 325 | config_sources = [] |
| 326 | config_source_names = [] |
| 327 | key_source_map = {} |
| 328 | key_defined_in = {} |
| 329 | |
| 330 | def _append_source(source_name: str, source_values: dict): |
| 331 | if not source_values: |
| 332 | return |
| 333 | config_sources.append(source_values) |
| 334 | config_source_names.append(source_name) |
| 335 | for k, v in source_values.items(): |
| 336 | if v is None: |
| 337 | continue |
| 338 | key_source_map[k] = source_name |
| 339 | key_defined_in.setdefault(k, []).append(source_name) |
| 340 | |
| 341 | # 4. Local mcp.json (lowest priority - project defaults) |
| 342 | mcp_file_path = Path.cwd() / "mcp.json" |
| 343 | if mcp_file_path.exists(): |
| 344 | try: |
| 345 | with open(mcp_file_path, "r", encoding="utf-8", errors="replace") as f: |
| 346 | mcp_config = json.load(f) |
| 347 | server_env = mcp_config.get("mcpServers", {}).get("CodeGraphContext", {}).get("env", {}) |
| 348 | if isinstance(server_env, dict): |
| 349 | normalized_env = {} |
| 350 | for env_key, env_value in server_env.items(): |
| 351 | if env_value is not None and "PATH" in env_key: |
| 352 | normalized_env[env_key] = normalize_config_path(str(env_value), absolute=True) |
| 353 | else: |
| 354 | normalized_env[env_key] = env_value |