fix(config): derive mode flags in Settings.__post_init__; address review round 2

The integration jobs for `mcp-multi-user-basic` and `mcp-login-flow`
were failing with HTTP 500s. Root cause: `get_settings()` builds a
fresh Settings on every call (not cached). Commits 3 and 4 set the
derived `enable_login_flow` / `enable_multi_user_basic_auth` flags as
a side effect of `detect_auth_mode`. detect_auth_mode runs once at
startup, against the Settings instance owned by `validate_configuration`.
Every per-request call site that does `settings = get_settings()` got
a fresh Settings with both flags at their default `False` (since the
env-var aliases were dropped), causing the multi-user dispatcher in
`context.py` to take the wrong branch and crash.

Fix: move the derivation into `Settings.__post_init__`. Every Settings
instance now carries correct flags from the moment it's constructed —
no caching needed, no mutation-after-construction race. detect_auth_mode
becomes a pure reader of the already-derived state.

The legacy env-var deprecation check moves with it. It also picks up
the reviewer's truthy-string fix: previously `os.getenv(legacy)` fired
for the literal string "false" (a non-empty Python string is truthy),
which would have errored on any user with a leftover
`ENABLE_LOGIN_FLOW=false` in their `.env`. The check now only fires
when the value lowercases to one of {"1", "true", "yes", "on"}.

- nextcloud_mcp_server/config.py: extend Settings.__post_init__ with
  the legacy-deprecation block and the derived-flag derivation
  (resolve mode from deployment_mode + username/password, set flags).
- nextcloud_mcp_server/config_validators.py: drop the
  `_sync_derived_flags` helper (superseded by __post_init__). Drop the
  legacy-env-var deprecation block (moved). `detect_auth_mode` is now
  pure — no mutation. Drop the now-unused `import os`.
- tests/unit/test_config_validators.py: legacy-env-var tests now
  expect `ValueError` at `Settings(...)` construction (via `get_settings()`),
  not at `detect_auth_mode` call. Added two new tests:
    * `test_legacy_env_var_check_ignores_falsy_strings` — pins the
      truthy-string fix (reviewer round 2 finding).
    * `test_derived_flags_stable_across_get_settings_calls` — regression
      test pinning the integration-test fix (two consecutive
      `get_settings()` calls return Settings instances with the same
      derived flags).
  Also reworked `test_login_flow_mode_auto_derives_enable_login_flow_flag`
  to assert at-construction derivation (not the old mutation pattern).
- docs/configuration-migration-v2.md: dropped the duplicate
  `MCP_DEPLOYMENT_MODE=multi_user_basic` line (review round 2 nit — a
  sed artifact from commit 4).
- docs/ADR-021-configuration-consolidation.md: sed-replaced the in-body
  `MCP_DEPLOYMENT_MODE=oauth_single_audience` examples with `login_flow`
  (review round 2 nit — only the status header was updated in commit 4).
- tests/conftest.py: docstring comment for the multi-user-basic fixture
  switched from `ENABLE_MULTI_USER_BASIC_AUTH=true` to
  `MCP_DEPLOYMENT_MODE=multi_user_basic` (review round 2 nit).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Chris Coutinho
2026-05-12 20:44:17 +02:00
co-authored by Claude Opus 4.7
parent 282c245da1
commit 6e7c821761
6 changed files with 137 additions and 68 deletions
+5 -5
View File
@@ -86,7 +86,7 @@ Add `MCP_DEPLOYMENT_MODE` environment variable to remove detection ambiguity:
```bash
# Optional: Explicitly declare deployment mode
MCP_DEPLOYMENT_MODE=oauth_single_audience
MCP_DEPLOYMENT_MODE=login_flow
# Valid values: single_user_basic, multi_user_basic,
# oauth_single_audience, oauth_token_exchange
@@ -114,7 +114,7 @@ TOKEN_STORAGE_DB=/path/to/tokens.db
```bash
# Multi-user OAuth with semantic search
NEXTCLOUD_HOST=https://nextcloud.example.com
MCP_DEPLOYMENT_MODE=oauth_single_audience # Explicit (optional)
MCP_DEPLOYMENT_MODE=login_flow # Explicit (optional)
ENABLE_SEMANTIC_SEARCH=true # Auto-enables background ops
QDRANT_URL=http://qdrant:6333
TOKEN_ENCRYPTION_KEY=<key>
@@ -271,7 +271,7 @@ def enable_semantic_search(self) -> bool:
- `ENABLE_SEMANTIC_SEARCH=false``enable_background_operations=false` (unless explicitly set)
**Mode selection tests**:
- `MCP_DEPLOYMENT_MODE=oauth_single_audience` → mode correctly detected
- `MCP_DEPLOYMENT_MODE=login_flow` → mode correctly detected
- `MCP_DEPLOYMENT_MODE` conflicts with detected mode → validation error
- No `MCP_DEPLOYMENT_MODE` → auto-detection works as before
@@ -363,7 +363,7 @@ QDRANT_URL=http://qdrant:6333
**After** (simplified):
```bash
NEXTCLOUD_HOST=https://nextcloud.example.com
MCP_DEPLOYMENT_MODE=oauth_single_audience # Explicit (optional)
MCP_DEPLOYMENT_MODE=login_flow # Explicit (optional)
ENABLE_SEMANTIC_SEARCH=true # Auto-enables background operations
TOKEN_ENCRYPTION_KEY=<key>
TOKEN_STORAGE_DB=/path/to/tokens.db
@@ -384,7 +384,7 @@ TOKEN_STORAGE_DB=/path/to/tokens.db
**After** (optional migration):
```bash
NEXTCLOUD_HOST=https://nextcloud.example.com
MCP_DEPLOYMENT_MODE=oauth_single_audience
MCP_DEPLOYMENT_MODE=login_flow
ENABLE_BACKGROUND_OPERATIONS=true # Renamed for clarity
TOKEN_ENCRYPTION_KEY=<key>
TOKEN_STORAGE_DB=/path/to/tokens.db
-3
View File
@@ -207,9 +207,6 @@ NEXTCLOUD_OIDC_CLIENT_SECRET=secret
NEXTCLOUD_HOST=https://nextcloud.example.com
MCP_DEPLOYMENT_MODE=multi_user_basic
# Optional: Explicit mode declaration
MCP_DEPLOYMENT_MODE=multi_user_basic
# One variable handles both!
ENABLE_SEMANTIC_SEARCH=true # Auto-enables background operations