The previous round of review feedback rested on a misunderstanding —
that the MCP server is "the OAuth issuer" under Login Flow v2 and that
NEXTCLOUD_OIDC_CLIENT_ID/SECRET are external-IdP-only. Code says
otherwise (app.py:619/625/703-717, unified_verifier.py:72):
- The MCP server is an OIDC relying party of Nextcloud OIDC. Tokens are
signed by Nextcloud and validated against Nextcloud's JWKS in all
modes — the server has no private signing keys.
- Static NEXTCLOUD_OIDC_CLIENT_ID/SECRET are the preferred way to
register the MCP server as that relying party; RFC 7591 DCR is a
fallback when both are unset.
- Login Flow v2 layers per-user app-password acquisition on top — it
governs the MCP→Nextcloud data leg, not the relying-party setup.
This commit reverts the inaccuracies introduced by 35c115e and reframes
the original `login-flow-v2.md` to match what the code does:
- login-flow-v2.md: revise "How It Works" to describe the MCP server
as an OIDC RP + OAuth facade (not a standalone issuer); rename
"OAuth Issuer Endpoints" → "OAuth Endpoints" with a note that those
endpoints front Nextcloud OIDC; add NEXTCLOUD_OIDC_CLIENT_ID/SECRET
to the required env vars with DCR documented as fallback.
- running.md: restore the static-creds Docker example (deleted in
35c115e on the wrong reasoning that it was tied to the retired
direct-OAuth-to-Nextcloud flow); rewrite the OAuth Mode section
intro to describe the actual relying-party + facade architecture.
- configuration.md: fix Best Practices "For Production" to mention
static creds as preferred / DCR as fallback; restore the .oauth
Docker volume alongside data so DCR-registered MCP-client state and
the encrypted app-password DB both persist.
- auth-flows.md: drop the note added in 35c115e that wrongly claimed
the MCP server validates Bearer tokens against its own JWKS under
Login Flow v2 — it validates against Nextcloud's JWKS in all modes;
reword the Login Flow v2 "Key characteristics" bullet that called
the MCP server "the OAuth authorization server".
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Round-2 cleanup of PR #743 review comments not covered by d153e96:
- configuration.md: fix broken `#multi-user-oauth-modes` anchor; replace
with the two real anchors (Multi-User BasicAuth, Login Flow v2). Rewrite
the stale "always use OAuth2/OIDC with pre-configured clients" Best
Practices section to reflect the post-pivot mode matrix, and update the
Docker volume example to mount the encrypted app-password store
(`TOKEN_STORAGE_DB`) rather than obsolete `.oauth` client storage.
- semantic-search-architecture.md: rename remaining body references from
the deprecated `VECTOR_SYNC_ENABLED` to `ENABLE_SEMANTIC_SEARCH` so the
doc matches configuration.md / troubleshooting.md.
- running.md: relabel "OAuth Mode (Recommended)" as
"Login Flow v2 / OAuth issuer mode (--oauth)", drop the misleading
"(Legacy)" suffix from BasicAuth, drop the
`NEXTCLOUD_OIDC_CLIENT_ID/SECRET` example (tied to the retired
direct-OAuth-to-Nextcloud flow), and add a note explaining what
`--oauth` actually enables post-pivot.
- keycloak-multi-client-validation.md, oauth-impersonation-findings.md:
add a deprecation banner pointing at ADR-022 / Login Flow v2. Files
retained because ADR-002 and CLAUDE.md still cite them.
- auth-flows.md: clarify under the Astrolabe → MCP diagram that the
Nextcloud-OIDC JWKS path applies to Multi-User BasicAuth; under
Login Flow v2 the MCP server validates tokens against its own JWKS.
- login-flow-v2.md: clarify the sticky-session note — affinity must key
on the OAuth bearer token (or user-bound cookie), not source IP, since
MCP clients may not maintain stable IPs across the provisioning flow.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Fix issues raised by reviewer on PR #743:
- troubleshooting.md: renumber "Getting Help" steps (4→3, 5→4) after
earlier consolidation left a gap
- installation.md: drop stale "OIDC app" prerequisite; admin access is
now optional under Login Flow v2 (works on stock Nextcloud 16+)
- semantic-search-architecture.md: rename VECTOR_SYNC_ENABLED to
ENABLE_SEMANTIC_SEARCH in the Status callout (renamed in v0.58.0)
- configuration.md: remove Quick Start references to deprecated
oauth-multi-user / oauth-advanced templates and point to
login-flow-v2.md; update "OAuth, Multi-User BasicAuth" label to
"Login Flow v2, Multi-User BasicAuth"
- auth-flows.md: fix background-sync diagram so Encrypt+persist step
no longer crosses into the Nextcloud column
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the seven OAuth-to-Nextcloud docs (oauth-setup, quickstart-oauth,
oauth-architecture, oauth-upstream-status, oauth-troubleshooting,
jwt-oauth-reference, audience-validation-setup) with a single new
docs/login-flow-v2.md. The deprecated flow required upstream user_oidc
patches that were never merged; Login Flow v2 is the forward-looking
multi-user mode (see ADR-022), and works with stock Nextcloud 16+.
Rewrite docs/authentication.md and docs/auth-flows.md around three modes:
Single-User BasicAuth, Multi-User BasicAuth pass-through, and Login Flow v2.
Update README to add an Astrolabe Cloud (https://astrolabecloud.com)
callout for users who prefer not to self-host, drop the OAuth deployment
mode from the auth table, simplify the Docker block, and trim the
Examples and Security sections.
Sweep configuration.md, installation.md, troubleshooting.md, running.md,
and semantic-search-architecture.md to replace links to the deleted docs
and update deprecated mode names.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Use dynaconf (get_settings()) instead of os.getenv for OIDC_RESOURCE_SERVER_ID
- Re-add Settings field, _field_map entry, and settings.toml default
- Add trailing-slash guard (.rstrip("/")) to prevent double-slash in scopes
- Add double-prefixing guard: skip scopes already carrying the prefix
- Add @pytest.mark.unit to test module
- Add test for already-prefixed scopes
- Document OIDC_RESOURCE_SERVER_ID in docs/configuration.md
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The helm chart has been migrated to a dedicated repository at
https://github.com/cbcoutinho/helm-charts. This removes the chart
source, release workflow, bump script, and updates all documentation
to point to the new repository.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Incorporate reviewer feedback across three review rounds:
- Remove post_hooks from Phase 1 constructor; defer to Phase 4
- Fix Validator syntax: use condition=lambda instead of ne= kwarg
- Add MCP_DEPLOYMENT_MODE validator to catch typos at startup
- Add CRITICAL to LOG_LEVEL validator enum
- Make OTEL_TRACES_SAMPLER_ARG validation conditional on ratio samplers
- Add all missing provider env vars to settings.toml (Bedrock, Anthropic, Ollama, Simple)
- Add provider secrets to .secrets.toml.example
- Fix DynaconfDict import to stable public API path
- Strengthen ignore_unknown_envvars risk: CI lint check mandatory before Phase 2
- Document ValidationError vs ValueError breaking change in Phase 3
- Acknowledge environments=True legacy risk with mitigation
- Address root_path pip-install concern (intentional: pip uses env vars)
- Add enable_token_exchange to adapter example; note exhaustive field mapping
- Clarify Provider Registry is Phase 6 with explanation of os.getenv coexistence
- Improve test isolation fixture with teardown reload + _dynaconf visibility note
- Add Docker Compose volume mount host-file existence note
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Many identity providers (AWS Cognito, Okta, Azure AD) reject or mishandle
colons in OAuth scope names. This migrates all custom scopes from
`resource:action` to `resource.action` format (e.g., `notes:read` →
`notes.read`), which is universally accepted and aligns with industry
conventions (Microsoft, Google).
Includes Alembic migration 004 for stored scope strings and ADR-024
documenting the rationale and RFC references.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Address all 9 review points from PR #680:
- Fix post_hooks code examples to use correct return-dict signature
- Expand test isolation section with fixture factory, DynaconfDict, and
reload patterns
- Document ignore_unknown_envvars silent failure mode in Negative
Consequences and add env var audit to Phase 1 checklist
- Fix NEXTCLOUD_HOST validator to be unconditional (required in all modes)
- Document environments=True edge cases (unset mode, ENV_FOR_DYNACONF
shadowing)
- Add upper bound to dynaconf version pin (>=3.2.13,<4.0)
- Tighten Pydantic Settings comparison to acknowledge 2.x TOML support
- Make .gitignore additions explicit in Phase 1 checklist
- Clarify that shell-level .env loading still works with load_dotenv=False
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fix incorrect hook syntax (@hookable.post → Dynaconf(post_hooks=[...])),
broken Qdrant mutual exclusivity validator, missing root_path for settings
file resolution, and empty string defaults that bypass validators. Add test
isolation section, mark Phase 4 as optional/future with risk note, and
correct Pydantic comparison (already a project dependency).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Propose migrating from manual os.getenv() calls to dynaconf for
file-based configuration. Key decisions: envvar_prefix=False for
backward compatibility, MCP_DEPLOYMENT_MODE as environment switcher,
TOML settings files with secret separation, and incremental migration
via adapter pattern.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Smithery is no longer a supported deployment mode. Remove all Smithery-specific
code paths, middleware, configuration, and tests. This simplifies the codebase
by eliminating DeploymentMode enum, SmitheryConfigMiddleware, session config
context variables, and the smithery_main entrypoint.
Files deleted: Dockerfile.smithery, smithery.yaml, smithery_main.py
ADR-016 retained with deprecated status for historical reference.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
MCP clients like Claude Code were unable to use tools because tokens
obtained directly from Nextcloud had the wrong audience claim. The MCP
server now acts as its own OAuth Authorization Server, proxying auth
to Nextcloud with its own client_id so tokens have the correct audience.
New endpoints: /.well-known/oauth-authorization-server, /oauth/token,
/oauth/register. Modified /oauth/authorize from pass-through to
intermediary pattern. PRM now points authorization_servers to the MCP
server instead of Nextcloud.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Changes based on review:
1. Add Nextcloud platform limitation section documenting OAuth/scope
support by endpoint type (WebDAV supports OAuth, others don't)
2. Update MCP elicitation to show capability negotiation and graceful
fallback - URL in error message when elicitation not supported
3. Simplify Smithery section - recommend self-hosted for privacy,
don't detail platform changes
4. Expand re-auth section with scope merging behavior, scenarios table,
and explicit design choice for tool-based re-auth over auto-elicitation
5. Make rate limiting configurable with environment variables and
admin guidance by deployment size
6. Clarify OAuth alternative - keep simple now, revisit if Nextcloud
adds scoped OAuth support
7. Expand verification steps with required tests, add recommended
Nextcloud configuration, add required README security notice
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Proposes consolidating five deployment modes into two:
- Single-User: App password in env vars (trusted environment)
- Multi-User: Login Flow v2 for per-user app password acquisition
Key changes:
- Use Nextcloud Login Flow v2 (NC 16+) for delegated authentication
- Application-level scope enforcement (app passwords have no native scopes)
- MCP elicitation for seamless authorization prompting
- Astrolabe front-end integration for scope management UI
- Clear security posture documentation for administrators
This removes the need for upstream Nextcloud OAuth patches and simplifies
deployment while maintaining security through defense-in-depth.
Related: #521
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add NEXTCLOUD_VERIFY_SSL and NEXTCLOUD_CA_BUNDLE env vars to configure
TLS certificate verification for all outbound Nextcloud connections.
Centralizes SSL config via a new HTTP client factory (http.py) used by
all 27 Nextcloud-bound call sites, including API clients, OIDC endpoints,
OAuth flows, and health checks.
Closes#560
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove astrolabe-specific docs and sections that belong in the
astrolabe repo. Update remaining references to point to the
astrolabe repo where appropriate.
- Fix .gitmodules SSH → HTTPS URL for astrolabe submodule
- Remove bump-version.yml stale "astrolabe" scope comment
- Delete blog-introducing-astrolabe.md (moved to astrolabe repo)
- Remove "Astrolabe Background Token Refresh" section from auth-flows.md
- Replace "Astrolabe User Setup" section in authentication.md with link
- Remove "Astrolabe Internal URL" section from configuration.md
- Remove "Webhook Presets (via Astrolabe UI)" from webhook guide
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Create unified documentation covering authentication flows across all five
deployment modes. Documents three communication patterns (MCP Client → MCP
Server → Nextcloud, background sync, Astrolabe → MCP Server) with ASCII
sequence diagrams and implementation references.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add unit tests for /api/v1/status endpoint focusing on OIDC config:
- Test hybrid mode (multi_user_basic + enable_offline_access) returns OIDC
- Test pure multi_user_basic mode without offline_access omits OIDC
- Test OAuth mode returns OIDC config
- Test single-user BasicAuth mode omits OIDC config
- Test partial OIDC config (only discovery_url or only issuer)
Also updates docs/authentication.md with Astrolabe hybrid mode setup:
- Two-step credential setup (OAuth + app password)
- Technical details for each credential type
- Request direction table explaining why two credentials needed
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace direct os.getenv() calls with get_settings().vector_sync_enabled
to ensure consistent behavior with both VECTOR_SYNC_ENABLED (deprecated)
and ENABLE_SEMANTIC_SEARCH environment variables.
Also add webhook management documentation guide.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
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>
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>
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>
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>
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
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).
Add architecture decision record for supporting Smithery-hosted MCP
server in a stateless mode for multi-user public Nextcloud instances.
Key decisions:
- New SMITHERY_STATELESS deployment mode alongside SELF_HOSTED
- Session-based configuration (nextcloud_url, username, app_password)
- Feature subset excluding semantic search and background sync
- Admin UI (/app) excluded in Smithery mode
- Per-request client creation from session config
This enables users to try the MCP server without self-hosting
infrastructure while supporting multiple Nextcloud instances.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Extract CSS and JavaScript into separate static files
- Created nextcloud_mcp_server/auth/static/vector-viz.css
- Created nextcloud_mcp_server/auth/static/vector-viz.js
- Updated templates to reference external assets
- Fix vector visualization issues:
- Normalize vectors before PCA to match Qdrant's cosine distance
- Add zero-norm and NaN detection/handling for large datasets
- Enable responsive Plotly sizing (autosize + responsive config)
- Widen plot area to full viewport width with minimized margins
- Improve visualization accuracy:
- Query point now positioned correctly relative to documents
- Handles 200+ points without JSON serialization errors
- Full-width plot maximizes screen space utilization
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Migrates from custom word-based chunking to LangChain's MarkdownTextSplitter
for better semantic search quality. This implements the chunking portion of
ADR-011.
Changes:
- Replace custom regex word chunker with MarkdownTextSplitter
- Optimized for Markdown content (headers, code blocks, lists)
- Convert from word-based (512 words) to character-based (2048 chars) chunking
- Maintain backward-compatible ChunkWithPosition interface
- Update configuration defaults and validation
- Update all unit tests (12/12 passing)
Benefits:
- Respects markdown structure boundaries
- Never breaks code blocks or headers mid-chunk
- Preserves semantic coherence within chunks
- Expected 20-30% improvement in recall quality
- Industry-standard approach (used by production RAG systems)
Note: Full reindex required to apply new chunking to existing documents.
Current vector database still contains old word-based chunks.
Related: ADR-011 (Improving Semantic Search Quality)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added support for two fusion algorithms (RRF and DBSF) to combine dense
semantic and sparse BM25 search results, with comprehensive documentation
and unit tests.
Changes:
- Added fusion parameter to nc_semantic_search and nc_semantic_search_answer tools
- Updated ADR-014 with detailed comparison of RRF vs DBSF fusion algorithms
- Added unit tests for fusion algorithm initialization and validation
- Updated search_method in responses to include fusion type (e.g., "bm25_hybrid_rrf")
Fusion Algorithms:
- RRF (Reciprocal Rank Fusion): Default, rank-based, general-purpose
- DBSF (Distribution-Based Score Fusion): Score normalization using statistics
RRF is recommended for most use cases due to its robustness and established
track record. DBSF may provide better results when retrieval systems have
very different score distributions.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Refactored LLM provider infrastructure to support sustainable additions of new providers with both embedding and text generation capabilities.
## Major Changes
### Unified Provider Architecture (ADR-015)
- Created `nextcloud_mcp_server/providers/` with unified Provider ABC
- Providers now support optional capabilities (embeddings and/or generation)
- Auto-detection registry with priority: Bedrock → Ollama → Simple
- Backward compatible - existing code continues to work
### New Providers
- **BedrockProvider**: Full Amazon Bedrock integration
- Embeddings: Titan Embed, Cohere Embed models
- Generation: Claude, Llama, Titan Text, Mistral models
- Model-specific request/response handling
- AWS credential chain integration
- **OllamaProvider**: Migrated with both capabilities support
- **AnthropicProvider**: Moved from test code to production providers
- **SimpleProvider**: Migrated in-memory fallback provider
### Breaking Changes
None - full backward compatibility maintained:
- `embedding.get_embedding_service()` still works
- RAG evaluation tests updated to use unified providers
- All existing tests pass (127 unit tests)
### Testing
- Added 9 comprehensive Bedrock unit tests with mocked boto3
- All existing unit tests pass
- Type checking (ty) and linting (ruff) pass
- Verified backward compatibility
### Documentation
- `docs/ADR-015-unified-provider-architecture.md`: Comprehensive ADR
- `docs/bedrock-setup.md`: AWS setup guide with IAM permissions
- `CLAUDE.md`: Updated with provider architecture section
### Dependencies
- Added `boto3>=1.35.0` to dev dependencies (optional)
## Environment Variables
### Bedrock
- `AWS_REGION`: AWS region (e.g., "us-east-1")
- `BEDROCK_EMBEDDING_MODEL`: Model ID for embeddings
- `BEDROCK_GENERATION_MODEL`: Model ID for generation
- `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`: Optional credentials
### Ollama
- `OLLAMA_BASE_URL`: API URL
- `OLLAMA_EMBEDDING_MODEL`: Embedding model (default: "nomic-embed-text")
- `OLLAMA_GENERATION_MODEL`: Generation model
## AWS Bedrock Permissions Required
Minimal IAM policy:
```json
{
"Effect": "Allow",
"Action": ["bedrock:InvokeModel"],
"Resource": ["arn:aws:bedrock:*::foundation-model/*"]
}
```
See `docs/bedrock-setup.md` for detailed setup instructions.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>