Commit Graph

114 Commits

Author SHA1 Message Date
Chris Coutinho e96c02e4d4 fix: remove unnecessary urllib3<2.0 constraint
The urllib3<2.0 constraint was added unnecessarily during troubleshooting.
urllib3 2.x works perfectly fine with qdrant-client. The import path for
urllib3.util.Url and parse_url remains the same across 1.x and 2.x versions.

Changes:
- Remove urllib3<2.0 constraint from pyproject.toml
- Upgrade to urllib3 2.5.0 (latest)
- All integration tests pass with urllib3 2.x

Verified:
- from urllib3.util import Url, parse_url works in 2.5.0
- All 6 semantic search integration tests pass
- qdrant-client 1.15.1 works correctly with urllib3 2.5.0

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-08 22:18:31 +01:00
Chris Coutinho 7b8c3f93a8 test: add integration tests for semantic search with in-process embeddings
Adds comprehensive integration tests for vector database semantic search that
work without external dependencies (Ollama), making them suitable for CI/CD.

Changes:
- Add SimpleEmbeddingProvider: in-process TF-IDF-like embeddings using feature hashing
- Make Ollama optional: embedding service now falls back to SimpleEmbeddingProvider
- Add 6 integration tests covering semantic search, filtering, and batch operations
- Downgrade urllib3 to 1.26.x for qdrant-client compatibility
- Update docker-compose.yml to comment out Ollama configuration (optional)

The SimpleEmbeddingProvider generates deterministic, normalized embeddings
suitable for testing semantic similarity without requiring external services.
Tests validate that similar texts have higher cosine similarity and that
semantic search correctly ranks results by relevance.

Test coverage:
- Deterministic embedding generation
- Semantic similarity between texts
- Full search flow with Qdrant (in-memory)
- Category filtering
- Empty result handling
- Batch embedding generation

All tests pass and can run in GitHub CI without Ollama infrastructure.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-08 22:13:33 +01:00
Chris Coutinho 8f45e996e8 feat: implement vector sync scanner and processor (ADR-007 Phase 2)
Implements background vector database synchronization using anyio
TaskGroups for BasicAuth mode with single-user credentials.

Scanner Implementation:
- Periodic document discovery (hourly, configurable)
- Timestamp-based change detection (Nextcloud vs Qdrant)
- Wake event for immediate scanning on-demand
- Supports both initial sync (all docs) and incremental sync (changes only)
- Detects deleted documents and queues for removal

Processor Implementation:
- Concurrent document processing pool (3 workers default)
- I/O-bound embedding generation via Ollama API
- Retry logic with exponential backoff (3 retries)
- Document chunking (512 words, 50-word overlap)
- Handles both index and delete operations
- Upserts vectors to Qdrant with rich metadata

App Lifespan Integration:
- Extended AppContext with background task state
- Modified app_lifespan_basic() to start tasks via anyio TaskGroups
- Graceful shutdown with coordinated task cancellation
- Only activates when VECTOR_SYNC_ENABLED=true

Embedding Service:
- OllamaEmbeddingProvider with TLS support
- Singleton pattern for shared client instances
- Batch embedding support for efficiency
- Auto-detects embedding dimension (768 for nomic-embed-text)

Qdrant Client:
- Async client wrapper with singleton pattern
- Auto-creates collection on first use
- COSINE distance metric for semantic similarity
- Integrates with embedding service for dimension detection

Health Check Enhancement:
- Added Qdrant status check to /health/ready endpoint
- Only checks when VECTOR_SYNC_ENABLED=true
- 2-second timeout for health probe
- Reports connection errors with details

Configuration:
- VECTOR_SYNC_ENABLED: Enable background sync
- VECTOR_SYNC_SCAN_INTERVAL: Scanner frequency (3600s default)
- VECTOR_SYNC_PROCESSOR_WORKERS: Concurrent processors (3 default)
- QDRANT_URL, QDRANT_API_KEY, QDRANT_COLLECTION: Vector DB config
- OLLAMA_BASE_URL, OLLAMA_EMBEDDING_MODEL: Embedding service config

Dependencies Added:
- qdrant-client>=1.7.0: Vector database client

Docker Compose:
- Added Qdrant service with health check
- Exposed ports 6333 (REST) and 6334 (gRPC)
- Configured MCP service with vector sync environment
- Added qdrant-data volume for persistence

Known Issue:
- FastMCP lifespan not triggering for streamable-http transport
- Background tasks will start once lifespan integration is complete
- Lifespan triggers on MCP session establishment, not server startup

Related: ADR-007 Background Vector Database Synchronization

🤖 Generated with Claude Code (https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-08 21:14:38 +01:00
github-actions[bot] 4ceaf45ffd bump: version 0.26.0 → 0.26.1 2025-11-08 03:59:28 +00:00
Chris Coutinho 21b878a2e7 Merge pull request #265 from cbcoutinho/renovate/mcp-1.x
fix(deps): update dependency mcp to >=1.21,<1.22
2025-11-08 04:59:05 +01:00
github-actions[bot] 218f0bd366 bump: version 0.25.0 → 0.26.0 2025-11-08 03:48:50 +00:00
renovate-bot-cbcoutinho[bot] c1e135c4a2 fix(deps): update dependency mcp to >=1.21,<1.22 2025-11-07 05:06:10 +00:00
github-actions[bot] 77e491beea bump: version 0.24.1 → 0.25.0 2025-11-05 23:02:25 +00:00
Chris Coutinho 461971a1a8 Merge pull request #262 from cbcoutinho/feature/user-settings
Feature/user settings
2025-11-05 15:59:54 +01:00
Chris Coutinho 6cccd92b3b build: Add type checking 2025-11-05 15:19:55 +01:00
github-actions[bot] 7052c19de0 bump: version 0.24.0 → 0.24.1 2025-11-04 12:28:13 +00:00
renovate-bot-cbcoutinho[bot] 3e988acb97 fix(deps): update dependency mcp to >=1.20,<1.21 2025-11-04 11:08:34 +00:00
github-actions[bot] f587a4e31f bump: version 0.23.0 → 0.24.0 2025-11-04 10:27:39 +00:00
Chris Coutinho 4e1d143e54 Merge remote-tracking branch 'origin/master' into feature/keycloak 2025-11-03 02:49:00 +01:00
github-actions[bot] 02a2c4a16f bump: version 0.22.7 → 0.23.0 2025-11-03 01:48:39 +00:00
Chris Coutinho babd60e08b feat: Implement ADR-004 Hybrid Flow with comprehensive integration tests
Implement the ADR-004 Hybrid Flow OAuth pattern where the MCP server
intercepts the OAuth callback to obtain master refresh tokens while
maintaining PKCE security for clients.

## Implementation

### OAuth Routes (ADR-004 Hybrid Flow)
- Add `/oauth/authorize` endpoint: Intercepts client OAuth initiation
- Add `/oauth/callback` endpoint: Receives IdP callback, stores master token
- Add `/oauth/token` endpoint: Exchanges MCP code for client access token
- Implement PKCE code challenge/verifier validation
- Store OAuth sessions with state/challenge correlation

### MCP Server Integration
- Update `setup_oauth_config()` to return client_id and client_secret
- Initialize OAuth context in Starlette lifespan for login routes
- Add OAuth session storage to RefreshTokenStorage
- Configure authlib dependency for OAuth flow management

### Integration Tests
- Create `test_adr004_hybrid_flow.py` with Playwright automation
- Add `adr004_hybrid_flow_mcp_client` session-scoped fixture
- Test MCP session establishment with hybrid flow token
- Test tool execution using stored refresh tokens (on-behalf-of pattern)
- Test persistent access across multiple operations
- All tests passing:  3 passed in 8.82s

### Documentation
- Update ADR-004 with comprehensive Testing section
- Add integration test commands and coverage details
- Document test implementation and verification steps
- Create TESTING_INSTRUCTIONS.md for manual and automated testing
- Include manual test scripts for reference/debugging

## What This Enables

 PKCE code challenge/verifier flow
 MCP server intercepts OAuth callback and stores master refresh token
 Client receives MCP access token (not master token)
 MCP session establishment with hybrid flow token
 Tool execution using stored refresh tokens (on-behalf-of pattern)
 Multiple operations without re-authentication
 Proper token isolation (client never sees master token)

## Testing

Run ADR-004 integration tests:
```bash
uv run pytest tests/server/oauth/test_adr004_hybrid_flow.py --browser firefox -v
```

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 02:18:30 +01:00
Chris Coutinho e331544cee feat: Implement RFC 8693 token exchange for Keycloak (ADR-002 Tier 2)
Implements OAuth 2.0 Token Exchange (RFC 8693) enabling the MCP server to
exchange service account tokens for user-scoped tokens. This provides an
alternative to refresh tokens for background operations.

**Core Implementation:**
- Added `get_service_account_token()` method to KeycloakOAuthClient for
  client_credentials grant
- Added `exchange_token_for_user()` method implementing RFC 8693 token exchange
- Fixed Fernet encryption key handling in RefreshTokenStorage (was incorrectly
  base64 decoding already-encoded keys)
- Updated OAuth configuration to support offline_access scope and refresh token
  storage infrastructure

**Keycloak Configuration:**
- Enabled `serviceAccountsEnabled` in realm-export.json
- Added `token.exchange.grant.enabled` attribute
- Added `client.token.exchange.standard.enabled` attribute (required for
  Keycloak 26.2+ Standard Token Exchange V2)
- Fresh Keycloak imports now correctly enable token exchange

**Docker Compose:**
- Added TOKEN_ENCRYPTION_KEY and ENABLE_OFFLINE_ACCESS environment variables
- Created oauth-tokens volume for refresh token storage
- Configured both mcp-oauth and mcp-keycloak services

**Testing & Documentation:**
- Added tests/manual/test_token_exchange.py - Validates complete RFC 8693 flow
- Added tests/manual/test_nextcloud_impersonate.py - Documents session-based
  impersonation limitations
- Added docs/oauth-impersonation-findings.md - Comprehensive investigation
  findings and resolution documentation

**Verified Working:**
 Service account token acquisition (client_credentials grant)
 RFC 8693 token exchange for internal-to-internal tokens
 Exchanged tokens validate with Nextcloud APIs
 Keycloak 26.4.2 Standard Token Exchange V2 support

**Known Limitations:**
- User impersonation (requested_subject) requires Keycloak Legacy V1 with
  preview features
- Cross-client token exchange limited to same realm
- Refresh token storage infrastructure ready but unused (MCP protocol limitation)

Dependencies: aiosqlite>=0.20.0

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 22:03:19 +01:00
github-actions[bot] 5259658458 bump: version 0.22.6 → 0.22.7 2025-10-29 11:18:41 +00:00
github-actions[bot] e1aca04aff bump: version 0.22.5 → 0.22.6 2025-10-29 10:57:44 +00:00
github-actions[bot] e647c87dd8 bump: version 0.22.4 → 0.22.5 2025-10-29 10:54:54 +00:00
github-actions[bot] 202058bdc8 bump: version 0.22.3 → 0.22.4 2025-10-29 10:44:11 +00:00
github-actions[bot] 8221046d8a bump: version 0.22.2 → 0.22.3 2025-10-29 10:35:58 +00:00
github-actions[bot] 9ec7637579 bump: version 0.22.1 → 0.22.2 2025-10-29 10:30:39 +00:00
github-actions[bot] 3878beaf65 bump: version 0.22.0 → 0.22.1 2025-10-29 10:17:08 +00:00
github-actions[bot] 0e7e74867f bump: version 0.21.0 → 0.22.0 2025-10-29 09:32:27 +00:00
github-actions[bot] 57a2157c58 bump: version 0.20.0 → 0.21.0 2025-10-25 18:33:56 +00:00
yuisheaven f0e5333e43 Merge branch 'master' into feature/introduce_files_parsing_with_unstructured_service_for_webdav_files_retrieval 2025-10-25 17:23:38 +02:00
github-actions[bot] 04e0ab127a bump: version 0.19.1 → 0.20.0 2025-10-24 18:24:45 +00:00
Chris Coutinho 1117a83a52 Merge pull request #237 from cbcoutinho/feature/app-scopes
Feature/app scopes
2025-10-24 20:24:15 +02:00
github-actions[bot] 50a824155c bump: version 0.19.0 → 0.19.1 2025-10-24 04:36:51 +00:00
renovate-bot-cbcoutinho[bot] 3baf10662f fix(deps): update dependency mcp to >=1.19,<1.20 2025-10-24 04:06:55 +00:00
Chris Coutinho d452684535 feat: Split read/write scopes into app:read/write scopes 2025-10-24 04:38:49 +02:00
github-actions[bot] bfbaed9a66 bump: version 0.18.0 → 0.19.0 2025-10-23 23:50:51 +00:00
yuisheaven 29df645d53 Merge branch 'master' into feature/introduce_files_parsing_with_unstructured_service_for_webdav_files_retrieval 2025-10-23 21:30:09 +02:00
github-actions[bot] 87c6f077f3 bump: version 0.17.1 → 0.18.0 2025-10-23 10:23:48 +00:00
Chris Coutinho c069d78f80 feat: Initialize JWT-scoped tools 2025-10-22 06:21:16 +02:00
yuisheaven 98627593d5 corrected smaller merge issues 2025-10-21 20:55:33 +02:00
yuisheaven 64649c902d Merge branch 'master' into feature/introduce_files_parsing_with_unstructured_service_for_webdav_files_retrieval 2025-10-21 20:37:00 +02:00
github-actions[bot] 4984496d81 bump: version 0.17.0 → 0.17.1 2025-10-20 21:16:09 +00:00
Chris Coutinho e8f1340133 fix(caldav): Fix caldav search() due to missing todos 2025-10-20 22:18:46 +02:00
Chris Coutinho 6ba87e7e05 chore: update caldav ref 2025-10-20 11:52:29 +02:00
github-actions[bot] 45bbf97033 bump: version 0.16.0 → 0.17.0 2025-10-19 22:55:23 +00:00
Chris Coutinho a143123acc fix(caldav): Check that calendar exists after creation to avoid race condition
Verify that field preservation tests still operate
2025-10-19 23:44:39 +02:00
Chris Coutinho 92e18825bc feat(caldav): Add support for tasks 2025-10-19 18:02:43 +02:00
Chris Coutinho d398a8c8e6 refactor: Migrate from internal CalendarClient to caldav library 2025-10-19 15:47:17 +02:00
github-actions[bot] cb7a609ec2 bump: version 0.15.2 → 0.16.0 2025-10-19 00:13:49 +00:00
Chris Coutinho 5395f8d3d6 chore: Update lock file 2025-10-19 02:02:05 +02:00
Chris Coutinho b72514bb32 ci: Add pytest-timeout to dev deps 2025-10-19 00:27:19 +02:00
Chris Coutinho 1459fe9bc8 test: Replace pytest-asyncio plugin fixtures with anyio fixtures 2025-10-18 22:02:25 +02:00
github-actions[bot] a389f2940e bump: version 0.15.1 → 0.15.2 2025-10-17 23:17:32 +00:00