Skip to content

fix: config file values no longer override env vars and CLI flags - #403

Merged
AutomateIP merged 1 commit into
itential:develfrom
AutomateIP:fix/config-file-overrides-env-vars
Aug 10, 2026
Merged

fix: config file values no longer override env vars and CLI flags#403
AutomateIP merged 1 commit into
itential:develfrom
AutomateIP:fix/config-file-overrides-env-vars

Conversation

@AutomateIP

Copy link
Copy Markdown
Collaborator

Summary

Restores the documented configuration precedence (environment variables and
CLI flags over the config file). load_config() was always passing
config-file values as explicit constructor kwargs, so a file value won
whenever its key was present in the file -- silently overriding env vars and
CLI flags and contradicting this project's own documented precedence
(env/CLI > file > defaults). Long-standing, traced to the config-module
refactor in PR #292.

Changes

  • config/loaders.py: add _filter_file_data() to drop file-derived keys
    whose backing env var is already present in os.environ, letting that
    field's env-backed default_factory run instead.
  • config/loaders.py: add _env_key_for_field() to resolve a field's
    backing env var from its default_factory (env keys aren't always derivable
    from the section name -- e.g. auth fields map to ITENTIAL_MCP_SERVER_AUTH_*).
  • Apply the filter at the ServerConfig / AuthConfig / PlatformConfig
    construction sites; correct the stale inline precedence comment.
  • Tests: full precedence coverage across all 39 env-backed fields
    (env-beats-file, file-beats-default, CLI-flag-beats-file).

Scope is limited to config/loaders.py and its tests. config/models.py
and the section-dispatch/prefix-stripping logic are intentionally untouched.
Two unrelated pre-existing bugs were found during this work and are
deliberately NOT fixed here (documented in test comments, will be tracked
separately): (a) [server] auth_type in a config file is silently absorbed
due to section-dispatch ordering, (b) a global (non-prefix) string replace
mangles oauth_* field names read from a config file.

Testing

  • Unit tests pass (make ci, 2793 tests, ruff/bandit/headers clean)
  • Integration tests pass (live Itential Platform: file-only baseline
    still connects; env vars override file across server/platform/auth
    fields incl. ITENTIAL_MCP_SERVER_AUTH_TYPE; a live SSE server started
    via --transport sse against a file saying transport=stdio; live
    OAuth unaffected when nothing overrides; live get_health succeeded)
  • Manual testing completed

Related Issues

Fixes the config-precedence bug tracked as Tier B #25 in the 0.14.0 roadmap.

load_config() always passed config-file values as explicit constructor
kwargs, so a file value won whenever the key was present -- silently
overriding env vars and CLI flags and contradicting the documented
precedence (env/CLI > file > defaults).

Filter file-derived kwargs via _filter_file_data() so any field whose
backing env var is already set in os.environ is omitted, letting its
env-backed default_factory fire instead. _env_key_for_field() resolves
the backing env var from each field's default_factory (needed because
the env key isn't always mechanically derivable from the section name,
e.g. auth fields map to ITENTIAL_MCP_SERVER_AUTH_*).

Scope limited to config/loaders.py and its tests; config/models.py and
the section-dispatch logic are untouched.
@AutomateIP AutomateIP added the bug fix Fixes a bug in the application label Aug 7, 2026
@AutomateIP
AutomateIP merged commit 616f537 into itential:devel Aug 10, 2026
7 checks passed
AutomateIP added a commit that referenced this pull request Aug 10, 2026
… mangling (#404)

Fixes two coupled bugs in the config-file loader that broke the documented
file-based auth configuration path. docs/mcp.conf.example documents all
auth fields under [server] with an auth_* prefix, but those keys were
being silently dropped, and oauth_* field names were being mangled.

- Reorder the section-dispatch loop in config/loaders.py so server_auth_/
  auth_ keys are matched before the broad server_ prefix. Previously the
  server_ branch absorbed every [server] auth_* key into server_data,
  where pydantic silently dropped them (dead server_auth_ branch).
- Add _strip_auth_prefix(), a prefix-only strip (removeprefix), replacing
  the global key.replace("auth_", "") that mangled all 8 oauth_* field
  names (e.g. oauth_client_id -> oclient_id).
- Add coverage in tests/test_config.py for the documented [server] auth_*
  path, the [auth] section, oauth field-name integrity, and confirmation
  that #403's env/CLI-over-file precedence still holds.

Addresses roadmap 0.14.0 Tier B #26 and #27. Both surfaced during #403
and are fixed together as they share the same dispatch/parsing path.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug fix Fixes a bug in the application

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant