Commit Graph
100 Commits
Author SHA1 Message Date
Chris Coutinho 4248b67b2e feat: Migrate to vue 3 2025-12-23 05:46:49 +01:00
Chris Coutinho d7c99fcc69 feat(astrolabe): upgrade to Vue 3 and @nextcloud/vue 9
- Merge renovate/major-vue-monorepo: Vue 2.7.16 → 3.5.26
- Merge renovate/nextcloud-vue-9.x: @nextcloud/vue 8.29.2 → 9.3.1
- Update component imports to new @nextcloud/vue v9 paths
- Replace .sync modifiers with v-model:prop (Vue 3 syntax)
- Replace beforeDestroy with beforeUnmount lifecycle hook
- Remove Vue.() usage (automatic reactivity in Vue 3)
- Update main.js to use createApp() instead of Vue.extend()
- Add @vitejs/plugin-vue and configure Vite for Vue 3
- All builds passing, ready for admin UX improvements
2025-12-23 00:47:21 +01:00
Chris Coutinho 47095fabcd Merge remote-tracking branch 'origin/renovate/nextcloud-vue-9.x' into feat/improve-admin-ux-vue3
# Conflicts:
#	third_party/astrolabe/package-lock.json
2025-12-23 00:38:50 +01:00
Chris Coutinho 85b7b935b3 Merge remote-tracking branch 'origin/renovate/major-vue-monorepo' into feat/improve-admin-ux-vue3 2025-12-23 00:36:51 +01:00
Chris CoutinhoandClaude Sonnet 4.5 8ba3ae73ab fix(helm): set OIDC client env vars when using existingSecret
The deployment template only checked for clientId being set in
values.yaml, so when using existingSecret without setting clientId,
the NEXTCLOUD_OIDC_CLIENT_ID and NEXTCLOUD_OIDC_CLIENT_SECRET env
vars were never created.

This broke existingSecret for OIDC-based auth - the server would
always fall back to DCR even when pre-registered credentials were
provided via secret.

Fix: Check for EITHER clientId OR existingSecret being set before
creating the OIDC client credential env vars.

Affects both OIDC-based auth modes:
- auth.oauth.existingSecret (OAuth mode)
- auth.multiUserBasic.existingSecret (multi-user BasicAuth with offline access)

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-22 22:21:23 +01:00
Chris CoutinhoandClaude Sonnet 4.5 5b9e76ddb4 fix(helm): trigger chart release workflow on helm chart tags
The helm-release workflow was only triggering on v* tags (MCP server
releases), not on nextcloud-mcp-server-* tags (helm chart releases).

This caused chart releases to be skipped because:
1. Helm chart version bump creates tag nextcloud-mcp-server-X.Y.Z
2. Workflow never runs for this tag (pattern didn't match)
3. Next v* tag triggers workflow at wrong commit (Chart.yaml not updated)
4. chart-releaser skips because version already exists

Fix: Add nextcloud-mcp-server-* to workflow trigger pattern so chart
releases execute at the correct commit where Chart.yaml has the new version.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-22 21:48:26 +01:00
Chris Coutinho ea96a58678 fix(helm): address PR #447 reviewer feedback
Critical fix:
- deployment.yaml: Only reference OAuth credentials when clientId is set
- Fixes pod failure when using existingSecret without static OAuth creds
- Aligns deployment behavior with secret template logic

Previously, the deployment referenced OAuth credentials when either
clientId OR existingSecret was set. However, the secret template only
includes OAuth credentials when clientId is explicitly provided. This
caused pod failures when users provided an existingSecret for offline
access without static OAuth credentials (intending to use DCR).

The fix ensures OAuth env vars are only referenced when clientId is set,
matching the OAuth mode pattern and allowing DCR to work correctly with
existingSecret configurations.

Minor improvements:
- values.yaml: Clarify OAuth credentials are optional (uses DCR if not provided)

Testing verified all scenarios:
 Pass-through only (no offline access): No secrets/PVCs/OAuth vars
 Offline + DCR (no clientId): Secret with encryption key only, no OAuth vars
 Offline + static OAuth: Secret with all keys, OAuth vars present
 existingSecret without clientId: No auto secret, no OAuth vars (FIXED)

Resolves reviewer feedback from PR #447
2025-12-22 21:34:40 +01:00
Chris Coutinho 9b5c6779e9 fix(helm): include MCP server version bumps in changelog pattern
Updates helm chart commitizen config to recognize MCP server version
bump commits (which update appVersion in Chart.yaml) as valid triggers
for helm chart version bumps.

Problem:
- When MCP server version bumps, it updates Chart.yaml appVersion
- Helm chart commitizen only matched "(helm)" scoped commits
- Result: appVersion updated but chart version not bumped

Solution:
- Extended changelog_pattern to include "bump: version X → Y" commits
- Now helm chart version will bump when either:
  1. Commits with (helm) scope are made, OR
  2. MCP server version bumps (updating appVersion)

This ensures chart version stays in sync with appVersion updates.
2025-12-22 21:17:14 +01:00
Chris Coutinho 04140d671e feat(helm): add support for multi-user BasicAuth mode
Adds multi-user-basic authentication mode to the helm chart alongside
existing basic (single-user) and oauth modes.

Multi-user BasicAuth mode enables:
- Pass-through authentication (credentials in request headers)
- Optional background operations using app passwords via Astrolabe
- Optional OAuth client credentials (uses DCR if not provided)
- Token encryption and persistent storage for background sync

Changes:
- values.yaml: Add auth.multiUserBasic configuration section
- deployment.yaml: Add ENABLE_MULTI_USER_BASIC_AUTH and related env vars
- secret.yaml: Add secret template for token encryption key and OAuth credentials
- pvc.yaml: Add PVC template for token database persistence
- _helpers.tpl: Add helper functions for secret/PVC names

Tested with:
  helm template --set auth.mode=multi-user-basic \
    --set auth.multiUserBasic.enableOfflineAccess=true \
    --set auth.multiUserBasic.tokenEncryptionKey=... \
    --set vectorSync.enabled=true

Related: Multi-user deployment support (ADR-020)
2025-12-22 21:03:10 +01:00
Chris CoutinhoandClaude Sonnet 4.5 4a5766b84e feat(config): enable DCR for multi-user BasicAuth with offline access
Allows multi-user BasicAuth mode to use Dynamic Client Registration (DCR)
for OAuth credentials when ENABLE_OFFLINE_ACCESS is enabled, making it
consistent with OAuth modes and reducing configuration burden.

**Changes:**

Configuration Validation:
- Relaxed OAuth credential requirements for multi-user BasicAuth
- OAuth credentials now optional when offline access enabled
- Will use DCR as fallback if NEXTCLOUD_OIDC_CLIENT_ID/SECRET not set
- Updated validation to log info instead of error when DCR will be used

Startup Logic (app.py):
- Added DCR workflow for multi-user BasicAuth before uvicorn starts
- Creates oauth_context for management APIs when offline access enabled
- Allows Astrolabe to authenticate management API calls with OAuth
- DCR runs synchronously at same lifecycle point as OAuth modes
- Added traceback import for better error logging
- Fixed type assertions for nextcloud_host
- Fixed undefined variable references in vector sync logging

Management API:
- Improved auth mode detection using proper detect_auth_mode()
- Added auth_mode field to /status endpoint:
  * "basic" - Single-user BasicAuth
  * "multi_user_basic" - Multi-user BasicAuth
  * "oauth" - OAuth modes
  * "smithery" - Smithery stateless
- Added supports_app_passwords indicator for multi-user BasicAuth

Docker Compose:
- Updated mcp-multi-user-basic service configuration:
  * Enabled vector sync (VECTOR_SYNC_ENABLED=true)
  * Added ENABLE_OFFLINE_ACCESS=true for app password support
  * Added NEXTCLOUD_MCP_SERVER_URL for Astrolabe integration
  * Documented optional static OAuth credentials

Testing:
- Updated test_config_validators.py to expect DCR fallback
- Enhanced configure_astrolabe_for_mcp_server fixture with verification
- Added debug logging to test_users_setup fixture

**Workflow:**
1. User configures ENABLE_OFFLINE_ACCESS=true
2. Server checks for static NEXTCLOUD_OIDC_CLIENT_ID/SECRET
3. If not found, performs DCR before uvicorn starts
4. DCR registers client with Nextcloud OIDC provider
5. OAuth credentials used for Astrolabe management API auth
6. Background sync can retrieve user app passwords via Astrolabe

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-22 19:43:24 +01:00
Chris CoutinhoandClaude Sonnet 4.5 65c3f099fa feat(astrolabe): implement app password provisioning for multi-user background sync
Adds complete app password provisioning workflow for multi-user BasicAuth
deployments, allowing users to independently enable background sync by
generating and storing Nextcloud app passwords.

**New Components:**

Backend (PHP):
- CredentialsController: Validates and stores app passwords
  * Validates app password format and authenticity via OCS API
  * Stores encrypted passwords in oc_preferences
  * Provides status and credential management endpoints
- AstrolabeAdminSettings: Admin configuration page for MCP server URL
- AstrolabeAdminSettingsListener: Event listener for admin section
- Updated McpTokenStorage: Added background sync credential methods

Frontend:
- personalSettings.js: Form handling for app password entry
  * AJAX submission with error handling
  * Shows success/error notifications
  * Triggers page reload after successful save
- settings.css: Styling for settings pages
- Updated personal.php template: Two-option UI
  * Option 1: OAuth refresh token (future, not yet available)
  * Option 2: App password (works today, recommended)
  * Shows "Active" badge when provisioned
  * Displays credential type and provisioned timestamp

Routes:
- POST /api/v1/background-sync/credentials - Store app password
- GET /api/v1/background-sync/status - Get provisioning status
- DELETE /api/v1/background-sync/credentials - Revoke credentials
- GET /api/v1/background-sync/credentials/{userId} - Admin only

**Testing:**
- test_astrolabe_settings_buttons.py: Integration test for UI buttons

**Workflow:**
1. User generates app password in Nextcloud Security settings
2. User navigates to Astrolabe personal settings
3. User enters app password in "Option 2: App Password" form
4. Backend validates password via OCS API call
5. Password stored encrypted in oc_preferences
6. Page reloads showing "Active" badge with credential details
7. MCP server can now use stored password for background operations

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-22 19:39:13 +01:00
Chris CoutinhoandClaude Sonnet 4.5 b293258210 test(astrolabe): fix app password extraction in multi-user background sync test
Fixes the Playwright-based integration test that verifies multi-user app
password provisioning for background sync in Astrolabe.

**Root Cause:**
The test was failing to extract the generated app password from Nextcloud's
"New app password" dialog due to overly specific CSS selectors that didn't
match the actual DOM structure.

**Changes:**
- Enhanced network response logging to capture HTTP status codes
- Simplified app password extraction logic:
  * Wait for dialog heading using text selector
  * Iterate through ALL text inputs on page
  * Find password by pattern: contains dashes and length > 20
  * Validate extracted password against expected format
- Added format validation with regex before returning password
- Added detailed debug logging for each extraction step
- Improved error messages with screenshot paths

**Testing:**
Test now successfully completes for both alice and bob test users:
- Logs in to Nextcloud
- Generates app password in Security settings
- Extracts password from dialog
- Navigates to Astrolabe settings
- Enters and saves app password
- Verifies "Active" badge appears
- Confirms credentials stored in database

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-22 19:32:06 +01:00
Chris CoutinhoandClaude Sonnet 4.5 1a5bb10cd0 feat(config): consolidate configuration with smart dependency resolution (ADR-021)
Simplifies configuration by consolidating overlapping settings and adding
automatic dependency resolution. This makes semantic search configuration
significantly easier for users while maintaining 100% backward compatibility.

## Key Changes

### Variable Renaming (Backward Compatible)
- `VECTOR_SYNC_ENABLED` → `ENABLE_SEMANTIC_SEARCH` (old name still works)
- `ENABLE_OFFLINE_ACCESS` → `ENABLE_BACKGROUND_OPERATIONS` (old name still works)
- Deprecation warnings logged when old names used
- Old names will be removed in v1.0.0

### Smart Dependency Resolution
- `ENABLE_SEMANTIC_SEARCH` automatically enables background operations in multi-user modes
- No need to set both `ENABLE_OFFLINE_ACCESS` and `VECTOR_SYNC_ENABLED` anymore
- Single-user mode doesn't auto-enable background ops (not needed)

### Explicit Mode Selection (Optional)
- New `MCP_DEPLOYMENT_MODE` environment variable
- Valid values: single_user_basic, multi_user_basic, oauth_single_audience,
  oauth_token_exchange, smithery
- Removes ambiguity about which deployment mode is active
- Falls back to auto-detection if not set (existing behavior)

### Configuration Templates
- Reorganized `env.sample` by deployment mode with clear sections
- Added mode-specific quick-start templates:
  - `env.sample.single-user` - Simplest configuration
  - `env.sample.oauth-multi-user` - Recommended multi-user
  - `env.sample.oauth-advanced` - Token exchange mode

## Implementation Details

### Files Modified
- `nextcloud_mcp_server/config.py` - Smart dependency resolution helpers
- `nextcloud_mcp_server/config_validators.py` - Simplified validation, explicit mode
- `tests/unit/test_config_validators.py` - 19 new tests (60 total, all passing)
- `env.sample` - Reorganized by deployment mode
- `docs/configuration.md` - Complete rewrite with consolidated approach
- `docs/troubleshooting.md` - New consolidation troubleshooting section
- `README.md` - Updated variable references

### New Files
- `docs/ADR-021-configuration-consolidation.md` - Architecture decision record
- `docs/configuration-migration-v2.md` - Comprehensive migration guide
- `env.sample.single-user` - Single-user quick-start template
- `env.sample.oauth-multi-user` - OAuth multi-user quick-start template
- `env.sample.oauth-advanced` - Token exchange quick-start template

## User Impact

### Before (Confusing)
```bash
ENABLE_OFFLINE_ACCESS=true      # Why both?
VECTOR_SYNC_ENABLED=true        # What's the relationship?
```

### After (Simplified)
```bash
MCP_DEPLOYMENT_MODE=oauth_single_audience  # Explicit (optional)
ENABLE_SEMANTIC_SEARCH=true                # Auto-enables background ops!
```

### Benefits
- 📉 2 fewer variables to understand for semantic search
- 📋 Clear intent ("I want semantic search")
- 🎯 Explicit mode declaration available
- 🔄 100% backward compatible
-  All 265 unit tests passing

## Testing
- All 60 config validation tests passing
- 10 new tests for configuration consolidation
- 9 new tests for explicit mode selection
- Full unit test suite: 265 tests passing
- Backward compatibility verified

## Migration
Users can migrate at their own pace. Old variable names continue working
with deprecation warnings. See docs/configuration-migration-v2.md for
detailed migration instructions.

Related: ADR-021

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-21 20:36:36 +01:00
Chris Coutinho 981f102b27 fix(config): address reviewer feedback
- Restore CI test filter (-m unit -m smoke) for faster CI runs
- Replace local path reference with ADR-020 reference in config_validators.py
- Add comprehensive BasicAuthMiddleware unit tests (10 tests covering all edge cases)

Addresses critical CI issue and improves test coverage for multi-user BasicAuth mode.
2025-12-20 21:16:17 +01:00
Chris CoutinhoandClaude Sonnet 4.5 94febf1602 ci(test): build Astrolabe app before running tests
Add build step for Astrolabe app in CI workflow to compile frontend
assets before docker-compose starts.

Changes:
- Install Node.js 20 for Astrolabe build
- Run composer install --no-dev for Astrolabe PHP dependencies
- Run npm ci and npm run build to compile frontend assets

This ensures the Astrolabe app is properly built in CI, similar to
the existing OIDC app build process.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-20 21:11:28 +01:00
Chris CoutinhoandClaude Sonnet 4.5 286a3eb20f feat(auth): add multi-user BasicAuth pass-through mode
Implement multi-user BasicAuth pass-through mode (ADR-020) where each
request includes BasicAuth credentials that are forwarded to Nextcloud
APIs without persistent storage.

Changes:
- Add _get_client_from_basic_auth() in context.py to extract credentials
  from Authorization header (set by BasicAuthMiddleware)
- Add AstrolabeClient for app password provisioning via Astrolabe API
- Update oauth_sync.py with dual credential support (app passwords first,
  then refresh tokens as fallback)
- Simplify oauth_tools.py provisioning logic
- Add integration tests for app password provisioning and multi-user BasicAuth

Features:
- Stateless multi-user mode: credentials passed per-request
- Optional background sync via app passwords (stored in Astrolabe)
- Falls back to refresh tokens if app password not available
- Test coverage for provisioning flow and pass-through mode

Related: ADR-019 (Multi-user BasicAuth), ADR-020 (Deployment Modes)

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-20 20:55:31 +01:00
Chris Coutinho 19b209f412 test: Enable all tests 2025-12-20 20:53:02 +01:00
Chris CoutinhoandClaude Sonnet 4.5 cd7ba5685a feat(astrolabe): add dynamic MCP server configuration for testing
Replace static post-installation configuration with dynamic test-time
configuration to support testing multiple MCP server deployments.

Changes:
- Remove static MCP server URL and OAuth client setup from post-installation
- Add configure_astrolabe_for_mcp_server fixture (session-scoped)
- Fixture dynamically configures:
  * Nextcloud system config (mcp_server_url, mcp_server_public_url)
  * OAuth client creation via occ oidc:create
  * Client credential storage (astrolabe_client_id, astrolabe_client_secret)
- Update existing OAuth tests to use dynamic configuration
- Add test_astrolabe_multi_server_integration.py with parametrized tests

Benefits:
- Test Astrolabe with mcp-oauth, mcp-keycloak, mcp-multi-user-basic
- Each test configures for its specific MCP server
- No static configuration conflicts between deployments
- Cleaner post-installation (37 lines, down from 85)

Test Results:
- test_astrolabe_configuration_for_different_servers: PASSED (mcp-oauth, mcp-keycloak)
- test_astrolabe_reconfiguration: PASSED

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-20 20:49:53 +01:00
Chris CoutinhoandClaude Sonnet 4.5 4507359760 refactor(config): centralize configuration validation and simplify startup
Implement centralized configuration validation (ADR-020) to simplify
deployment mode detection and improve error messages.

Changes:
- Create ADR-020 documenting 5 deployment modes with required/optional config
- Add config_validators.py with validate_configuration() and mode detection
- Simplify app.py startup with single validation point at get_app()
- Remove duplicate is_oauth_mode() function (43 lines)
- Fix DeploymentMode mapping (only SELF_HOSTED and SMITHERY_STATELESS exist)
- Add comprehensive unit tests (41 tests covering all modes and edge cases)
- Add enable_multi_user_basic_auth to Settings and BasicAuthMiddleware

Docker Compose:
- Remove conflicting ENABLE_MULTI_USER_BASIC_AUTH from mcp-oauth service
- Add dedicated mcp-multi-user-basic service on port 8003

Test Results:
- 237/237 integration tests PASSED
- All deployment modes verified: single-user BasicAuth, multi-user BasicAuth,
  OAuth single-audience, OAuth token exchange (Keycloak), Smithery stateless

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-20 20:49:28 +01:00
Chris CoutinhoandClaude Sonnet 4.5 8682fa4f88 ci: Handle NO_COMMITS_TO_BUMP gracefully in bump scripts
When commitizen finds no eligible commits to bump, it exits with
code 1 and outputs [NO_COMMITS_TO_BUMP]. This was causing the
GitHub Actions workflow to fail even though this is an expected
scenario.

Updated all three bump scripts (bump-mcp.sh, bump-helm.sh,
bump-astrolabe.sh) to:
- Detect the [NO_COMMITS_TO_BUMP] message
- Exit with code 0 (success) instead of code 1
- Output an informational message instead of an error

This allows the bump-version workflow to complete successfully
when no version bumps are needed, matching the workflow's existing
logic that handles empty BUMPED_COMPONENTS.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-20 13:17:50 +01:00
Chris Coutinho 53b84200d4 ci: Gracefully exit on no commit bump 2025-12-20 13:11:11 +01:00
Chris Coutinho 18298177f7 fix(astrolabe): screenshots in info.xml 2025-12-20 01:04:20 +01:00
Chris Coutinho 651b73545d fix(astrolabe): screenshots in info.xml 2025-12-20 00:58:40 +01:00
Chris Coutinho d4d1a332fb fix(astrolabe): Update screenshots 2025-12-20 00:52:21 +01:00
Chris Coutinho a3ed321e14 fix(ci): skip existing Helm chart releases to prevent duplicate release errors
The chart-releaser workflow was failing when the Helm chart version hadn't
changed but the MCP server version was bumped. Added skip_existing: true to
gracefully handle this scenario.
2025-12-19 22:41:04 +01:00
Chris Coutinho 2bb738ed3f bump: version 0.4.0 → 0.4.1 2025-12-19 22:31:29 +01:00
Chris Coutinho 10c8b62818 bump: version 0.3.2 → 0.4.0 2025-12-19 22:30:46 +01:00
Chris Coutinho defc55a5dc feat(ci): add --increment flag to bump scripts for manual version control
Allows forcing specific version bumps (PATCH|MINOR|MAJOR) instead of
relying solely on commitizen's automatic detection based on conventional
commits.

Usage:
  ./scripts/bump-mcp.sh --increment MINOR
  ./scripts/bump-helm.sh --increment PATCH
  ./scripts/bump-astrolabe.sh --increment MAJOR
2025-12-19 22:28:43 +01:00
Chris Coutinho a2fa4b2832 fix(astrolabe): add contents:write permission to appstore workflow
The workflow was failing to create GitHub releases with 'Not Found' error
because it lacked the required permissions. Added contents:write permission
to allow creating releases and uploading artifacts.
2025-12-19 22:12:06 +01:00
Chris Coutinho 6fed78196e fix(astrolabe): update commitizen pattern to properly update info.xml version
The pattern 'version' was too broad and matched multiple lines:
- <?xml version="1.0"?>
- <version>0.2.1</version>
- min-version="30" max-version="32"

Changed to '<version>' to specifically match only the version tag.

Also fixed version mismatch: info.xml now correctly shows 0.3.0 to match
the version in .cz.toml and package.json.
2025-12-19 22:04:26 +01:00
Chris Coutinho 3618aed39e fix(astrolabe): prevent workflow failure when only helm/astrolabe commits exist
When filtering commits with grep -v, if all commits are filtered out,
grep returns exit code 1 which causes the pipeline to fail with set -e.

Wrap grep commands in { ... || true; } to ensure they don't fail the
pipeline when they filter out all results.

This fixes the workflow failure when a fix(astrolabe): commit is pushed
without any MCP server changes.
2025-12-19 21:55:36 +01:00
Chris Coutinho 4c083c7314 fix(astrolabe): info.xml 2025-12-19 21:48:27 +01:00
Chris Coutinho c9bbe71869 fix(ci): push all tags explicitly in bump workflow
The --follow-tags flag only pushes annotated tags by default.
Commitizen creates lightweight tags, so we need to explicitly push
all tags with --tags to ensure version tags are pushed to trigger
release workflows.
2025-12-19 21:45:06 +01:00
Chris Coutinho 608b3282dd fix(ci): make MCP server default bump target for all non-scoped commits
BREAKING CHANGE: MCP server now bumps for ANY conventional commit except
those explicitly scoped to helm or astrolabe.

Previous behavior:
- MCP bumped only for unscoped or scope=mcp commits
- fix(ci): commits were ignored → no version bump

New behavior:
- MCP bumps for ALL commits except scope=helm or scope=astrolabe
- fix(ci): commits now trigger MCP version bump ✓
- feat(api): commits now trigger MCP version bump ✓
- Any custom scope triggers MCP version bump ✓

This treats the MCP server as the default/primary component in the
monorepo, with Helm chart and Astrolabe as opt-in specialized components.

Changes:
1. Updated bump-version.yml workflow logic to exclude helm/astrolabe
   instead of only including mcp/unscoped
2. Updated pyproject.toml commitizen patterns to use negative lookahead:
   (?!\((?:helm|astrolabe)\))
3. Fixed docker-build-publish.yml to only trigger on v* tags (MCP only)
4. Fixed appstore-build-publish.yml action version (v1.0.4)
5. Updated test script to use grep -P for PCRE support
6. Added test cases for ci, api, and custom scopes

All 19 scope filtering tests now pass.
2025-12-19 21:34:49 +01:00
Chris Coutinho 2888bd5693 fix(ci): restrict docker build to MCP server tags only
Docker images should only be built for MCP server releases (v* tags),
not for Helm chart (nextcloud-mcp-server-*) or Astrolabe (astrolabe-v*)
releases.

Changed trigger from all tags to v* pattern only.
2025-12-19 20:48:55 +01:00
Chris Coutinho 90d95da48d fix(ci): correct appstore-push-action version to v1.0.4
The latest available version is v1.0.4, not v1.0.6. This was causing
the Astrolabe app store deployment workflow to fail.
2025-12-19 20:48:28 +01:00
Chris Coutinho 31fb52761e bump: version 0.53.0 → 0.54.0 2025-12-19 20:46:11 +01:00
Chris Coutinho f7e651d0bc bump: version 0.1.0 → 0.2.0 2025-12-19 20:45:59 +01:00
Chris Coutinho ff41fb37fd feat(ci): implement monorepo-aware version bumping workflow
Replace commitizen-action with custom workflow that detects which
components have changes based on commit scopes and bumps them
independently.

The workflow:
1. Checks for commits with scope patterns since last tag for each component:
   - MCP server: scope=mcp or unscoped, tags=v*
   - Helm chart: scope=helm, tags=nextcloud-mcp-server-*
   - Astrolabe: scope=astrolabe, tags=astrolabe-v*

2. Runs appropriate bump script for components with changes:
   - ./scripts/bump-mcp.sh
   - ./scripts/bump-helm.sh
   - ./scripts/bump-astrolabe.sh

3. Pushes all created tags at once

4. Provides GitHub Actions summary showing which components were bumped

This ensures each component versions independently based on its
relevant commits, preventing the issue where all components bump
together or some components are missed.

Fixes the issue where PR #418 only bumped MCP server, leaving Helm
chart and Astrolabe at their previous versions despite having changes.
2025-12-19 20:45:47 +01:00
Chris Coutinho e2e0ffce44 fix(ci): improve versioning and error handling
Addresses remaining high-priority code review feedback:

VERSIONING SCHEME FIXES:
- Helm chart: Changed from pep440 to semver (correct for Helm)
- Astrolabe: Changed from pep440 to semver (correct for Nextcloud apps)
- MCP server: Remains pep440 (correct for Python packages)

Helm charts must use semantic versioning per Helm specification.
Nextcloud apps use semantic versioning in info.xml and package.json.

ENHANCED ERROR HANDLING IN BUMP SCRIPTS:
All three bump scripts now include:
- Comprehensive validation checks
  * Tool availability (uv)
  * Directory structure (must run from repo root)
  * Required files exist (Chart.yaml, info.xml, package.json)
- Better error messages
  * Stderr output for errors
  * Captured commitizen output on failure
  * Common failure causes listed
- Success confirmation
  * Clear indication of what was updated
  * Next steps guidance (git push --follow-tags)
- Robust shell options (set -euo pipefail)

Scripts now provide helpful guidance when:
- No conventional commits found
- No commits with required scope
- Git working directory not clean
- Required dependencies missing
2025-12-19 19:38:24 +01:00
Chris Coutinho 2f3a3e0be4 fix(ci): address critical workflow and validation issues
Addresses code review feedback on PR #418:

CRITICAL FIXES:
1. Workflow trigger: Changed from release:published to push:tags
   - Enables "tag and publish in one step" workflow as intended
   - Automatically creates GitHub release on tag push
   - Removed redundant if condition (filtering now via trigger)
   - Added prerelease detection based on tag (-alpha, -beta, -rc)

2. Server path: Explicitly pass server_dir to make command
   - Fixes path mismatch between local (../../server) and CI
   - Uses absolute path: server_dir=${{ github.workspace }}/server
   - Prevents signing failures in GitHub Actions

3. Regex validation: Added test script for commitizen patterns
   - Validates scope filtering works correctly
   - Tests all three components: mcp, helm, astrolabe
   - Tests unscoped commits (default to mcp)
   - Tests breaking changes and invalid commits
   - Location: scripts/test-commitizen-scopes.sh

WORKFLOW IMPROVEMENTS:
- Release creation now automatic on tag push
- Better step naming for clarity
- Consistent prerelease handling across GitHub and App Store
- Explicit server_dir prevents reliance on fragile relative paths

All 16 test cases pass for scope filtering patterns.
2025-12-19 19:34:21 +01:00
Chris Coutinho c5f7221fb2 fix(astrolabe): address code review feedback
CRITICAL FIXES:
- Fix tag parsing in workflow to strip "astrolabe-v" instead of "v"
  For tag astrolabe-v0.1.0, now correctly extracts "0.1.0"
- Add workflow filtering to only run on astrolabe-v* tags
  Prevents wasting CI resources on MCP/Helm releases

RECOMMENDED IMPROVEMENTS:
- Make Nextcloud server path configurable in Makefile
  Can now override: make appstore server_dir=/path/to/server
- Add dependency validation to Makefile
  Checks for composer, npm, php before building
- Add signing prerequisite validation
  Verifies server/occ, private key, and certificate exist
- Add dependency checks to all bump scripts
  Validates uv is installed before running cz bump

These changes improve local build experience and prevent common
errors with clear error messages and installation guidance.
2025-12-19 18:34:14 +01:00
Chris Coutinho 4a42b947bc feat(astrolabe): add Nextcloud App Store deployment automation
Add complete CI/CD pipeline for automated Astrolabe app releases:
- GitHub Actions workflow for build, sign, and publish
- Makefile for app store package creation
- Version synchronization between info.xml and package.json
- CHANGELOG.md with v0.1.0 release notes

feat: configure commitizen monorepo with independent versioning

Enable independent versioning for three components using scope-based commits:
- MCP Server (feat(mcp) or unscoped): v* tags, updates pyproject.toml + Chart.yaml:appVersion
- Helm Chart (feat(helm)): nextcloud-mcp-server-* tags, updates Chart.yaml:version
- Astrolabe App (feat(astrolabe)): astrolabe-v* tags, updates info.xml + package.json

Changes:
- Add .cz.toml configs for Astrolabe and Helm chart
- Update root pyproject.toml with scope filtering and tag ignores
- Create bump helper scripts (bump-mcp.sh, bump-helm.sh, bump-astrolabe.sh)
- Add CONTRIBUTING.md with version management documentation
- Create component-specific changelogs
- Configure appVersion/version separation for Helm chart

This allows each component to release independently while maintaining
proper version tracking and changelog generation.
2025-12-19 18:06:39 +01:00
Chris CoutinhoandClaude Sonnet 4.5 daabd90359 fix(security): address critical security issues from PR #401 code review
Implemented 6 critical security fixes identified during PR #401 review:

1. Token Rotation Race Condition (Issue 1)
   - Added in-progress marker pattern to prevent concurrent refresh
   - Prevents token invalidation when multiple requests refresh simultaneously
   - File: token_broker.py:324, 343-390

2. Hardcoded Localhost URL (Issue 2)
   - Added getNextcloudBaseUrl() with fallback chain
   - Supports overwrite.cli.url, trusted_domains, and localhost fallback
   - File: IdpTokenRefresher.php:38-61, 116

3. Error Information Leakage (Issue 3)
   - Replaced 13 instances of str(e) with sanitized errors
   - Prevents exposure of stack traces, paths, and tokens
   - File: management.py:368, 444, 492, 510, 546, 571, 625, 643, 695, 750, 919, 956, 1121

4. Input Validation Gaps (Issue 4)
   - Added validation helpers: _parse_int_param, _parse_float_param, _validate_query_string
   - Applied bounds checking to get_chunk_context and unified_search
   - File: management.py:119-164, 807-835, 1197-1212

5. PHP Refresh Token Validation (Issue 5)
   - Added explicit refresh_token presence check
   - Prevents silent token rotation failures
   - File: IdpTokenRefresher.php:122-132

6. Cookie Security Configuration (Issue 6)
   - Added _should_use_secure_cookies() with auto-detection
   - Supports explicit COOKIE_SECURE env var or auto-detect from NEXTCLOUD_HOST
   - Files: browser_oauth_routes.py:27-44, 470; env.sample:54-57

Testing:
- Unit tests: 195 passed
- Integration tests: 102 passed, 4 skipped
- OAuth tests: 9 passed
- All linting and type checks passed

Follow-up work tracked in issues #408-#417

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-19 13:57:33 +01:00
Chris CoutinhoandClaude Sonnet 4.5 fe54733a39 fix(oauth): enable PKCE for all clients and add token_broker to oauth_context
This commit fixes two OAuth issues in the Astrolabe app:

1. **Always use PKCE (RFC 9207)**:
   - PKCE is now used for all OAuth flows (public and confidential clients)
   - Previous code only used PKCE for public clients, causing failures
   - Confidential clients now use both PKCE + client_secret (defense in depth)
   - Nextcloud OIDC provider requires PKCE, so token exchange was failing

2. **Add token_broker to oauth_context**:
   - Token broker is now stored in oauth_context for management API access
   - Fixes "Token broker not configured" error when revoking access
   - Revoke endpoint needs token_broker to delete refresh tokens and invalidate cache

Changes:
- OAuthController.php: Always generate PKCE verifier/challenge for all clients
- OAuthController.php: Always include code_verifier in token exchange
- app.py: Store token_broker in oauth_context after creation

Fixes:
- Astrolabe OAuth flow now works with Nextcloud OIDC
- Revoke/disconnect functionality now works in Astrolabe settings

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-18 01:55:04 +01:00
Chris CoutinhoandClaude Sonnet 4.5 8d6eff2792 fix(astrolabe): revert invalid files_pdfviewer URL for file links
The files_pdfviewer app route is internal to Nextcloud and not a valid
external URL. Reverted to using the standard Files app viewer URL for
all file types.

- Removed PDF-specific handling that used /apps/files_pdfviewer/
- All files now link to /apps/files/files/{id} (standard Files viewer)
- Fixes broken links in chunk modal titles and search results

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-18 01:54:39 +01:00
Chris CoutinhoandClaude Sonnet 4.5 e4f3beee01 fix: resolve type checking warnings for CI
- Add type casts for Starlette app state access
- Add assertions for cipher, card, board, stack after initialization
- Add None checks for XML element text attributes
- Handle __package__ being None in tracing setup
- Fix TokenBrokerService initialization to use storage credentials

Resolves 42 type warnings from ty-check, enabling CI linting to pass.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-18 00:44:58 +01:00
Chris CoutinhoandClaude Sonnet 4.5 54b69f0d68 fix: move Alembic to package submodule for Docker compatibility
- Move alembic/ directory to nextcloud_mcp_server/alembic/ subpackage
- Update migrations.py to use package location instead of alembic.ini
- Update env.py to set script_location dynamically
- Update alembic.ini for development CLI usage
- Fix Dockerfile typo: .vnev -> .venv

This fixes FileNotFoundError when running in Docker with non-editable
install. The alembic package is now installed with the main package,
making it work in both development and production environments.

Resolves: Docker startup error 'alembic.ini not found at
/app/.venv/lib/python3.12/site-packages/alembic.ini'

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-18 00:42:59 +01:00
Chris CoutinhoandClaude Sonnet 4.5 c4b3df04a0 docs(astrolabe): rewrite README for release with pitch integration
Rewrote Astrolabe README to be user-friendly and release-ready by
incorporating pitch.md content and moving technical details to linked
documentation.

Key changes:
- Incorporated compelling pitch narrative as opening
- Restructured around "What You Can Do" rather than architecture
- Added clear use cases for individuals, teams, and developers
- Simplified installation to 3 steps
- Moved OAuth flow and architecture details to ADR links
- Added emoji sections for visual appeal
- Focused on benefits over implementation

Sections:
- What You Can Do (search, visualization, AI agents)
- Installation (app store + manual)
- Quick Start (3-step setup)
- Features (personal, admin, unified search)
- Use Cases (research, collaboration, RAG workflows)
- Requirements (Nextcloud 30+, MCP server, OAuth)
- Documentation (links to installation, configuration, ADRs)
- Troubleshooting (quick fixes with links to detailed guides)

This README is now suitable for Nextcloud App Store submission.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-18 00:02:09 +01:00
Chris CoutinhoandClaude Sonnet 4.5 d4c0da85da docs: update running guide to prioritize Docker usage
Updated docs/running.md to use Docker container examples instead of
direct Python commands. This aligns with the CLI change to require
explicit 'run' subcommand while maintaining backward compatibility
for Docker users (ENTRYPOINT includes 'run').

Key changes:
- Quick Start: Use Docker commands instead of uv run
- Running Locally → Running with Docker: All examples use Docker
- Development Mode: Added CLI subcommands documentation (run/db)
- Database Migrations: Documented Alembic integration for developers
- Server Options: Docker port mapping instead of --host/--port flags
- Process Management: Simplified to Docker Compose only (removed systemd)
- Performance Tuning: Production Docker Compose with resource limits
- Troubleshooting: Docker logs and debug commands

Updated Dockerfile ENTRYPOINT:
- Changed from: ["/app/.venv/bin/nextcloud-mcp-server", "--host", "0.0.0.0"]
- Changed to: ["/app/.venv/bin/nextcloud-mcp-server", "run", "--host", "0.0.0.0"]

No breaking changes for Docker/Helm users - container interface unchanged.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-18 00:02:09 +01:00
Chris CoutinhoandClaude Sonnet 4.5 3fa376905c feat: add Alembic database migration system
Implements Alembic for managing token storage database schema versions.
Migrations run automatically on startup with full backward compatibility.

**Changes:**
- Add Alembic dependency (1.14.0+) and SQLAlchemy (auto-installed)
- Create migration infrastructure in alembic/ directory
- Add initial migration (001) capturing current schema
- Modify RefreshTokenStorage.initialize() to run migrations via anyio
- Add CLI commands: db upgrade, current, history, downgrade, migrate
- Add comprehensive migration documentation

**Backward Compatibility:**
- Pre-Alembic databases automatically stamped with revision 001
- No schema changes for existing databases
- Automatic upgrade on first startup after update

**Migration Strategy:**
Three scenarios handled:
1. New database → Run migrations from scratch
2. Pre-Alembic database → Stamp with 001 (no changes)
3. Alembic-managed → Upgrade to latest

**Architecture:**
- Uses anyio.to_thread.run_sync() for structured concurrency
- Alembic env.py runs with anyio.run() in worker thread
- SQLite-friendly migration patterns documented
- No ThreadPoolExecutor needed (anyio handles it)

**CLI Usage:**
```bash
nextcloud-mcp-server db upgrade    # Upgrade to latest
nextcloud-mcp-server db current    # Show version
nextcloud-mcp-server db history    # View changelog
nextcloud-mcp-server db downgrade  # Rollback (with confirmation)
nextcloud-mcp-server db migrate "description"  # Create migration
```

**Testing:**
- All 13 webhook storage tests pass
- New/pre-Alembic database scenarios validated
- anyio integration tested

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-18 00:02:09 +01:00
Chris CoutinhoandClaude Sonnet 4.5 a4a34e46a8 feat: make chunk modal title clickable link to documents
- Add clickable link to modal title with OpenInNew icon
- Store currentResult to enable document navigation
- Fix deck_card URLs to use metadata.board_id
- Fix news_item URLs to use external article URL from metadata.url
- Add hover styling for title link and icon

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-18 00:02:09 +01:00
Chris Coutinho d235dfa023 chore: Rename Astroglobe -> Astrolabe 2025-12-18 00:02:08 +01:00
Chris Coutinho 24898439cb fix: update unified search results to match chunk viz display
Update the unified search provider to show only chunk/page metadata
in search results, consistent with the chunk visualization result list.
Also fix news item URLs to link directly to the specific item.

Changes to SemanticSearchProvider:

1. Result display improvements:
   - Remove excerpt text from search result subline
   - Show only chunk/page metadata (e.g., "Chunk 2/5 · Page 3/10")
   - Consistent with chunk visualization UI in App.vue

2. News item URL fix:
   - Change from generic news index to specific item URL
   - Format: /apps/news/item/{id}
   - Allows direct navigation to the news article

3. Code cleanup:
   - Remove unused $excerpt variable
   - Remove unused truncateExcerpt() method
   - Simplify transformResult() logic

Benefits:
- Cleaner, more scannable search results
- Consistent UX between unified search and app UI
- Functional links to news items instead of generic news page
- Reduced code complexity
2025-12-18 00:02:08 +01:00
Chris Coutinho 6da98b4e7b feat: add native Plotly hover styling for clickable points
Replace expensive Plotly.restyle() hover handlers with native hoverlabel
styling to indicate clickable points without performance degradation.

Implementation:
- Add hoverlabel configuration to document trace with distinct styling
- Bright blue background (#0082c9) to make hover state obvious
- Larger font size (15px) for better visibility
- White text for contrast against blue background
- Handled natively by Plotly - no JavaScript event handlers needed

Benefits:
- Zero performance impact - no chart re-renders on hover
- Smooth, responsive hover feedback
- Clear visual indication that points are clickable
- Consistent with existing hover tooltip pattern

Removed:
- Expensive handlePlotHover() and handlePlotUnhover() methods
- Plotly.restyle() calls that caused severe lag and freezing
- hover/unhover event listener registrations

The hover tooltip now uses the styled hoverlabel to stand out visually,
providing clear feedback that points are interactive without any
performance cost.
2025-12-18 00:02:08 +01:00
Chris Coutinho fba4b9b785 feat: add click interactivity to Plotly 3D scatter chart
Enable users to click on points in the vector space visualization to
open the chunk viewer modal, providing a more direct interaction
method alongside the existing "Show Chunk" button.

Implementation details:
- Register plotly_click event handler in renderPlot() after chart creation
- Add handlePlotClick() method to process click events
- Use point index mapping to access full result object from this.results
- Add loading guard in viewChunk() to prevent concurrent chunk loading
- Add cursor styling: pointer for result points, default for query point
- Add beforeDestroy() lifecycle hook to cleanup event handlers

Features:
- Both interaction methods work: click chart points OR "Show Chunk" button
- Only result points (trace 0) are clickable, query point (trace 1) ignored
- Pointer cursor on hover indicates clickable points
- Loading state prevents rapid clicks from causing issues
- Memory leak prevention through proper event handler cleanup

Technical approach:
- Uses index mapping (not data duplication) for efficiency
- Results and coordinates arrays have guaranteed 1:1 mapping from API
- Event handler re-registered on each chart re-render
- CSS-based cursor styling (more performant than JS hover handlers)

Testing:
- ESLint validation passed
- Follows Vue 2.7 component property order conventions
- Compatible with existing chunk viewer modal
2025-12-18 00:02:08 +01:00
Chris Coutinho b246a03ac4 feat: improve chunk viewer with fixed navigation and markdown rendering
This commit implements three UI improvements for the chunk viewer:

1. Fixed modal footer with navigation controls
   - Moved PDF navigation buttons to a fixed footer
   - Footer remains visible while scrolling content
   - Three-section layout: fixed header, scrollable body, fixed footer

2. Removed duplicate navigation controls
   - Removed previous/next buttons from PDFViewer component
   - Controls now only in App.vue modal footer
   - Cleaned up unused imports and CSS

3. Markdown rendering for chunk content
   - Created MarkdownViewer component using markdown-it
   - Renders markdown content aligned with Nextcloud design system
   - Removed problematic markdown-it-task-checkbox dependency
   - Combines before/chunk/after context with visual separators

4. Cleaned up search results display
   - Removed excerpt snippets from results list
   - Kept only chunk/page metadata for cleaner UI

The modal structure now has:
- Fixed header (title + close button)
- Scrollable body (PDF canvas or markdown content)
- Fixed footer (page navigation - always visible)

Fixes markdown rendering "require is not defined" error by using
only markdown-it without CommonJS plugins.
2025-12-18 00:02:08 +01:00
Chris CoutinhoandClaude Sonnet 4.5 04c64e97b0 fix(astrolabe): handle OAuth refresh token rotation
Fixes 401 errors after first token refresh when using IdPs that
implement refresh token rotation (Keycloak, modern OAuth providers).

**Root Cause**:
McpTokenStorage::getAccessToken() was discarding the new refresh token
returned by the IdP after successful refresh, always keeping the old one.
This caused:
- First refresh: works (uses original refresh token)
- Second refresh: fails with 401 (old refresh token invalidated by IdP)

**Solution**:
Use new refresh token from IdP response if provided, fall back to old
token for providers that don't rotate refresh tokens.

**Changed**:
- lib/Service/McpTokenStorage.php:184
  From: $token['refresh_token']  // Always old token
  To:   $newTokenData['refresh_token'] ?? $token['refresh_token']

**Verified**:
ApiController already handles rotation correctly using the same pattern.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-18 00:02:02 +01:00
Chris CoutinhoandClaude Sonnet 4.5 af9a55cebd feat(astrolabe): enable multi-select for document types and refactor PDF viewer
This commit includes two improvements to the Astroglobe semantic search UI:

1. **Multi-select Document Types** (App.vue):
   - Changed NcCheckboxRadioSwitch binding from v-model to :checked/:update:checked
   - Implemented toggleDocType() method to manually manage selectedDocTypes array
   - Fixes issue where only single document type could be selected at a time
   - Users can now filter search results by multiple doc types simultaneously

2. **PDF Viewer Reactive Rendering** (PDFViewer.vue):
   - Refactored canvas rendering to use Vue reactive watcher pattern
   - Added watcher on 'loading' state that triggers rendering when canvas available
   - Removed imperative renderPage() call from loadPDF() method
   - Inspired by files_pdfviewer's promise/event-based initialization approach
   - Improves alignment with Vue's reactive data flow

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-18 00:01:54 +01:00
Chris CoutinhoandClaude Sonnet 4.5 44391d3d1d fix: address critical code review issues (4 fixes)
This commit addresses 4 critical issues identified in code review:

1. **Token Rotation Race Condition** (token_broker.py)
   - Added per-user locking mechanism to prevent concurrent refresh token corruption
   - Implemented double-check pattern for cache after acquiring lock
   - Users can now safely refresh concurrently without token desync

2. **Hardcoded OAuth Client ID** (PHP files)
   - Made client ID configurable via `astroglobe_client_id` in system config
   - Updated McpServerClient to provide getClientId() method
   - Injected McpServerClient into IdpTokenRefresher and OAuthController
   - Updated admin settings UI to display client ID configuration status
   - App gracefully handles missing client ID with warnings in admin UI

3. **Missing Cache Invalidation** (management.py:revoke_user_access)
   - Added cache.invalidate() call when revoking user access
   - Ensures both storage AND cache are cleared atomically
   - Prevents stale cached tokens from being used after revocation

4. **Error Message Exposure** (management.py)
   - Created _sanitize_error_for_client() helper function
   - Updated all error handlers to log detailed errors internally
   - Returns generic messages to clients to prevent information leakage
   - Protects against exposing database paths, API URLs, tokens, etc.

All changes are backward compatible and preserve existing functionality.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-18 00:01:54 +01:00
Chris Coutinho 619c62d89a ci: Remove --headed from pyproject.toml 2025-12-18 00:01:54 +01:00
Chris CoutinhoandClaude Sonnet 4.5 dfc81923ba fix: resolve CI linting issues for Astroglobe
Fix all ESLint, Stylelint, PHP CS Fixer, and Psalm workflow errors.

Changes:
- ESLint fixes:
  - Remove unused APP_NAME constant
  - Remove unused TextBoxOutline and TextBoxRemoveOutline components
  - Remove unused container variable in adminSettings.js
  - Auto-fix trailing commas, line breaks, attribute ordering
- PHP CS Fixer:
  - Add trailing commas after last function parameters
  - Convert double quotes to single quotes in log messages
  - Remove unused NoCSRFRequired import
  - Fix arrow function formatting
- Stylelint:
  - Update config to use @nextcloud/stylelint-config
  - Fix extends directive (was using non-existent package)
- Psalm workflow:
  - Fix jq object indexing (.include[0] instead of .[0])
  - Correctly extract OCP version from matrix output

All checks now pass locally.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-18 00:01:54 +01:00
Chris CoutinhoandClaude Sonnet 4.5 5a6205476a ci: add consolidated GitHub workflow for Astroglobe app
Create single workflow that includes all key checks from Nextcloud app
skeleton instead of copying 14 separate workflow files.

Changes:
- Create astroglobe-ci.yml workflow:
  - Triggers on PRs modifying third_party/astroglobe/
  - Detects frontend vs PHP changes separately
  - Frontend checks: Node.js build, ESLint, Stylelint
  - PHP checks: CS Fixer, Psalm static analysis
  - Uses official Nextcloud actions (version-matrix, read-package-engines)
  - Runs checks only for changed file types
  - Summary job for branch protection rules

Benefits:
- Consolidated workflow easier to maintain than 14 files
- Follows Nextcloud app quality standards
- Catches issues before deployment
- Automatic checks on every PR

Based on Nextcloud app skeleton workflows from:
https://github.com/nextcloud/.github

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-18 00:01:53 +01:00
Chris CoutinhoandClaude Sonnet 4.5 be7f512244 docs: document deployment modes and Nextcloud log querying
Update ADR-018 with comprehensive deployment architecture and add
Nextcloud application log querying patterns to CLAUDE.md.

Changes:
- ADR-018 deployment modes documentation:
  - Mode 1: Basic single-user (development/simple)
  - Mode 2: Basic multi-user pass-through (no OIDC)
  - Mode 3: OAuth multi-user with progressive consent
  - Authentication flows for each mode
  - Communication path diagrams
  - Implementation examples
  - Use cases and limitations
- CLAUDE.md additions:
  - Nextcloud application log querying patterns
  - Common jq filters for debugging
  - Log structure documentation
  - App-specific filtering examples

Benefits:
- Clear guidance on deployment architecture selection
- Documented authentication flows for all scenarios
- Easier debugging with log query patterns
- Complete reference for mode-specific configurations

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-18 00:01:53 +01:00
Chris CoutinhoandClaude Sonnet 4.5 5eec34c17e feat(auth): implement refresh token rotation for Nextcloud OIDC
Add support for one-time use refresh tokens with automatic rotation
to align with Nextcloud OIDC security model.

Changes:
- TokenBrokerService improvements:
  - Add user_id parameter to refresh methods
  - Detect and store rotated refresh tokens
  - Add offline_access scope to token requests
  - Handle refresh token rotation on every use
- Add management API endpoints:
  - /api/v1/webhooks (GET/POST) - List/create webhooks
  - /api/v1/webhooks/{id} (DELETE) - Delete webhook
  - /api/v1/search (POST) - Unified search
  - /api/v1/chunk-context (GET) - Get chunk context
  - /api/v1/apps (GET) - List installed apps
- Update tests for refresh token rotation
- Add --headed flag to pytest for Playwright debugging

Benefits:
- Aligns with Nextcloud OIDC one-time refresh token model
- Prevents refresh token invalidation after first use
- Enables long-lived background operations
- Provides full webhook lifecycle management

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-18 00:01:53 +01:00
Chris CoutinhoandClaude Sonnet 4.5 656214b162 chore(docker): update app hooks for confidential OAuth client
Configure Astroglobe as a confidential OAuth client with client_secret
to support token refresh for long-lived sessions.

Changes:
- Update install-astroglobe-app hook to:
  - Create confidential client instead of public
  - Add offline_access scope for refresh tokens
  - Extract and store client_secret in system config
  - Display secret (truncated) for verification
- Update trusted-domains hook (formatting)

Benefits:
- Enables automatic token refresh without re-authentication
- Supports long-lived backend operations
- Better security for server-side OAuth flows

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-18 00:01:53 +01:00
Chris CoutinhoandClaude Sonnet 4.5 45fc25d02b feat(astrolabe): enhance unified search and add webhook management
Improve unified search results with chunk/page metadata and add
webhook management capabilities to McpServerClient.

Changes:
- SemanticSearchProvider improvements:
  - Display chunk position (e.g., "Chunk 2/5")
  - Display page numbers for PDFs (e.g., "Page 3/10")
  - Fix file links to open in Files app correctly
  - Fix deck card links to use proper URL format
  - Show metadata in subline before excerpt
  - Use proper icons and thumbnails for each doc type
- McpServerClient webhook methods:
  - listWebhooks() - Get all registered webhooks
  - createWebhook() - Register new webhook
  - deleteWebhook() - Remove webhook registration
  - enableWebhook() / disableWebhook() - Toggle webhook status
  - getWebhookLogs() - Retrieve delivery logs

Benefits:
- Better search result context with chunk and page info
- Clickable links that open correct resources
- Full webhook lifecycle management via API

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-18 00:01:47 +01:00
Chris CoutinhoandClaude Sonnet 4.5 9aec5582db feat(astrolabe): add webhook management UI to admin settings
Add admin interface for configuring real-time webhook sync with
pre-configured presets for common scenarios.

Changes:
- Add webhook presets section to admin settings page
  - Shows available presets filtered by installed apps
  - Enable/disable presets with one click
  - Displays current webhook status
- Add client secret configuration status display
  - Shows whether confidential client is configured
  - Provides setup instructions for optional client secret
- Add adminSettings.js for webhook management
  - Load webhook presets via API
  - Enable/disable webhook presets
  - Handle search settings form submission
- Update vite.config.js to build adminSettings entry point
- Pass clientSecretConfigured flag to template

UI Features:
- Real-time preset status (enabled/disabled)
- One-click enable/disable for webhook bundles
- App-aware filtering (only shows presets for installed apps)
- Clear instructions for requirements and setup

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-18 00:01:42 +01:00
Chris CoutinhoandClaude Sonnet 4.5 0f7e87a91c feat(astrolabe): add OAuth token refresh and webhook presets
Implement automatic token refresh and pre-configured webhook bundles
to simplify vector sync configuration.

Changes:
- Add IdpTokenRefresher service for automatic OAuth token renewal
  - Works with both Nextcloud OIDC and external IdPs (Keycloak)
  - Uses OIDC discovery for automatic endpoint detection
  - Supports confidential clients with client_secret
- Add WebhookPresets service with pre-configured bundles:
  - Notes sync (file created/written/deleted in Notes folder)
  - Calendar sync (calendar object created/updated/deleted)
  - Tables sync (row added/updated/deleted, Nextcloud 30+)
  - Forms sync (form submitted, Nextcloud 30+)
- Update ApiController to use automatic token refresh
  - Pass refresh callback to McpTokenStorage
  - Add getWebhookPresets endpoint (admin-only)
  - Add configureWebhooks endpoint for bulk setup
- Update OAuthController for webhook management
- Add new API routes for webhook configuration

Benefits:
- Eliminates manual token refresh
- Simplifies webhook setup with one-click presets
- Provides app-aware filtering (only shows presets for installed apps)

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-18 00:01:35 +01:00
Chris CoutinhoandClaude Sonnet 4.5 5acac804a1 refactor(astrolabe): extract PDF viewer to dedicated component
Replace fragile 20-iteration retry loop with proper Vue lifecycle management.

Changes:
- Create PDFViewer.vue component (~200 lines) with:
  - Proper mounted/beforeUnmount lifecycle hooks
  - Loading/error/content states
  - Page navigation controls
  - PDF document cleanup
  - User-friendly error messages
- Simplify App.vue by removing ~120 lines:
  - Remove loadPdf() and renderPdfPage() methods
  - Remove manual DOM polling with $nextTick() loops
  - Replace PDF template with <PDFViewer> component
- Add pdfjs-dist@4.0.379 dependency

Result: Canvas found on first attempt instead of requiring 20 retries.

Follows patterns from Nextcloud's production files_pdfviewer app and
2025 Vue.js + PDF.js best practices.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-18 00:01:30 +01:00
Chris CoutinhoandClaude Sonnet 4.5 85db90a2df feat(search): add file_path metadata and chunk offsets to search results
Changes:
- Add file_path to metadata in semantic and BM25 hybrid search algorithms
  for PDF viewer integration (search/semantic.py:161-163, search/bm25_hybrid.py:230-232)
- Include chunk_start_offset, chunk_end_offset, page_number, and page_count
  in search results for rich chunk display (api/management.py:981-1004)
- Add point_id field to SearchResult for batch retrieval (models/semantic.py)
- Fix type narrowing for chunk context API parameters (api/management.py:1102-1111)
- Fix None-safety in doc_types discovery (search/algorithms.py:114)

This enables the Astroglobe UI to display PDF pages at the correct
location for matched chunks.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-18 00:01:29 +01:00
Chris Coutinho a026f2eddb chore: update .gitignore for build artifacts and screenshots
Add patterns to ignore:
- Screenshots and test images (*.png, *.jpg, *.jpeg)
- Astroglobe frontend build artifacts (dist/, node_modules/)
- Unrelated third-party apps (third_party/spreed/)
2025-12-18 00:01:29 +01:00
Chris CoutinhoandClaude Sonnet 4.5 73783b85d5 feat(astrolabe): use proper icons and thumbnails in unified search
Improve search result display to match Nextcloud's native search providers by using mimetype-specific icons and preview thumbnails.

**File Results:**
- Use preview thumbnails for images/PDFs (core.Preview API)
- Use mimetype-specific icon classes (icon-pdf, icon-text, icon-image, etc.)
- Detect folders and use icon-folder appropriately

**Other Document Types:**
- Notes: icon-notes
- Deck Cards: icon-deck
- Calendar: icon-calendar
- News: icon-rss
- Contacts: icon-contacts

**API Changes:**
- Management API now includes mime_type in search results
- SemanticSearchProvider uses IMimeTypeDetector and IPreview services

This makes Astroglobe search results visually consistent with Files, Notes, and other native providers.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-18 00:01:24 +01:00
Chris CoutinhoandClaude Sonnet 4.5 4cce4f6392 feat(astrolabe): add admin search settings and enhanced UI
Add comprehensive admin controls for the unified search provider and enhance the frontend UI with filtering and visualization improvements.

**Admin Settings:**
- Configure default search algorithm (hybrid, semantic, bm25)
- Set fusion method for hybrid search (rrf, dbsf)
- Adjust minimum score threshold (0-100%)
- Set result limit (1-100 results)

**Frontend Enhancements:**
- Add score-based result filtering with slider control
- Add expandable excerpts for search results
- Improve result visualization and formatting
- Add algorithm badge to show search method used

**API Changes:**
- Add `/api/admin/search-settings` POST endpoint
- Add `searchForUnifiedSearch()` method to McpServerClient
- Load and apply admin settings in SemanticSearchProvider

**Technical Details:**
- Settings stored in app config table
- Defaults: hybrid algorithm, rrf fusion, 0% threshold, 20 results
- SemanticSearchProvider respects admin-configured limits

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-18 00:01:19 +01:00
Chris Coutinho 24e63a967a ci: Update oidc app 2025-12-18 00:01:18 +01:00
Chris CoutinhoandClaude Sonnet 4.5 dbb6ba333a feat(astrolabe): add unified search provider with clickable file links
Integrate semantic search into Nextcloud's unified search UI. File results now use fileId parameter to properly open files instead of just navigating to the Files app.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-18 00:01:14 +01:00
Chris CoutinhoandClaude Opus 4.5 97b48ca3dd feat(astrolabe): add 3D PCA visualization for semantic search
- Add Plotly.js 3D scatter plot showing search results in PCA space
- Create shared visualization.py module to avoid code duplication
- Pass include_pca parameter through API chain to enable coordinates
- Fix OAuth redirects to use /settings/user/astroglobe

The visualization shows document embeddings projected to 3D via PCA,
with the query point highlighted in red. Uses Viridis colorscale
for score visualization, matching the existing vector-viz page.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 00:01:09 +01:00
Chris CoutinhoandClaude Opus 4.5 a4106ee20d refactor(astrolabe): reframe UI as semantic search service
Update all user-facing text to focus on Astroglobe as a semantic
search service for Nextcloud users:

- info.xml: New description focusing on finding content by meaning
- Settings sections: Renamed from "MCP Server" to "Astroglobe"
- Personal settings: Reframed as content indexing controls
- Admin settings: Reframed as semantic search administration
- OAuth flow: Explains semantic search benefits to users

Key messaging changes:
- "MCP Server" → "Astroglobe"
- "Grant Background Access" → "Enable Semantic Search"
- "Vector Sync" → "Content Indexing"
- Focus on user benefits: natural language search, finding by meaning

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 00:01:00 +01:00
Chris CoutinhoandClaude Opus 4.5 21817543ad feat(astrolabe): add Nextcloud PHP app for MCP server management
Adds a native Nextcloud app "Astroglobe" that provides:
- Personal settings: OAuth authorization for background MCP access
- Admin settings: Server status and vector sync monitoring
- API endpoints for MCP server communication

The app uses PKCE OAuth flow to obtain tokens for the MCP server,
enabling features like background vector sync per ADR-018.

Includes:
- PHP app structure (controllers, services, settings)
- Vue.js frontend components
- Docker compose mount configuration
- Installation hook for development testing
- ADR-018 documentation

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 00:00:40 +01:00
Chris CoutinhoandClaude Opus 4.5 a58a14111b feat(vector-sync): enable background sync in OAuth mode
Add multi-user background vector synchronization when running in OAuth
mode with ENABLE_OFFLINE_ACCESS=true. Key changes:

Architecture (oauth_sync.py):
- User Manager task polls RefreshTokenStorage for provisioned users
- Per-user scanner tasks fetch documents using OAuth tokens
- Shared processor pool indexes documents from all users

Token Broker improvements:
- Accept client_id/client_secret instead of encryption_key
- Remove redundant token audience pre-validation (Nextcloud validates)
- Add _rewrite_token_endpoint for Docker internal URL routing
- Remove double-decryption (storage handles encryption internally)

Browser OAuth flow fixes:
- Add 'resource' parameter to request Nextcloud-scoped tokens
- Store and retrieve next_url for proper redirect after consent
- Rewrite token endpoint URLs for internal Docker access

Configuration:
- Add vector_sync_user_poll_interval setting (default: 60s)

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-14 20:00:41 +01:00
Chris Coutinho 54fdc8addc Merge remote-tracking branch 'origin/master' into feat/deck-vector-search 2025-12-14 00:23:16 +01:00
Chris CoutinhoandClaude Sonnet 4.5 e0320e761c perf(deck): optimize card lookup by storing board_id/stack_id in metadata
Addresses reviewer feedback on PR #395 about O(n²) performance issue.

Changes:
- scanner.py: Add metadata field to DocumentTask with board_id/stack_id
- scanner.py: Populate metadata during deck card scanning (both initial and incremental sync)
- processor.py: Use metadata for O(1) card lookup via get_card() API when available
- processor.py: Fallback to iteration for legacy data without metadata
- context.py: Add _get_deck_metadata_from_qdrant() helper to retrieve metadata from Qdrant
- context.py: Use metadata for fast path lookup in chunk context expansion
- context.py: Add user_id parameter to _fetch_document_text() for metadata retrieval

Performance Impact:
- Before: O(boards × stacks × cards) iteration for each card lookup
- After: O(1) direct API call using stored board_id/stack_id
- Graceful degradation: Falls back to iteration for legacy data

Testing:
- All existing integration tests pass (test_deck_vector_search.py)
- Type checking passes with no new errors

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-14 00:23:12 +01:00
Chris Coutinho 034e405824 build: Add qdrant-client until upstream issue is merged 2025-12-13 23:51:43 +01:00
Chris CoutinhoandClaude Sonnet 4.5 20404cf3f2 feat(vector): add Deck card vector search with visualization support
Adds comprehensive vector search support for Nextcloud Deck cards,
including semantic search indexing, chunk preview in the vector viz UI,
and proper deep linking to cards.

**Vector Search Indexing**
- Add deck_card scanning in scanner.py (scan_deck_cards function)
- Index cards from non-archived, non-deleted boards
- Store metadata: board_id, board_title, stack_id, stack_title, card_type, duedate, owner
- Content structure: title + "\n\n" + description (matches indexing format)
- Incremental sync based on lastModified timestamp
- Deletion tracking with grace period

**Vector Visualization Support**
- Add deck_card handler in context.py for chunk preview expansion
- Include board_id in search result metadata (bm25_hybrid.py, semantic.py)
- Expose metadata in viz_routes.py JSON responses
- Update vector-viz.js to construct proper Deck URLs: /apps/deck/board/{board_id}/card/{card_id}
- Update vector_viz.html filter label from "Deck" to "Deck Cards"

**Bug Fixes**
- Skip soft-deleted boards (deletedAt > 0) to prevent 403 Forbidden errors
- Applies to scanner, processor, and context expansion code paths
- Deck API returns deleted boards but rejects stack access with 403

**Testing**
- Add integration tests in test_deck_vector_search.py:
  - test_deck_card_semantic_search: Filtered search with doc_type="deck_card"
  - test_deck_card_appears_in_cross_app_search: Cross-app search includes deck cards
  - test_deck_card_chunk_context: Chunk context fetching for viz preview

**Documentation**
- Update README.md: Add Deck cards to semantic search feature list
- Update semantic-search-architecture.md: Document deck_card support
- Update nc_semantic_search tool documentation

**Type Safety**
- Fix type narrowing for page_boundaries (could be None) using cast()
- Fix scanner.py payload None check for type safety

Resolves vector search for Deck cards across indexing, search, and visualization.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-13 23:51:18 +01:00
Chris CoutinhoandClaude Opus 4.5 9d0a993c2a feat(vector-viz): add news_item support for links and chunk expansion
Add support for news_item document type in the vector visualization page:

- Add "News" checkbox to document type filter options
- Add URL handler to link news items to /apps/news/item/{id}
- Add content fetching for news items in chunk context expansion

This enables users to search and view news articles in the vector
visualization, with clickable links back to Nextcloud News and the
ability to expand chunks to see full article context.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-13 21:34:47 +01:00
Chris CoutinhoandClaude Sonnet 4.5 d61e33113c fix(news): revert get_item() to use get_items() + filter
Reverts the "perf(news): use direct API endpoint for get_item()" change
from commit 92c4bf3 which incorrectly assumed GET /items/{itemId} exists.

The News API (v1-2, v1-3, v2) does not provide a direct endpoint to
retrieve individual items. The only /items/{itemId} routes are POST
operations for marking items read/unread/starred.

Changes:
- Restore original get_item() implementation that fetches all items
  and filters in Python
- Update exception from HTTPStatusError to ValueError
- Restore documentation explaining API limitation
- Update unit tests to mock get_items() instead of _make_request()
- Add test for ValueError when item not found

Fixes vector processor 405 errors when indexing news items.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-13 15:47:27 +01:00
Chris Coutinho 5166c2c4d7 test: Add verification test for DNS rebinding protection fix
This test verifies that the MCP 1.23.x DNS rebinding protection fix works
correctly by sending requests with various Host headers that would be
rejected if the protection were enabled.

Test cases:
- Kubernetes service DNS (nextcloud-mcp-server.default.svc.cluster.local:8000)
- Custom domain (mcp.example.com:8000)
- Proxied hostname (proxy.internal:8000)
- Default localhost (localhost:8000)
- Malicious hostname (evil.attacker.com:8000)

Without the fix (enable_dns_rebinding_protection=False), these would fail with:
- 421 Misdirected Request (Host header not in allowed list)
- 403 Forbidden (Origin header not in allowed list)

With the fix, all requests succeed with 200 OK (SSE format).

Test results: All 2 tests passed
- test_accepts_various_host_headers: PASSED
- test_dns_rebinding_protection_is_disabled: PASSED
2025-12-12 17:56:16 +01:00
Chris Coutinho ec70e70a5d fix: Disable DNS rebinding protection for containerized deployments
MCP Python SDK 1.23.0 introduced automatic DNS rebinding protection that
auto-enables when host="127.0.0.1" (the default). This breaks containerized
deployments (Kubernetes, Docker) because the protection rejects requests
with Host headers like "nextcloud-mcp-server.default.svc.cluster.local:8000".

Root cause:
- FastMCP defaults to host="127.0.0.1"
- SDK auto-enables DNS rebinding protection with allowed_hosts=["127.0.0.1:*", "localhost:*", "[::1]:*"]
- K8s/Docker requests use service DNS names or proxied hostnames
- Protection middleware rejects these requests (421 Misdirected Request)

Solution:
- Explicitly pass transport_security=TransportSecuritySettings(enable_dns_rebinding_protection=False)
- Applied to all three FastMCP initializations (OAuth, Smithery, BasicAuth)
- DNS rebinding attacks mitigated by OAuth authentication and network isolation

This fixes issue #373 and enables MCP 1.23.x upgrade in PR #382.

For detailed analysis, see docs/MCP-1.23-DNS-REBINDING-FIX.md
2025-12-12 17:30:22 +01:00
Chris Coutinho 19183ad14a fix: address PR review feedback
Address all reviewer comments from PR #387:

1.  Add unit tests for annotations (tests/server/test_annotations.py)
   - 10 comprehensive test functions validating all annotation patterns
   - Tests for titles, read-only, destructive, idempotent operations
   - Validates specific ADR-017 decisions (webdav write, semantic search)
   - Cross-category consistency checks

2.  Fix nc_webdav_write_file idempotency classification
   - Changed from idempotentHint=False to idempotentHint=True
   - Rationale: Uses HTTP PUT without version control
   - Writing same content to same path = same end state (idempotent)

3.  Fix semantic search openWorldHint inconsistency
   - Changed from openWorldHint=False to openWorldHint=True
   - Rationale: Consistent with other Nextcloud tools
   - Nextcloud is external to MCP server (indexed data is implementation detail)

4.  Update ADR-017 with resolved decisions
   - Converted Open Questions to Resolved Questions
   - Added detailed rationale for webdav write and semantic search
   - Updated status from Proposed to Implemented
   - Added decision timeline with dates

5.  Add MCP Tool Annotations guidelines to CLAUDE.md
   - Comprehensive section with code examples for all patterns
   - Key principles documented (idempotency, destructive, open world)
   - References ADR-017 for detailed rationale

All OAuth tools verified to have proper annotations (oauth_tools.py lines 686-751).
2025-12-11 13:50:55 +01:00
Chris CoutinhoandClaude Sonnet 4.5 e1412320a7 feat: add MCP tool annotations for enhanced UX
Add ToolAnnotations to all 105+ MCP tools across 13 modules to enable
better client-side UX with human-readable titles and behavioral hints.

Changes:
- Add title and ToolAnnotations to all @mcp.tool() decorators
- Apply correct idempotency classification per ADR-017
- Add destructiveHint for delete operations
- Set openWorldHint=False for semantic search (internal data only)

Modules updated:
- OAuth (4 tools): Authentication and provisioning
- Notes (7 tools): Note management
- WebDAV (11 tools): File operations
- Semantic (3 tools): Semantic search and RAG
- Calendar (16 tools): Events and todos
- Contacts (7 tools): Address book management
- Sharing (5 tools): File/folder sharing
- Tables (6 tools): Structured data
- Deck (25 tools): Kanban board management
- Cookbook (13 tools): Recipe management
- News (8 tools): RSS feed reader

Annotation patterns:
- Read operations: readOnlyHint=True, openWorldHint=True
- Create operations: idempotentHint=False, openWorldHint=True
- Update operations: idempotentHint=False, openWorldHint=True
- Delete operations: destructiveHint=True, idempotentHint=True, openWorldHint=True

See docs/ADR-017-mcp-tool-annotations.md for rationale and implementation details.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-11 12:45:02 +01:00
Chris Coutinho c3af591810 fix: Update lockfile 2025-12-09 18:42:03 +01:00
Chris Coutinho 44573366eb build: Update lockfile 2025-12-09 15:49:25 +01:00
Chris Coutinho 7d5bb54b64 fix: Revert mcp version <1.23 2025-12-09 15:46:00 +01:00
Chris CoutinhoandClaude Sonnet 4.5 3f06e2ee77 fix: resolve all type checking errors (8 errors fixed)
Fixed 8 type checker errors across the codebase:

- vector/scanner.py: Handle None scroll results with null-safe iteration
- search/{bm25_hybrid,semantic}.py: Add None checks for result.payload
- auth/{unified_verifier,webhook_routes}.py: Assert non-None auth credentials
- client/webdav.py: Add None checks before int() conversions
- providers/openai.py: Assert embedding_model is not None
- search/algorithms.py: Explicitly type doc_types set and cast values
- observability/logging_config.py: Match parent class signature (log_data)

Also fixed test_create_tag_creates_system_tag to match WebDAV implementation
(was testing OCS API endpoint, now tests correct WebDAV endpoint with
Content-Location header).

Type checker: 0 errors (down from 8), 20 warnings (ignored)
Tests: All 192 unit tests passing

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-08 01:09:02 +01:00
Chris Coutinho 7f11c793ef Merge remote-tracking branch 'origin/master' into feature/news-app-integration 2025-12-07 22:36:48 +01:00
Chris CoutinhoandClaude 92c4bf36f6 perf(news): use direct API endpoint for get_item()
Replace O(n) fetch-all-and-filter approach with O(1) direct API call.
The News API v1-3 supports GET /items/{id} for single-item retrieval.

- Update get_item() to use direct endpoint
- Add unit test for get_item() method
- Fixes critical performance issue identified in code review

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-29 17:22:51 +01:00
Chris Coutinho 0bedbf1877 Merge remote-tracking branch 'origin/master' into feature/news-app-integration 2025-11-29 17:19:16 +01:00
Chris CoutinhoandClaude a5cb6e1242 refactor(news): simplify vector sync to fetch all items
Remove the complex starred+unread filtering logic in scan_news_items().
The News app's auto-purge feature (default: 200 items per feed) already
limits the total number of items, making explicit filtering unnecessary.

Changes:
- Replace two API calls (starred + unread) with single all-items call
- Remove deduplication logic that merged both lists
- Update docstring to explain the simpler approach

This reduces code complexity while maintaining the same effective coverage.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-29 15:05:34 +01:00
Chris CoutinhoandClaude a33f6a2f15 feat(news): add Nextcloud News app integration
Add full integration for the Nextcloud News (RSS/Atom reader) app:

- Add NewsClient with complete CRUD operations for folders, feeds, and items
- Add 8 read-only MCP tools for listing/getting folders, feeds, items
- Add Pydantic models for News entities with camelCase alias support
- Add vector sync support for starred + unread items
- Add HTML to Markdown converter using markdownify for better embeddings
- Add Docker post-install hook to enable News app
- Add 25 unit tests for NewsClient API methods

Vector sync indexes starred and unread items, providing a balanced approach
that captures important (starred) and current (unread) content without
indexing the entire article history.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-29 14:39:31 +01:00
Chris CoutinhoandClaude 92b97bda00 fix: Add rate limit retry logic to OpenAI provider
Add exponential backoff retry handling for OpenAI API rate limits
(429 errors). This is needed for GitHub Models API which has stricter
rate limits than standard OpenAI API.

- Add retry_on_rate_limit decorator with exponential backoff
- Max 5 retries with delays: 2s → 4s → 8s → 16s → 32s
- Apply to embed(), _embed_batch_request(), and generate() methods

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-23 17:24:48 +01:00