Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 17 additions & 9 deletions linkedin_mcp_server/config/loaders.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,13 @@
logger = logging.getLogger(__name__)

# Boolean value mappings for environment variable parsing
TRUTHY_VALUES = ("1", "true", "True", "yes", "Yes")
FALSY_VALUES = ("0", "false", "False", "no", "No")
TRUTHY_VALUES = ("1", "true", "yes", "on")
FALSY_VALUES = ("0", "false", "no", "off")


def _normalize_env(value: str) -> str:
"""Normalize environment variable values for tolerant parsing."""
return value.strip().lower()


def positive_int(value: str) -> int:
Expand Down Expand Up @@ -67,24 +72,27 @@ def load_from_env(config: AppConfig) -> AppConfig:

# Log level
if log_level_env := os.environ.get(EnvironmentKeys.LOG_LEVEL):
log_level_upper = log_level_env.upper()
log_level_upper = log_level_env.strip().upper()
if log_level_upper in ("DEBUG", "INFO", "WARNING", "ERROR"):
config.server.log_level = cast(
Literal["DEBUG", "INFO", "WARNING", "ERROR"], log_level_upper
)

# Headless mode
if os.environ.get(EnvironmentKeys.HEADLESS) in FALSY_VALUES:
config.browser.headless = False
elif os.environ.get(EnvironmentKeys.HEADLESS) in TRUTHY_VALUES:
config.browser.headless = True
if headless_env := os.environ.get(EnvironmentKeys.HEADLESS):
headless_value = _normalize_env(headless_env)
if headless_value in FALSY_VALUES:
config.browser.headless = False
elif headless_value in TRUTHY_VALUES:
config.browser.headless = True

# Transport mode
if transport_env := os.environ.get(EnvironmentKeys.TRANSPORT):
config.server.transport_explicitly_set = True
if transport_env == "stdio":
transport_value = _normalize_env(transport_env)
if transport_value == "stdio":
config.server.transport = "stdio"
elif transport_env == "streamable-http":
elif transport_value == "streamable-http":
config.server.transport = "streamable-http"
else:
raise ConfigurationError(
Expand Down
51 changes: 51 additions & 0 deletions tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,48 @@ def test_load_from_env_headless_true(self, monkeypatch):
config = load_from_env(AppConfig())
assert config.browser.headless is True

def test_load_from_env_headless_true_with_whitespace_and_case(self, monkeypatch):
monkeypatch.setenv("HEADLESS", " TrUe ")
from linkedin_mcp_server.config.loaders import load_from_env

config = load_from_env(AppConfig())
assert config.browser.headless is True

def test_load_from_env_headless_false_with_off_alias(self, monkeypatch):
monkeypatch.setenv("HEADLESS", "off")
from linkedin_mcp_server.config.loaders import load_from_env

config = load_from_env(AppConfig())
assert config.browser.headless is False

def test_load_from_env_headless_false_with_whitespace_and_case(self, monkeypatch):
monkeypatch.setenv("HEADLESS", " FaLsE ")
from linkedin_mcp_server.config.loaders import load_from_env

config = load_from_env(AppConfig())
assert config.browser.headless is False

def test_load_from_env_headless_true_with_on_alias(self, monkeypatch):
monkeypatch.setenv("HEADLESS", "on")
from linkedin_mcp_server.config.loaders import load_from_env

config = load_from_env(AppConfig())
assert config.browser.headless is True

def test_load_from_env_log_level(self, monkeypatch):
monkeypatch.setenv("LOG_LEVEL", "DEBUG")
from linkedin_mcp_server.config.loaders import load_from_env

config = load_from_env(AppConfig())
assert config.server.log_level == "DEBUG"

def test_load_from_env_log_level_with_whitespace_and_case(self, monkeypatch):
monkeypatch.setenv("LOG_LEVEL", " dEbUg ")
from linkedin_mcp_server.config.loaders import load_from_env

config = load_from_env(AppConfig())
assert config.server.log_level == "DEBUG"

def test_load_from_env_defaults(self, monkeypatch):
# Clear env vars
for var in ["HEADLESS", "LOG_LEVEL"]:
Expand All @@ -100,6 +135,22 @@ def test_load_from_env_transport(self, monkeypatch):
assert config.server.transport == "streamable-http"
assert config.server.transport_explicitly_set is True

def test_load_from_env_transport_with_whitespace_and_case(self, monkeypatch):
monkeypatch.setenv("TRANSPORT", " StReAmAbLe-HtTp ")
from linkedin_mcp_server.config.loaders import load_from_env

config = load_from_env(AppConfig())
assert config.server.transport == "streamable-http"
assert config.server.transport_explicitly_set is True

def test_load_from_env_transport_stdio_with_whitespace_and_case(self, monkeypatch):
monkeypatch.setenv("TRANSPORT", " StDiO ")
from linkedin_mcp_server.config.loaders import load_from_env

config = load_from_env(AppConfig())
assert config.server.transport == "stdio"
assert config.server.transport_explicitly_set is True

def test_load_from_env_invalid_transport(self, monkeypatch):
monkeypatch.setenv("TRANSPORT", "invalid")
from linkedin_mcp_server.config.loaders import load_from_env
Expand Down
Loading