Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b11c3ddfb6 | |||
| 562c102711 | |||
| d7a8719d0e | |||
| 97fa9ef8a7 | |||
| 77dd17b3e1 | |||
| d56ec33b77 | |||
| a1c5acc1c2 | |||
| 6833f7f117 | |||
| 7db2a5c586 | |||
| b76c10f18c | |||
| ab7411d9fd | |||
| d02fe3c3b6 |
@@ -0,0 +1,29 @@
|
|||||||
|
name: Release Charts
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- v*
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
# depending on default permission settings for your org (contents being read-only or read-write for workloads), you will have to add permissions
|
||||||
|
# see: https://docs.github.com/en/actions/security-guides/automatic-token-authentication#modifying-the-permissions-for-the-github_token
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Configure Git
|
||||||
|
run: |
|
||||||
|
git config user.name "$GITHUB_ACTOR"
|
||||||
|
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
|
||||||
|
|
||||||
|
- name: Run chart-releaser
|
||||||
|
uses: helm/chart-releaser-action@v1.7.0
|
||||||
|
env:
|
||||||
|
CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# Patterns to ignore when building packages.
|
||||||
|
# This supports shell glob matching, relative path matching, and
|
||||||
|
# negation (prefixed with !). Only one pattern per line.
|
||||||
|
.DS_Store
|
||||||
|
# Common VCS dirs
|
||||||
|
.git/
|
||||||
|
.gitignore
|
||||||
|
.bzr/
|
||||||
|
.bzrignore
|
||||||
|
.hg/
|
||||||
|
.hgignore
|
||||||
|
.svn/
|
||||||
|
# Common backup files
|
||||||
|
*.swp
|
||||||
|
*.bak
|
||||||
|
*.tmp
|
||||||
|
*.orig
|
||||||
|
*~
|
||||||
|
# Various IDEs
|
||||||
|
.project
|
||||||
|
.idea/
|
||||||
|
*.tmproj
|
||||||
|
.vscode/
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
apiVersion: v2
|
||||||
|
name: nextcloud-mcp-server
|
||||||
|
description: A Helm chart for Nextcloud MCP Server - enables AI assistants to interact with Nextcloud
|
||||||
|
type: application
|
||||||
|
version: 0.1.0
|
||||||
|
appVersion: "0.21.0"
|
||||||
|
keywords:
|
||||||
|
- nextcloud
|
||||||
|
- mcp
|
||||||
|
- model-context-protocol
|
||||||
|
- llm
|
||||||
|
- ai
|
||||||
|
- claude
|
||||||
|
- webdav
|
||||||
|
- caldav
|
||||||
|
- carddav
|
||||||
|
maintainers:
|
||||||
|
- name: Chris Coutinho
|
||||||
|
email: chris@coutinho.io
|
||||||
|
home: https://github.com/cbcoutinho/nextcloud-mcp-server
|
||||||
|
sources:
|
||||||
|
- https://github.com/cbcoutinho/nextcloud-mcp-server
|
||||||
|
icon: https://raw.githubusercontent.com/nextcloud/server/master/core/img/logo/logo.svg
|
||||||
@@ -0,0 +1,470 @@
|
|||||||
|
# Nextcloud MCP Server Helm Chart
|
||||||
|
|
||||||
|
This Helm chart deploys the Nextcloud MCP (Model Context Protocol) Server on a Kubernetes cluster, enabling AI assistants to interact with your Nextcloud instance.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Kubernetes 1.19+
|
||||||
|
- Helm 3.0+
|
||||||
|
- A running Nextcloud instance (accessible from the Kubernetes cluster)
|
||||||
|
- Nextcloud credentials (username/password for basic auth OR OAuth client for OAuth mode)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### Quick Start with Basic Authentication
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install with basic auth (recommended for most users)
|
||||||
|
helm install nextcloud-mcp ./helm/nextcloud-mcp-server \
|
||||||
|
--set nextcloud.host=https://cloud.example.com \
|
||||||
|
--set auth.basic.username=myuser \
|
||||||
|
--set auth.basic.password=mypassword
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using a values file
|
||||||
|
|
||||||
|
Create a `custom-values.yaml` file:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
nextcloud:
|
||||||
|
host: https://cloud.example.com
|
||||||
|
|
||||||
|
auth:
|
||||||
|
mode: basic
|
||||||
|
basic:
|
||||||
|
username: myuser
|
||||||
|
password: mypassword
|
||||||
|
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpu: 1000m
|
||||||
|
memory: 512Mi
|
||||||
|
requests:
|
||||||
|
cpu: 100m
|
||||||
|
memory: 128Mi
|
||||||
|
```
|
||||||
|
|
||||||
|
Install with your custom values:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
helm install nextcloud-mcp ./helm/nextcloud-mcp-server -f custom-values.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
### OAuth Authentication Mode (Experimental)
|
||||||
|
|
||||||
|
**Warning:** OAuth mode is experimental and requires patches to the Nextcloud `user_oidc` app. See the [Authentication Guide](https://github.com/cbcoutinho/nextcloud-mcp-server#authentication) for details.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
nextcloud:
|
||||||
|
host: https://cloud.example.com
|
||||||
|
mcpServerUrl: https://mcp.example.com
|
||||||
|
publicIssuerUrl: https://cloud.example.com
|
||||||
|
|
||||||
|
auth:
|
||||||
|
mode: oauth
|
||||||
|
oauth:
|
||||||
|
# Optional: provide pre-registered client credentials
|
||||||
|
# If not provided, will use Dynamic Client Registration
|
||||||
|
clientId: "your-client-id"
|
||||||
|
clientSecret: "your-client-secret"
|
||||||
|
persistence:
|
||||||
|
enabled: true
|
||||||
|
size: 100Mi
|
||||||
|
|
||||||
|
ingress:
|
||||||
|
enabled: true
|
||||||
|
className: nginx
|
||||||
|
hosts:
|
||||||
|
- host: mcp.example.com
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
tls:
|
||||||
|
- secretName: nextcloud-mcp-tls
|
||||||
|
hosts:
|
||||||
|
- mcp.example.com
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### Key Configuration Parameters
|
||||||
|
|
||||||
|
#### Nextcloud Connection
|
||||||
|
|
||||||
|
| Parameter | Description | Default |
|
||||||
|
|-----------|-------------|---------|
|
||||||
|
| `nextcloud.host` | URL of your Nextcloud instance (required) | `""` |
|
||||||
|
| `nextcloud.mcpServerUrl` | MCP server URL for OAuth callbacks (OAuth only, optional) | Smart default* |
|
||||||
|
| `nextcloud.publicIssuerUrl` | Public issuer URL for OAuth (OAuth only, optional) | Smart default** |
|
||||||
|
|
||||||
|
**Smart Defaults:**
|
||||||
|
- `*mcpServerUrl`: If not set, automatically uses ingress host (if enabled) or `http://localhost:8000` (for port-forward setups)
|
||||||
|
- `**publicIssuerUrl`: If not set, automatically defaults to `nextcloud.host` (which works when both clients and MCP server access Nextcloud at the same URL)
|
||||||
|
|
||||||
|
#### Authentication
|
||||||
|
|
||||||
|
| Parameter | Description | Default |
|
||||||
|
|-----------|-------------|---------|
|
||||||
|
| `auth.mode` | Authentication mode: `basic` or `oauth` | `basic` |
|
||||||
|
| `auth.basic.username` | Nextcloud username (basic auth) | `""` |
|
||||||
|
| `auth.basic.password` | Nextcloud password (basic auth) | `""` |
|
||||||
|
| `auth.basic.existingSecret` | Use existing secret for credentials | `""` |
|
||||||
|
| `auth.oauth.clientId` | OAuth client ID (OAuth mode, optional) | `""` |
|
||||||
|
| `auth.oauth.clientSecret` | OAuth client secret (OAuth mode, optional) | `""` |
|
||||||
|
| `auth.oauth.persistence.enabled` | Enable persistent storage for OAuth | `true` |
|
||||||
|
| `auth.oauth.persistence.size` | Size of OAuth storage PVC | `100Mi` |
|
||||||
|
|
||||||
|
#### Image Configuration
|
||||||
|
|
||||||
|
| Parameter | Description | Default |
|
||||||
|
|-----------|-------------|---------|
|
||||||
|
| `image.repository` | Container image repository | `ghcr.io/cbcoutinho/nextcloud-mcp-server` |
|
||||||
|
| `image.tag` | Container image tag | `""` (uses chart appVersion) |
|
||||||
|
| `image.pullPolicy` | Image pull policy | `IfNotPresent` |
|
||||||
|
|
||||||
|
#### Resources
|
||||||
|
|
||||||
|
| Parameter | Description | Default |
|
||||||
|
|-----------|-------------|---------|
|
||||||
|
| `resources.limits.cpu` | CPU limit | `1000m` |
|
||||||
|
| `resources.limits.memory` | Memory limit | `512Mi` |
|
||||||
|
| `resources.requests.cpu` | CPU request | `100m` |
|
||||||
|
| `resources.requests.memory` | Memory request | `128Mi` |
|
||||||
|
|
||||||
|
#### Service
|
||||||
|
|
||||||
|
| Parameter | Description | Default |
|
||||||
|
|-----------|-------------|---------|
|
||||||
|
| `service.type` | Service type | `ClusterIP` |
|
||||||
|
| `service.port` | Service port | `8000` |
|
||||||
|
| `service.oauthPort` | OAuth service port | `8001` |
|
||||||
|
|
||||||
|
#### Ingress
|
||||||
|
|
||||||
|
| Parameter | Description | Default |
|
||||||
|
|-----------|-------------|---------|
|
||||||
|
| `ingress.enabled` | Enable ingress | `false` |
|
||||||
|
| `ingress.className` | Ingress class name | `""` |
|
||||||
|
| `ingress.hosts` | Ingress host configuration | See values.yaml |
|
||||||
|
| `ingress.tls` | Ingress TLS configuration | `[]` |
|
||||||
|
|
||||||
|
#### Autoscaling
|
||||||
|
|
||||||
|
| Parameter | Description | Default |
|
||||||
|
|-----------|-------------|---------|
|
||||||
|
| `autoscaling.enabled` | Enable HPA | `false` |
|
||||||
|
| `autoscaling.minReplicas` | Minimum replicas | `1` |
|
||||||
|
| `autoscaling.maxReplicas` | Maximum replicas | `10` |
|
||||||
|
| `autoscaling.targetCPUUtilizationPercentage` | Target CPU % | `80` |
|
||||||
|
|
||||||
|
#### Health Probes
|
||||||
|
|
||||||
|
| Parameter | Description | Default |
|
||||||
|
|-----------|-------------|---------|
|
||||||
|
| `livenessProbe.httpGet.path` | Liveness probe endpoint | `/health/live` |
|
||||||
|
| `livenessProbe.initialDelaySeconds` | Initial delay for liveness | `30` |
|
||||||
|
| `livenessProbe.periodSeconds` | Check interval for liveness | `10` |
|
||||||
|
| `readinessProbe.httpGet.path` | Readiness probe endpoint | `/health/ready` |
|
||||||
|
| `readinessProbe.initialDelaySeconds` | Initial delay for readiness | `10` |
|
||||||
|
| `readinessProbe.periodSeconds` | Check interval for readiness | `5` |
|
||||||
|
|
||||||
|
The application exposes HTTP health check endpoints:
|
||||||
|
- `/health/live` - Liveness probe (checks if application is running)
|
||||||
|
- `/health/ready` - Readiness probe (checks if application is ready to serve traffic)
|
||||||
|
|
||||||
|
#### Document Processing (Optional)
|
||||||
|
|
||||||
|
| Parameter | Description | Default |
|
||||||
|
|-----------|-------------|---------|
|
||||||
|
| `documentProcessing.enabled` | Enable document processing | `false` |
|
||||||
|
| `documentProcessing.defaultProcessor` | Default processor | `unstructured` |
|
||||||
|
| `documentProcessing.unstructured.enabled` | Enable Unstructured.io processor | `false` |
|
||||||
|
| `documentProcessing.unstructured.apiUrl` | Unstructured API URL | `http://unstructured:8000` |
|
||||||
|
| `documentProcessing.tesseract.enabled` | Enable Tesseract OCR | `false` |
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Example 1: Basic Auth with Ingress
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
nextcloud:
|
||||||
|
host: https://cloud.example.com
|
||||||
|
|
||||||
|
auth:
|
||||||
|
mode: basic
|
||||||
|
basic:
|
||||||
|
username: admin
|
||||||
|
password: secure-password
|
||||||
|
|
||||||
|
ingress:
|
||||||
|
enabled: true
|
||||||
|
className: nginx
|
||||||
|
annotations:
|
||||||
|
cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||||
|
hosts:
|
||||||
|
- host: mcp.example.com
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
tls:
|
||||||
|
- secretName: mcp-tls
|
||||||
|
hosts:
|
||||||
|
- mcp.example.com
|
||||||
|
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpu: 2000m
|
||||||
|
memory: 1Gi
|
||||||
|
requests:
|
||||||
|
cpu: 200m
|
||||||
|
memory: 256Mi
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 2: Using Existing Secrets
|
||||||
|
|
||||||
|
#### Basic Auth with Existing Secret
|
||||||
|
|
||||||
|
Create a secret manually:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl create secret generic nextcloud-credentials \
|
||||||
|
--from-literal=username=myuser \
|
||||||
|
--from-literal=password=mypassword
|
||||||
|
```
|
||||||
|
|
||||||
|
Then reference it in your values:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
nextcloud:
|
||||||
|
host: https://cloud.example.com
|
||||||
|
|
||||||
|
auth:
|
||||||
|
mode: basic
|
||||||
|
basic:
|
||||||
|
existingSecret: nextcloud-credentials
|
||||||
|
usernameKey: username
|
||||||
|
passwordKey: password
|
||||||
|
```
|
||||||
|
|
||||||
|
#### OAuth with Existing Secret (Pre-registered Client)
|
||||||
|
|
||||||
|
If you have a pre-registered OAuth client:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl create secret generic nextcloud-oauth-creds \
|
||||||
|
--from-literal=clientId=my-oauth-client-id \
|
||||||
|
--from-literal=clientSecret=my-oauth-client-secret
|
||||||
|
```
|
||||||
|
|
||||||
|
Then reference it in your values:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
nextcloud:
|
||||||
|
host: https://cloud.example.com
|
||||||
|
# mcpServerUrl and publicIssuerUrl are optional!
|
||||||
|
# If not set, mcpServerUrl defaults to ingress host or localhost
|
||||||
|
# publicIssuerUrl defaults to nextcloud.host
|
||||||
|
|
||||||
|
auth:
|
||||||
|
mode: oauth
|
||||||
|
oauth:
|
||||||
|
existingSecret: nextcloud-oauth-creds
|
||||||
|
clientIdKey: clientId
|
||||||
|
clientSecretKey: clientSecret
|
||||||
|
persistence:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
ingress:
|
||||||
|
enabled: true
|
||||||
|
hosts:
|
||||||
|
- host: mcp.example.com
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
tls:
|
||||||
|
- secretName: mcp-tls
|
||||||
|
hosts:
|
||||||
|
- mcp.example.com
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 3: OAuth with Document Processing and Dynamic Client Registration
|
||||||
|
|
||||||
|
This example shows OAuth without pre-registered credentials (using DCR) and optional URL values:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
nextcloud:
|
||||||
|
host: https://cloud.example.com
|
||||||
|
# mcpServerUrl will automatically use ingress host (https://mcp.example.com)
|
||||||
|
# publicIssuerUrl will automatically default to nextcloud.host
|
||||||
|
|
||||||
|
auth:
|
||||||
|
mode: oauth
|
||||||
|
oauth:
|
||||||
|
# No clientId/clientSecret - will use Dynamic Client Registration!
|
||||||
|
persistence:
|
||||||
|
enabled: true
|
||||||
|
storageClass: fast-ssd
|
||||||
|
size: 200Mi
|
||||||
|
|
||||||
|
documentProcessing:
|
||||||
|
enabled: true
|
||||||
|
defaultProcessor: unstructured
|
||||||
|
unstructured:
|
||||||
|
enabled: true
|
||||||
|
apiUrl: http://unstructured-api:8000
|
||||||
|
strategy: hi_res
|
||||||
|
languages: eng,deu,fra
|
||||||
|
|
||||||
|
ingress:
|
||||||
|
enabled: true
|
||||||
|
className: nginx
|
||||||
|
hosts:
|
||||||
|
- host: mcp.example.com
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 4: High Availability with Autoscaling
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
replicaCount: 2
|
||||||
|
|
||||||
|
autoscaling:
|
||||||
|
enabled: true
|
||||||
|
minReplicas: 2
|
||||||
|
maxReplicas: 20
|
||||||
|
targetCPUUtilizationPercentage: 70
|
||||||
|
targetMemoryUtilizationPercentage: 80
|
||||||
|
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpu: 2000m
|
||||||
|
memory: 1Gi
|
||||||
|
requests:
|
||||||
|
cpu: 500m
|
||||||
|
memory: 512Mi
|
||||||
|
|
||||||
|
affinity:
|
||||||
|
podAntiAffinity:
|
||||||
|
preferredDuringSchedulingIgnoredDuringExecution:
|
||||||
|
- weight: 100
|
||||||
|
podAffinityTerm:
|
||||||
|
labelSelector:
|
||||||
|
matchExpressions:
|
||||||
|
- key: app.kubernetes.io/name
|
||||||
|
operator: In
|
||||||
|
values:
|
||||||
|
- nextcloud-mcp-server
|
||||||
|
topologyKey: kubernetes.io/hostname
|
||||||
|
```
|
||||||
|
|
||||||
|
## Upgrading
|
||||||
|
|
||||||
|
### To upgrade an existing deployment:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
helm upgrade nextcloud-mcp ./helm/nextcloud-mcp-server -f custom-values.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
### To upgrade with new values:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
helm upgrade nextcloud-mcp ./helm/nextcloud-mcp-server \
|
||||||
|
--set image.tag=0.21.0 \
|
||||||
|
--set resources.limits.memory=1Gi
|
||||||
|
```
|
||||||
|
|
||||||
|
## Uninstalling
|
||||||
|
|
||||||
|
```bash
|
||||||
|
helm uninstall nextcloud-mcp
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note:** This will delete all resources including PVCs. If you want to preserve OAuth client data, backup the PVC before uninstalling.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Check pod status
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl get pods -l app.kubernetes.io/name=nextcloud-mcp-server
|
||||||
|
```
|
||||||
|
|
||||||
|
### View logs
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl logs -l app.kubernetes.io/name=nextcloud-mcp-server --tail=100 -f
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check health endpoints
|
||||||
|
|
||||||
|
The application exposes health check endpoints for monitoring:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Port forward to the service
|
||||||
|
kubectl port-forward svc/nextcloud-mcp 8000:8000
|
||||||
|
|
||||||
|
# Check liveness (if app is running)
|
||||||
|
curl http://localhost:8000/health/live
|
||||||
|
|
||||||
|
# Check readiness (if app is ready to serve traffic)
|
||||||
|
curl http://localhost:8000/health/ready
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example responses:**
|
||||||
|
|
||||||
|
Liveness (always returns 200 if running):
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "alive",
|
||||||
|
"mode": "basic"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Readiness (returns 200 if ready, 503 if not ready):
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "ready",
|
||||||
|
"checks": {
|
||||||
|
"nextcloud_configured": "ok",
|
||||||
|
"auth_mode": "basic",
|
||||||
|
"auth_configured": "ok"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
1. **Connection refused to Nextcloud**
|
||||||
|
- Verify `nextcloud.host` is accessible from the Kubernetes cluster
|
||||||
|
- Check network policies and firewall rules
|
||||||
|
|
||||||
|
2. **Authentication failures**
|
||||||
|
- For basic auth: verify username/password are correct
|
||||||
|
- For OAuth: check that OIDC app is properly configured
|
||||||
|
|
||||||
|
3. **OAuth persistence issues**
|
||||||
|
- Verify PVC is bound: `kubectl get pvc`
|
||||||
|
- Check storage class exists: `kubectl get storageclass`
|
||||||
|
|
||||||
|
4. **Resource constraints**
|
||||||
|
- Increase memory limits if seeing OOM errors
|
||||||
|
- Adjust CPU requests based on load
|
||||||
|
|
||||||
|
## Security Considerations
|
||||||
|
|
||||||
|
1. **Secrets Management**: Consider using external secret management (e.g., Sealed Secrets, External Secrets Operator)
|
||||||
|
2. **TLS**: Always use TLS/HTTPS for production deployments
|
||||||
|
3. **Network Policies**: Restrict network access to necessary services only
|
||||||
|
4. **RBAC**: Review and customize ServiceAccount permissions as needed
|
||||||
|
5. **App Passwords**: For basic auth, use Nextcloud app passwords instead of main account passwords
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
- GitHub Issues: https://github.com/cbcoutinho/nextcloud-mcp-server/issues
|
||||||
|
- Documentation: https://github.com/cbcoutinho/nextcloud-mcp-server#readme
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This chart is licensed under AGPL-3.0, consistent with the Nextcloud MCP Server project.
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
Thank you for installing {{ .Chart.Name }}!
|
||||||
|
|
||||||
|
Your Nextcloud MCP Server has been deployed in {{ .Values.auth.mode }} authentication mode.
|
||||||
|
|
||||||
|
1. Get the application URL by running these commands:
|
||||||
|
{{- if .Values.ingress.enabled }}
|
||||||
|
{{- range $host := .Values.ingress.hosts }}
|
||||||
|
{{- range .paths }}
|
||||||
|
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- else if contains "NodePort" .Values.service.type }}
|
||||||
|
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "nextcloud-mcp-server.fullname" . }})
|
||||||
|
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
|
||||||
|
echo http://$NODE_IP:$NODE_PORT
|
||||||
|
{{- else if contains "LoadBalancer" .Values.service.type }}
|
||||||
|
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
|
||||||
|
You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "nextcloud-mcp-server.fullname" . }}'
|
||||||
|
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "nextcloud-mcp-server.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
|
||||||
|
echo http://$SERVICE_IP:{{ .Values.service.port }}
|
||||||
|
{{- else if contains "ClusterIP" .Values.service.type }}
|
||||||
|
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "nextcloud-mcp-server.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
|
||||||
|
export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
|
||||||
|
echo "Visit http://127.0.0.1:8080 to use your MCP server"
|
||||||
|
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
2. Check the deployment status:
|
||||||
|
kubectl --namespace {{ .Release.Namespace }} get pods -l "app.kubernetes.io/name={{ include "nextcloud-mcp-server.name" . }},app.kubernetes.io/instance={{ .Release.Name }}"
|
||||||
|
|
||||||
|
{{- if eq .Values.auth.mode "basic" }}
|
||||||
|
|
||||||
|
3. Basic Authentication Mode:
|
||||||
|
{{- if .Values.auth.basic.existingSecret }}
|
||||||
|
- Credentials: (using existing secret {{ .Values.auth.basic.existingSecret }})
|
||||||
|
{{- else }}
|
||||||
|
- Username: {{ .Values.auth.basic.username }}
|
||||||
|
- Password: (stored in secret {{ include "nextcloud-mcp-server.basicAuthSecretName" . }})
|
||||||
|
{{- end }}
|
||||||
|
- Connected to: {{ .Values.nextcloud.host }}
|
||||||
|
{{- else if eq .Values.auth.mode "oauth" }}
|
||||||
|
|
||||||
|
3. OAuth Authentication Mode:
|
||||||
|
- Server URL: {{ include "nextcloud-mcp-server.mcpServerUrl" . }}
|
||||||
|
- Issuer URL: {{ include "nextcloud-mcp-server.publicIssuerUrl" . }}
|
||||||
|
- Connected to: {{ .Values.nextcloud.host }}
|
||||||
|
{{- if .Values.auth.oauth.existingSecret }}
|
||||||
|
- Using existing OAuth client secret: {{ .Values.auth.oauth.existingSecret }}
|
||||||
|
{{- else if and .Values.auth.oauth.clientId .Values.auth.oauth.clientSecret }}
|
||||||
|
- Using pre-registered OAuth client
|
||||||
|
{{- else }}
|
||||||
|
- Using Dynamic Client Registration (DCR)
|
||||||
|
{{- end }}
|
||||||
|
{{- if .Values.auth.oauth.persistence.enabled }}
|
||||||
|
- OAuth client credentials are persisted in PVC: {{ include "nextcloud-mcp-server.oauthPvcName" . }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
IMPORTANT: OAuth mode is experimental and requires patches to the user_oidc app.
|
||||||
|
See: https://github.com/cbcoutinho/nextcloud-mcp-server#authentication
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{- if .Values.documentProcessing.enabled }}
|
||||||
|
|
||||||
|
4. Document Processing:
|
||||||
|
- Enabled: {{ .Values.documentProcessing.enabled }}
|
||||||
|
- Default processor: {{ .Values.documentProcessing.defaultProcessor }}
|
||||||
|
{{- if .Values.documentProcessing.unstructured.enabled }}
|
||||||
|
- Unstructured API: {{ .Values.documentProcessing.unstructured.apiUrl }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
For more information and documentation:
|
||||||
|
- GitHub: https://github.com/cbcoutinho/nextcloud-mcp-server
|
||||||
|
- Documentation: https://github.com/cbcoutinho/nextcloud-mcp-server#readme
|
||||||
|
|
||||||
|
To upgrade this deployment:
|
||||||
|
helm upgrade {{ .Release.Name }} nextcloud-mcp-server
|
||||||
|
|
||||||
|
To uninstall:
|
||||||
|
helm uninstall {{ .Release.Name }}
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
{{/*
|
||||||
|
Expand the name of the chart.
|
||||||
|
*/}}
|
||||||
|
{{- define "nextcloud-mcp-server.name" -}}
|
||||||
|
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Create a default fully qualified app name.
|
||||||
|
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
|
||||||
|
If release name contains chart name it will be used as a full name.
|
||||||
|
*/}}
|
||||||
|
{{- define "nextcloud-mcp-server.fullname" -}}
|
||||||
|
{{- if .Values.fullnameOverride }}
|
||||||
|
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
|
||||||
|
{{- else }}
|
||||||
|
{{- $name := default .Chart.Name .Values.nameOverride }}
|
||||||
|
{{- if contains $name .Release.Name }}
|
||||||
|
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
|
||||||
|
{{- else }}
|
||||||
|
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Create chart name and version as used by the chart label.
|
||||||
|
*/}}
|
||||||
|
{{- define "nextcloud-mcp-server.chart" -}}
|
||||||
|
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Common labels
|
||||||
|
*/}}
|
||||||
|
{{- define "nextcloud-mcp-server.labels" -}}
|
||||||
|
helm.sh/chart: {{ include "nextcloud-mcp-server.chart" . }}
|
||||||
|
{{ include "nextcloud-mcp-server.selectorLabels" . }}
|
||||||
|
{{- if .Chart.AppVersion }}
|
||||||
|
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
||||||
|
{{- end }}
|
||||||
|
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Selector labels
|
||||||
|
*/}}
|
||||||
|
{{- define "nextcloud-mcp-server.selectorLabels" -}}
|
||||||
|
app.kubernetes.io/name: {{ include "nextcloud-mcp-server.name" . }}
|
||||||
|
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Create the name of the service account to use
|
||||||
|
*/}}
|
||||||
|
{{- define "nextcloud-mcp-server.serviceAccountName" -}}
|
||||||
|
{{- if .Values.serviceAccount.create }}
|
||||||
|
{{- default (include "nextcloud-mcp-server.fullname" .) .Values.serviceAccount.name }}
|
||||||
|
{{- else }}
|
||||||
|
{{- default "default" .Values.serviceAccount.name }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Create the name of the secret to use for basic auth
|
||||||
|
*/}}
|
||||||
|
{{- define "nextcloud-mcp-server.basicAuthSecretName" -}}
|
||||||
|
{{- if .Values.auth.basic.existingSecret }}
|
||||||
|
{{- .Values.auth.basic.existingSecret }}
|
||||||
|
{{- else }}
|
||||||
|
{{- include "nextcloud-mcp-server.fullname" . }}-basic-auth
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Create the name of the secret to use for OAuth
|
||||||
|
*/}}
|
||||||
|
{{- define "nextcloud-mcp-server.oauthSecretName" -}}
|
||||||
|
{{- if .Values.auth.oauth.existingSecret }}
|
||||||
|
{{- .Values.auth.oauth.existingSecret }}
|
||||||
|
{{- else }}
|
||||||
|
{{- include "nextcloud-mcp-server.fullname" . }}-oauth
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Create the name of the PVC to use for OAuth storage
|
||||||
|
*/}}
|
||||||
|
{{- define "nextcloud-mcp-server.oauthPvcName" -}}
|
||||||
|
{{- if .Values.auth.oauth.persistence.existingClaim }}
|
||||||
|
{{- .Values.auth.oauth.persistence.existingClaim }}
|
||||||
|
{{- else }}
|
||||||
|
{{- include "nextcloud-mcp-server.fullname" . }}-oauth-storage
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Return the appropriate MCP server port based on auth mode
|
||||||
|
*/}}
|
||||||
|
{{- define "nextcloud-mcp-server.port" -}}
|
||||||
|
{{- if eq .Values.auth.mode "oauth" }}
|
||||||
|
{{- .Values.auth.oauth.port }}
|
||||||
|
{{- else }}
|
||||||
|
{{- .Values.mcp.port }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Return the image tag
|
||||||
|
*/}}
|
||||||
|
{{- define "nextcloud-mcp-server.imageTag" -}}
|
||||||
|
{{- .Values.image.tag | default .Chart.AppVersion }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Return the public issuer URL for OAuth
|
||||||
|
Defaults to nextcloud.host if not specified
|
||||||
|
*/}}
|
||||||
|
{{- define "nextcloud-mcp-server.publicIssuerUrl" -}}
|
||||||
|
{{- if .Values.nextcloud.publicIssuerUrl }}
|
||||||
|
{{- .Values.nextcloud.publicIssuerUrl }}
|
||||||
|
{{- else }}
|
||||||
|
{{- .Values.nextcloud.host }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
Return the MCP server URL for OAuth callbacks
|
||||||
|
If not specified:
|
||||||
|
- Uses ingress host if ingress is enabled
|
||||||
|
- Otherwise defaults to http://localhost:8000 (for port-forward setups)
|
||||||
|
*/}}
|
||||||
|
{{- define "nextcloud-mcp-server.mcpServerUrl" -}}
|
||||||
|
{{- if .Values.nextcloud.mcpServerUrl }}
|
||||||
|
{{- .Values.nextcloud.mcpServerUrl }}
|
||||||
|
{{- else if .Values.ingress.enabled }}
|
||||||
|
{{- $host := index .Values.ingress.hosts 0 }}
|
||||||
|
{{- if .Values.ingress.tls }}
|
||||||
|
{{- printf "https://%s" $host.host }}
|
||||||
|
{{- else }}
|
||||||
|
{{- printf "http://%s" $host.host }}
|
||||||
|
{{- end }}
|
||||||
|
{{- else }}
|
||||||
|
{{- printf "http://localhost:%d" (int .Values.mcp.port) }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
@@ -0,0 +1,189 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: {{ include "nextcloud-mcp-server.fullname" . }}
|
||||||
|
labels:
|
||||||
|
{{- include "nextcloud-mcp-server.labels" . | nindent 4 }}
|
||||||
|
spec:
|
||||||
|
{{- if not .Values.autoscaling.enabled }}
|
||||||
|
replicas: {{ .Values.replicaCount }}
|
||||||
|
{{- end }}
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
{{- include "nextcloud-mcp-server.selectorLabels" . | nindent 6 }}
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
checksum/secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }}
|
||||||
|
{{- with .Values.podAnnotations }}
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
labels:
|
||||||
|
{{- include "nextcloud-mcp-server.labels" . | nindent 8 }}
|
||||||
|
{{- with .Values.podLabels }}
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
spec:
|
||||||
|
{{- with .Values.imagePullSecrets }}
|
||||||
|
imagePullSecrets:
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
serviceAccountName: {{ include "nextcloud-mcp-server.serviceAccountName" . }}
|
||||||
|
securityContext:
|
||||||
|
{{- toYaml .Values.podSecurityContext | nindent 8 }}
|
||||||
|
{{- with .Values.initContainers }}
|
||||||
|
initContainers:
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
containers:
|
||||||
|
- name: {{ .Chart.Name }}
|
||||||
|
securityContext:
|
||||||
|
{{- toYaml .Values.securityContext | nindent 12 }}
|
||||||
|
image: "{{ .Values.image.repository }}:{{ include "nextcloud-mcp-server.imageTag" . }}"
|
||||||
|
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||||
|
args:
|
||||||
|
- "--transport"
|
||||||
|
- "{{ .Values.mcp.transport }}"
|
||||||
|
{{- if eq .Values.auth.mode "oauth" }}
|
||||||
|
- "--oauth"
|
||||||
|
- "--port"
|
||||||
|
- "{{ .Values.auth.oauth.port }}"
|
||||||
|
- "--oauth-token-type"
|
||||||
|
- "{{ .Values.auth.oauth.tokenType }}"
|
||||||
|
{{- end }}
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
containerPort: {{ include "nextcloud-mcp-server.port" . }}
|
||||||
|
protocol: TCP
|
||||||
|
env:
|
||||||
|
# Nextcloud connection
|
||||||
|
- name: NEXTCLOUD_HOST
|
||||||
|
value: {{ .Values.nextcloud.host | quote }}
|
||||||
|
{{- if eq .Values.auth.mode "basic" }}
|
||||||
|
# Basic auth mode
|
||||||
|
- name: NEXTCLOUD_USERNAME
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "nextcloud-mcp-server.basicAuthSecretName" . }}
|
||||||
|
key: {{ .Values.auth.basic.usernameKey }}
|
||||||
|
- name: NEXTCLOUD_PASSWORD
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "nextcloud-mcp-server.basicAuthSecretName" . }}
|
||||||
|
key: {{ .Values.auth.basic.passwordKey }}
|
||||||
|
{{- else if eq .Values.auth.mode "oauth" }}
|
||||||
|
# OAuth mode
|
||||||
|
- name: NEXTCLOUD_MCP_SERVER_URL
|
||||||
|
value: {{ include "nextcloud-mcp-server.mcpServerUrl" . | quote }}
|
||||||
|
- name: NEXTCLOUD_PUBLIC_ISSUER_URL
|
||||||
|
value: {{ include "nextcloud-mcp-server.publicIssuerUrl" . | quote }}
|
||||||
|
- name: NEXTCLOUD_OIDC_CLIENT_STORAGE
|
||||||
|
value: "/app/.oauth/nextcloud_oauth_client.json"
|
||||||
|
- name: NEXTCLOUD_OIDC_SCOPES
|
||||||
|
value: {{ .Values.auth.oauth.scopes | quote }}
|
||||||
|
{{- if .Values.auth.oauth.clientId }}
|
||||||
|
- name: NEXTCLOUD_OIDC_CLIENT_ID
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "nextcloud-mcp-server.oauthSecretName" . }}
|
||||||
|
key: {{ .Values.auth.oauth.clientIdKey }}
|
||||||
|
- name: NEXTCLOUD_OIDC_CLIENT_SECRET
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "nextcloud-mcp-server.oauthSecretName" . }}
|
||||||
|
key: {{ .Values.auth.oauth.clientSecretKey }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .Values.documentProcessing.enabled }}
|
||||||
|
# Document processing
|
||||||
|
- name: ENABLE_DOCUMENT_PROCESSING
|
||||||
|
value: {{ .Values.documentProcessing.enabled | quote }}
|
||||||
|
- name: DOCUMENT_PROCESSOR
|
||||||
|
value: {{ .Values.documentProcessing.defaultProcessor | quote }}
|
||||||
|
- name: PROGRESS_INTERVAL
|
||||||
|
value: {{ .Values.documentProcessing.progressInterval | quote }}
|
||||||
|
{{- if .Values.documentProcessing.unstructured.enabled }}
|
||||||
|
- name: ENABLE_UNSTRUCTURED
|
||||||
|
value: "true"
|
||||||
|
- name: UNSTRUCTURED_API_URL
|
||||||
|
value: {{ .Values.documentProcessing.unstructured.apiUrl | quote }}
|
||||||
|
- name: UNSTRUCTURED_TIMEOUT
|
||||||
|
value: {{ .Values.documentProcessing.unstructured.timeout | quote }}
|
||||||
|
- name: UNSTRUCTURED_STRATEGY
|
||||||
|
value: {{ .Values.documentProcessing.unstructured.strategy | quote }}
|
||||||
|
- name: UNSTRUCTURED_LANGUAGES
|
||||||
|
value: {{ .Values.documentProcessing.unstructured.languages | quote }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .Values.documentProcessing.tesseract.enabled }}
|
||||||
|
- name: ENABLE_TESSERACT
|
||||||
|
value: "true"
|
||||||
|
{{- if .Values.documentProcessing.tesseract.cmd }}
|
||||||
|
- name: TESSERACT_CMD
|
||||||
|
value: {{ .Values.documentProcessing.tesseract.cmd | quote }}
|
||||||
|
{{- end }}
|
||||||
|
- name: TESSERACT_LANG
|
||||||
|
value: {{ .Values.documentProcessing.tesseract.lang | quote }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .Values.documentProcessing.custom.enabled }}
|
||||||
|
- name: ENABLE_CUSTOM_PROCESSOR
|
||||||
|
value: "true"
|
||||||
|
- name: CUSTOM_PROCESSOR_NAME
|
||||||
|
value: {{ .Values.documentProcessing.custom.name | quote }}
|
||||||
|
- name: CUSTOM_PROCESSOR_URL
|
||||||
|
value: {{ .Values.documentProcessing.custom.url | quote }}
|
||||||
|
{{- if .Values.documentProcessing.custom.apiKey }}
|
||||||
|
- name: CUSTOM_PROCESSOR_API_KEY
|
||||||
|
value: {{ .Values.documentProcessing.custom.apiKey | quote }}
|
||||||
|
{{- end }}
|
||||||
|
- name: CUSTOM_PROCESSOR_TIMEOUT
|
||||||
|
value: {{ .Values.documentProcessing.custom.timeout | quote }}
|
||||||
|
- name: CUSTOM_PROCESSOR_TYPES
|
||||||
|
value: {{ .Values.documentProcessing.custom.types | quote }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- with .Values.extraEnv }}
|
||||||
|
{{- toYaml . | nindent 12 }}
|
||||||
|
{{- end }}
|
||||||
|
{{- with .Values.extraEnvFrom }}
|
||||||
|
envFrom:
|
||||||
|
{{- toYaml . | nindent 12 }}
|
||||||
|
{{- end }}
|
||||||
|
livenessProbe:
|
||||||
|
{{- toYaml .Values.livenessProbe | nindent 12 }}
|
||||||
|
readinessProbe:
|
||||||
|
{{- toYaml .Values.readinessProbe | nindent 12 }}
|
||||||
|
resources:
|
||||||
|
{{- toYaml .Values.resources | nindent 12 }}
|
||||||
|
volumeMounts:
|
||||||
|
- name: tmp
|
||||||
|
mountPath: /tmp
|
||||||
|
{{- if and (eq .Values.auth.mode "oauth") .Values.auth.oauth.persistence.enabled }}
|
||||||
|
- name: oauth-storage
|
||||||
|
mountPath: /app/.oauth
|
||||||
|
{{- end }}
|
||||||
|
{{- with .Values.volumeMounts }}
|
||||||
|
{{- toYaml . | nindent 12 }}
|
||||||
|
{{- end }}
|
||||||
|
volumes:
|
||||||
|
- name: tmp
|
||||||
|
emptyDir: {}
|
||||||
|
{{- if and (eq .Values.auth.mode "oauth") .Values.auth.oauth.persistence.enabled }}
|
||||||
|
- name: oauth-storage
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: {{ include "nextcloud-mcp-server.oauthPvcName" . }}
|
||||||
|
{{- end }}
|
||||||
|
{{- with .Values.volumes }}
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
{{- with .Values.nodeSelector }}
|
||||||
|
nodeSelector:
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
{{- with .Values.affinity }}
|
||||||
|
affinity:
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
|
{{- with .Values.tolerations }}
|
||||||
|
tolerations:
|
||||||
|
{{- toYaml . | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
{{- if .Values.autoscaling.enabled }}
|
||||||
|
apiVersion: autoscaling/v2
|
||||||
|
kind: HorizontalPodAutoscaler
|
||||||
|
metadata:
|
||||||
|
name: {{ include "nextcloud-mcp-server.fullname" . }}
|
||||||
|
labels:
|
||||||
|
{{- include "nextcloud-mcp-server.labels" . | nindent 4 }}
|
||||||
|
spec:
|
||||||
|
scaleTargetRef:
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
name: {{ include "nextcloud-mcp-server.fullname" . }}
|
||||||
|
minReplicas: {{ .Values.autoscaling.minReplicas }}
|
||||||
|
maxReplicas: {{ .Values.autoscaling.maxReplicas }}
|
||||||
|
metrics:
|
||||||
|
{{- if .Values.autoscaling.targetCPUUtilizationPercentage }}
|
||||||
|
- type: Resource
|
||||||
|
resource:
|
||||||
|
name: cpu
|
||||||
|
target:
|
||||||
|
type: Utilization
|
||||||
|
averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
|
||||||
|
- type: Resource
|
||||||
|
resource:
|
||||||
|
name: memory
|
||||||
|
target:
|
||||||
|
type: Utilization
|
||||||
|
averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
{{- if .Values.ingress.enabled -}}
|
||||||
|
{{- $fullName := include "nextcloud-mcp-server.fullname" . -}}
|
||||||
|
{{- $svcPort := .Values.service.port -}}
|
||||||
|
{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }}
|
||||||
|
{{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }}
|
||||||
|
{{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
|
||||||
|
apiVersion: networking.k8s.io/v1beta1
|
||||||
|
{{- else -}}
|
||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
{{- end }}
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: {{ $fullName }}
|
||||||
|
labels:
|
||||||
|
{{- include "nextcloud-mcp-server.labels" . | nindent 4 }}
|
||||||
|
{{- with .Values.ingress.annotations }}
|
||||||
|
annotations:
|
||||||
|
{{- toYaml . | nindent 4 }}
|
||||||
|
{{- end }}
|
||||||
|
spec:
|
||||||
|
{{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }}
|
||||||
|
ingressClassName: {{ .Values.ingress.className }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .Values.ingress.tls }}
|
||||||
|
tls:
|
||||||
|
{{- range .Values.ingress.tls }}
|
||||||
|
- hosts:
|
||||||
|
{{- range .hosts }}
|
||||||
|
- {{ . | quote }}
|
||||||
|
{{- end }}
|
||||||
|
secretName: {{ .secretName }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
rules:
|
||||||
|
{{- range .Values.ingress.hosts }}
|
||||||
|
- host: {{ .host | quote }}
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
{{- range .paths }}
|
||||||
|
- path: {{ .path }}
|
||||||
|
{{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }}
|
||||||
|
pathType: {{ .pathType }}
|
||||||
|
{{- end }}
|
||||||
|
backend:
|
||||||
|
{{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }}
|
||||||
|
service:
|
||||||
|
name: {{ $fullName }}
|
||||||
|
port:
|
||||||
|
number: {{ $svcPort }}
|
||||||
|
{{- else }}
|
||||||
|
serviceName: {{ $fullName }}
|
||||||
|
servicePort: {{ $svcPort }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{{- if and (eq .Values.auth.mode "oauth") .Values.auth.oauth.persistence.enabled (not .Values.auth.oauth.persistence.existingClaim) }}
|
||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: {{ include "nextcloud-mcp-server.fullname" . }}-oauth-storage
|
||||||
|
labels:
|
||||||
|
{{- include "nextcloud-mcp-server.labels" . | nindent 4 }}
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- {{ .Values.auth.oauth.persistence.accessMode }}
|
||||||
|
{{- if .Values.auth.oauth.persistence.storageClass }}
|
||||||
|
storageClassName: {{ .Values.auth.oauth.persistence.storageClass }}
|
||||||
|
{{- end }}
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: {{ .Values.auth.oauth.persistence.size }}
|
||||||
|
{{- end }}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
{{- if eq .Values.auth.mode "basic" }}
|
||||||
|
{{- if not .Values.auth.basic.existingSecret }}
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: {{ include "nextcloud-mcp-server.fullname" . }}-basic-auth
|
||||||
|
labels:
|
||||||
|
{{- include "nextcloud-mcp-server.labels" . | nindent 4 }}
|
||||||
|
type: Opaque
|
||||||
|
data:
|
||||||
|
{{ .Values.auth.basic.usernameKey }}: {{ .Values.auth.basic.username | b64enc | quote }}
|
||||||
|
{{ .Values.auth.basic.passwordKey }}: {{ .Values.auth.basic.password | b64enc | quote }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
---
|
||||||
|
{{- if eq .Values.auth.mode "oauth" }}
|
||||||
|
{{- if and .Values.auth.oauth.clientId (not .Values.auth.oauth.existingSecret) }}
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: {{ include "nextcloud-mcp-server.fullname" . }}-oauth
|
||||||
|
labels:
|
||||||
|
{{- include "nextcloud-mcp-server.labels" . | nindent 4 }}
|
||||||
|
type: Opaque
|
||||||
|
data:
|
||||||
|
{{ .Values.auth.oauth.clientIdKey }}: {{ .Values.auth.oauth.clientId | b64enc | quote }}
|
||||||
|
{{ .Values.auth.oauth.clientSecretKey }}: {{ .Values.auth.oauth.clientSecret | b64enc | quote }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: {{ include "nextcloud-mcp-server.fullname" . }}
|
||||||
|
labels:
|
||||||
|
{{- include "nextcloud-mcp-server.labels" . | nindent 4 }}
|
||||||
|
{{- with .Values.service.annotations }}
|
||||||
|
annotations:
|
||||||
|
{{- toYaml . | nindent 4 }}
|
||||||
|
{{- end }}
|
||||||
|
spec:
|
||||||
|
type: {{ .Values.service.type }}
|
||||||
|
ports:
|
||||||
|
- port: {{ .Values.service.port }}
|
||||||
|
targetPort: http
|
||||||
|
protocol: TCP
|
||||||
|
name: http
|
||||||
|
selector:
|
||||||
|
{{- include "nextcloud-mcp-server.selectorLabels" . | nindent 4 }}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{{- if .Values.serviceAccount.create -}}
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: {{ include "nextcloud-mcp-server.serviceAccountName" . }}
|
||||||
|
labels:
|
||||||
|
{{- include "nextcloud-mcp-server.labels" . | nindent 4 }}
|
||||||
|
{{- with .Values.serviceAccount.annotations }}
|
||||||
|
annotations:
|
||||||
|
{{- toYaml . | nindent 4 }}
|
||||||
|
{{- end }}
|
||||||
|
automountServiceAccountToken: {{ .Values.serviceAccount.automount }}
|
||||||
|
{{- end }}
|
||||||
@@ -0,0 +1,268 @@
|
|||||||
|
# Default values for nextcloud-mcp-server
|
||||||
|
# This is a YAML-formatted file.
|
||||||
|
# Declare variables to be passed into your templates.
|
||||||
|
|
||||||
|
# Number of replicas
|
||||||
|
replicaCount: 1
|
||||||
|
|
||||||
|
image:
|
||||||
|
repository: ghcr.io/cbcoutinho/nextcloud-mcp-server
|
||||||
|
pullPolicy: IfNotPresent
|
||||||
|
# Overrides the image tag whose default is the chart appVersion.
|
||||||
|
tag: ""
|
||||||
|
|
||||||
|
imagePullSecrets: []
|
||||||
|
nameOverride: ""
|
||||||
|
fullnameOverride: ""
|
||||||
|
|
||||||
|
# Nextcloud connection settings
|
||||||
|
nextcloud:
|
||||||
|
# URL of your Nextcloud instance (required)
|
||||||
|
# Example: https://cloud.example.com
|
||||||
|
host: ""
|
||||||
|
|
||||||
|
# MCP server URL for OAuth callbacks (OAuth mode only)
|
||||||
|
# If not specified, will be constructed from ingress.hosts[0] if ingress is enabled,
|
||||||
|
# or defaults to http://localhost:8000 (suitable for port-forward setups)
|
||||||
|
# Example: https://mcp.example.com
|
||||||
|
mcpServerUrl: ""
|
||||||
|
|
||||||
|
# Public issuer URL for OAuth (OAuth mode only)
|
||||||
|
# If not specified, defaults to nextcloud.host
|
||||||
|
# Only set this if your Nextcloud is accessible at a different URL for OAuth
|
||||||
|
# Example: https://cloud.example.com
|
||||||
|
publicIssuerUrl: ""
|
||||||
|
|
||||||
|
# Authentication configuration
|
||||||
|
# Choose either basic auth OR oauth (not both)
|
||||||
|
auth:
|
||||||
|
# Authentication mode: "basic" or "oauth"
|
||||||
|
# basic: Uses username/password (recommended for most users)
|
||||||
|
# oauth: Uses OAuth2/OIDC (experimental, requires patches)
|
||||||
|
mode: basic
|
||||||
|
|
||||||
|
# Basic authentication settings
|
||||||
|
basic:
|
||||||
|
# Nextcloud username (ignored if existingSecret is set)
|
||||||
|
username: ""
|
||||||
|
# Nextcloud password or app password (recommended) (ignored if existingSecret is set)
|
||||||
|
password: ""
|
||||||
|
# Use existing secret instead of creating one
|
||||||
|
# If set, username and password above are ignored
|
||||||
|
# Secret must contain keys specified in usernameKey and passwordKey
|
||||||
|
# Example:
|
||||||
|
# kubectl create secret generic my-nextcloud-creds \
|
||||||
|
# --from-literal=username=myuser \
|
||||||
|
# --from-literal=password=mypassword
|
||||||
|
existingSecret: ""
|
||||||
|
# Keys in the existing secret
|
||||||
|
usernameKey: "username"
|
||||||
|
passwordKey: "password"
|
||||||
|
|
||||||
|
# OAuth2/OIDC settings (experimental)
|
||||||
|
oauth:
|
||||||
|
# Port for OAuth MCP server (default: 8001)
|
||||||
|
port: 8001
|
||||||
|
# OAuth token type: "jwt" or "opaque"
|
||||||
|
tokenType: "jwt"
|
||||||
|
# Pre-registered OAuth client ID (optional, ignored if existingSecret is set)
|
||||||
|
# If not provided and no existingSecret, will use Dynamic Client Registration (DCR)
|
||||||
|
clientId: ""
|
||||||
|
# Pre-registered OAuth client secret (optional, ignored if existingSecret is set)
|
||||||
|
clientSecret: ""
|
||||||
|
# OAuth scopes to request (space-separated)
|
||||||
|
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"
|
||||||
|
# Use existing secret for OAuth client credentials
|
||||||
|
# If set, clientId and clientSecret above are ignored
|
||||||
|
# Secret must contain keys specified in clientIdKey and clientSecretKey
|
||||||
|
# Example:
|
||||||
|
# kubectl create secret generic my-oauth-creds \
|
||||||
|
# --from-literal=clientId=my-client-id \
|
||||||
|
# --from-literal=clientSecret=my-client-secret
|
||||||
|
existingSecret: ""
|
||||||
|
# Keys in the existing secret
|
||||||
|
clientIdKey: "clientId"
|
||||||
|
clientSecretKey: "clientSecret"
|
||||||
|
# Persistent storage for OAuth client credentials
|
||||||
|
persistence:
|
||||||
|
enabled: true
|
||||||
|
# Storage class (leave empty for default)
|
||||||
|
storageClass: ""
|
||||||
|
accessMode: ReadWriteOnce
|
||||||
|
size: 100Mi
|
||||||
|
# Use existing PVC
|
||||||
|
existingClaim: ""
|
||||||
|
|
||||||
|
# MCP server configuration
|
||||||
|
mcp:
|
||||||
|
# Transport mode (default: streamable-http for SSE)
|
||||||
|
transport: "streamable-http"
|
||||||
|
# Port for basic auth mode
|
||||||
|
port: 8000
|
||||||
|
|
||||||
|
# Document processing configuration (optional)
|
||||||
|
documentProcessing:
|
||||||
|
# Enable document processing (PDF, DOCX, images, etc.)
|
||||||
|
enabled: false
|
||||||
|
# Default processor: unstructured, tesseract, or custom
|
||||||
|
defaultProcessor: "unstructured"
|
||||||
|
# Progress reporting interval in seconds
|
||||||
|
progressInterval: 10
|
||||||
|
|
||||||
|
# Unstructured.io processor
|
||||||
|
unstructured:
|
||||||
|
enabled: false
|
||||||
|
# Unstructured API endpoint
|
||||||
|
apiUrl: "http://unstructured:8000"
|
||||||
|
# Request timeout in seconds
|
||||||
|
timeout: 120
|
||||||
|
# Parsing strategy: auto, fast, or hi_res
|
||||||
|
strategy: "auto"
|
||||||
|
# OCR languages (comma-separated ISO 639-3 codes)
|
||||||
|
languages: "eng,deu"
|
||||||
|
|
||||||
|
# Tesseract processor (local OCR)
|
||||||
|
tesseract:
|
||||||
|
enabled: false
|
||||||
|
# Path to tesseract executable (optional, auto-detected if in PATH)
|
||||||
|
cmd: ""
|
||||||
|
# OCR language (e.g., eng, deu, eng+deu for multiple)
|
||||||
|
lang: "eng"
|
||||||
|
|
||||||
|
# Custom processor
|
||||||
|
custom:
|
||||||
|
enabled: false
|
||||||
|
# Unique name for your processor
|
||||||
|
name: "my_ocr"
|
||||||
|
# Custom processor API endpoint
|
||||||
|
url: ""
|
||||||
|
# Optional API key for authentication
|
||||||
|
apiKey: ""
|
||||||
|
# Request timeout in seconds
|
||||||
|
timeout: 60
|
||||||
|
# Comma-separated MIME types your processor supports
|
||||||
|
types: "application/pdf,image/jpeg,image/png"
|
||||||
|
|
||||||
|
serviceAccount:
|
||||||
|
# Specifies whether a service account should be created
|
||||||
|
create: true
|
||||||
|
# Automatically mount a ServiceAccount's API credentials?
|
||||||
|
automount: true
|
||||||
|
# Annotations to add to the service account
|
||||||
|
annotations: {}
|
||||||
|
# The name of the service account to use.
|
||||||
|
# If not set and create is true, a name is generated using the fullname template
|
||||||
|
name: ""
|
||||||
|
|
||||||
|
podAnnotations: {}
|
||||||
|
podLabels: {}
|
||||||
|
|
||||||
|
podSecurityContext:
|
||||||
|
fsGroup: 2000
|
||||||
|
|
||||||
|
securityContext:
|
||||||
|
capabilities:
|
||||||
|
drop:
|
||||||
|
- ALL
|
||||||
|
readOnlyRootFilesystem: true
|
||||||
|
runAsNonRoot: true
|
||||||
|
runAsUser: 1000
|
||||||
|
|
||||||
|
service:
|
||||||
|
type: ClusterIP
|
||||||
|
port: 8000
|
||||||
|
# For OAuth mode, you may want to expose both ports
|
||||||
|
oauthPort: 8001
|
||||||
|
annotations: {}
|
||||||
|
|
||||||
|
ingress:
|
||||||
|
enabled: false
|
||||||
|
className: ""
|
||||||
|
annotations: {}
|
||||||
|
# kubernetes.io/ingress.class: nginx
|
||||||
|
# kubernetes.io/tls-acme: "true"
|
||||||
|
# cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||||
|
hosts:
|
||||||
|
- host: mcp.example.com
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
tls: []
|
||||||
|
# - secretName: nextcloud-mcp-tls
|
||||||
|
# hosts:
|
||||||
|
# - mcp.example.com
|
||||||
|
|
||||||
|
resources:
|
||||||
|
# We recommend setting resource requests and limits
|
||||||
|
limits:
|
||||||
|
cpu: 1000m
|
||||||
|
memory: 512Mi
|
||||||
|
requests:
|
||||||
|
cpu: 100m
|
||||||
|
memory: 128Mi
|
||||||
|
|
||||||
|
# Liveness probe configuration
|
||||||
|
# Checks if the application process is running
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /health/live
|
||||||
|
port: http
|
||||||
|
scheme: HTTP
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
periodSeconds: 10
|
||||||
|
timeoutSeconds: 5
|
||||||
|
failureThreshold: 3
|
||||||
|
|
||||||
|
# Readiness probe configuration
|
||||||
|
# Checks if the application is ready to serve traffic
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /health/ready
|
||||||
|
port: http
|
||||||
|
scheme: HTTP
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 5
|
||||||
|
timeoutSeconds: 3
|
||||||
|
failureThreshold: 3
|
||||||
|
|
||||||
|
# Autoscaling configuration
|
||||||
|
autoscaling:
|
||||||
|
enabled: false
|
||||||
|
minReplicas: 1
|
||||||
|
maxReplicas: 10
|
||||||
|
targetCPUUtilizationPercentage: 80
|
||||||
|
# targetMemoryUtilizationPercentage: 80
|
||||||
|
|
||||||
|
# Additional volumes on the output Deployment definition.
|
||||||
|
volumes: []
|
||||||
|
# - name: foo
|
||||||
|
# secret:
|
||||||
|
# secretName: mysecret
|
||||||
|
# optional: false
|
||||||
|
|
||||||
|
# Additional volumeMounts on the output Deployment definition.
|
||||||
|
volumeMounts: []
|
||||||
|
# - name: foo
|
||||||
|
# mountPath: "/etc/foo"
|
||||||
|
# readOnly: true
|
||||||
|
|
||||||
|
nodeSelector: {}
|
||||||
|
|
||||||
|
tolerations: []
|
||||||
|
|
||||||
|
affinity: {}
|
||||||
|
|
||||||
|
# Init containers
|
||||||
|
initContainers: []
|
||||||
|
|
||||||
|
# Additional environment variables
|
||||||
|
extraEnv: []
|
||||||
|
# - name: CUSTOM_VAR
|
||||||
|
# value: "custom_value"
|
||||||
|
|
||||||
|
# Additional environment variables from ConfigMaps or Secrets
|
||||||
|
extraEnvFrom: []
|
||||||
|
# - configMapRef:
|
||||||
|
# name: my-configmap
|
||||||
|
# - secretRef:
|
||||||
|
# name: my-secret
|
||||||
+1
-1
@@ -52,7 +52,7 @@ services:
|
|||||||
- ./tests/fixtures/nginx.conf:/etc/nginx/nginx.conf:ro
|
- ./tests/fixtures/nginx.conf:/etc/nginx/nginx.conf:ro
|
||||||
|
|
||||||
unstructured:
|
unstructured:
|
||||||
image: downloads.unstructured.io/unstructured-io/unstructured-api:latest
|
image: downloads.unstructured.io/unstructured-io/unstructured-api:latest@sha256:a43ab55898599157fb0e0e097dabb8ecdd1d8e3df1ae5b67c6e15a136b171a6c
|
||||||
restart: always
|
restart: always
|
||||||
ports:
|
ports:
|
||||||
- 127.0.0.1:8002:8000
|
- 127.0.0.1:8002:8000
|
||||||
|
|||||||
@@ -648,8 +648,71 @@ def get_app(transport: str = "sse", enabled_apps: list[str] | None = None):
|
|||||||
await stack.enter_async_context(mcp.session_manager.run())
|
await stack.enter_async_context(mcp.session_manager.run())
|
||||||
yield
|
yield
|
||||||
|
|
||||||
|
# Health check endpoints for Kubernetes probes
|
||||||
|
def health_live(request):
|
||||||
|
"""Liveness probe endpoint.
|
||||||
|
|
||||||
|
Returns 200 OK if the application process is running.
|
||||||
|
This is a simple check that doesn't verify external dependencies.
|
||||||
|
"""
|
||||||
|
return JSONResponse(
|
||||||
|
{
|
||||||
|
"status": "alive",
|
||||||
|
"mode": "oauth" if oauth_enabled else "basic",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
async def health_ready(request):
|
||||||
|
"""Readiness probe endpoint.
|
||||||
|
|
||||||
|
Returns 200 OK if the application is ready to serve traffic.
|
||||||
|
Checks that required configuration is present.
|
||||||
|
"""
|
||||||
|
checks = {}
|
||||||
|
is_ready = True
|
||||||
|
|
||||||
|
# Check Nextcloud host configuration
|
||||||
|
nextcloud_host = os.getenv("NEXTCLOUD_HOST")
|
||||||
|
if nextcloud_host:
|
||||||
|
checks["nextcloud_configured"] = "ok"
|
||||||
|
else:
|
||||||
|
checks["nextcloud_configured"] = "error: NEXTCLOUD_HOST not set"
|
||||||
|
is_ready = False
|
||||||
|
|
||||||
|
# Check authentication configuration
|
||||||
|
if oauth_enabled:
|
||||||
|
# OAuth mode - just verify we got this far (token_verifier initialized in lifespan)
|
||||||
|
checks["auth_mode"] = "oauth"
|
||||||
|
checks["auth_configured"] = "ok"
|
||||||
|
else:
|
||||||
|
# BasicAuth mode - verify credentials are set
|
||||||
|
username = os.getenv("NEXTCLOUD_USERNAME")
|
||||||
|
password = os.getenv("NEXTCLOUD_PASSWORD")
|
||||||
|
if username and password:
|
||||||
|
checks["auth_mode"] = "basic"
|
||||||
|
checks["auth_configured"] = "ok"
|
||||||
|
else:
|
||||||
|
checks["auth_mode"] = "basic"
|
||||||
|
checks["auth_configured"] = "error: credentials not set"
|
||||||
|
is_ready = False
|
||||||
|
|
||||||
|
status_code = 200 if is_ready else 503
|
||||||
|
return JSONResponse(
|
||||||
|
{
|
||||||
|
"status": "ready" if is_ready else "not_ready",
|
||||||
|
"checks": checks,
|
||||||
|
},
|
||||||
|
status_code=status_code,
|
||||||
|
)
|
||||||
|
|
||||||
# Add Protected Resource Metadata (PRM) endpoint for OAuth mode
|
# Add Protected Resource Metadata (PRM) endpoint for OAuth mode
|
||||||
routes = []
|
routes = []
|
||||||
|
|
||||||
|
# Add health check routes (available in both OAuth and BasicAuth modes)
|
||||||
|
routes.append(Route("/health/live", health_live, methods=["GET"]))
|
||||||
|
routes.append(Route("/health/ready", health_ready, methods=["GET"]))
|
||||||
|
logger.info("Health check endpoints enabled: /health/live, /health/ready")
|
||||||
|
|
||||||
if oauth_enabled:
|
if oauth_enabled:
|
||||||
|
|
||||||
def oauth_protected_resource_metadata(request):
|
def oauth_protected_resource_metadata(request):
|
||||||
|
|||||||
+11
-3
@@ -253,9 +253,17 @@ def test_default_values(runner, clean_env, monkeypatch):
|
|||||||
_ = runner.invoke(run, [])
|
_ = runner.invoke(run, [])
|
||||||
|
|
||||||
# Verify default values
|
# Verify default values
|
||||||
assert (
|
assert captured_env["NEXTCLOUD_OIDC_SCOPES"] == (
|
||||||
captured_env["NEXTCLOUD_OIDC_SCOPES"]
|
"openid profile email "
|
||||||
== "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"
|
"notes:read notes:write "
|
||||||
|
"calendar:read calendar:write "
|
||||||
|
"todo:read todo: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"
|
||||||
)
|
)
|
||||||
assert captured_env["NEXTCLOUD_OIDC_TOKEN_TYPE"] == "bearer"
|
assert captured_env["NEXTCLOUD_OIDC_TOKEN_TYPE"] == "bearer"
|
||||||
assert captured_env["NEXTCLOUD_MCP_SERVER_URL"] == "http://localhost:8000"
|
assert captured_env["NEXTCLOUD_MCP_SERVER_URL"] == "http://localhost:8000"
|
||||||
|
|||||||
Reference in New Issue
Block a user