Skip to main content
Glama

Nextcloud MCP Server

by cbcoutinho
docker-compose.yml11.1 kB
services: # Note: MariaDB is external service. You can find more information about the configuration here: # https://hub.docker.com/_/mariadb db: # Note: Check the recommend version here: https://docs.nextcloud.com/server/latest/admin_manual/installation/system_requirements.html#server image: docker.io/library/mariadb:lts@sha256:1cac8492bd78b1ec693238dc600be173397efd7b55eabc725abc281dc855b482 restart: always command: --transaction-isolation=READ-COMMITTED volumes: - db:/var/lib/mysql environment: - MYSQL_ROOT_PASSWORD=password - MYSQL_PASSWORD=password - MYSQL_DATABASE=nextcloud - MYSQL_USER=nextcloud # Note: Redis is an external service. You can find more information about the configuration here: # https://hub.docker.com/_/redis redis: image: docker.io/library/redis:alpine@sha256:6cbef353e480a8a6e7f10ec545f13d7d3fa85a212cdcc5ffaf5a1c818b9d3798 restart: always app: image: docker.io/library/nextcloud:32.0.2@sha256:ac08482d73ffd85d94069ba291bbd5fb39a70ff21502030a2e3e2d89a7246a48 restart: always ports: - 0.0.0.0:8080:80 depends_on: - redis - db - keycloak volumes: - nextcloud:/var/www/html - ./app-hooks:/docker-entrypoint-hooks.d:ro # Mount OIDC development directory outside /var/www/html to avoid rsync conflicts # The post-installation hook will register /opt/apps as an additional app directory #- ./third_party:/opt/apps:ro environment: - NEXTCLOUD_TRUSTED_DOMAINS=app - NEXTCLOUD_ADMIN_USER=admin - NEXTCLOUD_ADMIN_PASSWORD=admin - MYSQL_PASSWORD=password - MYSQL_DATABASE=nextcloud - MYSQL_USER=nextcloud - MYSQL_HOST=db - REDIS_HOST=redis healthcheck: test: ["CMD-SHELL", "curl -Ss http://localhost/status.php | grep '\"installed\":true' || exit 1"] interval: 10s timeout: 30s retries: 30 recipes: image: docker.io/library/nginx:alpine@sha256:b3c656d55d7ad751196f21b7fd2e8d4da9cb430e32f646adcf92441b72f82b14 restart: always volumes: - ./tests/fixtures/test_recipe.html:/usr/share/nginx/html/test_recipe.html:ro - ./tests/fixtures/nginx.conf:/etc/nginx/nginx.conf:ro unstructured: image: downloads.unstructured.io/unstructured-io/unstructured-api:latest@sha256:54282d3a25f33fd6cf69bc45b3d37770f213593f58b6dfe5e85fe546376b2807 restart: always ports: - 127.0.0.1:8002:8000 # Unstructured API runs on port 8000 internally # We expose it on 8002 externally to avoid conflict profiles: - unstructured mcp: build: . restart: always command: ["--transport", "streamable-http"] depends_on: app: condition: service_healthy ports: - 127.0.0.1:8000:8000 - 127.0.0.1:9090:9090 volumes: - mcp-data:/app/data environment: - NEXTCLOUD_HOST=http://app:80 - NEXTCLOUD_USERNAME=admin - NEXTCLOUD_PASSWORD=admin - NEXTCLOUD_PUBLIC_ISSUER_URL=http://localhost:8080 # Vector sync configuration (ADR-007) - VECTOR_SYNC_ENABLED=true - VECTOR_SYNC_SCAN_INTERVAL=60 - VECTOR_SYNC_PROCESSOR_WORKERS=1 #- LOG_FORMAT=json # Qdrant configuration (three modes): # 1. Network mode: Set QDRANT_URL=http://qdrant:6333 (requires qdrant service) # 2. In-memory mode: Set QDRANT_LOCATION=:memory: (default if nothing set) # 3. Persistent local: Set QDRANT_LOCATION=/app/data/qdrant (stored in mcp-data volume) #- QDRANT_LOCATION=/app/data/qdrant # In-memory mode used if not set #- QDRANT_URL=http://qdrant:6333 # Uncomment for network mode #- QDRANT_API_KEY=${QDRANT_API_KEY:-my_secret_api_key} # Only for network mode # Observability #- OTEL_SERVICE_NAME=nextcloud-mcp-docker-compose #- OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317 # Collection naming: Auto-generated as {deployment-id}-{model-name} # - Deployment ID: OTEL_SERVICE_NAME (if set) or hostname (fallback) # - Model name: OLLAMA_EMBEDDING_MODEL # - Example: "nextcloud-mcp-server-nomic-embed-text" # - Changing models creates new collection (requires re-embedding) # - Set QDRANT_COLLECTION to override auto-generation: #- QDRANT_COLLECTION=nextcloud_content # Ollama configuration (optional - uses SimpleEmbeddingProvider if not set) # - OLLAMA_BASE_URL=http://ollama:11434 # - OLLAMA_EMBEDDING_MODEL=nomic-embed-text # Changing this creates new collection # - OLLAMA_VERIFY_SSL=false # Document chunking configuration (for vector embeddings) # Tune these based on your embedding model and content type # - DOCUMENT_CHUNK_SIZE=512 # Words per chunk (default: 512) # - DOCUMENT_CHUNK_OVERLAP=50 # Overlapping words (default: 50, recommended: 10-20% of chunk size) mcp-oauth: build: . command: ["--transport", "streamable-http", "--oauth", "--port", "8001", "--oauth-token-type", "jwt"] restart: always depends_on: app: condition: service_healthy ports: - 127.0.0.1:8001:8001 environment: # Generic OIDC configuration (integrated mode - Nextcloud OIDC app) # OIDC_DISCOVERY_URL not set - defaults to NEXTCLOUD_HOST/.well-known/openid-configuration # OIDC_CLIENT_ID not set - uses Dynamic Client Registration (DCR) - NEXTCLOUD_HOST=http://app:80 - NEXTCLOUD_MCP_SERVER_URL=http://localhost:8001 - NEXTCLOUD_RESOURCE_URI=http://localhost:8080 # ADR-005: Nextcloud resource identifier for audience validation - NEXTCLOUD_PUBLIC_ISSUER_URL=http://localhost:8080 - NEXTCLOUD_OIDC_SCOPES=openid profile email notes:read notes:write calendar:read calendar:write contacts:read contacts:write cookbook:read cookbook:write deck:read deck:write tables:read tables:write files:read files:write sharing:read sharing:write todo:read todo:write # Refresh token storage (ADR-002 Tier 1) - ENABLE_OFFLINE_ACCESS=true - TOKEN_ENCRYPTION_KEY=ESF1BvEQdGYsCluwMx9Cxvw3uh5pFowPH7Rg_nIliyo= - TOKEN_STORAGE_DB=/app/data/tokens.db # ADR-005: Multi-audience mode (default - ENABLE_TOKEN_EXCHANGE=false) # Tokens must contain BOTH MCP and Nextcloud audiences # No token exchange needed - tokens work for both MCP auth and Nextcloud APIs # NO admin credentials - using OAuth with Dynamic Client Registration (DCR) # Client credentials registered via RFC 7591 and stored in volume # JWT token type is used for testing (faster validation, scopes embedded in token) volumes: - oauth-client-storage:/app/.oauth - oauth-tokens:/app/data keycloak: image: quay.io/keycloak/keycloak:26.4.5@sha256:653852bfdea2be6e958b9e90a976eff1c6de34edd55f2f679bdc48ef16bc528e command: - "start-dev" - "--import-realm" - "--hostname=http://localhost:8888" - "--hostname-strict=false" - "--hostname-backchannel-dynamic=true" - "--features=preview" # Enable Legacy V1 token exchange (supports both Standard V2 and Legacy V1) ports: - 127.0.0.1:8888:8080 environment: - KC_BOOTSTRAP_ADMIN_USERNAME=admin - KC_BOOTSTRAP_ADMIN_PASSWORD=admin volumes: - ./keycloak/realm-export.json:/opt/keycloak/data/import/realm.json:ro healthcheck: test: ["CMD-SHELL", "exec 3<>/dev/tcp/localhost/8080 && echo -e 'GET /realms/nextcloud-mcp HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n' >&3 && cat <&3 | grep -q 'HTTP/1.1 200'"] interval: 10s timeout: 5s retries: 30 mcp-keycloak: build: . command: ["--transport", "streamable-http", "--oauth", "--port", "8002"] restart: always depends_on: keycloak: condition: service_healthy app: condition: service_started ports: - 127.0.0.1:8002:8002 environment: # Generic OIDC configuration (external IdP mode - Keycloak) # Provider auto-detected from OIDC_DISCOVERY_URL issuer # Using internal Docker hostname for discovery to get consistent issuer - OIDC_DISCOVERY_URL=http://keycloak:8080/realms/nextcloud-mcp/.well-known/openid-configuration - NEXTCLOUD_OIDC_CLIENT_ID=nextcloud-mcp-server - NEXTCLOUD_OIDC_CLIENT_SECRET=mcp-secret-change-in-production - OIDC_JWKS_URI=http://keycloak:8080/realms/nextcloud-mcp/protocol/openid-connect/certs # Nextcloud API endpoint (for accessing APIs with validated token) - NEXTCLOUD_HOST=http://app:80 - NEXTCLOUD_MCP_SERVER_URL=http://localhost:8002 - NEXTCLOUD_RESOURCE_URI=nextcloud # ADR-005: Keycloak uses client IDs as audiences, not URLs - NEXTCLOUD_PUBLIC_ISSUER_URL=http://localhost:8888/realms/nextcloud-mcp # Refresh token storage (ADR-002 Tier 1 & 2) - ENABLE_OFFLINE_ACCESS=true - TOKEN_ENCRYPTION_KEY=ESF1BvEQdGYsCluwMx9Cxvw3uh5pFowPH7Rg_nIliyo= - TOKEN_STORAGE_DB=/app/data/tokens.db # ADR-005: Token exchange mode (RFC 8693) # Exchange MCP tokens (aud: nextcloud-mcp-server) for Nextcloud tokens (aud: http://localhost:8080) # Provides strict audience separation between MCP session and Nextcloud API access - ENABLE_TOKEN_EXCHANGE=true - TOKEN_EXCHANGE_CACHE_TTL=300 # Cache exchanged tokens for 5 minutes (default) # OAuth scopes (optional - uses defaults if not specified) - NEXTCLOUD_OIDC_SCOPES=openid profile email offline_access notes:read notes:write calendar:read calendar:write contacts:read contacts:write cookbook:read cookbook:write deck:read deck:write tables:read tables:write files:read files:write sharing:read sharing:write todo:read todo:write # NO admin credentials - using external IdP OAuth only! volumes: - keycloak-tokens:/app/data - keycloak-oauth-storage:/app/.oauth # Smithery stateless deployment mode (ADR-016) # Test with: docker compose --profile smithery up smithery # Then: curl http://localhost:8081/.well-known/mcp-config smithery: build: context: . dockerfile: Dockerfile.smithery restart: always depends_on: app: condition: service_healthy ports: - 127.0.0.1:8081:8081 environment: - SMITHERY_DEPLOYMENT=true - VECTOR_SYNC_ENABLED=false - PORT=8081 profiles: - smithery qdrant: image: qdrant/qdrant:v1.16.0@sha256:1005201498cf927d835383d0f918b17d8c9da7db58550f169f694455e42d78f4 restart: always ports: - 127.0.0.1:6333:6333 # REST API - 127.0.0.1:6334:6334 # gRPC (optional) volumes: - qdrant-data:/qdrant/storage environment: - QDRANT__SERVICE__API_KEY=${QDRANT_API_KEY:-my_secret_api_key} healthcheck: test: ["CMD-SHELL", "test -f /qdrant/.qdrant-initialized"] interval: 10s timeout: 5s retries: 10 profiles: - qdrant volumes: nextcloud: db: oauth-client-storage: oauth-tokens: keycloak-tokens: keycloak-oauth-storage: qdrant-data: mcp-data:

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/cbcoutinho/nextcloud-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server