Compare commits

..

10 Commits

Author SHA1 Message Date
Chris Coutinho a96e430e56 build: bump submodule 2025-11-16 11:20:51 +01:00
Chris Coutinho 758cd5dbfb build: bump submodule 2025-11-16 09:18:45 +01:00
Chris Coutinho f36f92120c build: bump submodule 2025-11-16 08:27:49 +01:00
Chris Coutinho b96657c935 ci: Add open-webui to docker-compose 2025-11-16 07:00:20 +01:00
Chris Coutinho b174e7f8fb ci: Add notes app for development 2025-11-16 06:57:28 +01:00
github-actions[bot] 7a7ed79d56 bump: version 0.35.0 → 0.36.0 2025-11-15 23:32:55 +00:00
Chris Coutinho 7e7d861797 Merge pull request #302 from cbcoutinho/feature/viz
feat: Vector visualization enhancements and search optimizations
2025-11-16 00:32:31 +01:00
github-actions[bot] 682923dcc8 bump: version 0.34.2 → 0.35.0 2025-11-15 00:46:11 +00:00
Chris Coutinho b1a756145e Merge pull request #301 from cbcoutinho/feature/sse
feat: Enable SSE transport for validation testing
2025-11-15 01:45:48 +01:00
Chris Coutinho 00e72d24a6 feat: Enable SSE transport for mcp service and update test fixtures
Changes:
- Remove streamable-http transport override from mcp service in docker-compose.yml
- Service now uses CLI default SSE transport on /sse endpoint
- Add create_mcp_client_session_sse() helper for SSE connections
- Update nc_mcp_client fixture to use SSE transport
- Fix unpacking for SSE client (yields 2 values vs 3 for streamable-http)

Testing:
- All 4 smoke tests pass with SSE transport
- 32/34 affected tests pass (2 skipped for vector sync)
- OAuth services remain on streamable-http (unchanged)

Note: SSE transport is being deprecated in favor of streamable-http.
This enables minimal validation testing before deprecation.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-14 19:20:30 +01:00
10 changed files with 136 additions and 10 deletions
+3
View File
@@ -4,3 +4,6 @@
[submodule "third_party/oidc"] [submodule "third_party/oidc"]
path = third_party/oidc path = third_party/oidc
url = https://github.com/cbcoutinho/oidc url = https://github.com/cbcoutinho/oidc
[submodule "third_party/notes"]
path = third_party/notes
url = https://github.com/cbcoutinho/notes
+37
View File
@@ -1,3 +1,40 @@
## v0.36.0 (2025-11-15)
### BREAKING CHANGE
- Search algorithms now require Qdrant to be populated.
Vector sync must be enabled and documents indexed for search to work.
### Feat
- Normalize hybrid search RRF scores to 0-1 range
- Enhance vector visualization UI and parallelize search verification
- Add Vector Viz tab to app home page
- Add vector visualization pane with multi-select document types
- Implement custom PCA to remove sklearn dependency
- Add multi-document Protocol with cross-app search support
- Update nc_semantic_search tool with algorithm selection
- Implement unified search algorithm module
### Fix
- Reorder tabs and fix viz pane session access
### Refactor
- Optimize Nextcloud access verification with centralized filtering
- Make all search algorithms query Qdrant payload, not Nextcloud
### Perf
- Exclude vector-sync status polling from distributed tracing
## v0.35.0 (2025-11-15)
### Feat
- Enable SSE transport for mcp service and update test fixtures
## v0.34.2 (2025-11-13) ## v0.34.2 (2025-11-13)
### Fix ### Fix
@@ -2,4 +2,30 @@
set -euox pipefail set -euox pipefail
php /var/www/html/occ app:enable notes echo "Installing and configuring notes app for testing..."
# Check if development notes app is mounted at /opt/apps/notes
if [ -d /opt/apps/notes ]; then
echo "Development notes app found at /opt/apps/notes"
# Remove any existing notes app in apps (from app store or old symlink)
if [ -e /var/www/html/apps/notes ]; then
echo "Removing existing notes in apps..."
rm -rf /var/www/html/apps/notes
fi
# Create symlink from apps to the mounted development version
# Per Nextcloud docs: apps outside server root need symlinks in server root
echo "Creating symlink: apps/notes -> /opt/apps/notes"
ln -sf /opt/apps/notes /var/www/html/apps/notes
echo "Enabling notes app from /opt/apps (development mode via symlink)"
php /var/www/html/occ app:enable notes
elif [ -d /var/www/html/apps/notes ]; then
echo "notes app directory found in apps (already installed)"
php /var/www/html/occ app:enable notes
else
echo "notes app not found, installing from app store..."
php /var/www/html/occ app:install notes
php /var/www/html/occ app:enable notes
fi
+2 -2
View File
@@ -2,8 +2,8 @@ apiVersion: v2
name: nextcloud-mcp-server name: nextcloud-mcp-server
description: A Helm chart for Nextcloud MCP Server - enables AI assistants to interact with Nextcloud description: A Helm chart for Nextcloud MCP Server - enables AI assistants to interact with Nextcloud
type: application type: application
version: 0.34.2 version: 0.36.0
appVersion: "0.34.2" appVersion: "0.36.0"
keywords: keywords:
- nextcloud - nextcloud
- mcp - mcp
+12 -1
View File
@@ -69,7 +69,6 @@ services:
mcp: mcp:
build: . build: .
command: ["--transport", "streamable-http"]
restart: always restart: always
depends_on: depends_on:
app: app:
@@ -241,6 +240,17 @@ services:
profiles: profiles:
- qdrant - qdrant
open-webui:
image: ghcr.io/open-webui/open-webui:main
environment:
- OLLAMA_BASE_URL=https://ollama.internal.coutinho.io
ports:
- 127.0.0.1:3000:8080
volumes:
- open-webui:/app/backend/data
profiles:
- open-webui
volumes: volumes:
nextcloud: nextcloud:
db: db:
@@ -250,3 +260,4 @@ volumes:
keycloak-oauth-storage: keycloak-oauth-storage:
qdrant-data: qdrant-data:
mcp-data: mcp-data:
open-webui:
+1 -1
View File
@@ -1,6 +1,6 @@
[project] [project]
name = "nextcloud-mcp-server" name = "nextcloud-mcp-server"
version = "0.34.2" version = "0.36.0"
description = "Model Context Protocol (MCP) server for Nextcloud integration - enables AI assistants to interact with Nextcloud data" description = "Model Context Protocol (MCP) server for Nextcloud integration - enables AI assistants to interact with Nextcloud data"
authors = [ authors = [
{name = "Chris Coutinho", email = "chris@coutinho.io"} {name = "Chris Coutinho", email = "chris@coutinho.io"}
+51 -3
View File
@@ -9,6 +9,7 @@ import pytest
from httpx import HTTPStatusError from httpx import HTTPStatusError
from mcp import ClientSession from mcp import ClientSession
from mcp.client.session import RequestContext from mcp.client.session import RequestContext
from mcp.client.sse import sse_client
from mcp.client.streamable_http import streamablehttp_client from mcp.client.streamable_http import streamablehttp_client
from mcp.types import ElicitRequestParams, ElicitResult, ErrorData from mcp.types import ElicitRequestParams, ElicitResult, ErrorData
@@ -165,6 +166,51 @@ async def create_mcp_client_session(
logger.debug(f"{client_name} client session cleaned up successfully") logger.debug(f"{client_name} client session cleaned up successfully")
async def create_mcp_client_session_sse(
url: str,
token: str | None = None,
client_name: str = "MCP",
elicitation_callback: Any = None,
) -> AsyncGenerator[ClientSession, Any]:
"""
Factory function to create an MCP client session using SSE transport.
Similar to create_mcp_client_session but uses SSE transport instead of streamable-http.
Uses native async context managers to ensure correct LIFO cleanup order.
Args:
url: MCP server URL (e.g., "http://localhost:8000/sse")
token: Optional OAuth access token for Bearer authentication
client_name: Client name for logging (e.g., "Basic MCP (SSE)")
elicitation_callback: Optional callback for handling elicitation requests
Yields:
Initialized MCP ClientSession
Note:
SSE transport is being deprecated in favor of streamable-http.
This function exists for compatibility testing only.
"""
logger.info(f"Creating SSE client for {client_name}")
# Prepare headers with OAuth token if provided
headers = {"Authorization": f"Bearer {token}"} if token else None
# Use native async with - Python ensures LIFO cleanup
# Cleanup order will be: ClientSession.__aexit__ -> sse_client.__aexit__
# Note: sse_client yields only (read_stream, write_stream), not 3 values like streamablehttp_client
async with sse_client(url, headers=headers) as (read_stream, write_stream):
async with ClientSession(
read_stream, write_stream, elicitation_callback=elicitation_callback
) as session:
await session.initialize()
logger.info(f"{client_name} client session initialized successfully")
yield session
# Cleanup happens automatically in LIFO order - no exception suppression needed
logger.debug(f"{client_name} client session cleaned up successfully")
@pytest.fixture(scope="session") @pytest.fixture(scope="session")
async def nc_client(anyio_backend) -> AsyncGenerator[NextcloudClient, Any]: async def nc_client(anyio_backend) -> AsyncGenerator[NextcloudClient, Any]:
""" """
@@ -203,12 +249,14 @@ async def nc_client(anyio_backend) -> AsyncGenerator[NextcloudClient, Any]:
@pytest.fixture(scope="session") @pytest.fixture(scope="session")
async def nc_mcp_client(anyio_backend) -> AsyncGenerator[ClientSession, Any]: async def nc_mcp_client(anyio_backend) -> AsyncGenerator[ClientSession, Any]:
""" """
Fixture to create an MCP client session for integration tests using streamable-http. Fixture to create an MCP client session for integration tests using SSE transport.
Uses anyio pytest plugin for proper async fixture handling. Uses anyio pytest plugin for proper async fixture handling.
Note: SSE transport is being deprecated. This fixture uses SSE for compatibility testing.
""" """
async for session in create_mcp_client_session( async for session in create_mcp_client_session_sse(
url="http://localhost:8000/mcp", client_name="Basic MCP" url="http://localhost:8000/sse", client_name="Basic MCP (SSE)"
): ):
yield session yield session
Vendored Submodule
+1
Submodule third_party/notes added at 0d6517e999
Generated
+1 -1
View File
@@ -1053,7 +1053,7 @@ wheels = [
[[package]] [[package]]
name = "nextcloud-mcp-server" name = "nextcloud-mcp-server"
version = "0.34.2" version = "0.36.0"
source = { editable = "." } source = { editable = "." }
dependencies = [ dependencies = [
{ name = "aiosqlite" }, { name = "aiosqlite" },