diff --git a/mcp_bridge/config/__init__.py b/mcp_bridge/config/__init__.py index 8e7c7ed..17e5517 100644 --- a/mcp_bridge/config/__init__.py +++ b/mcp_bridge/config/__init__.py @@ -1,3 +1,4 @@ +from mcp_bridge.config.env_subst import substitute_env_vars from mcp_bridge.config.initial import initial_settings from mcp_bridge.config.final import Settings from typing import Any, Callable @@ -38,6 +39,8 @@ for cfg in configs: always_merger.merge(result, cfg) + result = substitute_env_vars(result) + # build the config try: config = Settings(**result) diff --git a/mcp_bridge/config/env_subst.py b/mcp_bridge/config/env_subst.py new file mode 100644 index 0000000..7f6b9e3 --- /dev/null +++ b/mcp_bridge/config/env_subst.py @@ -0,0 +1,31 @@ +from string import Template +from typing import Any +import os + +from loguru import logger + + +def substitute_env_vars(config: Any, env: dict[str, str] | None = None) -> Any: + """Substitute environment variables in a configuration object.""" + + # copy the environment if it is not provided + if env is None: + env = os.environ.copy() + + assert env is not None, "env is None" # the guard should have caught this + + # handle strings + if isinstance(config, str): + return Template(config).safe_substitute(env) + + # handle other types + elif isinstance(config, dict): + return { + k: substitute_env_vars(v, env) for k, v in config.items() if v is not None + } + + # handle lists + elif isinstance(config, list): + return [substitute_env_vars(v, env) for v in config] + + return config