diff --git a/README.md b/README.md index c13968f..7d3a843 100644 --- a/README.md +++ b/README.md @@ -127,7 +127,7 @@ This enables natural language queries and helps discover related content across > [!NOTE] > **Semantic Search is experimental and opt-in:** -> - Disabled by default (`VECTOR_SYNC_ENABLED=false`) +> - Disabled by default (`ENABLE_SEMANTIC_SEARCH=false`) > - Currently supports Notes app only (multi-app support planned) > - Requires additional infrastructure: vector database + embedding service > - Answer generation (`nc_semantic_search_answer`) requires MCP client sampling support diff --git a/docs/ADR-021-configuration-consolidation.md b/docs/ADR-021-configuration-consolidation.md new file mode 100644 index 0000000..e96aee0 --- /dev/null +++ b/docs/ADR-021-configuration-consolidation.md @@ -0,0 +1,391 @@ +# ADR-021: Configuration Consolidation and Simplification + +**Status:** Accepted +**Date:** 2025-12-21 +**Deciders:** Development Team +**Related:** ADR-020 (Deployment Modes), ADR-002 (Vector Sync), ADR-004 (Progressive Consent) + +## Context + +The configuration system has grown complex with overlapping concerns that make it difficult for users to switch between deployment modes and understand configuration dependencies. + +### Problems Identified + +1. **Confusing variable names don't reflect purpose**: + - `ENABLE_OFFLINE_ACCESS` - Actually controls refresh token storage for background operations, not general "offline" capabilities + - `VECTOR_SYNC_ENABLED` - Controls semantic search background indexing (implementation detail, not user-facing feature name) + - Users struggle to understand what these variables actually control + +2. **Redundant configuration requirements**: + - Multi-user semantic search requires setting BOTH `ENABLE_OFFLINE_ACCESS=true` AND `VECTOR_SYNC_ENABLED=true` + - The dependency is one-way (semantic search needs background ops, but background ops don't need semantic search) + - Users must understand internal implementation details to configure a user-facing feature + +3. **Implicit mode detection creates ambiguity**: + - Five deployment modes detected via priority-based logic + - Users can't easily predict which mode will activate + - Configuration errors don't clearly indicate which mode triggered the requirement + +4. **OIDC_CLIENT_ID vs NEXTCLOUD_OIDC_CLIENT_ID confusion**: + - Investigation revealed these are NOT actually overlapping (`OIDC_CLIENT_ID` is test-only) + - However, their similar names create confusion + +### Current Configuration Complexity + +**Example: Multi-user OAuth with semantic search**: +```bash +NEXTCLOUD_HOST=https://nextcloud.example.com +ENABLE_OFFLINE_ACCESS=true # Why is this needed? +VECTOR_SYNC_ENABLED=true # And this separately? +QDRANT_URL=http://qdrant:6333 +TOKEN_ENCRYPTION_KEY= +TOKEN_STORAGE_DB=/path/to/tokens.db +``` + +Users must understand: +- Semantic search requires background token storage (ENABLE_OFFLINE_ACCESS) +- Background token storage requires encryption keys +- The relationship between ENABLE_OFFLINE_ACCESS and VECTOR_SYNC_ENABLED +- Which deployment mode these settings will activate + +## Decision + +We consolidate overlapping functionality and add explicit mode selection while maintaining 100% backward compatibility. + +### 1. Automatic Dependency Resolution + +**Make ENABLE_SEMANTIC_SEARCH the primary control** that automatically enables required dependencies: + +**New behavior**: +```python +@property +def enable_background_operations(self) -> bool: + """Background operations - auto-enabled by semantic search in multi-user modes.""" + # Check new names first + explicit = os.getenv("ENABLE_BACKGROUND_OPERATIONS", "").lower() == "true" + # Fall back to old name with deprecation warning + legacy = os.getenv("ENABLE_OFFLINE_ACCESS", "").lower() == "true" + # Auto-enable if semantic search needs it + auto_enabled = self.enable_semantic_search and self.is_multi_user_mode() + + return explicit or legacy or auto_enabled + +@property +def enable_semantic_search(self) -> bool: + """Semantic search - renamed from VECTOR_SYNC_ENABLED.""" + new_value = os.getenv("ENABLE_SEMANTIC_SEARCH", "").lower() == "true" + old_value = os.getenv("VECTOR_SYNC_ENABLED", "").lower() == "true" + return new_value or old_value +``` + +**Result**: Users set `ENABLE_SEMANTIC_SEARCH=true` and the system automatically enables background token storage when needed. + +### 2. Explicit Mode Selection (Optional) + +Add `MCP_DEPLOYMENT_MODE` environment variable to remove detection ambiguity: + +```bash +# Optional: Explicitly declare deployment mode +MCP_DEPLOYMENT_MODE=oauth_single_audience + +# Valid values: single_user_basic, multi_user_basic, +# oauth_single_audience, oauth_token_exchange, smithery +``` + +**Detection logic**: +1. If `MCP_DEPLOYMENT_MODE` is set → validate and use it +2. Otherwise → use priority-based auto-detection (existing behavior) +3. Validate explicit mode doesn't conflict with detected mode + +### 3. Simplified User Experience + +**Before**: +```bash +# Multi-user OAuth with semantic search +NEXTCLOUD_HOST=https://nextcloud.example.com +ENABLE_OFFLINE_ACCESS=true # Confusing +VECTOR_SYNC_ENABLED=true # Why both? +QDRANT_URL=http://qdrant:6333 +TOKEN_ENCRYPTION_KEY= +TOKEN_STORAGE_DB=/path/to/tokens.db +``` + +**After**: +```bash +# Multi-user OAuth with semantic search +NEXTCLOUD_HOST=https://nextcloud.example.com +MCP_DEPLOYMENT_MODE=oauth_single_audience # Explicit (optional) +ENABLE_SEMANTIC_SEARCH=true # Auto-enables background ops +QDRANT_URL=http://qdrant:6333 +TOKEN_ENCRYPTION_KEY= +TOKEN_STORAGE_DB=/path/to/tokens.db +``` + +**Benefits**: +- 2 fewer variables to understand/set +- Clear intent ("I want semantic search") +- Explicit mode declaration (optional) +- All existing configs continue working + +### 4. Variable Naming Strategy + +**Deprecated (but still functional)**: +- `ENABLE_OFFLINE_ACCESS` → Renamed to `ENABLE_BACKGROUND_OPERATIONS` +- `VECTOR_SYNC_ENABLED` → Renamed to `ENABLE_SEMANTIC_SEARCH` + +**No change needed**: +- `VECTOR_SYNC_SCAN_INTERVAL` - Implementation tuning parameter (keep as-is) +- `VECTOR_SYNC_PROCESSOR_WORKERS` - Implementation tuning parameter (keep as-is) +- `VECTOR_SYNC_QUEUE_MAX_SIZE` - Implementation tuning parameter (keep as-is) + +**Rationale**: Only rename user-facing feature flags, not internal tuning parameters. + +### 5. Backward Compatibility + +**Support both old and new names for minimum 2 major versions**: + +```python +@property +def enable_semantic_search(self) -> bool: + new_value = os.getenv("ENABLE_SEMANTIC_SEARCH", "").lower() == "true" + old_value = os.getenv("VECTOR_SYNC_ENABLED", "").lower() == "true" + + if new_value and old_value: + logger.warning( + "Both ENABLE_SEMANTIC_SEARCH and VECTOR_SYNC_ENABLED are set. " + "Using ENABLE_SEMANTIC_SEARCH. VECTOR_SYNC_ENABLED is deprecated." + ) + + if old_value and not new_value: + logger.warning( + "VECTOR_SYNC_ENABLED is deprecated. Please use ENABLE_SEMANTIC_SEARCH instead." + ) + + return new_value or old_value +``` + +**Deprecation timeline**: +- v0.6.0: Add new variables, deprecate old ones (both work with warnings) +- v1.0.0: Remove old variables (breaking change, well-announced) +- Minimum 2 major versions of support (12+ months) + +## Consequences + +### Positive + +1. **Reduced cognitive load**: Users set `ENABLE_SEMANTIC_SEARCH=true` instead of understanding internal dependencies +2. **Clearer intent**: Variable names reflect user-facing features, not implementation details +3. **Explicit mode control**: `MCP_DEPLOYMENT_MODE` removes detection ambiguity +4. **Better onboarding**: New users see simpler configuration in env.sample +5. **Improved error messages**: Validation can suggest "set MCP_DEPLOYMENT_MODE=X" instead of relying on implicit detection +6. **No breaking changes**: All existing configurations continue working + +### Negative + +1. **Transition period complexity**: Both old and new names supported for 2+ versions +2. **Documentation burden**: All docs must be updated to show new approach +3. **Test coverage expansion**: Must test both old and new variable names in all modes +4. **Migration effort**: Existing deployments should eventually migrate (optional but recommended) + +### Neutral + +1. **Same functionality**: No new features, just better organization +2. **Same validation**: Underlying requirements unchanged (e.g., semantic search still needs Qdrant) +3. **Same performance**: No runtime performance impact + +## Implementation + +### Phase 1: Configuration Consolidation (v0.6.0) + +**Files to modify**: +- `nextcloud_mcp_server/config.py` - Add property-based deprecation with auto-enablement +- `nextcloud_mcp_server/config_validators.py` - Simplify validation (semantic search no longer requires explicit background operations setting) +- `nextcloud_mcp_server/app.py` - Add informative logging for auto-enablement +- `tests/unit/test_config_validators.py` - Add auto-enablement tests +- `docs/configuration-migration-v2.md` - Create migration guide + +**Key changes**: +1. `enable_background_operations` property auto-enables when `enable_semantic_search=true` in multi-user modes +2. `enable_semantic_search` property accepts both `ENABLE_SEMANTIC_SEARCH` and `VECTOR_SYNC_ENABLED` +3. Smart logging when auto-enablement occurs or deprecated variables used +4. Validation simplified to remove redundant requirements + +### Phase 2: Explicit Mode Selection (v0.6.0) + +**Files to modify**: +- `nextcloud_mcp_server/config.py` - Add `deployment_mode` field +- `nextcloud_mcp_server/config_validators.py` - Check explicit mode first, fall back to auto-detection +- `tests/unit/test_config_validators.py` - Test mode override and conflict detection +- `docs/configuration.md` - Document mode selection + +**Key changes**: +1. Add `MCP_DEPLOYMENT_MODE` environment variable (optional) +2. Mode detection checks explicit mode first, then auto-detects +3. Validate explicit mode doesn't conflict with detected mode +4. Better error messages referencing explicit mode setting + +### Phase 3: env.sample Reorganization (v0.6.0) + +**Files to create/modify**: +- `env.sample` - Reorganize by deployment mode +- `env.sample.single-user` - Simplest config template +- `env.sample.oauth-multi-user` - Multi-user template showing consolidation +- `env.sample.oauth-advanced` - Token exchange mode template +- `README.md` - Update Quick Start to reference templates + +**Key changes**: +1. Group related settings by deployment mode +2. Show simplified configuration (only essential variables) +3. Document automatic dependencies inline +4. Provide mode-specific quick-start templates + +### Phase 4: Documentation Updates (v0.7.0) + +**Files to modify**: +- `docs/configuration.md` - Lead with consolidated approach +- `docs/authentication.md` - Update mode guidance with `MCP_DEPLOYMENT_MODE` +- `docs/troubleshooting.md` - Add consolidation troubleshooting section +- `docs/configuration-migration-v2.md` - Expand with comprehensive examples +- `docs/ADR-020-deployment-modes-and-configuration-validation.md` - Update configuration matrix +- All other ADRs - Update variable references + +**Key changes**: +1. Update all examples to use new variable names +2. Add before/after migration examples +3. Document automatic dependency resolution +4. Add mode selection decision tree diagram + +## Validation Strategy + +### Test Coverage Requirements + +**Backward compatibility tests**: +- Old variable names still work (ENABLE_OFFLINE_ACCESS, VECTOR_SYNC_ENABLED) +- New variable names work (ENABLE_BACKGROUND_OPERATIONS, ENABLE_SEMANTIC_SEARCH) +- Setting both old and new triggers deprecation warning but works correctly +- All 41 existing config validation tests pass + +**Auto-enablement tests**: +- `ENABLE_SEMANTIC_SEARCH=true` in OAuth mode → `enable_background_operations=true` +- `ENABLE_SEMANTIC_SEARCH=true` in single-user mode → `enable_background_operations=false` (not needed) +- `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` conflicts with detected mode → validation error +- No `MCP_DEPLOYMENT_MODE` → auto-detection works as before + +## Success Metrics + +**Immediate** (v0.6.0 release): +- Zero breaking changes in existing deployments +- All 41 config validation tests pass +- New users report clearer configuration process + +**Medium-term** (6 months after v0.6.0): +- 80% of new deployments use new variable names +- Mode selection errors decrease by 50% +- Support requests about configuration decrease + +**Long-term** (12+ months): +- 90% of deployments migrated to new names +- Old variable names can be safely removed in v1.0.0 +- Configuration-related issues in issue tracker decrease + +## Alternatives Considered + +### Alternative 1: Just Rename Variables + +**Rejected**: User feedback: "There's no reason to just rename variables without consolidating functionality" + +This would make names clearer but wouldn't reduce the number of variables users need to set. The real problem is requiring users to set both ENABLE_OFFLINE_ACCESS and VECTOR_SYNC_ENABLED when they just want semantic search. + +### Alternative 2: Remove ENABLE_OFFLINE_ACCESS Entirely + +**Rejected**: Advanced users need background operations without semantic search + +Some deployments might want background token storage for future features (background Deck sync, background Calendar sync, etc.) without enabling semantic search. Keeping ENABLE_BACKGROUND_OPERATIONS (renamed) allows this. + +### Alternative 3: Always Auto-Enable Background Operations + +**Rejected**: Single-user mode doesn't need background token storage + +Auto-enablement is only needed in multi-user modes. Single-user mode uses a shared client with BasicAuth, so background token storage is unnecessary. Always enabling it would waste resources and create confusing log messages. + +### Alternative 4: Require All New Names Immediately + +**Rejected**: Breaking change would affect all existing deployments + +Forcing migration to new variable names in v0.6.0 would break every existing deployment. Supporting both old and new names with deprecation warnings provides a smooth migration path. + +## References + +- [ADR-020: Deployment Modes and Configuration Validation](ADR-020-deployment-modes-and-configuration-validation.md) +- [ADR-002: Vector Sync Authentication](ADR-002-vector-sync-authentication.md) +- [ADR-004: Progressive Consent](ADR-004-mcp-application-oauth.md) +- [Issue: Configuration complexity for multi-user semantic search](https://github.com/cbcoutinho/nextcloud-mcp-server/issues/XXX) + +## Migration Examples + +### Example 1: Single-User BasicAuth with Semantic Search + +**Before**: +```bash +NEXTCLOUD_HOST=http://localhost:8080 +NEXTCLOUD_USERNAME=admin +NEXTCLOUD_PASSWORD=password +VECTOR_SYNC_ENABLED=true +QDRANT_LOCATION=:memory: +``` + +**After** (optional migration): +```bash +NEXTCLOUD_HOST=http://localhost:8080 +NEXTCLOUD_USERNAME=admin +NEXTCLOUD_PASSWORD=password +ENABLE_SEMANTIC_SEARCH=true # Renamed +QDRANT_LOCATION=:memory: +# Note: Background operations NOT auto-enabled (not needed in single-user mode) +``` + +### Example 2: Multi-User OAuth with Semantic Search + +**Before**: +```bash +NEXTCLOUD_HOST=https://nextcloud.example.com +ENABLE_OFFLINE_ACCESS=true +VECTOR_SYNC_ENABLED=true +TOKEN_ENCRYPTION_KEY= +TOKEN_STORAGE_DB=/path/to/tokens.db +QDRANT_URL=http://qdrant:6333 +``` + +**After** (simplified): +```bash +NEXTCLOUD_HOST=https://nextcloud.example.com +MCP_DEPLOYMENT_MODE=oauth_single_audience # Explicit (optional) +ENABLE_SEMANTIC_SEARCH=true # Auto-enables background operations +TOKEN_ENCRYPTION_KEY= +TOKEN_STORAGE_DB=/path/to/tokens.db +QDRANT_URL=http://qdrant:6333 +# Note: ENABLE_OFFLINE_ACCESS no longer needed (auto-enabled) +``` + +### Example 3: Multi-User OAuth WITHOUT Semantic Search + +**Before**: +```bash +NEXTCLOUD_HOST=https://nextcloud.example.com +ENABLE_OFFLINE_ACCESS=true # For future background features +TOKEN_ENCRYPTION_KEY= +TOKEN_STORAGE_DB=/path/to/tokens.db +``` + +**After** (optional migration): +```bash +NEXTCLOUD_HOST=https://nextcloud.example.com +MCP_DEPLOYMENT_MODE=oauth_single_audience +ENABLE_BACKGROUND_OPERATIONS=true # Renamed for clarity +TOKEN_ENCRYPTION_KEY= +TOKEN_STORAGE_DB=/path/to/tokens.db +``` diff --git a/docs/configuration-migration-v2.md b/docs/configuration-migration-v2.md new file mode 100644 index 0000000..e7fde7b --- /dev/null +++ b/docs/configuration-migration-v2.md @@ -0,0 +1,564 @@ +# Configuration Migration Guide v2 + +**Version:** v0.58.0 +**Status:** Active +**Related ADR:** [ADR-021: Configuration Consolidation and Simplification](ADR-021-configuration-consolidation.md) + +## Overview + +This guide helps you migrate from the old configuration variables to the new consolidated approach introduced in v0.58.0. + +**Key Changes:** +- `VECTOR_SYNC_ENABLED` → `ENABLE_SEMANTIC_SEARCH` +- `ENABLE_OFFLINE_ACCESS` → `ENABLE_BACKGROUND_OPERATIONS` +- New: `MCP_DEPLOYMENT_MODE` for explicit mode selection +- Automatic dependency resolution: semantic search auto-enables background operations + +**Backward Compatibility:** +- Old variable names still work in v0.58.0+ +- Deprecation warnings logged when old names used +- Old names will be removed in v1.0.0 + +--- + +## Quick Reference: Variable Name Changes + +| Old Name | New Name | Status | +|----------|----------|--------| +| `VECTOR_SYNC_ENABLED` | `ENABLE_SEMANTIC_SEARCH` | Deprecated | +| `ENABLE_OFFLINE_ACCESS` | `ENABLE_BACKGROUND_OPERATIONS` | Deprecated | +| N/A (auto-detected) | `MCP_DEPLOYMENT_MODE` | New (optional) | + +**Tuning parameters unchanged:** +- `VECTOR_SYNC_SCAN_INTERVAL` - Keep as-is +- `VECTOR_SYNC_PROCESSOR_WORKERS` - Keep as-is +- `VECTOR_SYNC_QUEUE_MAX_SIZE` - Keep as-is + +--- + +## Migration Scenarios + +### Scenario 1: Single-User BasicAuth with Semantic Search + +**Before (v0.57.x):** +```bash +NEXTCLOUD_HOST=http://localhost:8080 +NEXTCLOUD_USERNAME=admin +NEXTCLOUD_PASSWORD=password +VECTOR_SYNC_ENABLED=true +QDRANT_LOCATION=:memory: +OLLAMA_BASE_URL=http://ollama:11434 +``` + +**After (v0.58.0+):** +```bash +NEXTCLOUD_HOST=http://localhost:8080 +NEXTCLOUD_USERNAME=admin +NEXTCLOUD_PASSWORD=password + +# Optional: Explicit mode declaration (recommended) +MCP_DEPLOYMENT_MODE=single_user_basic + +# Updated variable name +ENABLE_SEMANTIC_SEARCH=true # Previously VECTOR_SYNC_ENABLED + +QDRANT_LOCATION=:memory: +OLLAMA_BASE_URL=http://ollama:11434 +``` + +**What Changed:** +- ✅ Renamed `VECTOR_SYNC_ENABLED` to `ENABLE_SEMANTIC_SEARCH` +- ✅ Added optional `MCP_DEPLOYMENT_MODE` for clarity +- ✅ Background operations NOT auto-enabled (not needed in single-user mode) + +**Migration Steps:** +1. Replace `VECTOR_SYNC_ENABLED=true` with `ENABLE_SEMANTIC_SEARCH=true` +2. Optionally add `MCP_DEPLOYMENT_MODE=single_user_basic` +3. Restart server +4. Verify deprecation warnings are gone + +--- + +### Scenario 2: Multi-User OAuth with Semantic Search + +**Before (v0.57.x):** +```bash +NEXTCLOUD_HOST=https://nextcloud.example.com +NEXTCLOUD_USERNAME= +NEXTCLOUD_PASSWORD= + +# Both variables required - confusing! +ENABLE_OFFLINE_ACCESS=true +VECTOR_SYNC_ENABLED=true + +TOKEN_ENCRYPTION_KEY=your-key-here +TOKEN_STORAGE_DB=/app/data/tokens.db +QDRANT_URL=http://qdrant:6333 +OLLAMA_BASE_URL=http://ollama:11434 +NEXTCLOUD_OIDC_CLIENT_ID=mcp-server +NEXTCLOUD_OIDC_CLIENT_SECRET=secret +``` + +**After (v0.58.0+ - Simplified):** +```bash +NEXTCLOUD_HOST=https://nextcloud.example.com +NEXTCLOUD_USERNAME= +NEXTCLOUD_PASSWORD= + +# Optional: Explicit mode declaration +MCP_DEPLOYMENT_MODE=oauth_single_audience + +# One variable does it all! +ENABLE_SEMANTIC_SEARCH=true # Automatically enables background operations + +TOKEN_ENCRYPTION_KEY=your-key-here +TOKEN_STORAGE_DB=/app/data/tokens.db +QDRANT_URL=http://qdrant:6333 +OLLAMA_BASE_URL=http://ollama:11434 +NEXTCLOUD_OIDC_CLIENT_ID=mcp-server +NEXTCLOUD_OIDC_CLIENT_SECRET=secret + +# Note: ENABLE_OFFLINE_ACCESS no longer needed! +# Background operations are auto-enabled by ENABLE_SEMANTIC_SEARCH +``` + +**What Changed:** +- ✅ Removed need for explicit `ENABLE_OFFLINE_ACCESS` +- ✅ `ENABLE_SEMANTIC_SEARCH` automatically enables background operations in multi-user modes +- ✅ Renamed `VECTOR_SYNC_ENABLED` to `ENABLE_SEMANTIC_SEARCH` +- ✅ Added optional explicit mode declaration + +**Migration Steps:** +1. Replace `VECTOR_SYNC_ENABLED=true` with `ENABLE_SEMANTIC_SEARCH=true` +2. Remove `ENABLE_OFFLINE_ACCESS=true` (auto-enabled) +3. Optionally add `MCP_DEPLOYMENT_MODE=oauth_single_audience` +4. Restart server +5. Check logs for confirmation: "Automatically enabled background operations for semantic search" + +--- + +### Scenario 3: Multi-User OAuth WITHOUT Semantic Search + +**Before (v0.57.x):** +```bash +NEXTCLOUD_HOST=https://nextcloud.example.com +NEXTCLOUD_USERNAME= +NEXTCLOUD_PASSWORD= + +# Enable background operations for future features +ENABLE_OFFLINE_ACCESS=true + +TOKEN_ENCRYPTION_KEY=your-key-here +TOKEN_STORAGE_DB=/app/data/tokens.db +NEXTCLOUD_OIDC_CLIENT_ID=mcp-server +NEXTCLOUD_OIDC_CLIENT_SECRET=secret +``` + +**After (v0.58.0+):** +```bash +NEXTCLOUD_HOST=https://nextcloud.example.com +NEXTCLOUD_USERNAME= +NEXTCLOUD_PASSWORD= + +# Optional: Explicit mode declaration +MCP_DEPLOYMENT_MODE=oauth_single_audience + +# Renamed for clarity +ENABLE_BACKGROUND_OPERATIONS=true # Previously ENABLE_OFFLINE_ACCESS + +TOKEN_ENCRYPTION_KEY=your-key-here +TOKEN_STORAGE_DB=/app/data/tokens.db +NEXTCLOUD_OIDC_CLIENT_ID=mcp-server +NEXTCLOUD_OIDC_CLIENT_SECRET=secret +``` + +**What Changed:** +- ✅ Renamed `ENABLE_OFFLINE_ACCESS` to `ENABLE_BACKGROUND_OPERATIONS` +- ✅ Added optional explicit mode declaration + +**Migration Steps:** +1. Replace `ENABLE_OFFLINE_ACCESS=true` with `ENABLE_BACKGROUND_OPERATIONS=true` +2. Optionally add `MCP_DEPLOYMENT_MODE=oauth_single_audience` +3. Restart server + +--- + +### Scenario 4: Multi-User BasicAuth with Semantic Search + +**Before (v0.57.x):** +```bash +NEXTCLOUD_HOST=https://nextcloud.example.com +ENABLE_MULTI_USER_BASIC_AUTH=true + +# Both required - redundant +ENABLE_OFFLINE_ACCESS=true +VECTOR_SYNC_ENABLED=true + +TOKEN_ENCRYPTION_KEY=your-key-here +TOKEN_STORAGE_DB=/app/data/tokens.db +QDRANT_URL=http://qdrant:6333 +OLLAMA_BASE_URL=http://ollama:11434 +NEXTCLOUD_OIDC_CLIENT_ID=mcp-server +NEXTCLOUD_OIDC_CLIENT_SECRET=secret +``` + +**After (v0.58.0+ - Simplified):** +```bash +NEXTCLOUD_HOST=https://nextcloud.example.com +ENABLE_MULTI_USER_BASIC_AUTH=true + +# Optional: Explicit mode declaration +MCP_DEPLOYMENT_MODE=multi_user_basic + +# One variable handles both! +ENABLE_SEMANTIC_SEARCH=true # Auto-enables background operations + +TOKEN_ENCRYPTION_KEY=your-key-here +TOKEN_STORAGE_DB=/app/data/tokens.db +QDRANT_URL=http://qdrant:6333 +OLLAMA_BASE_URL=http://ollama:11434 +NEXTCLOUD_OIDC_CLIENT_ID=mcp-server +NEXTCLOUD_OIDC_CLIENT_SECRET=secret + +# Note: ENABLE_OFFLINE_ACCESS no longer needed! +``` + +**What Changed:** +- ✅ Semantic search auto-enables background operations +- ✅ Removed need for explicit `ENABLE_OFFLINE_ACCESS` +- ✅ Clearer variable naming + +**Migration Steps:** +1. Replace `VECTOR_SYNC_ENABLED=true` with `ENABLE_SEMANTIC_SEARCH=true` +2. Remove `ENABLE_OFFLINE_ACCESS=true` (auto-enabled) +3. Optionally add `MCP_DEPLOYMENT_MODE=multi_user_basic` +4. Restart server + +--- + +### Scenario 5: Token Exchange Mode with Semantic Search + +**Before (v0.57.x):** +```bash +NEXTCLOUD_HOST=https://nextcloud.example.com +ENABLE_TOKEN_EXCHANGE=true + +# Both required +ENABLE_OFFLINE_ACCESS=true +VECTOR_SYNC_ENABLED=true + +TOKEN_ENCRYPTION_KEY=your-key-here +TOKEN_STORAGE_DB=/app/data/tokens.db +TOKEN_EXCHANGE_CACHE_TTL=300 +QDRANT_URL=http://qdrant:6333 +OLLAMA_BASE_URL=http://ollama:11434 +``` + +**After (v0.58.0+ - Simplified):** +```bash +NEXTCLOUD_HOST=https://nextcloud.example.com +ENABLE_TOKEN_EXCHANGE=true + +# Optional: Explicit mode declaration +MCP_DEPLOYMENT_MODE=oauth_token_exchange + +# One variable! +ENABLE_SEMANTIC_SEARCH=true # Auto-enables background operations + +TOKEN_ENCRYPTION_KEY=your-key-here +TOKEN_STORAGE_DB=/app/data/tokens.db +TOKEN_EXCHANGE_CACHE_TTL=300 +QDRANT_URL=http://qdrant:6333 +OLLAMA_BASE_URL=http://ollama:11434 +``` + +**What Changed:** +- ✅ Semantic search auto-enables background operations +- ✅ Explicit mode declaration available + +**Migration Steps:** +1. Replace `VECTOR_SYNC_ENABLED=true` with `ENABLE_SEMANTIC_SEARCH=true` +2. Remove `ENABLE_OFFLINE_ACCESS=true` (auto-enabled) +3. Optionally add `MCP_DEPLOYMENT_MODE=oauth_token_exchange` +4. Restart server + +--- + +## Understanding Automatic Dependency Resolution + +### How It Works + +In v0.58.0+, the server uses smart dependency resolution: + +```python +# In multi-user modes (OAuth, Multi-User BasicAuth): +if ENABLE_SEMANTIC_SEARCH == true: + background_operations = automatically enabled + refresh_tokens = automatically requested + token_storage = required (TOKEN_ENCRYPTION_KEY, TOKEN_STORAGE_DB) + oauth_credentials = required (for app password retrieval) +``` + +**What this means:** +- ✅ Set `ENABLE_SEMANTIC_SEARCH=true` +- ✅ Provide required infrastructure (Qdrant, Ollama, encryption key) +- ✅ System automatically enables background operations +- ❌ No need to set `ENABLE_BACKGROUND_OPERATIONS` separately + +### When Automatic Enablement Happens + +| Deployment Mode | Semantic Search Enabled | Background Operations Auto-Enabled? | +|----------------|------------------------|-----------------------------------| +| Single-User BasicAuth | ✅ | ❌ No (not needed) | +| Multi-User BasicAuth | ✅ | ✅ Yes | +| OAuth Single-Audience | ✅ | ✅ Yes | +| OAuth Token Exchange | ✅ | ✅ Yes | +| Smithery Stateless | N/A (not supported) | N/A | + +### When to Explicitly Set ENABLE_BACKGROUND_OPERATIONS + +Only needed when you want background operations **without** semantic search: + +```bash +# Example: OAuth mode with background operations but NO semantic search +NEXTCLOUD_HOST=https://nextcloud.example.com +MCP_DEPLOYMENT_MODE=oauth_single_audience + +# Explicitly enable background operations for future features +ENABLE_BACKGROUND_OPERATIONS=true + +TOKEN_ENCRYPTION_KEY=your-key-here +TOKEN_STORAGE_DB=/app/data/tokens.db + +# Semantic search disabled +ENABLE_SEMANTIC_SEARCH=false +``` + +--- + +## Explicit Mode Selection + +### Why Use MCP_DEPLOYMENT_MODE? + +**Benefits:** +- ✅ Removes ambiguity about which mode is active +- ✅ Validation errors reference specific mode requirements +- ✅ Catches configuration mistakes early +- ✅ Self-documenting configuration + +**Example:** +```bash +# Without explicit mode: +NEXTCLOUD_HOST=https://nextcloud.example.com +# Is this OAuth or Multi-User BasicAuth? Not immediately clear. + +# With explicit mode: +MCP_DEPLOYMENT_MODE=oauth_single_audience +NEXTCLOUD_HOST=https://nextcloud.example.com +# Clear: This is OAuth mode +``` + +### Valid Mode Values + +| Mode Value | Description | +|-----------|-------------| +| `single_user_basic` | Single-user with username/password | +| `multi_user_basic` | Multi-user with BasicAuth pass-through | +| `oauth_single_audience` | Multi-user OAuth (recommended) | +| `oauth_token_exchange` | Multi-user OAuth with token exchange | +| `smithery` | Smithery platform deployment | + +### Mode Detection Priority + +When `MCP_DEPLOYMENT_MODE` is set: +1. ✅ Explicit mode is used +2. ✅ Server validates configuration matches explicit mode +3. ❌ Auto-detection is skipped + +When `MCP_DEPLOYMENT_MODE` is NOT set: +1. ✅ Auto-detection runs (existing behavior) +2. ✅ Priority: Smithery → Token Exchange → Multi-User BasicAuth → Single-User BasicAuth → OAuth Single-Audience + +--- + +## Validation and Error Messages + +### Old Validation (v0.57.x) + +``` +Error: [multi_user_basic] ENABLE_OFFLINE_ACCESS is required when VECTOR_SYNC_ENABLED is enabled +``` + +**Problem:** User must understand internal dependency relationship + +### New Validation (v0.58.0+) + +``` +Error: [multi_user_basic] TOKEN_ENCRYPTION_KEY is required when ENABLE_SEMANTIC_SEARCH is enabled +``` + +**Benefit:** Clear what's needed, no mention of internal ENABLE_BACKGROUND_OPERATIONS flag + +--- + +## Troubleshooting Migration + +### Issue: Deprecation Warning After Migration + +**Symptom:** +``` +WARNING: VECTOR_SYNC_ENABLED is deprecated. Please use ENABLE_SEMANTIC_SEARCH instead. +``` + +**Solution:** +1. Check for `VECTOR_SYNC_ENABLED` in `.env` file +2. Replace with `ENABLE_SEMANTIC_SEARCH` +3. Search for any scripts/CI configs using old name +4. Restart server + +### Issue: Both Old and New Names Set + +**Symptom:** +``` +WARNING: Both ENABLE_SEMANTIC_SEARCH and VECTOR_SYNC_ENABLED are set. Using ENABLE_SEMANTIC_SEARCH. +``` + +**Solution:** +1. Remove `VECTOR_SYNC_ENABLED` from `.env` +2. Keep `ENABLE_SEMANTIC_SEARCH` +3. Restart server + +### Issue: Missing Required Dependencies + +**Symptom:** +``` +Error: [oauth_single_audience] TOKEN_ENCRYPTION_KEY is required when ENABLE_SEMANTIC_SEARCH is enabled +``` + +**Solution:** +When semantic search is enabled in multi-user modes, you need: +- `TOKEN_ENCRYPTION_KEY` - Generate with: `python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"` +- `TOKEN_STORAGE_DB` - Path to SQLite database (e.g., `/app/data/tokens.db`) +- `NEXTCLOUD_OIDC_CLIENT_ID` and `NEXTCLOUD_OIDC_CLIENT_SECRET` - For app password retrieval + +### Issue: Unexpected Mode Detected + +**Symptom:** +Server activates `oauth_single_audience` mode when you expected `multi_user_basic` + +**Solution:** +Add explicit mode declaration: +```bash +MCP_DEPLOYMENT_MODE=multi_user_basic +ENABLE_MULTI_USER_BASIC_AUTH=true +``` + +--- + +## Testing Your Migration + +### Step 1: Verify Configuration + +```bash +# Set new variable names in .env +cat .env | grep -E "(ENABLE_SEMANTIC_SEARCH|ENABLE_BACKGROUND_OPERATIONS|MCP_DEPLOYMENT_MODE)" +``` + +### Step 2: Check for Old Variable Names + +```bash +# Should return nothing after migration +cat .env | grep -E "(VECTOR_SYNC_ENABLED|ENABLE_OFFLINE_ACCESS)" +``` + +### Step 3: Start Server and Check Logs + +```bash +# Start server +docker-compose up mcp + +# Look for: +# 1. No deprecation warnings +# 2. Correct mode detected +# 3. Auto-enablement messages (if using semantic search in multi-user mode) +``` + +**Expected Log Output (Multi-User OAuth + Semantic Search):** +``` +INFO: Using explicit deployment mode: oauth_single_audience +INFO: Automatically enabled background operations for semantic search in multi-user mode. +INFO: Vector sync enabled. Starting background scanner... +``` + +### Step 4: Verify Functionality + +Test that existing features still work: +- [ ] Semantic search returns results +- [ ] Background indexing runs +- [ ] OAuth flow completes successfully +- [ ] Refresh tokens are stored/retrieved + +--- + +## Quick Start Templates + +We provide mode-specific templates for new deployments: + +| Template | Use Case | +|----------|----------| +| `env.sample.single-user` | Simplest setup | +| `env.sample.oauth-multi-user` | Recommended multi-user | +| `env.sample.oauth-advanced` | Token exchange mode | + +**Usage:** +```bash +cp env.sample.oauth-multi-user .env +# Edit .env with your values +docker-compose up -d +``` + +--- + +## Timeline and Support + +| Version | Status | Old Variable Support | +|---------|--------|---------------------| +| v0.57.x | Stable | Old names only | +| v0.58.0 | Current | Both old and new (with warnings) | +| v1.0.0 | Breaking | New names only | + +**Recommendation:** Migrate before v1.0.0 (12+ months minimum) + +--- + +## Getting Help + +If you encounter issues during migration: + +1. **Check the logs** - Look for deprecation warnings and error messages +2. **Review ADR-021** - See [docs/ADR-021-configuration-consolidation.md](ADR-021-configuration-consolidation.md) +3. **Use mode-specific templates** - See `env.sample.*` files +4. **File an issue** - Include your `.env` (redacted), logs, and mode + +--- + +## Summary + +**What You Need to Do:** +1. ✅ Rename `VECTOR_SYNC_ENABLED` → `ENABLE_SEMANTIC_SEARCH` +2. ✅ (Optional) Rename `ENABLE_OFFLINE_ACCESS` → `ENABLE_BACKGROUND_OPERATIONS` +3. ✅ (Recommended) Add `MCP_DEPLOYMENT_MODE` for clarity +4. ✅ Remove redundant settings (semantic search auto-enables background ops in multi-user modes) +5. ✅ Test your configuration + +**What the Server Does Automatically:** +- ✅ Supports both old and new variable names +- ✅ Logs deprecation warnings for old names +- ✅ Auto-enables background operations when semantic search is enabled in multi-user modes +- ✅ Validates configuration and provides clear error messages + +**Migration Timeline:** +- Now → v1.0.0: Both old and new names work +- v1.0.0+: Only new names supported + +**Questions?** See [docs/configuration.md](configuration.md) or file an issue. diff --git a/docs/configuration.md b/docs/configuration.md index e451c3f..f29fbdd 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -2,25 +2,82 @@ The Nextcloud MCP server requires configuration to connect to your Nextcloud instance. Configuration is provided through environment variables, typically stored in a `.env` file. +> **Note:** Configuration was significantly simplified in v0.58.0. If you're upgrading from v0.57.x, see the [Configuration Migration Guide](configuration-migration-v2.md). + ## Quick Start -Create a `.env` file based on `env.sample`: +We provide mode-specific configuration templates for quick setup: ```bash +# Choose a template based on your deployment mode: +cp env.sample.single-user .env # Simplest - one user, local dev +cp env.sample.oauth-multi-user .env # Recommended - multi-user OAuth +cp env.sample.oauth-advanced .env # Advanced - token exchange mode + +# Or start from the full example: cp env.sample .env + # Edit .env with your Nextcloud details ``` -Then choose your authentication mode: +Then choose your deployment mode: -- [OAuth2/OIDC Configuration](#oauth2oidc-configuration) (Recommended) -- [Basic Authentication Configuration](#basic-authentication-legacy) +- [Single-User BasicAuth](#single-user-basicauth-mode) - Simplest for personal instances +- [Multi-User OAuth](#multi-user-oauth-modes) - Recommended for production +- [Deployment Mode Selection](#deployment-mode-selection) - Explicit mode declaration --- -## OAuth2/OIDC Configuration +## Deployment Mode Selection -OAuth2/OIDC is the recommended authentication mode for production deployments. +**New in v0.58.0:** You can explicitly declare your deployment mode to remove ambiguity and catch configuration errors early. + +```dotenv +# Optional but recommended +MCP_DEPLOYMENT_MODE=oauth_single_audience +``` + +**Valid values:** +- `single_user_basic` - Single-user with username/password +- `multi_user_basic` - Multi-user with BasicAuth pass-through +- `oauth_single_audience` - Multi-user OAuth (recommended) +- `oauth_token_exchange` - Multi-user OAuth with token exchange +- `smithery` - Smithery platform deployment + +**Benefits:** +- ✅ Clear which mode is active +- ✅ Better validation error messages +- ✅ Self-documenting configuration +- ✅ Catches configuration mistakes early + +**Auto-detection:** If `MCP_DEPLOYMENT_MODE` is not set, the server auto-detects the mode based on other settings (existing behavior). + +See [Authentication Modes](authentication.md) for detailed comparison of deployment modes. + +--- + +## Single-User BasicAuth Mode + +BasicAuth with a single user is the simplest deployment mode. Use for personal instances, local development, and testing. + +```dotenv +# Minimal single-user configuration +NEXTCLOUD_HOST=http://localhost:8080 +NEXTCLOUD_USERNAME=admin +NEXTCLOUD_PASSWORD=password + +# Optional: Explicit mode declaration +MCP_DEPLOYMENT_MODE=single_user_basic +``` + +> [!WARNING] +> **Security Notice:** BasicAuth stores credentials in environment variables and is less secure than OAuth. Use OAuth for production multi-user deployments. + +--- + +## Multi-User OAuth Modes + +OAuth2/OIDC is the recommended authentication mode for production multi-user deployments. ### Minimal Configuration (Auto-registration) @@ -28,6 +85,9 @@ OAuth2/OIDC is the recommended authentication mode for production deployments. # .env file for OAuth with auto-registration NEXTCLOUD_HOST=https://your.nextcloud.instance.com +# Optional: Explicit mode declaration (recommended) +MCP_DEPLOYMENT_MODE=oauth_single_audience + # Leave these EMPTY for OAuth mode NEXTCLOUD_USERNAME= NEXTCLOUD_PASSWORD= @@ -41,6 +101,9 @@ This minimal configuration uses dynamic client registration to automatically reg # .env file for OAuth with pre-configured client NEXTCLOUD_HOST=https://your.nextcloud.instance.com +# Optional: Explicit mode declaration (recommended) +MCP_DEPLOYMENT_MODE=oauth_single_audience + # OAuth Client Credentials (optional - auto-registers if not provided) NEXTCLOUD_OIDC_CLIENT_ID=your-client-id NEXTCLOUD_OIDC_CLIENT_SECRET=your-client-secret @@ -110,8 +173,50 @@ NEXTCLOUD_PASSWORD=your_app_password_or_password ## Semantic Search Configuration (Optional) +**New in v0.58.0:** Simplified semantic search configuration with automatic dependency resolution. + The MCP server includes semantic search capabilities powered by vector embeddings. This feature requires a vector database (Qdrant) and an embedding service. +### Quick Start + +**Single-User Mode:** +```dotenv +NEXTCLOUD_HOST=http://localhost:8080 +NEXTCLOUD_USERNAME=admin +NEXTCLOUD_PASSWORD=password + +# Enable semantic search +ENABLE_SEMANTIC_SEARCH=true + +# Vector database +QDRANT_LOCATION=:memory: + +# Embedding provider +OLLAMA_BASE_URL=http://ollama:11434 +``` + +**Multi-User OAuth Mode:** +```dotenv +NEXTCLOUD_HOST=https://nextcloud.example.com +MCP_DEPLOYMENT_MODE=oauth_single_audience + +# Enable semantic search +# In multi-user modes, this AUTOMATICALLY enables background operations! +ENABLE_SEMANTIC_SEARCH=true + +# Required for background operations (auto-enabled by semantic search) +TOKEN_ENCRYPTION_KEY=your-key-here +TOKEN_STORAGE_DB=/app/data/tokens.db + +# Vector database +QDRANT_URL=http://qdrant:6333 + +# Embedding provider +OLLAMA_BASE_URL=http://ollama:11434 +``` + +> **Note:** In multi-user modes (OAuth, Multi-User BasicAuth), enabling `ENABLE_SEMANTIC_SEARCH` automatically enables background operations and refresh token storage. You don't need to set `ENABLE_BACKGROUND_OPERATIONS` separately! + ### Qdrant Vector Database Modes The server supports three Qdrant deployment modes: @@ -126,7 +231,7 @@ No configuration needed! If neither `QDRANT_URL` nor `QDRANT_LOCATION` is set, t ```dotenv # No Qdrant configuration needed - defaults to :memory: -VECTOR_SYNC_ENABLED=true +ENABLE_SEMANTIC_SEARCH=true ``` **Pros:** @@ -145,7 +250,7 @@ For single-instance deployments that need persistence without a separate Qdrant ```dotenv # Local persistent storage QDRANT_LOCATION=/app/data/qdrant # Or any writable path -VECTOR_SYNC_ENABLED=true +ENABLE_SEMANTIC_SEARCH=true ``` **Pros:** @@ -166,7 +271,7 @@ For production deployments with a dedicated Qdrant service: QDRANT_URL=http://qdrant:6333 QDRANT_API_KEY=your-secret-api-key # Optional QDRANT_COLLECTION=nextcloud_content # Optional -VECTOR_SYNC_ENABLED=true +ENABLE_SEMANTIC_SEARCH=true ``` **Pros:** @@ -283,13 +388,15 @@ Solutions: - Data corruption in Qdrant - Confusing error messages during indexing -### Vector Sync Configuration +### Background Indexing Configuration Control background indexing behavior: ```dotenv -# Vector sync settings (ADR-007) -VECTOR_SYNC_ENABLED=true # Enable background indexing +# Semantic search (ADR-007, ADR-021) +ENABLE_SEMANTIC_SEARCH=true # Enable background indexing + +# Tuning parameters (advanced - only modify if needed) VECTOR_SYNC_SCAN_INTERVAL=300 # Scan interval in seconds (default: 5 minutes) VECTOR_SYNC_PROCESSOR_WORKERS=3 # Concurrent indexing workers (default: 3) VECTOR_SYNC_QUEUE_MAX_SIZE=10000 # Max queued documents (default: 10000) @@ -299,6 +406,8 @@ DOCUMENT_CHUNK_SIZE=512 # Words per chunk (default: 512) DOCUMENT_CHUNK_OVERLAP=50 # Overlapping words between chunks (default: 50) ``` +> **Note:** The `VECTOR_SYNC_*` tuning parameters keep their names as they're implementation details. Only the user-facing feature flag was renamed to `ENABLE_SEMANTIC_SEARCH`. + ### Embedding Service Configuration The server uses an embedding service to generate vector representations. Two options are available: @@ -369,11 +478,11 @@ DOCUMENT_CHUNK_OVERLAP=100 | Variable | Required | Default | Description | |----------|----------|---------|-------------| +| `ENABLE_SEMANTIC_SEARCH` | ⚠️ Optional | `false` | Enable semantic search with background indexing (replaces `VECTOR_SYNC_ENABLED`) | | `QDRANT_URL` | ⚠️ Optional | - | Qdrant service URL (network mode) - mutually exclusive with `QDRANT_LOCATION` | | `QDRANT_LOCATION` | ⚠️ Optional | `:memory:` | Local Qdrant path (`:memory:` or `/path/to/data`) - mutually exclusive with `QDRANT_URL` | | `QDRANT_API_KEY` | ⚠️ Optional | - | Qdrant API key (network mode only) | -| `QDRANT_COLLECTION` | ⚠️ Optional | `nextcloud_content` | Qdrant collection name | -| `VECTOR_SYNC_ENABLED` | ⚠️ Optional | `false` | Enable background vector indexing | +| `QDRANT_COLLECTION` | ⚠️ Optional | Auto-generated | Qdrant collection name | | `VECTOR_SYNC_SCAN_INTERVAL` | ⚠️ Optional | `300` | Document scan interval (seconds) | | `VECTOR_SYNC_PROCESSOR_WORKERS` | ⚠️ Optional | `3` | Concurrent indexing workers | | `VECTOR_SYNC_QUEUE_MAX_SIZE` | ⚠️ Optional | `10000` | Max queued documents | @@ -383,6 +492,9 @@ DOCUMENT_CHUNK_OVERLAP=100 | `DOCUMENT_CHUNK_SIZE` | ⚠️ Optional | `512` | Words per chunk for document embedding | | `DOCUMENT_CHUNK_OVERLAP` | ⚠️ Optional | `50` | Overlapping words between chunks (must be < chunk size) | +**Deprecated variables (still functional):** +- `VECTOR_SYNC_ENABLED` - Use `ENABLE_SEMANTIC_SEARCH` instead (will be removed in v1.0.0) + ### Docker Compose Example Enable network mode Qdrant with docker-compose: @@ -392,7 +504,7 @@ services: mcp: environment: - QDRANT_URL=http://qdrant:6333 - - VECTOR_SYNC_ENABLED=true + - ENABLE_SEMANTIC_SEARCH=true qdrant: image: qdrant/qdrant:latest @@ -545,6 +657,7 @@ uv run nextcloud-mcp-server --no-oauth \ ## See Also +- [Configuration Migration Guide v2](configuration-migration-v2.md) - **New in v0.58.0:** Migrate from old variable names - [OAuth Quick Start](quickstart-oauth.md) - 5-minute OAuth setup for development - [OAuth Setup Guide](oauth-setup.md) - Detailed OAuth configuration for production - [OAuth Architecture](oauth-architecture.md) - How OAuth works in the MCP server @@ -553,3 +666,4 @@ uv run nextcloud-mcp-server --no-oauth \ - [Running the Server](running.md) - Starting the server with different configurations - [Troubleshooting](troubleshooting.md) - Common configuration issues - [OAuth Troubleshooting](oauth-troubleshooting.md) - OAuth-specific troubleshooting +- [ADR-021](ADR-021-configuration-consolidation.md) - Configuration consolidation architecture decision diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 6fcc101..dbdb444 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -4,6 +4,146 @@ This guide covers common issues and solutions for the Nextcloud MCP server. > **OAuth-specific issues?** See the dedicated [OAuth Troubleshooting Guide](oauth-troubleshooting.md) for OAuth authentication problems, OIDC discovery issues, token validation failures, and more. +> **Upgrading from v0.57.x?** See the [Configuration Migration Guide](configuration-migration-v2.md) for help with new variable names. + +## Configuration Issues (v0.58.0+) + +### Issue: Deprecation warning for VECTOR_SYNC_ENABLED + +**Symptom:** +``` +WARNING: VECTOR_SYNC_ENABLED is deprecated. Please use ENABLE_SEMANTIC_SEARCH instead. +``` + +**Cause:** You're using the old variable name from v0.57.x. + +**Solution:** +```bash +# In your .env file, replace: +VECTOR_SYNC_ENABLED=true + +# With: +ENABLE_SEMANTIC_SEARCH=true +``` + +See [Configuration Migration Guide](configuration-migration-v2.md) for complete migration instructions. + +--- + +### Issue: Deprecation warning for ENABLE_OFFLINE_ACCESS + +**Symptom:** +``` +WARNING: ENABLE_OFFLINE_ACCESS is deprecated. Please use ENABLE_BACKGROUND_OPERATIONS instead. +``` + +**Cause:** You're using the old variable name from v0.57.x. + +**Solution:** + +**If you have semantic search enabled:** +```bash +# In multi-user modes, you can remove ENABLE_OFFLINE_ACCESS entirely! +# ENABLE_SEMANTIC_SEARCH automatically enables background operations + +# Before (v0.57.x): +ENABLE_OFFLINE_ACCESS=true +VECTOR_SYNC_ENABLED=true + +# After (v0.58.0+): +ENABLE_SEMANTIC_SEARCH=true # This is all you need! +``` + +**If you only want background operations (no semantic search):** +```bash +# Replace: +ENABLE_OFFLINE_ACCESS=true + +# With: +ENABLE_BACKGROUND_OPERATIONS=true +``` + +--- + +### Issue: "Invalid MCP_DEPLOYMENT_MODE" + +**Symptom:** +``` +ValueError: Invalid MCP_DEPLOYMENT_MODE: 'oauth'. Valid values: single_user_basic, multi_user_basic, oauth_single_audience, oauth_token_exchange, smithery +``` + +**Cause:** Invalid value for `MCP_DEPLOYMENT_MODE`. + +**Solution:** +Use one of the valid mode values: +```bash +# Correct values: +MCP_DEPLOYMENT_MODE=single_user_basic # Single-user with username/password +MCP_DEPLOYMENT_MODE=multi_user_basic # Multi-user BasicAuth +MCP_DEPLOYMENT_MODE=oauth_single_audience # OAuth (recommended) +MCP_DEPLOYMENT_MODE=oauth_token_exchange # OAuth with token exchange +MCP_DEPLOYMENT_MODE=smithery # Smithery deployment +``` + +Or remove `MCP_DEPLOYMENT_MODE` to use automatic detection. + +--- + +### Issue: Missing TOKEN_ENCRYPTION_KEY when semantic search enabled + +**Symptom:** +``` +Error: [oauth_single_audience] TOKEN_ENCRYPTION_KEY is required when ENABLE_SEMANTIC_SEARCH is enabled +``` + +**Cause:** In multi-user modes, semantic search automatically enables background operations, which require encrypted token storage. + +**Solution:** +Generate an encryption key and add required token storage configuration: + +```bash +# Generate encryption key +python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" + +# Add to .env: +TOKEN_ENCRYPTION_KEY= +TOKEN_STORAGE_DB=/app/data/tokens.db +NEXTCLOUD_OIDC_CLIENT_ID=your-client-id # Required for app password retrieval +NEXTCLOUD_OIDC_CLIENT_SECRET=your-client-secret +``` + +**Why this happens:** +- v0.58.0+ automatically enables background operations when `ENABLE_SEMANTIC_SEARCH=true` in multi-user modes +- Background operations need encrypted refresh token storage +- This simplifies configuration but requires the encryption infrastructure + +See [Configuration Guide - Semantic Search](configuration.md#semantic-search-configuration-optional) for details. + +--- + +### Issue: Both old and new variable names set + +**Symptom:** +``` +WARNING: Both ENABLE_SEMANTIC_SEARCH and VECTOR_SYNC_ENABLED are set. Using ENABLE_SEMANTIC_SEARCH. +``` + +**Cause:** You have both the old and new variable names in your configuration. + +**Solution:** +Remove the old variable name: +```bash +# Remove this line: +VECTOR_SYNC_ENABLED=true + +# Keep this line: +ENABLE_SEMANTIC_SEARCH=true +``` + +The server will use the new name and ignore the old one, but it's cleaner to remove the old variable entirely. + +--- + ## OAuth Issues (Quick Reference) ### Issue: "OAuth mode requires NEXTCLOUD_HOST environment variable" diff --git a/env.sample b/env.sample index d9dc0cf..3469ced 100644 --- a/env.sample +++ b/env.sample @@ -1,203 +1,236 @@ -# Nextcloud Instance +# ============================================ +# DEPLOYMENT MODE SELECTION +# ============================================ +# Optional: Explicitly declare deployment mode (ADR-021) +# If not set, mode is auto-detected from other settings +# Valid values: single_user_basic, multi_user_basic, oauth_single_audience, +# oauth_token_exchange, smithery +# +# Recommendation: Set this for clarity and to catch configuration errors early +#MCP_DEPLOYMENT_MODE=oauth_single_audience + +# ============================================ +# COMMON SETTINGS (Required for all modes) +# ============================================ +# Your Nextcloud instance URL (without trailing slash) NEXTCLOUD_HOST= -# ===== AUTHENTICATION MODE ===== -# Choose ONE of the following: - -# Option 1: OAuth2/OIDC (RECOMMENDED - More Secure) -# - Requires Nextcloud OIDC app installed and configured -# - Admin must enable "Dynamic Client Registration" in OIDC app settings -# - Leave NEXTCLOUD_USERNAME and NEXTCLOUD_PASSWORD empty to use OAuth mode -# - OAuth client credentials are stored encrypted in SQLite (TOKEN_STORAGE_DB) -# - Optional: Pre-register client and provide credentials (otherwise auto-registers) -NEXTCLOUD_OIDC_CLIENT_ID= -NEXTCLOUD_OIDC_CLIENT_SECRET= -NEXTCLOUD_MCP_SERVER_URL=http://localhost:8000 - -# OAuth Storage Configuration (SQLite storage for OAuth clients and refresh tokens) -# TOKEN_ENCRYPTION_KEY: Required for encrypting OAuth client secrets and refresh tokens -# Generate with: python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" -#TOKEN_ENCRYPTION_KEY= -# TOKEN_STORAGE_DB: Path to SQLite database (default: /app/data/tokens.db) -#TOKEN_STORAGE_DB=/app/data/tokens.db - -# ===== ADR-004 PROGRESSIVE CONSENT CONFIGURATION ===== -# Enable Progressive Consent mode (dual OAuth flows) -# When enabled: Flow 1 for client auth, Flow 2 for Nextcloud resource access -# When disabled: Uses existing hybrid flow (backward compatible) - -# MCP Server OAuth Client Configuration -# The MCP server's own OAuth client credentials for Flow 2 -# If not set, will use dynamic client registration -#MCP_SERVER_CLIENT_ID= -#MCP_SERVER_CLIENT_SECRET= - -# Allowed MCP Client IDs (comma-separated list) -# Client IDs that are allowed to authenticate in Flow 1 -# Examples: claude-desktop,continue-dev,zed-editor -#ALLOWED_MCP_CLIENTS=claude-desktop,continue-dev,zed-editor - -# Token cache configuration for Token Broker Service -# Cache TTL in seconds (default: 300 = 5 minutes) -#TOKEN_CACHE_TTL=300 -# Early refresh threshold in seconds (default: 30) -#TOKEN_CACHE_EARLY_REFRESH=30 - -# Option 2: Basic Authentication (LEGACY - Less Secure) -# - Requires username and password -# - Credentials stored in environment variables -# - Use only for backward compatibility or if OAuth unavailable -# - If these are set, OAuth mode is disabled +# ============================================ +# SINGLE-USER BASICAUTH MODE +# ============================================ +# Simplest deployment - one user, credentials in environment +# Use for: Personal instances, local development, testing +# +# Required: NEXTCLOUD_USERNAME= NEXTCLOUD_PASSWORD= +# +# Optional features (semantic search, document processing): +# See "Optional Features" section below +# ============================================ +# MULTI-USER BASICAUTH MODE +# ============================================ +# Users provide credentials in request headers (pass-through) +# Use for: Multi-user without OAuth, simple shared deployments +# +# Required: +#ENABLE_MULTI_USER_BASIC_AUTH=true +# +# Optional - Background Operations (for semantic search, future features): +# Enable background token storage using app passwords (via Astrolabe) +# Required for semantic search in multi-user mode +# Note: ENABLE_SEMANTIC_SEARCH automatically enables this in multi-user modes +#ENABLE_BACKGROUND_OPERATIONS=true +#NEXTCLOUD_OIDC_CLIENT_ID= +#NEXTCLOUD_OIDC_CLIENT_SECRET= +#TOKEN_ENCRYPTION_KEY= +#TOKEN_STORAGE_DB=/app/data/tokens.db +# +# Optional features (semantic search, document processing): +# See "Optional Features" section below + +# ============================================ +# OAUTH SINGLE-AUDIENCE MODE (Recommended) +# ============================================ +# Multi-user OAuth with single-audience tokens +# Use for: Multi-user production deployments, enhanced security +# Tokens work for both MCP server and Nextcloud APIs (pass-through) +# +# Required: None (uses Dynamic Client Registration if credentials not provided) +# +# Optional - Pre-registered OAuth Client: +# If you pre-register the client instead of using DCR: +#NEXTCLOUD_OIDC_CLIENT_ID= +#NEXTCLOUD_OIDC_CLIENT_SECRET= +# +# Optional - Background Operations (for semantic search, future features): +# Enable refresh token storage for offline access +# Note: ENABLE_SEMANTIC_SEARCH automatically enables this in multi-user modes +#ENABLE_BACKGROUND_OPERATIONS=true +#TOKEN_ENCRYPTION_KEY= +#TOKEN_STORAGE_DB=/app/data/tokens.db +# +# Optional - Custom OIDC Discovery: +# Auto-detected from NEXTCLOUD_HOST if not set +#NEXTCLOUD_OIDC_DISCOVERY_URL= +# +# Optional - Custom Scopes: +# Default: openid profile email offline_access notes:* calendar:* contacts:* tables:* webdav:* deck:* cookbook:* +#NEXTCLOUD_OIDC_SCOPES=openid profile email notes:* calendar:* +# +# MCP Server URL (for OAuth redirects): +#NEXTCLOUD_MCP_SERVER_URL=http://localhost:8000 +# +# Optional features (semantic search, document processing): +# See "Optional Features" section below + +# ============================================ +# OAUTH TOKEN EXCHANGE MODE (Advanced) +# ============================================ +# Multi-user OAuth with RFC 8693 token exchange +# Use for: Advanced deployments requiring separate MCP and Nextcloud tokens +# MCP tokens are separate from Nextcloud tokens +# +# Required: +#ENABLE_TOKEN_EXCHANGE=true +# +# Optional - Pre-registered OAuth Client: +# If you pre-register the client instead of using DCR: +#NEXTCLOUD_OIDC_CLIENT_ID= +#NEXTCLOUD_OIDC_CLIENT_SECRET= +# +# Optional - Token Exchange Configuration: +# Cache TTL in seconds (default: 300 = 5 minutes) +#TOKEN_EXCHANGE_CACHE_TTL=300 +# +# Optional - Background Operations: +# Note: ENABLE_SEMANTIC_SEARCH automatically enables this in multi-user modes +#ENABLE_BACKGROUND_OPERATIONS=true +#TOKEN_ENCRYPTION_KEY= +#TOKEN_STORAGE_DB=/app/data/tokens.db +# +# Optional - Custom OIDC Discovery: +#NEXTCLOUD_OIDC_DISCOVERY_URL= +# +# MCP Server URL (for OAuth redirects): +#NEXTCLOUD_MCP_SERVER_URL=http://localhost:8000 +# +# Optional features (semantic search, document processing): +# See "Optional Features" section below + +# ============================================ +# SMITHERY STATELESS MODE +# ============================================ +# Stateless multi-tenant deployment for Smithery platform +# Configuration comes from session URL parameters +# No persistent storage, no OAuth, no vector sync +# +# Required: None (all config from session URL) +# This mode is activated automatically when deployed to Smithery + +# ============================================ +# OPTIONAL FEATURES (All Deployment Modes) +# ============================================ + +# ===== SEMANTIC SEARCH ===== +# AI-powered semantic search across Nextcloud content +# Requires: Qdrant vector database + embedding provider (Ollama, Bedrock, or Simple fallback) +# +# Enable semantic search: +#ENABLE_SEMANTIC_SEARCH=true +# +# Note for Multi-User Modes: +# ENABLE_SEMANTIC_SEARCH automatically enables background operations when needed +# No need to set ENABLE_BACKGROUND_OPERATIONS separately +# The server will automatically request refresh tokens and store them encrypted +# +# Vector Database - Choose ONE mode: +# 1. In-memory (default): Set neither QDRANT_URL nor QDRANT_LOCATION +# 2. Persistent local: Set QDRANT_LOCATION=/path/to/data +# 3. Network: Set QDRANT_URL=http://qdrant:6333 +# +#QDRANT_URL=http://qdrant:6333 +#QDRANT_LOCATION=:memory: +#QDRANT_API_KEY= +#QDRANT_COLLECTION=nextcloud_content +# +# Embedding Provider - Choose ONE: +# 1. Ollama (recommended for local deployment): +#OLLAMA_BASE_URL=http://ollama:11434 +#OLLAMA_EMBEDDING_MODEL=nomic-embed-text +#OLLAMA_VERIFY_SSL=true +# +# 2. Amazon Bedrock (for AWS deployments): +#AWS_REGION=us-east-1 +#BEDROCK_EMBEDDING_MODEL=amazon.titan-embed-text-v2:0 +# Optional: AWS credentials (uses credential chain if not set) +#AWS_ACCESS_KEY_ID= +#AWS_SECRET_ACCESS_KEY= +# +# 3. Simple (automatic fallback, no configuration needed) +# Uses basic in-memory embeddings if no provider configured +# +# Document Chunking: +# Configure how documents are split before embedding +#DOCUMENT_CHUNK_SIZE=512 +#DOCUMENT_CHUNK_OVERLAP=50 + +# ===== SEMANTIC SEARCH TUNING ===== +# Advanced parameters for vector sync background operations +# Only modify if you understand the implications +# +# Document scan interval in seconds (default: 300 = 5 minutes) +#VECTOR_SYNC_SCAN_INTERVAL=300 +# +# Concurrent indexing workers (default: 3) +#VECTOR_SYNC_PROCESSOR_WORKERS=3 +# +# Max queued documents (default: 10000) +#VECTOR_SYNC_QUEUE_MAX_SIZE=10000 + +# ===== DOCUMENT PROCESSING ===== +# Extract text from PDFs, images, DOCX, etc. for semantic search +# Disabled by default +# +#ENABLE_DOCUMENT_PROCESSING=false +#DOCUMENT_PROCESSOR=unstructured +# +# Unstructured.io Processor (recommended): +#ENABLE_UNSTRUCTURED=false +#UNSTRUCTURED_API_URL=http://unstructured:8000 +#UNSTRUCTURED_TIMEOUT=120 +#UNSTRUCTURED_STRATEGY=auto +#UNSTRUCTURED_LANGUAGES=eng,deu +#PROGRESS_INTERVAL=10 +# +# Tesseract OCR (lightweight, images only): +#ENABLE_TESSERACT=false +#TESSERACT_CMD=/usr/bin/tesseract +#TESSERACT_LANG=eng +# +# Custom Processor (your own API): +#ENABLE_CUSTOM_PROCESSOR=false +#CUSTOM_PROCESSOR_NAME=my_ocr +#CUSTOM_PROCESSOR_URL=http://localhost:9000/process +#CUSTOM_PROCESSOR_API_KEY= +#CUSTOM_PROCESSOR_TIMEOUT=60 +#CUSTOM_PROCESSOR_TYPES=application/pdf,image/jpeg,image/png + +# ===== SECURITY & ADVANCED ===== # Cookie security (browser UI) # Auto-detects from NEXTCLOUD_HOST protocol if not set -# Set explicitly for non-standard setups #COOKIE_SECURE=true # ============================================ -# Document Processing Configuration +# DEPRECATED VARIABLES (Backward Compatibility) # ============================================ -# Enable document processing (PDF, DOCX, images, etc.) -# Set to false to disable all document processing -ENABLE_DOCUMENT_PROCESSING=false - -# Default processor to use when multiple are available -# Options: unstructured, tesseract, custom -DOCUMENT_PROCESSOR=unstructured - -# ============================================ -# Unstructured.io Processor -# ============================================ -# Enable Unstructured processor (requires unstructured service in docker-compose) -# This is a cloud-based/API processor supporting many document types -ENABLE_UNSTRUCTURED=false - -# Unstructured API endpoint -UNSTRUCTURED_API_URL=http://unstructured:8000 - -# Request timeout in seconds (default: 120) -# OCR operations can take 30-120 seconds for large documents -UNSTRUCTURED_TIMEOUT=120 - -# Parsing strategy: auto, fast, hi_res -# - auto: Automatically choose based on document type -# - fast: Fast parsing without OCR -# - hi_res: High-resolution with OCR (slowest, most accurate) -UNSTRUCTURED_STRATEGY=auto - -# OCR languages (comma-separated ISO 639-3 codes) -# Common: eng=English, deu=German, fra=French, spa=Spanish -UNSTRUCTURED_LANGUAGES=eng,deu - -# Progress reporting interval in seconds (default: 10) -# During long-running OCR operations, progress notifications are sent to the MCP client -# at this interval to prevent timeouts and provide status updates -PROGRESS_INTERVAL=10 - -# ============================================ -# Tesseract Processor (Local OCR) -# ============================================ -# Enable Tesseract processor (requires tesseract binary installed) -# This is a local, lightweight OCR solution for images only -ENABLE_TESSERACT=false - -# Path to tesseract executable (optional, auto-detected if in PATH) -#TESSERACT_CMD=/usr/bin/tesseract - -# OCR language (e.g., eng, deu, eng+deu for multiple) -TESSERACT_LANG=eng - -# ============================================ -# Custom Processor (Your own API) -# ============================================ -# Enable custom document processor via HTTP API -ENABLE_CUSTOM_PROCESSOR=false - -# Unique name for your processor -#CUSTOM_PROCESSOR_NAME=my_ocr - -# Your custom processor API endpoint -#CUSTOM_PROCESSOR_URL=http://localhost:9000/process - -# Optional API key for authentication -#CUSTOM_PROCESSOR_API_KEY=your-api-key-here - -# Request timeout in seconds -#CUSTOM_PROCESSOR_TIMEOUT=60 - -# Comma-separated MIME types your processor supports -#CUSTOM_PROCESSOR_TYPES=application/pdf,image/jpeg,image/png - -# ============================================ -# Semantic Search & Vector Sync Configuration -# ============================================ -# EXPERIMENTAL: Semantic search for Notes app (multi-app support planned) -# Requires: Qdrant vector database + Ollama embedding service -# Disabled by default - -# Enable background vector indexing -VECTOR_SYNC_ENABLED=false - -# Document scan interval in seconds (default: 300 = 5 minutes) -# How often to check for new/updated documents -#VECTOR_SYNC_SCAN_INTERVAL=300 - -# Concurrent indexing workers (default: 3) -# Number of parallel workers for embedding generation -#VECTOR_SYNC_PROCESSOR_WORKERS=3 - -# Max queued documents (default: 10000) -# Maximum documents waiting to be processed -#VECTOR_SYNC_QUEUE_MAX_SIZE=10000 - -# ============================================ -# Qdrant Vector Database Configuration -# ============================================ -# Choose ONE of three modes: -# 1. In-memory mode (default): Set neither QDRANT_URL nor QDRANT_LOCATION -# 2. Persistent local: Set QDRANT_LOCATION=/path/to/data -# 3. Network mode: Set QDRANT_URL=http://qdrant:6333 - -# Network mode: URL to Qdrant service -#QDRANT_URL=http://qdrant:6333 - -# Local mode: Path to store vectors (use :memory: for in-memory) -#QDRANT_LOCATION=:memory: - -# API key for network mode (optional) -#QDRANT_API_KEY= - -# Collection name (optional - auto-generated if not set) -# Auto-generation format: {deployment-id}-{model-name} -# Allows safe model switching and multi-server deployments -#QDRANT_COLLECTION=nextcloud_content - -# ============================================ -# Ollama Embedding Service Configuration -# ============================================ -# Ollama endpoint for embeddings (if not set, uses SimpleEmbeddingProvider fallback) -#OLLAMA_BASE_URL=http://ollama:11434 - -# Embedding model to use (default: nomic-embed-text, 768 dimensions) -# Changing this creates a new collection (requires re-embedding all documents) -#OLLAMA_EMBEDDING_MODEL=nomic-embed-text - -# Verify SSL certificates (default: true) -#OLLAMA_VERIFY_SSL=true - -# ============================================ -# Document Chunking Configuration -# ============================================ -# Configure how documents are split before embedding - -# Words per chunk (default: 512) -# Smaller chunks (256-384): More precise, less context, more storage -# Larger chunks (768-1024): More context, less precise, less storage -#DOCUMENT_CHUNK_SIZE=512 - -# Overlapping words between chunks (default: 50) -# Recommended: 10-20% of chunk size -# Preserves context across chunk boundaries -#DOCUMENT_CHUNK_OVERLAP=50 +# These variables still work but will be removed in v1.0.0 +# Please migrate to new names: +# +# Old Name → New Name +# VECTOR_SYNC_ENABLED → ENABLE_SEMANTIC_SEARCH +# ENABLE_OFFLINE_ACCESS → ENABLE_BACKGROUND_OPERATIONS +# +# Migration is optional - both old and new names work +# Deprecation warnings will be logged when old names are used diff --git a/env.sample.oauth-advanced b/env.sample.oauth-advanced new file mode 100644 index 0000000..ca80559 --- /dev/null +++ b/env.sample.oauth-advanced @@ -0,0 +1,80 @@ +# ============================================ +# OAUTH TOKEN EXCHANGE QUICK START (Advanced) +# ============================================ +# Advanced OAuth deployment with RFC 8693 token exchange +# Use for: Deployments requiring separate MCP and Nextcloud tokens +# Features: Dual-audience tokens, enhanced security boundaries +# +# Copy this file to .env and configure + +# ===== REQUIRED SETTINGS ===== +# Your Nextcloud instance URL (without trailing slash) +NEXTCLOUD_HOST=https://nextcloud.example.com + +# Enable token exchange mode +ENABLE_TOKEN_EXCHANGE=true + +# ===== REQUIRED: LEAVE USERNAME/PASSWORD EMPTY ===== +# OAuth mode activates when these are NOT set +NEXTCLOUD_USERNAME= +NEXTCLOUD_PASSWORD= + +# ===== OPTIONAL: EXPLICIT MODE DECLARATION ===== +# Recommended for clarity +MCP_DEPLOYMENT_MODE=oauth_token_exchange + +# ===== OPTIONAL: PRE-REGISTERED OAUTH CLIENT ===== +# If you pre-register the OAuth client instead of using DCR: +#NEXTCLOUD_OIDC_CLIENT_ID=your-client-id +#NEXTCLOUD_OIDC_CLIENT_SECRET=your-client-secret + +# MCP Server URL (for OAuth redirects) +NEXTCLOUD_MCP_SERVER_URL=http://localhost:8000 + +# ===== OPTIONAL: TOKEN EXCHANGE TUNING ===== +# Cache TTL for exchanged tokens (default: 300 seconds = 5 minutes) +TOKEN_EXCHANGE_CACHE_TTL=300 + +# ===== OPTIONAL: SEMANTIC SEARCH ===== +# AI-powered semantic search with automatic background operation setup +# +# Note: ENABLE_SEMANTIC_SEARCH automatically enables background operations +# in token exchange mode, just like in OAuth single-audience mode +# +ENABLE_SEMANTIC_SEARCH=true + +# Vector Database (required for semantic search) +QDRANT_URL=http://qdrant:6333 + +# Embedding Provider (required for semantic search) +OLLAMA_BASE_URL=http://ollama:11434 +OLLAMA_EMBEDDING_MODEL=nomic-embed-text + +# Token Storage (required for background operations - auto-enabled by semantic search) +# Generate encryption key: python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" +TOKEN_ENCRYPTION_KEY=your-encryption-key-here +TOKEN_STORAGE_DB=/app/data/tokens.db + +# ===== OPTIONAL: DOCUMENT PROCESSING ===== +# Extract text from PDFs, images, DOCX for semantic search +#ENABLE_DOCUMENT_PROCESSING=true +#ENABLE_UNSTRUCTURED=true +#UNSTRUCTURED_API_URL=http://unstructured:8000 + +# ===== TOKEN EXCHANGE MODE EXPLANATION ===== +# In this mode: +# 1. MCP clients authenticate with tokens scoped to "mcp-server" audience +# 2. Server exchanges MCP tokens for Nextcloud tokens on each request +# 3. Provides clear separation between MCP session and Nextcloud access +# 4. Enables fine-grained token lifecycle management +# +# When to use: +# - Strict security requirements (separate token contexts) +# - Complex multi-service architectures +# - Need independent token expiration policies +# +# When NOT to use: +# - Simple deployments (use oauth_single_audience instead) +# - High-performance requirements (token exchange adds latency) + +# For more configuration options, see env.sample diff --git a/env.sample.oauth-multi-user b/env.sample.oauth-multi-user new file mode 100644 index 0000000..f61ad18 --- /dev/null +++ b/env.sample.oauth-multi-user @@ -0,0 +1,77 @@ +# ============================================ +# OAUTH MULTI-USER QUICK START (Recommended) +# ============================================ +# Multi-user deployment with OAuth authentication +# Use for: Multi-user production deployments, enhanced security +# Features: Single-audience tokens, automatic client registration (DCR) +# +# Copy this file to .env and configure + +# ===== REQUIRED SETTINGS ===== +# Your Nextcloud instance URL (without trailing slash) +NEXTCLOUD_HOST=https://nextcloud.example.com + +# ===== REQUIRED: LEAVE USERNAME/PASSWORD EMPTY ===== +# OAuth mode activates when these are NOT set +NEXTCLOUD_USERNAME= +NEXTCLOUD_PASSWORD= + +# ===== OPTIONAL: EXPLICIT MODE DECLARATION ===== +# Recommended for clarity +MCP_DEPLOYMENT_MODE=oauth_single_audience + +# ===== OPTIONAL: PRE-REGISTERED OAUTH CLIENT ===== +# If you pre-register the OAuth client instead of using DCR: +#NEXTCLOUD_OIDC_CLIENT_ID=your-client-id +#NEXTCLOUD_OIDC_CLIENT_SECRET=your-client-secret + +# MCP Server URL (for OAuth redirects) +NEXTCLOUD_MCP_SERVER_URL=http://localhost:8000 + +# ===== OPTIONAL: SEMANTIC SEARCH (Recommended) ===== +# AI-powered semantic search with automatic background operation setup +# +# When you enable semantic search in multi-user mode: +# 1. ENABLE_SEMANTIC_SEARCH automatically enables background operations +# 2. Server requests refresh tokens for offline indexing +# 3. Tokens are stored encrypted in TOKEN_STORAGE_DB +# 4. No need to set ENABLE_BACKGROUND_OPERATIONS separately! +# +ENABLE_SEMANTIC_SEARCH=true + +# Vector Database (required for semantic search) +QDRANT_URL=http://qdrant:6333 +# OR for in-memory mode: +#QDRANT_LOCATION=:memory: + +# Embedding Provider (required for semantic search) +# Option 1: Ollama (recommended for local deployment) +OLLAMA_BASE_URL=http://ollama:11434 +OLLAMA_EMBEDDING_MODEL=nomic-embed-text + +# Option 2: Amazon Bedrock (for AWS deployments) +#AWS_REGION=us-east-1 +#BEDROCK_EMBEDDING_MODEL=amazon.titan-embed-text-v2:0 + +# Token Storage (required for background operations - auto-enabled by semantic search) +# Generate encryption key: python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" +TOKEN_ENCRYPTION_KEY=your-encryption-key-here +TOKEN_STORAGE_DB=/app/data/tokens.db + +# ===== OPTIONAL: DOCUMENT PROCESSING ===== +# Extract text from PDFs, images, DOCX for semantic search +#ENABLE_DOCUMENT_PROCESSING=true +#ENABLE_UNSTRUCTURED=true +#UNSTRUCTURED_API_URL=http://unstructured:8000 + +# ===== SUMMARY OF AUTO-ENABLEMENT ===== +# With ENABLE_SEMANTIC_SEARCH=true in OAuth mode: +# ✅ Background operations enabled automatically +# ✅ Refresh token storage enabled automatically +# ✅ OAuth credentials required (DCR or pre-registered) +# ✅ Encryption key required for token storage +# +# You only need to set ENABLE_SEMANTIC_SEARCH and provide the required +# infrastructure (Qdrant, Ollama, encryption key). The rest is automatic! + +# For more advanced configuration, see env.sample diff --git a/env.sample.single-user b/env.sample.single-user new file mode 100644 index 0000000..c62937d --- /dev/null +++ b/env.sample.single-user @@ -0,0 +1,37 @@ +# ============================================ +# SINGLE-USER BASICAUTH QUICK START +# ============================================ +# Simplest deployment mode - one user, credentials in environment +# Use for: Personal instances, local development, testing +# +# Copy this file to .env and fill in your credentials + +# ===== REQUIRED SETTINGS ===== +# Your Nextcloud instance URL (without trailing slash) +NEXTCLOUD_HOST=http://localhost:8080 + +# Your Nextcloud credentials +NEXTCLOUD_USERNAME=admin +NEXTCLOUD_PASSWORD=password + +# ===== OPTIONAL: EXPLICIT MODE DECLARATION ===== +# Recommended to avoid ambiguity +MCP_DEPLOYMENT_MODE=single_user_basic + +# ===== OPTIONAL: SEMANTIC SEARCH ===== +# Uncomment to enable AI-powered semantic search +# Requires: Qdrant + embedding provider (Ollama or Bedrock) +# +#ENABLE_SEMANTIC_SEARCH=true +#QDRANT_LOCATION=:memory: +#OLLAMA_BASE_URL=http://ollama:11434 +#OLLAMA_EMBEDDING_MODEL=nomic-embed-text + +# ===== OPTIONAL: DOCUMENT PROCESSING ===== +# Extract text from PDFs, images, DOCX for semantic search +#ENABLE_DOCUMENT_PROCESSING=true +#ENABLE_UNSTRUCTURED=true +#UNSTRUCTURED_API_URL=http://unstructured:8000 + +# That's it! Single-user mode is the simplest to configure. +# For more options, see env.sample diff --git a/nextcloud_mcp_server/config.py b/nextcloud_mcp_server/config.py index 11e2268..f0120ae 100644 --- a/nextcloud_mcp_server/config.py +++ b/nextcloud_mcp_server/config.py @@ -163,6 +163,12 @@ def get_document_processor_config() -> dict[str, Any]: class Settings: """Application settings from environment variables.""" + # Deployment mode (ADR-021: explicit mode selection) + # Optional: If not set, mode is auto-detected from other settings + # Valid values: single_user_basic, multi_user_basic, oauth_single_audience, + # oauth_token_exchange, smithery + deployment_mode: Optional[str] = None + # OAuth/OIDC settings oidc_discovery_url: Optional[str] = None oidc_client_id: Optional[str] = None @@ -351,13 +357,131 @@ class Settings: return f"{deployment_id}-{model_name}" +def _get_semantic_search_enabled() -> bool: + """Get semantic search enabled status, supporting both old and new variable names. + + Supports: + - ENABLE_SEMANTIC_SEARCH (new, preferred) + - VECTOR_SYNC_ENABLED (old, deprecated) + + Returns: + True if semantic search should be enabled + """ + logger = logging.getLogger(__name__) + + new_value = os.getenv("ENABLE_SEMANTIC_SEARCH", "").lower() == "true" + old_value = os.getenv("VECTOR_SYNC_ENABLED", "").lower() == "true" + + if new_value and old_value: + logger.warning( + "Both ENABLE_SEMANTIC_SEARCH and VECTOR_SYNC_ENABLED are set. " + "Using ENABLE_SEMANTIC_SEARCH. " + "VECTOR_SYNC_ENABLED is deprecated and will be removed in v1.0.0." + ) + elif old_value and not new_value: + logger.warning( + "VECTOR_SYNC_ENABLED is deprecated. " + "Please use ENABLE_SEMANTIC_SEARCH instead. " + "Support for VECTOR_SYNC_ENABLED will be removed in v1.0.0." + ) + + return new_value or old_value + + +def _is_multi_user_mode() -> bool: + """Detect if this is a multi-user deployment mode. + + Multi-user modes are: + - Multi-user BasicAuth (ENABLE_MULTI_USER_BASIC_AUTH=true) + - OAuth Single-Audience (no username/password set) + - OAuth Token Exchange (ENABLE_TOKEN_EXCHANGE=true) + + Single-user modes are: + - Single-user BasicAuth (username and password both set) + - Smithery Stateless (SMITHERY_DEPLOYMENT=true) + + Returns: + True if multi-user mode detected + """ + # Smithery is always single-user (stateless) + if os.getenv("SMITHERY_DEPLOYMENT", "false").lower() == "true": + return False + + # Multi-user BasicAuth explicitly enabled + if os.getenv("ENABLE_MULTI_USER_BASIC_AUTH", "false").lower() == "true": + return True + + # Token exchange implies OAuth multi-user + if os.getenv("ENABLE_TOKEN_EXCHANGE", "false").lower() == "true": + return True + + # If both username and password are set, it's single-user BasicAuth + has_username = bool(os.getenv("NEXTCLOUD_USERNAME")) + has_password = bool(os.getenv("NEXTCLOUD_PASSWORD")) + if has_username and has_password: + return False + + # Otherwise, assume OAuth multi-user (default when no credentials provided) + return True + + +def _get_background_operations_enabled() -> bool: + """Get background operations enabled status with auto-enablement for semantic search. + + Supports: + - ENABLE_BACKGROUND_OPERATIONS (new, preferred) + - ENABLE_OFFLINE_ACCESS (old, deprecated) + - Auto-enabled if ENABLE_SEMANTIC_SEARCH=true in multi-user modes + + Returns: + True if background operations should be enabled + """ + logger = logging.getLogger(__name__) + + # Check new and old variable names + explicit = os.getenv("ENABLE_BACKGROUND_OPERATIONS", "").lower() == "true" + legacy = os.getenv("ENABLE_OFFLINE_ACCESS", "").lower() == "true" + + if explicit and legacy: + logger.warning( + "Both ENABLE_BACKGROUND_OPERATIONS and ENABLE_OFFLINE_ACCESS are set. " + "Using ENABLE_BACKGROUND_OPERATIONS. " + "ENABLE_OFFLINE_ACCESS is deprecated and will be removed in v1.0.0." + ) + elif legacy and not explicit: + logger.warning( + "ENABLE_OFFLINE_ACCESS is deprecated. " + "Please use ENABLE_BACKGROUND_OPERATIONS instead. " + "Support for ENABLE_OFFLINE_ACCESS will be removed in v1.0.0." + ) + + # Auto-enable if semantic search is enabled in multi-user mode + semantic_search_enabled = _get_semantic_search_enabled() + is_multi_user = _is_multi_user_mode() + auto_enabled = semantic_search_enabled and is_multi_user + + if auto_enabled and not (explicit or legacy): + logger.info( + "Automatically enabled background operations for semantic search in multi-user mode. " + "Set ENABLE_BACKGROUND_OPERATIONS=false to disable (this will also disable semantic search)." + ) + + return explicit or legacy or auto_enabled + + def get_settings() -> Settings: """Get application settings from environment variables. Returns: Settings object with configuration values """ + # Get consolidated values with smart dependency resolution + enable_semantic_search = _get_semantic_search_enabled() + enable_background_operations = _get_background_operations_enabled() + return Settings( + # Deployment mode (ADR-021) + deployment_mode=os.getenv("MCP_DEPLOYMENT_MODE"), # OAuth/OIDC settings oidc_discovery_url=os.getenv("OIDC_DISCOVERY_URL"), oidc_client_id=os.getenv("NEXTCLOUD_OIDC_CLIENT_ID"), @@ -378,9 +502,7 @@ def get_settings() -> Settings: enable_token_exchange=( os.getenv("ENABLE_TOKEN_EXCHANGE", "false").lower() == "true" ), - enable_offline_access=( - os.getenv("ENABLE_OFFLINE_ACCESS", "false").lower() == "true" - ), + enable_offline_access=enable_background_operations, # Smart dependency resolution # Multi-user BasicAuth pass-through mode enable_multi_user_basic_auth=( os.getenv("ENABLE_MULTI_USER_BASIC_AUTH", "false").lower() == "true" @@ -391,9 +513,7 @@ def get_settings() -> Settings: token_encryption_key=os.getenv("TOKEN_ENCRYPTION_KEY"), token_storage_db=os.getenv("TOKEN_STORAGE_DB", "/tmp/tokens.db"), # Vector sync settings (ADR-007) - vector_sync_enabled=( - os.getenv("VECTOR_SYNC_ENABLED", "false").lower() == "true" - ), + vector_sync_enabled=enable_semantic_search, # Smart dependency resolution vector_sync_scan_interval=int(os.getenv("VECTOR_SYNC_SCAN_INTERVAL", "300")), vector_sync_processor_workers=int( os.getenv("VECTOR_SYNC_PROCESSOR_WORKERS", "3") diff --git a/nextcloud_mcp_server/config_validators.py b/nextcloud_mcp_server/config_validators.py index e6343fc..0fbc872 100644 --- a/nextcloud_mcp_server/config_validators.py +++ b/nextcloud_mcp_server/config_validators.py @@ -110,10 +110,9 @@ MODE_REQUIREMENTS: dict[AuthMode, ModeRequirements] = { "token_encryption_key", "token_storage_db", ], - "vector_sync_enabled": [ - # Requires offline access for background sync - "enable_offline_access", - ], + # Note: vector_sync_enabled (now ENABLE_SEMANTIC_SEARCH) automatically + # enables background operations in multi-user modes. No explicit + # enable_offline_access setting required. }, description="Multi-user deployment with BasicAuth pass-through. " "Users provide credentials in request headers. " @@ -152,9 +151,9 @@ MODE_REQUIREMENTS: dict[AuthMode, ModeRequirements] = { "token_encryption_key", "token_storage_db", ], - "vector_sync_enabled": [ - "enable_offline_access", # Background sync requires refresh tokens - ], + # Note: vector_sync_enabled (now ENABLE_SEMANTIC_SEARCH) automatically + # enables background operations in multi-user modes. No explicit + # enable_offline_access setting required. }, description="OAuth multi-user deployment with single-audience tokens. " "Tokens work for both MCP server and Nextcloud APIs (pass-through). " @@ -192,9 +191,9 @@ MODE_REQUIREMENTS: dict[AuthMode, ModeRequirements] = { "token_encryption_key", "token_storage_db", ], - "vector_sync_enabled": [ - "enable_offline_access", - ], + # Note: vector_sync_enabled (now ENABLE_SEMANTIC_SEARCH) automatically + # enables background operations in multi-user modes. No explicit + # enable_offline_access setting required. }, description="OAuth multi-user deployment with token exchange (RFC 8693). " "MCP tokens are separate from Nextcloud tokens. " @@ -225,7 +224,8 @@ MODE_REQUIREMENTS: dict[AuthMode, ModeRequirements] = { def detect_auth_mode(settings: Settings) -> AuthMode: """Detect authentication mode from configuration. - Mode detection priority (most specific to most general): + Mode detection priority (ADR-021): + 0. Explicit MCP_DEPLOYMENT_MODE (if set) - NEW in ADR-021 1. Smithery (explicit flag) 2. Token exchange (most specific OAuth mode) 3. Multi-user BasicAuth @@ -237,12 +237,43 @@ def detect_auth_mode(settings: Settings) -> AuthMode: Returns: Detected AuthMode + + Raises: + ValueError: If explicit deployment_mode is invalid or conflicts with detected mode """ + import logging + import os + + logger = logging.getLogger(__name__) + + # ADR-021: Check for explicit deployment mode first + if settings.deployment_mode: + mode_str = settings.deployment_mode.lower().strip() + + # Map string to AuthMode enum + mode_map = { + "single_user_basic": AuthMode.SINGLE_USER_BASIC, + "multi_user_basic": AuthMode.MULTI_USER_BASIC, + "oauth_single_audience": AuthMode.OAUTH_SINGLE_AUDIENCE, + "oauth_token_exchange": AuthMode.OAUTH_TOKEN_EXCHANGE, + "smithery": AuthMode.SMITHERY_STATELESS, + } + + if mode_str not in mode_map: + valid_modes = ", ".join(mode_map.keys()) + raise ValueError( + f"Invalid MCP_DEPLOYMENT_MODE: '{settings.deployment_mode}'. " + f"Valid values: {valid_modes}" + ) + + explicit_mode = mode_map[mode_str] + logger.info(f"Using explicit deployment mode: {explicit_mode.value}") + return explicit_mode + + # Auto-detection (existing behavior) # Check for Smithery mode (explicit environment variable) # Note: This checks the environment directly, not settings # because Smithery mode has no settings-based config - import os - if os.getenv("SMITHERY_DEPLOYMENT", "false").lower() == "true": return AuthMode.SMITHERY_STATELESS @@ -364,22 +395,20 @@ def validate_configuration(settings: Settings) -> tuple[AuthMode, list[str]]: ) if mode == AuthMode.MULTI_USER_BASIC: - # Validate that if offline access enabled, we have OAuth credentials + # Validate that if background operations enabled, we have OAuth credentials if settings.enable_offline_access: if not settings.oidc_client_id or not settings.oidc_client_secret: errors.append( f"[{mode.value}] NEXTCLOUD_OIDC_CLIENT_ID and " "NEXTCLOUD_OIDC_CLIENT_SECRET are required when " - "ENABLE_OFFLINE_ACCESS is enabled (for app password retrieval)" + "ENABLE_BACKGROUND_OPERATIONS (or deprecated ENABLE_OFFLINE_ACCESS) " + "is enabled (for app password retrieval)" ) - # Validate vector sync requirements - if settings.vector_sync_enabled and not settings.enable_offline_access: - errors.append( - f"[{mode.value}] ENABLE_OFFLINE_ACCESS must be enabled when " - "VECTOR_SYNC_ENABLED is true (background sync requires " - "app passwords or refresh tokens)" - ) + # Note: Vector sync no longer requires explicit ENABLE_OFFLINE_ACCESS setting + # ENABLE_SEMANTIC_SEARCH (formerly VECTOR_SYNC_ENABLED) automatically enables + # background operations in multi-user modes via smart dependency resolution + # in config.py # Note: Embedding provider validation removed - Simple provider is always # available as fallback (ADR-015). Users can optionally configure Ollama or OpenAI diff --git a/tests/unit/test_config_validators.py b/tests/unit/test_config_validators.py index aa3f546..07b50ee 100644 --- a/tests/unit/test_config_validators.py +++ b/tests/unit/test_config_validators.py @@ -311,20 +311,35 @@ class TestMultiUserBasicValidation: assert mode == AuthMode.MULTI_USER_BASIC assert any("token_encryption_key" in err.lower() for err in errors) - def test_vector_sync_requires_offline_access(self): - """Test error when vector sync enabled but offline access disabled.""" - settings = Settings( - nextcloud_host="http://localhost", - enable_multi_user_basic_auth=True, - vector_sync_enabled=True, - qdrant_location=":memory:", - ollama_base_url="http://ollama:11434", - ) + def test_vector_sync_auto_enables_background_ops_in_multi_user_mode(self): + """Test vector sync automatically enables background operations in multi-user mode (ADR-021).""" + # Before ADR-021: This would have failed validation (required explicit ENABLE_OFFLINE_ACCESS) + # After ADR-021: vector_sync_enabled auto-enables background operations + with patch.dict( + os.environ, + { + "NEXTCLOUD_HOST": "http://localhost:8080", + "ENABLE_MULTI_USER_BASIC_AUTH": "true", + "VECTOR_SYNC_ENABLED": "true", # Using old name for backward compat test + "QDRANT_LOCATION": ":memory:", + "OLLAMA_BASE_URL": "http://ollama:11434", + "TOKEN_ENCRYPTION_KEY": "test-key", + "TOKEN_STORAGE_DB": "/tmp/test.db", + "NEXTCLOUD_OIDC_CLIENT_ID": "test-client-id", + "NEXTCLOUD_OIDC_CLIENT_SECRET": "test-client-secret", + }, + clear=True, + ): + from nextcloud_mcp_server.config import get_settings - mode, errors = validate_configuration(settings) + settings = get_settings() + mode, errors = validate_configuration(settings) - assert mode == AuthMode.MULTI_USER_BASIC - assert any("enable_offline_access" in err.lower() for err in errors) + assert mode == AuthMode.MULTI_USER_BASIC + # Should have no errors - background operations auto-enabled + assert len(errors) == 0 + # Verify background operations were auto-enabled + assert settings.enable_offline_access is True class TestOAuthSingleAudienceValidation: @@ -396,19 +411,33 @@ class TestOAuthSingleAudienceValidation: assert mode == AuthMode.OAUTH_SINGLE_AUDIENCE assert any("token_encryption_key" in err.lower() for err in errors) - def test_vector_sync_requires_offline_access(self): - """Test error when vector sync enabled but offline access disabled.""" - settings = Settings( - nextcloud_host="http://localhost", - vector_sync_enabled=True, - qdrant_location=":memory:", - ollama_base_url="http://ollama:11434", - ) + def test_vector_sync_auto_enables_background_ops_in_oauth_mode(self): + """Test vector sync automatically enables background operations in OAuth mode (ADR-021).""" + # Before ADR-021: This would have failed validation (required explicit ENABLE_OFFLINE_ACCESS) + # After ADR-021: vector_sync_enabled auto-enables background operations in multi-user modes + with patch.dict( + os.environ, + { + "NEXTCLOUD_HOST": "http://localhost:8080", + "VECTOR_SYNC_ENABLED": "true", + "QDRANT_LOCATION": ":memory:", + "OLLAMA_BASE_URL": "http://ollama:11434", + "TOKEN_ENCRYPTION_KEY": "test-key", + "TOKEN_STORAGE_DB": "/tmp/test.db", + # Note: No username/password = OAuth mode + }, + clear=True, + ): + from nextcloud_mcp_server.config import get_settings - mode, errors = validate_configuration(settings) + settings = get_settings() + mode, errors = validate_configuration(settings) - assert mode == AuthMode.OAUTH_SINGLE_AUDIENCE - assert any("enable_offline_access" in err.lower() for err in errors) + assert mode == AuthMode.OAUTH_SINGLE_AUDIENCE + # Should have no errors - background operations auto-enabled + assert len(errors) == 0 + # Verify background operations were auto-enabled + assert settings.enable_offline_access is True class TestOAuthTokenExchangeValidation: @@ -576,3 +605,387 @@ class TestEdgeCases: # Should have errors for missing host (OAuth mode is default) assert len(errors) > 0 + + +class TestConfigurationConsolidation: + """Test ADR-021 configuration consolidation and backward compatibility. + + Tests verify: + - New variable names work (ENABLE_SEMANTIC_SEARCH, ENABLE_BACKGROUND_OPERATIONS) + - Old variable names still work (VECTOR_SYNC_ENABLED, ENABLE_OFFLINE_ACCESS) + - Deprecation warnings are logged + - Auto-enablement of background operations in multi-user modes + """ + + def test_new_semantic_search_variable_name(self): + """Test ENABLE_SEMANTIC_SEARCH (new name) works correctly.""" + with patch.dict( + os.environ, + { + "ENABLE_SEMANTIC_SEARCH": "true", + "QDRANT_LOCATION": ":memory:", + }, + clear=True, + ): + from nextcloud_mcp_server.config import get_settings + + settings = get_settings() + assert settings.vector_sync_enabled is True + + def test_old_vector_sync_variable_name_backward_compat(self): + """Test VECTOR_SYNC_ENABLED (old name) still works for backward compatibility.""" + with patch.dict( + os.environ, + { + "VECTOR_SYNC_ENABLED": "true", + "QDRANT_LOCATION": ":memory:", + }, + clear=True, + ): + from nextcloud_mcp_server.config import get_settings + + settings = get_settings() + assert settings.vector_sync_enabled is True + + def test_new_background_operations_variable_name(self): + """Test ENABLE_BACKGROUND_OPERATIONS (new name) works correctly.""" + with patch.dict( + os.environ, + { + "ENABLE_BACKGROUND_OPERATIONS": "true", + "TOKEN_ENCRYPTION_KEY": "test-key", + "TOKEN_STORAGE_DB": "/tmp/test.db", + }, + clear=True, + ): + from nextcloud_mcp_server.config import get_settings + + settings = get_settings() + assert settings.enable_offline_access is True + + def test_old_offline_access_variable_name_backward_compat(self): + """Test ENABLE_OFFLINE_ACCESS (old name) still works for backward compatibility.""" + with patch.dict( + os.environ, + { + "ENABLE_OFFLINE_ACCESS": "true", + "TOKEN_ENCRYPTION_KEY": "test-key", + "TOKEN_STORAGE_DB": "/tmp/test.db", + }, + clear=True, + ): + from nextcloud_mcp_server.config import get_settings + + settings = get_settings() + assert settings.enable_offline_access is True + + def test_semantic_search_auto_enables_background_ops_in_oauth_mode(self): + """Test ENABLE_SEMANTIC_SEARCH automatically enables background operations in OAuth mode.""" + with patch.dict( + os.environ, + { + "NEXTCLOUD_HOST": "http://localhost:8080", + "ENABLE_SEMANTIC_SEARCH": "true", + "QDRANT_LOCATION": ":memory:", + "TOKEN_ENCRYPTION_KEY": "test-key", + "TOKEN_STORAGE_DB": "/tmp/test.db", + # Note: No NEXTCLOUD_USERNAME/PASSWORD = OAuth mode + }, + clear=True, + ): + from nextcloud_mcp_server.config import get_settings + + settings = get_settings() + + # Semantic search enabled + assert settings.vector_sync_enabled is True + + # Background operations auto-enabled (even though not explicitly set) + assert settings.enable_offline_access is True + + def test_semantic_search_does_not_auto_enable_in_single_user_mode(self): + """Test ENABLE_SEMANTIC_SEARCH does NOT auto-enable background ops in single-user mode.""" + with patch.dict( + os.environ, + { + "NEXTCLOUD_HOST": "http://localhost:8080", + "NEXTCLOUD_USERNAME": "admin", + "NEXTCLOUD_PASSWORD": "password", + "ENABLE_SEMANTIC_SEARCH": "true", + "QDRANT_LOCATION": ":memory:", + # Note: Username/password set = single-user BasicAuth mode + }, + clear=True, + ): + from nextcloud_mcp_server.config import get_settings + + settings = get_settings() + + # Semantic search enabled + assert settings.vector_sync_enabled is True + + # Background operations NOT auto-enabled (not needed in single-user mode) + assert settings.enable_offline_access is False + + def test_explicit_background_ops_still_works(self): + """Test explicitly setting ENABLE_BACKGROUND_OPERATIONS works even without semantic search.""" + with patch.dict( + os.environ, + { + "NEXTCLOUD_HOST": "http://localhost:8080", + "ENABLE_BACKGROUND_OPERATIONS": "true", + "TOKEN_ENCRYPTION_KEY": "test-key", + "TOKEN_STORAGE_DB": "/tmp/test.db", + # Note: No semantic search enabled + }, + clear=True, + ): + from nextcloud_mcp_server.config import get_settings + + settings = get_settings() + + # Semantic search NOT enabled + assert settings.vector_sync_enabled is False + + # Background operations explicitly enabled + assert settings.enable_offline_access is True + + def test_both_old_and_new_semantic_search_names_prefers_new(self): + """Test setting both ENABLE_SEMANTIC_SEARCH and VECTOR_SYNC_ENABLED uses new name.""" + with patch.dict( + os.environ, + { + "ENABLE_SEMANTIC_SEARCH": "true", + "VECTOR_SYNC_ENABLED": "false", # Old name says false + "QDRANT_LOCATION": ":memory:", + }, + clear=True, + ): + from nextcloud_mcp_server.config import get_settings + + settings = get_settings() + + # Should use new name value (true) + assert settings.vector_sync_enabled is True + + def test_both_old_and_new_background_ops_names_prefers_new(self): + """Test setting both ENABLE_BACKGROUND_OPERATIONS and ENABLE_OFFLINE_ACCESS uses new name.""" + with patch.dict( + os.environ, + { + "ENABLE_BACKGROUND_OPERATIONS": "true", + "ENABLE_OFFLINE_ACCESS": "false", # Old name says false + "TOKEN_ENCRYPTION_KEY": "test-key", + "TOKEN_STORAGE_DB": "/tmp/test.db", + }, + clear=True, + ): + from nextcloud_mcp_server.config import get_settings + + settings = get_settings() + + # Should use new name value (true) + assert settings.enable_offline_access is True + + def test_validation_no_longer_requires_both_variables(self): + """Test validation no longer requires explicit ENABLE_OFFLINE_ACCESS when semantic search enabled.""" + with patch.dict( + os.environ, + { + "NEXTCLOUD_HOST": "http://localhost:8080", + "ENABLE_MULTI_USER_BASIC_AUTH": "true", + "ENABLE_SEMANTIC_SEARCH": "true", + "QDRANT_LOCATION": ":memory:", + "TOKEN_ENCRYPTION_KEY": "test-key", + "TOKEN_STORAGE_DB": "/tmp/test.db", + # OAuth credentials required for app password retrieval (when background ops enabled) + "NEXTCLOUD_OIDC_CLIENT_ID": "test-client-id", + "NEXTCLOUD_OIDC_CLIENT_SECRET": "test-client-secret", + # Note: ENABLE_OFFLINE_ACCESS not set - should auto-enable + }, + clear=True, + ): + from nextcloud_mcp_server.config import get_settings + + settings = get_settings() + mode, errors = validate_configuration(settings) + + # Should have no validation errors + # (Previously would have required explicit ENABLE_OFFLINE_ACCESS) + assert len(errors) == 0 + assert mode == AuthMode.MULTI_USER_BASIC + # Verify background operations were auto-enabled + assert settings.enable_offline_access is True + + +class TestExplicitModeSelection: + """Test ADR-021 explicit mode selection via MCP_DEPLOYMENT_MODE. + + Tests verify: + - Explicit mode selection works for all modes + - Invalid mode names raise ValueError + - Explicit mode takes precedence over auto-detection + """ + + def test_explicit_single_user_basic_mode(self): + """Test explicit single_user_basic mode selection.""" + with patch.dict( + os.environ, + { + "NEXTCLOUD_HOST": "http://localhost:8080", + "MCP_DEPLOYMENT_MODE": "single_user_basic", + "NEXTCLOUD_USERNAME": "admin", + "NEXTCLOUD_PASSWORD": "password", + }, + clear=True, + ): + from nextcloud_mcp_server.config import get_settings + + settings = get_settings() + mode = detect_auth_mode(settings) + + assert mode == AuthMode.SINGLE_USER_BASIC + + def test_explicit_multi_user_basic_mode(self): + """Test explicit multi_user_basic mode selection.""" + with patch.dict( + os.environ, + { + "NEXTCLOUD_HOST": "http://localhost:8080", + "MCP_DEPLOYMENT_MODE": "multi_user_basic", + }, + clear=True, + ): + from nextcloud_mcp_server.config import get_settings + + settings = get_settings() + mode = detect_auth_mode(settings) + + assert mode == AuthMode.MULTI_USER_BASIC + + def test_explicit_oauth_single_audience_mode(self): + """Test explicit oauth_single_audience mode selection.""" + with patch.dict( + os.environ, + { + "NEXTCLOUD_HOST": "http://localhost:8080", + "MCP_DEPLOYMENT_MODE": "oauth_single_audience", + }, + clear=True, + ): + from nextcloud_mcp_server.config import get_settings + + settings = get_settings() + mode = detect_auth_mode(settings) + + assert mode == AuthMode.OAUTH_SINGLE_AUDIENCE + + def test_explicit_oauth_token_exchange_mode(self): + """Test explicit oauth_token_exchange mode selection.""" + with patch.dict( + os.environ, + { + "NEXTCLOUD_HOST": "http://localhost:8080", + "MCP_DEPLOYMENT_MODE": "oauth_token_exchange", + }, + clear=True, + ): + from nextcloud_mcp_server.config import get_settings + + settings = get_settings() + mode = detect_auth_mode(settings) + + assert mode == AuthMode.OAUTH_TOKEN_EXCHANGE + + def test_explicit_smithery_mode(self): + """Test explicit smithery mode selection.""" + with patch.dict( + os.environ, + { + "MCP_DEPLOYMENT_MODE": "smithery", + }, + clear=True, + ): + from nextcloud_mcp_server.config import get_settings + + settings = get_settings() + mode = detect_auth_mode(settings) + + assert mode == AuthMode.SMITHERY_STATELESS + + def test_invalid_deployment_mode_raises_error(self): + """Test invalid MCP_DEPLOYMENT_MODE raises ValueError.""" + with patch.dict( + os.environ, + { + "NEXTCLOUD_HOST": "http://localhost:8080", + "MCP_DEPLOYMENT_MODE": "invalid_mode", + }, + clear=True, + ): + from nextcloud_mcp_server.config import get_settings + + settings = get_settings() + + # Should raise ValueError with clear message + try: + detect_auth_mode(settings) + assert False, "Should have raised ValueError" + except ValueError as e: + assert "Invalid MCP_DEPLOYMENT_MODE" in str(e) + assert "invalid_mode" in str(e) + assert "Valid values:" in str(e) + + def test_explicit_mode_overrides_auto_detection(self): + """Test explicit mode takes precedence over auto-detection.""" + with patch.dict( + os.environ, + { + "NEXTCLOUD_HOST": "http://localhost:8080", + "NEXTCLOUD_USERNAME": "admin", # Would auto-detect as single_user_basic + "NEXTCLOUD_PASSWORD": "password", + "MCP_DEPLOYMENT_MODE": "oauth_single_audience", # Explicit override + }, + clear=True, + ): + from nextcloud_mcp_server.config import get_settings + + settings = get_settings() + mode = detect_auth_mode(settings) + + # Should use explicit mode, not auto-detected mode + assert mode == AuthMode.OAUTH_SINGLE_AUDIENCE + + def test_case_insensitive_mode_names(self): + """Test MCP_DEPLOYMENT_MODE is case-insensitive.""" + with patch.dict( + os.environ, + { + "NEXTCLOUD_HOST": "http://localhost:8080", + "MCP_DEPLOYMENT_MODE": "OAUTH_SINGLE_AUDIENCE", # Uppercase + }, + clear=True, + ): + from nextcloud_mcp_server.config import get_settings + + settings = get_settings() + mode = detect_auth_mode(settings) + + assert mode == AuthMode.OAUTH_SINGLE_AUDIENCE + + def test_whitespace_in_mode_name_stripped(self): + """Test whitespace in MCP_DEPLOYMENT_MODE is stripped.""" + with patch.dict( + os.environ, + { + "NEXTCLOUD_HOST": "http://localhost:8080", + "MCP_DEPLOYMENT_MODE": " oauth_single_audience ", # Whitespace + }, + clear=True, + ): + from nextcloud_mcp_server.config import get_settings + + settings = get_settings() + mode = detect_auth_mode(settings) + + assert mode == AuthMode.OAUTH_SINGLE_AUDIENCE