chore: remove helm chart (migrated to cbcoutinho/helm-charts)
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>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
010c598b24
commit
c4b74e7e20
@@ -9,7 +9,7 @@ jobs:
|
|||||||
bump-version:
|
bump-version:
|
||||||
if: "!startsWith(github.event.head_commit.message, 'bump:') && !startsWith(github.event.head_commit.message, 'chore(release):')"
|
if: "!startsWith(github.event.head_commit.message, 'bump:') && !startsWith(github.event.head_commit.message, 'chore(release):')"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: "Bump version and create changelog for monorepo components"
|
name: "Bump version and create changelog"
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
packages: write
|
packages: write
|
||||||
@@ -35,94 +35,33 @@ jobs:
|
|||||||
git config user.name "github-actions[bot]"
|
git config user.name "github-actions[bot]"
|
||||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
|
||||||
- name: Detect and bump component versions
|
- name: Detect and bump version
|
||||||
id: bump
|
id: bump
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# Track which components were bumped
|
echo "Checking for version bump..."
|
||||||
BUMPED_COMPONENTS=""
|
|
||||||
|
|
||||||
# Helper function to check for commits with specific scope since last tag
|
# Get the most recent tag
|
||||||
has_commits_since_tag() {
|
last_tag=$(git tag --sort=-creatordate | grep -E "^v[0-9]" | head -n 1 || echo "")
|
||||||
local tag_pattern="$1"
|
|
||||||
local scope_pattern="$2"
|
|
||||||
|
|
||||||
# Get the most recent tag matching the pattern
|
if [ -z "$last_tag" ]; then
|
||||||
local last_tag=$(git tag --sort=-creatordate | grep -E "^${tag_pattern}" | head -n 1 || echo "")
|
|
||||||
|
|
||||||
if [ -z "$last_tag" ]; then
|
|
||||||
# No previous tag, check all commits on master
|
|
||||||
local commit_range="master"
|
|
||||||
else
|
|
||||||
# Check commits since last tag
|
|
||||||
local commit_range="${last_tag}..HEAD"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Count commits matching the scope pattern
|
|
||||||
local commit_count=$(git log "$commit_range" --oneline --grep="^${scope_pattern}" -E | wc -l)
|
|
||||||
|
|
||||||
if [ "$commit_count" -gt 0 ]; then
|
|
||||||
echo "Found $commit_count commits for scope '$scope_pattern' since $last_tag"
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
echo "No commits found for scope '$scope_pattern' since $last_tag"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Bump MCP server (default - all commits except helm scope)
|
|
||||||
echo "Checking MCP server for version bump..."
|
|
||||||
|
|
||||||
# Get the most recent MCP tag
|
|
||||||
last_mcp_tag=$(git tag --sort=-creatordate | grep -E "^v[0-9]" | head -n 1 || echo "")
|
|
||||||
|
|
||||||
if [ -z "$last_mcp_tag" ]; then
|
|
||||||
commit_range="master"
|
commit_range="master"
|
||||||
else
|
else
|
||||||
commit_range="${last_mcp_tag}..HEAD"
|
commit_range="${last_tag}..HEAD"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Count conventional commits that are NOT scoped to helm
|
# Count conventional commits
|
||||||
mcp_commit_count=$(git log "$commit_range" --oneline --grep="^(feat|fix|docs|refactor|perf|test|build|ci|chore)" -E | \
|
commit_count=$(git log "$commit_range" --oneline --grep="^(feat|fix|docs|refactor|perf|test|build|ci|chore)" -E | wc -l)
|
||||||
{ grep -v "(helm)" || true; } | wc -l)
|
|
||||||
|
|
||||||
MCP_BUMPED=false
|
if [ "$commit_count" -gt 0 ]; then
|
||||||
if [ "$mcp_commit_count" -gt 0 ]; then
|
echo "Found $commit_count commits since $last_tag"
|
||||||
echo "Found $mcp_commit_count commits for MCP server since $last_mcp_tag"
|
echo "Bumping version..."
|
||||||
echo "Bumping MCP server version..."
|
|
||||||
./scripts/bump-mcp.sh
|
./scripts/bump-mcp.sh
|
||||||
BUMPED_COMPONENTS="$BUMPED_COMPONENTS mcp"
|
|
||||||
MCP_BUMPED=true
|
|
||||||
else
|
|
||||||
echo "No commits found for MCP server since $last_mcp_tag"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Bump Helm chart (scope: helm OR when MCP appVersion changes)
|
|
||||||
echo "Checking Helm chart for version bump..."
|
|
||||||
HELM_HAS_COMMITS=false
|
|
||||||
if has_commits_since_tag "nextcloud-mcp-server-" "(feat|fix|docs|refactor|perf|test|build|ci|chore)\(helm\)(!)?:"; then
|
|
||||||
HELM_HAS_COMMITS=true
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$HELM_HAS_COMMITS" = true ]; then
|
|
||||||
echo "Bumping Helm chart version (helm-scoped commits)..."
|
|
||||||
./scripts/bump-helm.sh
|
|
||||||
BUMPED_COMPONENTS="$BUMPED_COMPONENTS helm"
|
|
||||||
elif [ "$MCP_BUMPED" = true ]; then
|
|
||||||
echo "Bumping Helm chart version (appVersion changed)..."
|
|
||||||
./scripts/bump-helm.sh --increment PATCH
|
|
||||||
BUMPED_COMPONENTS="$BUMPED_COMPONENTS helm"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Output summary
|
|
||||||
if [ -z "$BUMPED_COMPONENTS" ]; then
|
|
||||||
echo "No components required version bumps"
|
|
||||||
echo "bumped=false" >> $GITHUB_OUTPUT
|
|
||||||
else
|
|
||||||
echo "Bumped components:$BUMPED_COMPONENTS"
|
|
||||||
echo "bumped=true" >> $GITHUB_OUTPUT
|
echo "bumped=true" >> $GITHUB_OUTPUT
|
||||||
echo "components=$BUMPED_COMPONENTS" >> $GITHUB_OUTPUT
|
else
|
||||||
|
echo "No commits found since $last_tag"
|
||||||
|
echo "bumped=false" >> $GITHUB_OUTPUT
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Push tags
|
- name: Push tags
|
||||||
@@ -130,35 +69,19 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
git push
|
git push
|
||||||
git push --tags
|
git push --tags
|
||||||
echo "Pushed tags for components:${{ steps.bump.outputs.components }}"
|
echo "Pushed version tags"
|
||||||
|
|
||||||
- name: Summary
|
- name: Summary
|
||||||
run: |
|
run: |
|
||||||
if [ "${{ steps.bump.outputs.bumped }}" == "true" ]; then
|
if [ "${{ steps.bump.outputs.bumped }}" == "true" ]; then
|
||||||
|
tag=$(git tag --sort=-creatordate | grep -E '^v[0-9]' | head -n 1)
|
||||||
echo "## Version Bump Summary" >> $GITHUB_STEP_SUMMARY
|
echo "## Version Bump Summary" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "The following components were bumped:" >> $GITHUB_STEP_SUMMARY
|
echo "- **MCP Server**: \`$tag\`" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "Tag has been pushed and release workflows will trigger automatically." >> $GITHUB_STEP_SUMMARY
|
||||||
for component in ${{ steps.bump.outputs.components }}; do
|
|
||||||
case $component in
|
|
||||||
mcp)
|
|
||||||
tag=$(git tag --sort=-creatordate | grep -E '^v[0-9]' | head -n 1)
|
|
||||||
echo "- **MCP Server**: \`$tag\`" >> $GITHUB_STEP_SUMMARY
|
|
||||||
;;
|
|
||||||
helm)
|
|
||||||
tag=$(git tag --sort=-creatordate | grep -E '^nextcloud-mcp-server-' | head -n 1)
|
|
||||||
echo "- **Helm Chart**: \`$tag\`" >> $GITHUB_STEP_SUMMARY
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "Tags have been pushed and release workflows will trigger automatically." >> $GITHUB_STEP_SUMMARY
|
|
||||||
else
|
else
|
||||||
echo "## Version Bump Summary" >> $GITHUB_STEP_SUMMARY
|
echo "## Version Bump Summary" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "✅ No version bumps required - no relevant commits found since last release." >> $GITHUB_STEP_SUMMARY
|
echo "No version bump required - no relevant commits found since last release." >> $GITHUB_STEP_SUMMARY
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "The workflow completed successfully with no changes." >> $GITHUB_STEP_SUMMARY
|
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -1,137 +0,0 @@
|
|||||||
name: Release Charts
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- v*
|
|
||||||
- nextcloud-mcp-server-*
|
|
||||||
|
|
||||||
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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
||||||
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: Install Helm
|
|
||||||
uses: azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # v4.3.1
|
|
||||||
with:
|
|
||||||
version: v3.16.0
|
|
||||||
|
|
||||||
- name: Add Helm repositories and update dependencies
|
|
||||||
run: |
|
|
||||||
helm repo add qdrant https://qdrant.github.io/qdrant-helm
|
|
||||||
helm repo add ollama https://otwld.github.io/ollama-helm
|
|
||||||
helm repo update
|
|
||||||
helm dependency build charts/nextcloud-mcp-server
|
|
||||||
|
|
||||||
- name: Run chart-releaser
|
|
||||||
uses: helm/chart-releaser-action@cae68fefc6b5f367a0275617c9f83181ba54714f # v1.7.0
|
|
||||||
with:
|
|
||||||
skip_existing: true
|
|
||||||
env:
|
|
||||||
CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
|
||||||
|
|
||||||
- name: Update gh-pages with Chart README and Index
|
|
||||||
run: |
|
|
||||||
# Get the repository name
|
|
||||||
REPO_NAME="${GITHUB_REPOSITORY##*/}"
|
|
||||||
REPO_OWNER="${GITHUB_REPOSITORY%/*}"
|
|
||||||
|
|
||||||
# Switch to gh-pages branch
|
|
||||||
git fetch origin gh-pages
|
|
||||||
git checkout gh-pages
|
|
||||||
|
|
||||||
# Copy Chart README to root
|
|
||||||
git checkout ${GITHUB_REF#refs/tags/} -- charts/nextcloud-mcp-server/README.md
|
|
||||||
mv charts/nextcloud-mcp-server/README.md README.md || true
|
|
||||||
rm -rf charts 2>/dev/null || true
|
|
||||||
|
|
||||||
# Create index.html with installation instructions
|
|
||||||
cat > index.html <<'EOF'
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Nextcloud MCP Server Helm Chart</title>
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
||||||
max-width: 800px;
|
|
||||||
margin: 50px auto;
|
|
||||||
padding: 20px;
|
|
||||||
line-height: 1.6;
|
|
||||||
}
|
|
||||||
code {
|
|
||||||
background: #f4f4f4;
|
|
||||||
padding: 2px 6px;
|
|
||||||
border-radius: 3px;
|
|
||||||
font-family: "Monaco", "Courier New", monospace;
|
|
||||||
}
|
|
||||||
pre {
|
|
||||||
background: #f4f4f4;
|
|
||||||
padding: 15px;
|
|
||||||
border-radius: 5px;
|
|
||||||
overflow-x: auto;
|
|
||||||
}
|
|
||||||
h1, h2 { color: #0082c9; }
|
|
||||||
a { color: #0082c9; text-decoration: none; }
|
|
||||||
a:hover { text-decoration: underline; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Nextcloud MCP Server Helm Chart</h1>
|
|
||||||
|
|
||||||
<p>A Helm chart for deploying the Nextcloud MCP (Model Context Protocol) Server on Kubernetes, enabling AI assistants to interact with your Nextcloud instance.</p>
|
|
||||||
|
|
||||||
<h2>Installation</h2>
|
|
||||||
|
|
||||||
<p>Add the Helm repository:</p>
|
|
||||||
<pre><code>helm repo add nextcloud-mcp https://REPO_OWNER.github.io/REPO_NAME/
|
|
||||||
helm repo update</code></pre>
|
|
||||||
|
|
||||||
<p>Install the chart:</p>
|
|
||||||
<pre><code>helm install nextcloud-mcp nextcloud-mcp/nextcloud-mcp-server \
|
|
||||||
--set nextcloud.host=https://cloud.example.com \
|
|
||||||
--set auth.basic.username=myuser \
|
|
||||||
--set auth.basic.password=mypassword</code></pre>
|
|
||||||
|
|
||||||
<h2>Documentation</h2>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li><a href="README.md">Chart README</a> - Full documentation for the Helm chart</li>
|
|
||||||
<li><a href="https://github.com/REPO_OWNER/REPO_NAME">GitHub Repository</a> - Source code and issues</li>
|
|
||||||
<li><a href="index.yaml">Helm Repository Index</a> - Chart metadata</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h2>Quick Start</h2>
|
|
||||||
|
|
||||||
<p>See the <a href="README.md">full documentation</a> for detailed configuration options, examples, and troubleshooting guides.</p>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
<p><small>Generated by <a href="https://github.com/helm/chart-releaser">chart-releaser</a></small></p>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# Replace placeholders
|
|
||||||
sed -i "s/REPO_OWNER/$REPO_OWNER/g" index.html
|
|
||||||
sed -i "s/REPO_NAME/$REPO_NAME/g" index.html
|
|
||||||
|
|
||||||
# Commit changes
|
|
||||||
git add README.md index.html
|
|
||||||
git commit -m "Update README and index from chart release" || echo "No changes to commit"
|
|
||||||
git push origin gh-pages
|
|
||||||
+10
-61
@@ -2,58 +2,35 @@
|
|||||||
|
|
||||||
## Version Management
|
## Version Management
|
||||||
|
|
||||||
This monorepo uses commitizen for version management with **independent versioning** for two components:
|
This project uses [commitizen](https://commitizen-tools.github.io/commitizen/) for version management following PEP 440 (`major_version_zero = true`, 0.x.x for pre-1.0).
|
||||||
|
|
||||||
### Components
|
> **Note:** The Helm chart has been moved to [cbcoutinho/helm-charts](https://github.com/cbcoutinho/helm-charts). The Astrolabe Nextcloud app has been moved to [cbcoutinho/astrolabe](https://github.com/cbcoutinho/astrolabe).
|
||||||
|
|
||||||
| Component | Scope | Bump Command | Tag Example |
|
|
||||||
|-----------|-------|--------------|-------------|
|
|
||||||
| MCP Server | `mcp` or none | `./scripts/bump-mcp.sh` | `v0.54.0` |
|
|
||||||
| Helm Chart | `helm` | `./scripts/bump-helm.sh` | `nextcloud-mcp-server-0.54.0` |
|
|
||||||
|
|
||||||
> **Note:** The Astrolabe Nextcloud app has been moved to its own repository at [cbcoutinho/astrolabe](https://github.com/cbcoutinho/astrolabe).
|
|
||||||
|
|
||||||
### Commit Message Format
|
### Commit Message Format
|
||||||
|
|
||||||
Use conventional commits with **scopes** to target specific components:
|
Use [conventional commits](https://www.conventionalcommits.org/):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# MCP server changes
|
feat: add new feature
|
||||||
feat(mcp): add calendar sync API
|
feat(mcp): add calendar sync API
|
||||||
fix(mcp): resolve authentication bug
|
fix: resolve authentication bug
|
||||||
|
docs: update README
|
||||||
# Helm chart changes
|
|
||||||
feat(helm): add resource limits
|
|
||||||
docs(helm): update values documentation
|
|
||||||
```
|
|
||||||
|
|
||||||
**Unscoped commits** default to the MCP server:
|
|
||||||
```bash
|
|
||||||
feat: add new feature # → MCP server (v0.54.0)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Release Workflow
|
### Release Workflow
|
||||||
|
|
||||||
#### 1. Make Changes with Scoped Commits
|
#### 1. Make Changes with Conventional Commits
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git commit -m "feat(helm): add ingress annotations"
|
git commit -m "feat: add calendar sync"
|
||||||
git commit -m "feat(mcp): add calendar sync"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 2. Bump Component Versions
|
#### 2. Bump Version
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Bump MCP server (reads commits with scope=mcp or unscoped)
|
|
||||||
./scripts/bump-mcp.sh
|
./scripts/bump-mcp.sh
|
||||||
# → Creates tag: v0.54.0
|
# → Creates tag: v0.54.0
|
||||||
# → Updates: pyproject.toml, Chart.yaml:appVersion
|
# → Updates: pyproject.toml
|
||||||
|
|
||||||
# Bump Helm chart (reads commits with scope=helm)
|
|
||||||
./scripts/bump-helm.sh
|
|
||||||
# → Creates tag: nextcloud-mcp-server-0.54.0
|
|
||||||
# → Updates: Chart.yaml:version
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 3. Push Tags
|
#### 3. Push Tags
|
||||||
@@ -62,13 +39,6 @@ git commit -m "feat(mcp): add calendar sync"
|
|||||||
git push --follow-tags
|
git push --follow-tags
|
||||||
```
|
```
|
||||||
|
|
||||||
### Changelog Filtering
|
|
||||||
|
|
||||||
Each component maintains its own `CHANGELOG.md`:
|
|
||||||
|
|
||||||
- **MCP Server**: `CHANGELOG.md` (root) - includes `feat(mcp):` and unscoped commits
|
|
||||||
- **Helm Chart**: `charts/nextcloud-mcp-server/CHANGELOG.md` - includes `feat(helm):` only
|
|
||||||
|
|
||||||
### Manual Version Bumps
|
### Manual Version Bumps
|
||||||
|
|
||||||
For specific increments:
|
For specific increments:
|
||||||
@@ -82,25 +52,4 @@ uv run cz bump --increment MINOR
|
|||||||
|
|
||||||
# Major bump (0.53.0 → 1.0.0)
|
# Major bump (0.53.0 → 1.0.0)
|
||||||
uv run cz bump --increment MAJOR
|
uv run cz bump --increment MAJOR
|
||||||
|
|
||||||
# For non-MCP components, use --config
|
|
||||||
cd charts/nextcloud-mcp-server
|
|
||||||
uv run cz --config .cz.toml bump --increment MINOR
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Versioning Philosophy
|
|
||||||
|
|
||||||
- **MCP Server**: Follows PEP 440, `major_version_zero = true` (0.x.x for pre-1.0)
|
|
||||||
- **Helm Chart**: Follows PEP 440, starts at 0.53.0 (continues from current)
|
|
||||||
|
|
||||||
### Chart.yaml Version vs appVersion
|
|
||||||
|
|
||||||
The Helm chart has TWO version fields:
|
|
||||||
|
|
||||||
- **`version`**: Chart packaging version (bumped by `feat(helm):`)
|
|
||||||
- Example: `0.53.0` → `0.54.0` when adding resource limits
|
|
||||||
|
|
||||||
- **`appVersion`**: MCP server version being deployed (bumped by `feat(mcp):`)
|
|
||||||
- Example: `"0.53.0"` → `"0.54.0"` when MCP server releases
|
|
||||||
|
|
||||||
This allows the chart to evolve independently from the application.
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ docker compose --profile login-flow up -d # Port 8004
|
|||||||
|
|
||||||
**Next Steps:**
|
**Next Steps:**
|
||||||
- Connect your MCP client (Claude Desktop, IDEs, `mcp dev`, etc.)
|
- Connect your MCP client (Claude Desktop, IDEs, `mcp dev`, etc.)
|
||||||
- See [docs/installation.md](docs/installation.md) for other deployment options (local, Kubernetes)
|
- See [docs/installation.md](docs/installation.md) for other deployment options. For Kubernetes (Helm), see [cbcoutinho/helm-charts](https://github.com/cbcoutinho/helm-charts)
|
||||||
|
|
||||||
## Key Features
|
## Key Features
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ docker compose --profile login-flow up -d # Port 8004
|
|||||||
- **MCP Resources** - Structured data URIs for browsing Nextcloud data
|
- **MCP Resources** - Structured data URIs for browsing Nextcloud data
|
||||||
- **Semantic Search (Experimental)** - Optional vector-powered search for Notes, Files, News items, and Deck cards (requires Qdrant + Ollama)
|
- **Semantic Search (Experimental)** - Optional vector-powered search for Notes, Files, News items, and Deck cards (requires Qdrant + Ollama)
|
||||||
- **Document Processing** - OCR and text extraction from PDFs, DOCX, images with progress notifications
|
- **Document Processing** - OCR and text extraction from PDFs, DOCX, images with progress notifications
|
||||||
- **Flexible Deployment** - Docker, Kubernetes (Helm), VM, or local installation
|
- **Flexible Deployment** - Docker, Kubernetes ([Helm chart](https://github.com/cbcoutinho/helm-charts)), VM, or local installation
|
||||||
- **Production-Ready Auth** - Basic Auth with app passwords (recommended) or OAuth2/OIDC (experimental)
|
- **Production-Ready Auth** - Basic Auth with app passwords (recommended) or OAuth2/OIDC (experimental)
|
||||||
- **Multiple Transports** - SSE, HTTP, and streamable-http support
|
- **Multiple Transports** - SSE, HTTP, and streamable-http support
|
||||||
|
|
||||||
@@ -146,7 +146,7 @@ This enables natural language queries and helps discover related content across
|
|||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
### Getting Started
|
### Getting Started
|
||||||
- **[Installation](docs/installation.md)** - Docker, Kubernetes, local, or VM deployment
|
- **[Installation](docs/installation.md)** - Docker, local, or VM deployment. [Helm chart](https://github.com/cbcoutinho/helm-charts) for Kubernetes
|
||||||
- **[Configuration](docs/configuration.md)** - Environment variables and advanced options
|
- **[Configuration](docs/configuration.md)** - Environment variables and advanced options
|
||||||
- **[Authentication](docs/authentication.md)** - Basic Auth vs OAuth2/OIDC setup
|
- **[Authentication](docs/authentication.md)** - Basic Auth vs OAuth2/OIDC setup
|
||||||
- **[Running the Server](docs/running.md)** - Start, manage, and troubleshoot
|
- **[Running the Server](docs/running.md)** - Start, manage, and troubleshoot
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
[tool.commitizen]
|
|
||||||
name = "cz_conventional_commits"
|
|
||||||
version = "0.58.31"
|
|
||||||
tag_format = "nextcloud-mcp-server-$version"
|
|
||||||
version_scheme = "semver"
|
|
||||||
update_changelog_on_bump = true
|
|
||||||
major_version_zero = true
|
|
||||||
|
|
||||||
# Update chart version only (NOT appVersion)
|
|
||||||
version_files = [
|
|
||||||
"Chart.yaml:^version:"
|
|
||||||
]
|
|
||||||
|
|
||||||
# Ignore tags from other components
|
|
||||||
ignored_tag_formats = [
|
|
||||||
"v*", # MCP server tags
|
|
||||||
"astrolabe-v*", # Astrolabe tags
|
|
||||||
]
|
|
||||||
|
|
||||||
# Filter commits by scope
|
|
||||||
# Includes helm-scoped commits AND MCP server version bumps (which update appVersion)
|
|
||||||
[tool.commitizen.customize]
|
|
||||||
changelog_pattern = "^((feat|fix|docs|refactor|perf|test|build|ci|chore)\\(helm\\)(!)?:|bump: version.*→.*)"
|
|
||||||
schema_pattern = "^(feat|fix|docs|refactor|perf|test|build|ci|chore)\\(helm\\)(!)?:\\s.+"
|
|
||||||
message_template = "{{change_type}}(helm): {{message}}"
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
charts/
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
# 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/
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,9 +0,0 @@
|
|||||||
dependencies:
|
|
||||||
- name: qdrant
|
|
||||||
repository: https://qdrant.github.io/qdrant-helm
|
|
||||||
version: 1.17.1
|
|
||||||
- name: ollama
|
|
||||||
repository: https://otwld.github.io/ollama-helm
|
|
||||||
version: 1.47.0
|
|
||||||
digest: sha256:92b4741d6c8c9ef2303d179074335952e2867b8e2ace8ab74f2ab912e06f5d1c
|
|
||||||
generated: "2026-03-27T15:36:24.829508269Z"
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
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.58.31
|
|
||||||
appVersion: "0.68.4"
|
|
||||||
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
|
|
||||||
annotations:
|
|
||||||
# Grafana dashboard support
|
|
||||||
grafana_dashboard: "true"
|
|
||||||
grafana_dashboard_folder: "Nextcloud MCP"
|
|
||||||
artifacthub.io/changes: |
|
|
||||||
- kind: added
|
|
||||||
description: Login Flow v2 auth mode for Helm chart (ADR-022)
|
|
||||||
- kind: added
|
|
||||||
description: Multi-user BasicAuth guidance in post-install NOTES
|
|
||||||
- kind: added
|
|
||||||
description: Version and changelog info in post-install NOTES
|
|
||||||
- kind: changed
|
|
||||||
description: Updated appVersion to 0.64.4
|
|
||||||
dependencies:
|
|
||||||
- name: qdrant
|
|
||||||
version: "1.17.1"
|
|
||||||
repository: https://qdrant.github.io/qdrant-helm
|
|
||||||
condition: qdrant.networkMode.deploySubchart
|
|
||||||
- name: ollama
|
|
||||||
version: "1.47.0"
|
|
||||||
repository: https://otwld.github.io/ollama-helm
|
|
||||||
condition: ollama.enabled
|
|
||||||
@@ -1,742 +0,0 @@
|
|||||||
# 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
|
|
||||||
# Add the Helm repository
|
|
||||||
helm repo add nextcloud-mcp https://cbcoutinho.github.io/nextcloud-mcp-server
|
|
||||||
helm repo update
|
|
||||||
|
|
||||||
# Install with basic auth (recommended for most users)
|
|
||||||
helm install nextcloud-mcp nextcloud-mcp/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 nextcloud-mcp/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 URL for browser-accessible OAuth authorization endpoint (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, defaults to `nextcloud.host`. **Only used for authorization endpoints** that browsers must access. All server-to-server endpoints (token, JWKS, introspection, userinfo) use URLs from OIDC discovery without rewriting
|
|
||||||
|
|
||||||
#### 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` |
|
|
||||||
|
|
||||||
#### Data Storage
|
|
||||||
|
|
||||||
The `/app/data` directory is used for application data (token databases, Qdrant persistent storage, etc.). It is always mounted as writable to support the read-only root filesystem security context.
|
|
||||||
|
|
||||||
| Parameter | Description | Default |
|
|
||||||
|-----------|-------------|---------|
|
|
||||||
| `dataStorage.enabled` | Enable persistent storage for `/app/data` | `false` |
|
|
||||||
| `dataStorage.size` | Size of data storage PVC | `1Gi` |
|
|
||||||
| `dataStorage.storageClass` | Storage class (leave empty for default) | `""` |
|
|
||||||
| `dataStorage.accessMode` | Access mode | `ReadWriteOnce` |
|
|
||||||
| `dataStorage.existingClaim` | Use existing PVC | `""` |
|
|
||||||
|
|
||||||
**When to enable persistence:**
|
|
||||||
- Multi-user basic auth with offline access (stores `tokens.db`)
|
|
||||||
- Qdrant persistent mode (stores vector database)
|
|
||||||
- Any feature requiring persistent app data
|
|
||||||
|
|
||||||
**When persistence is disabled:** Uses `emptyDir` (non-persistent, data lost on pod restart, but directory remains writable).
|
|
||||||
|
|
||||||
#### MCP Server Configuration
|
|
||||||
|
|
||||||
| Parameter | Description | Default |
|
|
||||||
|-----------|-------------|---------|
|
|
||||||
| `mcp.transport` | Transport mode | `streamable-http` |
|
|
||||||
| `mcp.port` | Server port (used by both auth modes) | `8000` |
|
|
||||||
| `mcp.extraArgs` | Additional command-line arguments | `[]` |
|
|
||||||
|
|
||||||
The `extraArgs` parameter allows you to pass additional command-line arguments to the MCP server. This is useful for enabling debug logging, enabling specific apps, or other runtime configuration.
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
```yaml
|
|
||||||
mcp:
|
|
||||||
extraArgs:
|
|
||||||
- "--log-level"
|
|
||||||
- "debug"
|
|
||||||
- "--enable-app"
|
|
||||||
- "notes"
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Image Configuration
|
|
||||||
|
|
||||||
| Parameter | Description | Default |
|
|
||||||
|-----------|-------------|---------|
|
|
||||||
| `image.repository` | Container image repository | `ghcr.io/cbcoutinho/nextcloud-mcp-server` |
|
|
||||||
| `image.pullPolicy` | Image pull policy | `IfNotPresent` |
|
|
||||||
|
|
||||||
**Note:** Image tag is automatically set to the chart's `appVersion` and cannot be overridden.
|
|
||||||
|
|
||||||
#### 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` |
|
|
||||||
|
|
||||||
#### 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` |
|
|
||||||
|
|
||||||
#### Vector Search & Semantic Capabilities (Optional)
|
|
||||||
|
|
||||||
Enable semantic search capabilities with BM25 hybrid search by deploying a vector database (Qdrant) and embedding service (Ollama or OpenAI).
|
|
||||||
|
|
||||||
**Semantic Search Configuration:**
|
|
||||||
|
|
||||||
| Parameter | Description | Default |
|
|
||||||
|-----------|-------------|---------|
|
|
||||||
| `semanticSearch.enabled` | Enable semantic search and background vector synchronization | `false` |
|
|
||||||
| `semanticSearch.scanInterval` | Scan interval in seconds | `3600` |
|
|
||||||
| `semanticSearch.processorWorkers` | Number of concurrent processor workers | `3` |
|
|
||||||
| `semanticSearch.queueMaxSize` | Maximum queue size for pending documents | `10000` |
|
|
||||||
|
|
||||||
**Document Chunking Configuration:**
|
|
||||||
|
|
||||||
| Parameter | Description | Default |
|
|
||||||
|-----------|-------------|---------|
|
|
||||||
| `documentChunking.chunkSize` | Number of words per chunk for embedding | `512` |
|
|
||||||
| `documentChunking.chunkOverlap` | Number of overlapping words between chunks | `50` |
|
|
||||||
|
|
||||||
**Chunking Strategy:**
|
|
||||||
- **Small chunks (256-384)**: Better precision for searches, more storage overhead
|
|
||||||
- **Medium chunks (512-768)**: Balanced approach (recommended for most use cases)
|
|
||||||
- **Large chunks (1024+)**: Better context preservation, less precise matching
|
|
||||||
- **Overlap**: Should be 10-20% of chunk size to preserve context across boundaries
|
|
||||||
|
|
||||||
**Qdrant Vector Database:**
|
|
||||||
|
|
||||||
Qdrant is deployed as a subchart when `qdrant.enabled` is `true`. All configuration values are passed through to the [qdrant/qdrant](https://github.com/qdrant/qdrant-helm) chart.
|
|
||||||
|
|
||||||
| Parameter | Description | Default |
|
|
||||||
|-----------|-------------|---------|
|
|
||||||
| `qdrant.enabled` | Deploy Qdrant as a subchart | `false` |
|
|
||||||
| `qdrant.replicaCount` | Number of Qdrant replicas | `1` |
|
|
||||||
| `qdrant.image.tag` | Qdrant version | `v1.12.5` |
|
|
||||||
| `qdrant.apiKey` | Optional API key for authentication | `""` |
|
|
||||||
| `qdrant.persistence.size` | Storage size for vector data | `10Gi` |
|
|
||||||
| `qdrant.persistence.storageClass` | Storage class | `""` |
|
|
||||||
| `qdrant.resources.requests.cpu` | CPU request | `200m` |
|
|
||||||
| `qdrant.resources.requests.memory` | Memory request | `512Mi` |
|
|
||||||
| `qdrant.resources.limits.cpu` | CPU limit | `1000m` |
|
|
||||||
| `qdrant.resources.limits.memory` | Memory limit | `2Gi` |
|
|
||||||
|
|
||||||
**Ollama Embedding Service:**
|
|
||||||
|
|
||||||
Ollama is deployed as a subchart when `ollama.enabled` is `true`. All configuration values are passed through to the [ollama/ollama](https://github.com/otwld/ollama-helm) chart. Alternatively, set `ollama.url` to use an external Ollama instance.
|
|
||||||
|
|
||||||
| Parameter | Description | Default |
|
|
||||||
|-----------|-------------|---------|
|
|
||||||
| `ollama.enabled` | Deploy Ollama as a subchart | `false` |
|
|
||||||
| `ollama.url` | External Ollama URL (use with `enabled: false`) | `""` |
|
|
||||||
| `ollama.embeddingModel` | Embedding model to use | `nomic-embed-text` |
|
|
||||||
| `ollama.verifySsl` | Verify SSL certificates | `true` |
|
|
||||||
| `ollama.replicaCount` | Number of Ollama replicas | `1` |
|
|
||||||
| `ollama.ollama.models.pull` | Models to pull on startup | `["nomic-embed-text"]` |
|
|
||||||
| `ollama.persistentVolume.enabled` | Enable persistent storage | `true` |
|
|
||||||
| `ollama.persistentVolume.size` | Storage size for models | `20Gi` |
|
|
||||||
| `ollama.resources.requests.cpu` | CPU request | `500m` |
|
|
||||||
| `ollama.resources.requests.memory` | Memory request | `1Gi` |
|
|
||||||
| `ollama.resources.limits.cpu` | CPU limit | `2000m` |
|
|
||||||
| `ollama.resources.limits.memory` | Memory limit | `4Gi` |
|
|
||||||
|
|
||||||
**OpenAI Embedding Provider (Alternative):**
|
|
||||||
|
|
||||||
Use OpenAI or any OpenAI-compatible API instead of Ollama.
|
|
||||||
|
|
||||||
| Parameter | Description | Default |
|
|
||||||
|-----------|-------------|---------|
|
|
||||||
| `openai.enabled` | Enable OpenAI embedding provider | `false` |
|
|
||||||
| `openai.apiKey` | OpenAI API key | `""` |
|
|
||||||
| `openai.existingSecret` | Use existing secret for API key | `""` |
|
|
||||||
| `openai.secretKey` | Key in secret containing API key | `api-key` |
|
|
||||||
| `openai.baseUrl` | Custom API endpoint (optional) | `""` |
|
|
||||||
|
|
||||||
#### Observability & Monitoring
|
|
||||||
|
|
||||||
The chart includes comprehensive observability features including Prometheus metrics, OpenTelemetry tracing, and Grafana dashboards.
|
|
||||||
|
|
||||||
**Metrics Configuration:**
|
|
||||||
|
|
||||||
| Parameter | Description | Default |
|
|
||||||
|-----------|-------------|---------|
|
|
||||||
| `observability.metrics.enabled` | Enable Prometheus metrics | `true` |
|
|
||||||
| `observability.metrics.port` | Metrics port | `9090` |
|
|
||||||
| `observability.metrics.path` | Metrics endpoint path | `/metrics` |
|
|
||||||
|
|
||||||
**Tracing Configuration:**
|
|
||||||
|
|
||||||
| Parameter | Description | Default |
|
|
||||||
|-----------|-------------|---------|
|
|
||||||
| `observability.tracing.enabled` | Enable OpenTelemetry tracing | `false` |
|
|
||||||
| `observability.tracing.endpoint` | OTLP collector endpoint | `""` |
|
|
||||||
| `observability.tracing.serviceName` | Service name in traces | `nextcloud-mcp-server` |
|
|
||||||
| `observability.tracing.samplingRate` | Trace sampling rate (0.0-1.0) | `1.0` |
|
|
||||||
|
|
||||||
**Logging Configuration:**
|
|
||||||
|
|
||||||
| Parameter | Description | Default |
|
|
||||||
|-----------|-------------|---------|
|
|
||||||
| `observability.logging.format` | Log format (json or text) | `json` |
|
|
||||||
| `observability.logging.level` | Log level | `INFO` |
|
|
||||||
| `observability.logging.includeTraceContext` | Include trace IDs in logs | `true` |
|
|
||||||
|
|
||||||
**ServiceMonitor (Prometheus Operator):**
|
|
||||||
|
|
||||||
| Parameter | Description | Default |
|
|
||||||
|-----------|-------------|---------|
|
|
||||||
| `serviceMonitor.enabled` | Create ServiceMonitor resource | `false` |
|
|
||||||
| `serviceMonitor.interval` | Scrape interval | `30s` |
|
|
||||||
| `serviceMonitor.scrapeTimeout` | Scrape timeout | `10s` |
|
|
||||||
| `serviceMonitor.labels` | Additional labels for ServiceMonitor | `{}` |
|
|
||||||
|
|
||||||
**PrometheusRule (Prometheus Operator):**
|
|
||||||
|
|
||||||
| Parameter | Description | Default |
|
|
||||||
|-----------|-------------|---------|
|
|
||||||
| `prometheusRule.enabled` | Create PrometheusRule with alert rules | `false` |
|
|
||||||
| `prometheusRule.labels` | Additional labels for PrometheusRule | `{}` |
|
|
||||||
|
|
||||||
**Grafana Dashboards:**
|
|
||||||
|
|
||||||
| Parameter | Description | Default |
|
|
||||||
|-----------|-------------|---------|
|
|
||||||
| `dashboards.enabled` | Enable automatic dashboard provisioning | `false` |
|
|
||||||
| `dashboards.grafanaFolder` | Grafana folder name for dashboards | `Nextcloud MCP` |
|
|
||||||
| `dashboards.labels` | Additional labels for dashboard ConfigMap | `{}` |
|
|
||||||
| `dashboards.annotations` | Additional annotations for dashboard ConfigMap | `{}` |
|
|
||||||
|
|
||||||
When `dashboards.enabled` is `true`, a ConfigMap with the Grafana dashboard is created with the `grafana_dashboard: "1"` label. This enables automatic discovery by Grafana sidecar containers (commonly used with kube-prometheus-stack).
|
|
||||||
|
|
||||||
The dashboard provides comprehensive monitoring including:
|
|
||||||
- HTTP request metrics (RED pattern: Rate, Errors, Duration)
|
|
||||||
- MCP tool performance and errors
|
|
||||||
- Nextcloud API performance by app (notes, calendar, contacts, etc.)
|
|
||||||
- OAuth token operations and cache hit rates
|
|
||||||
- External dependency health (Nextcloud, Qdrant, Keycloak, Unstructured API)
|
|
||||||
- Vector sync processing pipeline (when enabled)
|
|
||||||
|
|
||||||
For manual import or more details, see `charts/nextcloud-mcp-server/dashboards/README.md`.
|
|
||||||
|
|
||||||
## 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 (only used for browser-accessible auth endpoint)
|
|
||||||
|
|
||||||
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 (only used for browser-accessible auth endpoint)
|
|
||||||
|
|
||||||
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
|
|
||||||
```
|
|
||||||
|
|
||||||
### Example 5: Semantic Search with Qdrant and Ollama
|
|
||||||
|
|
||||||
Deploy with vector search capabilities using embedded Qdrant and Ollama:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
nextcloud:
|
|
||||||
host: https://cloud.example.com
|
|
||||||
|
|
||||||
auth:
|
|
||||||
mode: basic
|
|
||||||
basic:
|
|
||||||
username: admin
|
|
||||||
password: secure-password
|
|
||||||
|
|
||||||
# Enable semantic search
|
|
||||||
semanticSearch:
|
|
||||||
enabled: true
|
|
||||||
scanInterval: 1800 # Scan every 30 minutes
|
|
||||||
processorWorkers: 5
|
|
||||||
|
|
||||||
# Deploy Qdrant as a subchart
|
|
||||||
qdrant:
|
|
||||||
enabled: true
|
|
||||||
persistence:
|
|
||||||
size: 20Gi
|
|
||||||
storageClass: fast-ssd
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: 500m
|
|
||||||
memory: 1Gi
|
|
||||||
limits:
|
|
||||||
cpu: 2000m
|
|
||||||
memory: 4Gi
|
|
||||||
|
|
||||||
# Deploy Ollama as a subchart
|
|
||||||
ollama:
|
|
||||||
enabled: true
|
|
||||||
embeddingModel: nomic-embed-text
|
|
||||||
persistentVolume:
|
|
||||||
size: 30Gi
|
|
||||||
storageClass: standard
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: 1000m
|
|
||||||
memory: 2Gi
|
|
||||||
limits:
|
|
||||||
cpu: 4000m
|
|
||||||
memory: 8Gi
|
|
||||||
```
|
|
||||||
|
|
||||||
Or use an external Ollama instance:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
semanticSearch:
|
|
||||||
enabled: true
|
|
||||||
|
|
||||||
qdrant:
|
|
||||||
enabled: true
|
|
||||||
|
|
||||||
# Use external Ollama instead of deploying subchart
|
|
||||||
ollama:
|
|
||||||
enabled: false
|
|
||||||
url: "http://ollama.ai-services.svc.cluster.local:11434"
|
|
||||||
embeddingModel: nomic-embed-text
|
|
||||||
```
|
|
||||||
|
|
||||||
Or use OpenAI for embeddings:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
semanticSearch:
|
|
||||||
enabled: true
|
|
||||||
|
|
||||||
qdrant:
|
|
||||||
enabled: true
|
|
||||||
|
|
||||||
# Use OpenAI instead of Ollama
|
|
||||||
openai:
|
|
||||||
enabled: true
|
|
||||||
apiKey: "sk-..."
|
|
||||||
# Or use existing secret:
|
|
||||||
# existingSecret: openai-api-key
|
|
||||||
# secretKey: api-key
|
|
||||||
```
|
|
||||||
|
|
||||||
## Upgrading
|
|
||||||
|
|
||||||
### To upgrade an existing deployment:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Update the repository
|
|
||||||
helm repo update
|
|
||||||
|
|
||||||
# Upgrade with your custom values
|
|
||||||
helm upgrade nextcloud-mcp nextcloud-mcp/nextcloud-mcp-server -f custom-values.yaml
|
|
||||||
```
|
|
||||||
|
|
||||||
### To upgrade with new values:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
helm upgrade nextcloud-mcp nextcloud-mcp/nextcloud-mcp-server \
|
|
||||||
--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
|
|
||||||
- For OAuth mode: Ensure MCP server can reach OIDC discovery endpoints (token, JWKS, introspection, userinfo URLs)
|
|
||||||
- Check network policies and firewall rules
|
|
||||||
- Note: Do not use internal Docker hostnames (like `http://app:80`) for `nextcloud.host` - use externally resolvable URLs
|
|
||||||
|
|
||||||
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.
|
|
||||||
@@ -1,161 +0,0 @@
|
|||||||
# Grafana Dashboards
|
|
||||||
|
|
||||||
This directory contains example Grafana dashboards for monitoring the Nextcloud MCP Server.
|
|
||||||
|
|
||||||
## Dashboards
|
|
||||||
|
|
||||||
### nextcloud-mcp-server.json
|
|
||||||
|
|
||||||
All-in-one Operations Dashboard with comprehensive monitoring across all system components.
|
|
||||||
|
|
||||||
#### Overview Row
|
|
||||||
High-level metrics for quick health assessment:
|
|
||||||
- **Request Rate** (stat): Total requests per second
|
|
||||||
- **Error Rate** (stat): Percentage of 5xx errors with color thresholds
|
|
||||||
- **P95 Latency** (stat): 95th percentile request latency
|
|
||||||
- **Active Requests** (stat): Current in-flight requests
|
|
||||||
|
|
||||||
#### HTTP Metrics (RED Pattern)
|
|
||||||
Core request/error/duration metrics:
|
|
||||||
- **Request Rate by Endpoint** (timeseries): RPS breakdown by endpoint
|
|
||||||
- **Error Rate by Status Code** (timeseries): Error rates for 4xx/5xx codes
|
|
||||||
- **Latency Percentiles** (timeseries): P50, P95, P99 latency trends
|
|
||||||
- **Status Code Distribution** (piechart): Percentage breakdown of all status codes
|
|
||||||
|
|
||||||
#### MCP Tools Row
|
|
||||||
MCP-specific tool performance:
|
|
||||||
- **Top Tools by Call Volume** (bargauge): Top 10 most-called tools
|
|
||||||
- **Tool Error Rate** (timeseries): Error rates per tool
|
|
||||||
- **Tool Execution Duration** (timeseries): P95 latency by tool
|
|
||||||
|
|
||||||
#### Nextcloud API Row
|
|
||||||
Backend API performance metrics:
|
|
||||||
- **API Calls by App** (timeseries): Request rate per Nextcloud app (notes, calendar, contacts, etc.)
|
|
||||||
- **API Latency by App** (timeseries): P95 latency per app
|
|
||||||
- **API Retries by Reason** (timeseries): Retry patterns (429, timeout, connection errors)
|
|
||||||
- **API Error Rate** (stat): Overall API error percentage
|
|
||||||
|
|
||||||
#### OAuth & Authentication Row
|
|
||||||
OAuth token operations and caching:
|
|
||||||
- **Token Validations** (timeseries): Success/failure rates for token validation
|
|
||||||
- **Token Exchange Operations** (timeseries): RFC 8693 token exchange operations
|
|
||||||
- **Token Cache Hit Rate** (stat): Percentage of cache hits (color-coded: red<50%, yellow<80%, green≥80%)
|
|
||||||
- **Refresh Token Operations** (timeseries): Refresh token storage operations by type
|
|
||||||
|
|
||||||
#### Dependencies & Health Row
|
|
||||||
External dependency status monitoring:
|
|
||||||
- **Nextcloud Health** (stat): UP/DOWN status with color coding
|
|
||||||
- **Qdrant Health** (stat): Vector database health status
|
|
||||||
- **Keycloak Health** (stat): Identity provider health status
|
|
||||||
- **Unstructured API Health** (stat): Document processing API status
|
|
||||||
- **Health Check Duration** (timeseries): Health check latency by dependency
|
|
||||||
- **Database Operation Latency** (timeseries): P95 latency for DB operations (SQLite, Qdrant)
|
|
||||||
|
|
||||||
#### Vector Sync Row (when enabled)
|
|
||||||
Document processing pipeline metrics:
|
|
||||||
- **Documents Processed Rate** (timeseries): Processing throughput by status (success/failure)
|
|
||||||
- **Processing Queue Depth** (gauge): Current queue size with thresholds (yellow>50, red>100)
|
|
||||||
- **Qdrant Operations** (timeseries): Vector database operations by type
|
|
||||||
- **Document Processing Duration** (timeseries): P95 processing latency
|
|
||||||
|
|
||||||
## Importing to Grafana
|
|
||||||
|
|
||||||
### Manual Import
|
|
||||||
|
|
||||||
1. Open Grafana UI
|
|
||||||
2. Navigate to Dashboards → Import
|
|
||||||
3. Upload `nextcloud-mcp-server.json`
|
|
||||||
4. Select your Prometheus data source
|
|
||||||
5. Click "Import"
|
|
||||||
|
|
||||||
### Automated Import (Helm Chart)
|
|
||||||
|
|
||||||
The Helm chart now supports automatic dashboard provisioning via Grafana sidecar pattern.
|
|
||||||
|
|
||||||
#### Option 1: Using Helm Chart (Recommended)
|
|
||||||
|
|
||||||
Enable dashboard provisioning in your Helm values:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# values.yaml for nextcloud-mcp-server chart
|
|
||||||
dashboards:
|
|
||||||
enabled: true
|
|
||||||
grafanaFolder: "Nextcloud MCP" # Folder name in Grafana
|
|
||||||
labels: {} # Additional labels if needed
|
|
||||||
```
|
|
||||||
|
|
||||||
Then deploy or upgrade:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
helm upgrade --install nextcloud-mcp nextcloud-mcp-server \
|
|
||||||
--set dashboards.enabled=true
|
|
||||||
```
|
|
||||||
|
|
||||||
The dashboard will be automatically imported by Grafana if the sidecar is configured
|
|
||||||
to watch for ConfigMaps with label `grafana_dashboard: "1"`.
|
|
||||||
|
|
||||||
#### Option 2: Using kube-prometheus-stack
|
|
||||||
|
|
||||||
If using kube-prometheus-stack with Grafana sidecar enabled, the dashboard will be
|
|
||||||
automatically discovered and imported. Ensure your Grafana deployment has:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# kube-prometheus-stack values
|
|
||||||
grafana:
|
|
||||||
sidecar:
|
|
||||||
dashboards:
|
|
||||||
enabled: true
|
|
||||||
label: grafana_dashboard
|
|
||||||
folder: /tmp/dashboards
|
|
||||||
provider:
|
|
||||||
foldersFromFilesStructure: true
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Option 3: Manual ConfigMap Creation
|
|
||||||
|
|
||||||
For other Grafana setups, create a ConfigMap manually:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
kubectl create configmap nextcloud-mcp-dashboard \
|
|
||||||
--from-file=nextcloud-mcp-server.json \
|
|
||||||
-n monitoring
|
|
||||||
|
|
||||||
# Add sidecar discovery label
|
|
||||||
kubectl label configmap nextcloud-mcp-dashboard \
|
|
||||||
grafana_dashboard=1 \
|
|
||||||
-n monitoring
|
|
||||||
|
|
||||||
# Add folder annotation (annotations support spaces, unlike labels)
|
|
||||||
kubectl annotate configmap nextcloud-mcp-dashboard \
|
|
||||||
grafana_folder="Nextcloud MCP" \
|
|
||||||
-n monitoring
|
|
||||||
```
|
|
||||||
|
|
||||||
## Dashboard Variables
|
|
||||||
|
|
||||||
The dashboard includes four template variables for dynamic filtering:
|
|
||||||
|
|
||||||
- **datasource**: Select your Prometheus data source
|
|
||||||
- **namespace**: Filter metrics by Kubernetes namespace (supports "All")
|
|
||||||
- **pod**: Filter by specific pod(s) - multi-select enabled (supports "All")
|
|
||||||
- **interval**: Query interval for rate calculations (1m, 5m, 10m, 30m, 1h - default: 5m)
|
|
||||||
|
|
||||||
## Customization
|
|
||||||
|
|
||||||
You can customize the dashboard by:
|
|
||||||
|
|
||||||
1. Adjusting refresh rate (default: 30s)
|
|
||||||
2. Modifying time range (default: last 6 hours)
|
|
||||||
3. Adding new panels for specific metrics
|
|
||||||
4. Adjusting thresholds in existing panels
|
|
||||||
|
|
||||||
## Metrics Reference
|
|
||||||
|
|
||||||
All metrics are documented in `/docs/observability.md`. Key metric prefixes:
|
|
||||||
|
|
||||||
- `mcp_http_*` - HTTP server metrics
|
|
||||||
- `mcp_tool_*` - MCP tool invocation metrics
|
|
||||||
- `mcp_nextcloud_api_*` - Nextcloud API call metrics
|
|
||||||
- `mcp_oauth_*` - OAuth token validation metrics
|
|
||||||
- `mcp_vector_sync_*` - Vector database sync metrics
|
|
||||||
- `mcp_db_*` - Database operation metrics
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,208 +0,0 @@
|
|||||||
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
|
|
||||||
{{- else if eq .Values.auth.mode "multi-user-basic" }}
|
|
||||||
|
|
||||||
3. Multi-User BasicAuth Mode (Pass-Through):
|
|
||||||
- Users provide credentials via Authorization header
|
|
||||||
- Connected to: {{ .Values.nextcloud.host }}
|
|
||||||
{{- if .Values.auth.multiUserBasic.enableOfflineAccess }}
|
|
||||||
- Offline access: Enabled (background operations with app passwords)
|
|
||||||
- Token storage: {{ .Values.auth.multiUserBasic.tokenStorageDb }}
|
|
||||||
{{- else }}
|
|
||||||
- Offline access: Disabled (stateless pass-through)
|
|
||||||
{{- end }}
|
|
||||||
{{- else if eq .Values.auth.mode "login-flow" }}
|
|
||||||
|
|
||||||
3. Login Flow v2 Mode (Experimental, ADR-022):
|
|
||||||
- Server URL: {{ include "nextcloud-mcp-server.mcpServerUrl" . }}
|
|
||||||
- Connected to: {{ .Values.nextcloud.host }}
|
|
||||||
- Token storage: {{ .Values.auth.loginFlow.tokenStorageDb }}
|
|
||||||
|
|
||||||
Users authenticate via Nextcloud's native Login Flow v2 — no OAuth patches required.
|
|
||||||
Each user gets a per-device app password managed by the MCP server.
|
|
||||||
|
|
||||||
IMPORTANT: Login Flow v2 is experimental. See ADR-022 for details.
|
|
||||||
{{- 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 }}
|
|
||||||
|
|
||||||
{{- if .Values.semanticSearch.enabled }}
|
|
||||||
|
|
||||||
5. Semantic Search & Vector Capabilities:
|
|
||||||
- Semantic Search: Enabled
|
|
||||||
- Scan Interval: {{ .Values.semanticSearch.scanInterval }}s
|
|
||||||
- Processor Workers: {{ .Values.semanticSearch.processorWorkers }}
|
|
||||||
{{- if .Values.qdrant.enabled }}
|
|
||||||
- Qdrant: Deployed as subchart ({{ .Release.Name }}-qdrant:6333)
|
|
||||||
{{- else }}
|
|
||||||
- Qdrant: Not deployed (configure external instance)
|
|
||||||
{{- end }}
|
|
||||||
{{- if .Values.ollama.enabled }}
|
|
||||||
- Ollama: Deployed as subchart ({{ .Release.Name }}-ollama:11434)
|
|
||||||
- Embedding Model: {{ .Values.ollama.embeddingModel }}
|
|
||||||
{{- else if .Values.ollama.url }}
|
|
||||||
- Ollama: Using external instance at {{ .Values.ollama.url }}
|
|
||||||
- Embedding Model: {{ .Values.ollama.embeddingModel }}
|
|
||||||
{{- else if .Values.openai.enabled }}
|
|
||||||
- OpenAI: Enabled for embeddings
|
|
||||||
{{- else }}
|
|
||||||
- WARNING: No embedding provider configured (Ollama or OpenAI required)
|
|
||||||
{{- end }}
|
|
||||||
|
|
||||||
Check vector sync status:
|
|
||||||
kubectl --namespace {{ .Release.Namespace }} exec -it deploy/{{ include "nextcloud-mcp-server.fullname" . }} -- curl -s http://localhost:{{ include "nextcloud-mcp-server.port" . }}/user/page | grep "Vector Sync"
|
|
||||||
{{- end }}
|
|
||||||
|
|
||||||
{{- if .Values.dashboards.enabled }}
|
|
||||||
|
|
||||||
6. Grafana Dashboards:
|
|
||||||
- Dashboard provisioning: Enabled
|
|
||||||
- ConfigMap: {{ include "nextcloud-mcp-server.fullname" . }}-dashboard
|
|
||||||
- Grafana Folder: {{ .Values.dashboards.grafanaFolder }}
|
|
||||||
|
|
||||||
The dashboard will be automatically imported by Grafana if the sidecar is configured
|
|
||||||
to watch for ConfigMaps with label "grafana_dashboard: 1".
|
|
||||||
|
|
||||||
To manually import the dashboard:
|
|
||||||
kubectl --namespace {{ .Release.Namespace }} get configmap {{ include "nextcloud-mcp-server.fullname" . }}-dashboard -o jsonpath='{.data.nextcloud-mcp-server\.json}' | jq . > dashboard.json
|
|
||||||
|
|
||||||
Then import dashboard.json via Grafana UI (Dashboards → Import).
|
|
||||||
{{- else }}
|
|
||||||
|
|
||||||
6. Grafana Dashboards:
|
|
||||||
- Dashboard provisioning: Disabled
|
|
||||||
- To enable automatic dashboard provisioning, set: dashboards.enabled=true
|
|
||||||
|
|
||||||
Manual import option:
|
|
||||||
The dashboard JSON is available in the chart at charts/nextcloud-mcp-server/dashboards/nextcloud-mcp-server.json
|
|
||||||
{{- end }}
|
|
||||||
|
|
||||||
{{- $legacyMultiUserBasic := eq (include "nextcloud-mcp-server.legacyMultiUserBasicPersistence" .) "true" }}
|
|
||||||
{{- $legacyQdrant := eq (include "nextcloud-mcp-server.legacyQdrantPersistence" .) "true" }}
|
|
||||||
{{- if or $legacyMultiUserBasic $legacyQdrant }}
|
|
||||||
|
|
||||||
================================================================================
|
|
||||||
DEPRECATION WARNING
|
|
||||||
================================================================================
|
|
||||||
|
|
||||||
You are using deprecated persistence configuration that will be removed in a
|
|
||||||
future release. Your deployment will continue to work, but please migrate to
|
|
||||||
the new unified dataStorage configuration.
|
|
||||||
|
|
||||||
Deprecated settings detected:
|
|
||||||
{{- if $legacyMultiUserBasic }}
|
|
||||||
- auth.multiUserBasic.persistence.* (currently enabled)
|
|
||||||
{{- end }}
|
|
||||||
{{- if $legacyQdrant }}
|
|
||||||
- qdrant.localPersistence.* (currently enabled)
|
|
||||||
{{- end }}
|
|
||||||
|
|
||||||
To migrate, update your values.yaml:
|
|
||||||
|
|
||||||
dataStorage:
|
|
||||||
enabled: true
|
|
||||||
{{- if $legacyMultiUserBasic }}
|
|
||||||
size: {{ .Values.auth.multiUserBasic.persistence.size }}
|
|
||||||
{{- else if $legacyQdrant }}
|
|
||||||
size: {{ .Values.qdrant.localPersistence.size }}
|
|
||||||
{{- end }}
|
|
||||||
# storageClass: "" # Optional: specify storage class
|
|
||||||
# existingClaim: "" # Optional: use existing PVC to preserve data
|
|
||||||
|
|
||||||
After migrating, remove the deprecated settings:
|
|
||||||
{{- if $legacyMultiUserBasic }}
|
|
||||||
- auth.multiUserBasic.persistence.enabled
|
|
||||||
- auth.multiUserBasic.persistence.size
|
|
||||||
- auth.multiUserBasic.persistence.storageClass
|
|
||||||
- auth.multiUserBasic.persistence.accessMode
|
|
||||||
{{- end }}
|
|
||||||
{{- if $legacyQdrant }}
|
|
||||||
- qdrant.localPersistence.enabled
|
|
||||||
- qdrant.localPersistence.size
|
|
||||||
- qdrant.localPersistence.storageClass
|
|
||||||
- qdrant.localPersistence.accessMode
|
|
||||||
{{- end }}
|
|
||||||
|
|
||||||
================================================================================
|
|
||||||
{{- end }}
|
|
||||||
|
|
||||||
Deployed version:
|
|
||||||
- Chart: {{ .Chart.Version }}
|
|
||||||
- App: {{ .Chart.AppVersion }}
|
|
||||||
|
|
||||||
Full changelog: https://github.com/cbcoutinho/nextcloud-mcp-server/blob/master/charts/nextcloud-mcp-server/CHANGELOG.md
|
|
||||||
|
|
||||||
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 }}
|
|
||||||
@@ -1,237 +0,0 @@
|
|||||||
{{/*
|
|
||||||
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 multi-user basic auth
|
|
||||||
*/}}
|
|
||||||
{{- define "nextcloud-mcp-server.multiUserBasicSecretName" -}}
|
|
||||||
{{- if .Values.auth.multiUserBasic.existingSecret }}
|
|
||||||
{{- .Values.auth.multiUserBasic.existingSecret }}
|
|
||||||
{{- else }}
|
|
||||||
{{- include "nextcloud-mcp-server.fullname" . }}-multi-user-basic
|
|
||||||
{{- end }}
|
|
||||||
{{- end }}
|
|
||||||
|
|
||||||
{{/*
|
|
||||||
Create the name of the PVC to use for multi-user basic token storage
|
|
||||||
*/}}
|
|
||||||
{{- define "nextcloud-mcp-server.multiUserBasicPvcName" -}}
|
|
||||||
{{- if .Values.auth.multiUserBasic.persistence.existingClaim }}
|
|
||||||
{{- .Values.auth.multiUserBasic.persistence.existingClaim }}
|
|
||||||
{{- else }}
|
|
||||||
{{- include "nextcloud-mcp-server.fullname" . }}-token-storage
|
|
||||||
{{- 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 secret to use for Login Flow v2
|
|
||||||
*/}}
|
|
||||||
{{- define "nextcloud-mcp-server.loginFlowSecretName" -}}
|
|
||||||
{{- if .Values.auth.loginFlow.existingSecret }}
|
|
||||||
{{- .Values.auth.loginFlow.existingSecret }}
|
|
||||||
{{- else }}
|
|
||||||
{{- include "nextcloud-mcp-server.fullname" . }}-login-flow
|
|
||||||
{{- 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 }}
|
|
||||||
|
|
||||||
{{/*
|
|
||||||
Create the name of the PVC to use for Qdrant local persistent storage
|
|
||||||
*/}}
|
|
||||||
{{- define "nextcloud-mcp-server.qdrantPvcName" -}}
|
|
||||||
{{- if .Values.qdrant.localPersistence.existingClaim }}
|
|
||||||
{{- .Values.qdrant.localPersistence.existingClaim }}
|
|
||||||
{{- else }}
|
|
||||||
{{- include "nextcloud-mcp-server.fullname" . }}-qdrant-data
|
|
||||||
{{- end }}
|
|
||||||
{{- end }}
|
|
||||||
|
|
||||||
{{/*
|
|
||||||
Create the name of the PVC to use for /app/data storage
|
|
||||||
*/}}
|
|
||||||
{{- define "nextcloud-mcp-server.dataStoragePvcName" -}}
|
|
||||||
{{- if .Values.dataStorage.existingClaim }}
|
|
||||||
{{- .Values.dataStorage.existingClaim }}
|
|
||||||
{{- else }}
|
|
||||||
{{- include "nextcloud-mcp-server.fullname" . }}-data-storage
|
|
||||||
{{- end }}
|
|
||||||
{{- end }}
|
|
||||||
|
|
||||||
{{/*
|
|
||||||
Determine if data storage PVC should be enabled (backward compatible)
|
|
||||||
Checks new dataStorage.enabled OR legacy persistence configs
|
|
||||||
*/}}
|
|
||||||
{{- define "nextcloud-mcp-server.dataStorageEnabled" -}}
|
|
||||||
{{- if .Values.dataStorage.enabled -}}
|
|
||||||
true
|
|
||||||
{{- else if and (eq .Values.auth.mode "multi-user-basic") .Values.auth.multiUserBasic.enableOfflineAccess .Values.auth.multiUserBasic.persistence.enabled -}}
|
|
||||||
true
|
|
||||||
{{- else if eq .Values.auth.mode "login-flow" -}}
|
|
||||||
true
|
|
||||||
{{- else if and (eq .Values.qdrant.mode "persistent") .Values.qdrant.localPersistence.enabled -}}
|
|
||||||
true
|
|
||||||
{{- else -}}
|
|
||||||
false
|
|
||||||
{{- end -}}
|
|
||||||
{{- end }}
|
|
||||||
|
|
||||||
{{/*
|
|
||||||
Check if legacy multi-user-basic persistence config is being used
|
|
||||||
*/}}
|
|
||||||
{{- define "nextcloud-mcp-server.legacyMultiUserBasicPersistence" -}}
|
|
||||||
{{- if and (eq .Values.auth.mode "multi-user-basic") .Values.auth.multiUserBasic.enableOfflineAccess .Values.auth.multiUserBasic.persistence.enabled (not .Values.dataStorage.enabled) -}}
|
|
||||||
true
|
|
||||||
{{- else -}}
|
|
||||||
false
|
|
||||||
{{- end -}}
|
|
||||||
{{- end }}
|
|
||||||
|
|
||||||
{{/*
|
|
||||||
Check if legacy qdrant persistence config is being used
|
|
||||||
*/}}
|
|
||||||
{{- define "nextcloud-mcp-server.legacyQdrantPersistence" -}}
|
|
||||||
{{- if and (eq .Values.qdrant.mode "persistent") .Values.qdrant.localPersistence.enabled (not .Values.dataStorage.enabled) -}}
|
|
||||||
true
|
|
||||||
{{- else -}}
|
|
||||||
false
|
|
||||||
{{- end -}}
|
|
||||||
{{- end }}
|
|
||||||
|
|
||||||
{{/*
|
|
||||||
Return the MCP server port
|
|
||||||
*/}}
|
|
||||||
{{- define "nextcloud-mcp-server.port" -}}
|
|
||||||
{{- .Values.mcp.port }}
|
|
||||||
{{- end }}
|
|
||||||
|
|
||||||
{{/*
|
|
||||||
Return the image tag (always uses chart appVersion)
|
|
||||||
*/}}
|
|
||||||
{{- define "nextcloud-mcp-server.imageTag" -}}
|
|
||||||
{{- .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 }}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
{{- if .Values.dashboards.enabled }}
|
|
||||||
apiVersion: v1
|
|
||||||
kind: ConfigMap
|
|
||||||
metadata:
|
|
||||||
name: {{ include "nextcloud-mcp-server.fullname" . }}-dashboard
|
|
||||||
namespace: {{ .Release.Namespace }}
|
|
||||||
labels:
|
|
||||||
{{- include "nextcloud-mcp-server.labels" . | nindent 4 }}
|
|
||||||
{{- with .Values.dashboards.labels }}
|
|
||||||
{{- toYaml . | nindent 4 }}
|
|
||||||
{{- end }}
|
|
||||||
# Grafana sidecar discovery label
|
|
||||||
grafana_dashboard: "1"
|
|
||||||
annotations:
|
|
||||||
{{- with .Values.dashboards.annotations }}
|
|
||||||
{{- toYaml . | nindent 4 }}
|
|
||||||
{{- end }}
|
|
||||||
# Grafana folder name (annotations support spaces, unlike labels)
|
|
||||||
{{- if .Values.dashboards.grafanaFolder }}
|
|
||||||
grafana_folder: {{ .Values.dashboards.grafanaFolder | quote }}
|
|
||||||
{{- end }}
|
|
||||||
data:
|
|
||||||
nextcloud-mcp-server.json: |-
|
|
||||||
{{ .Files.Get "dashboards/nextcloud-mcp-server.json" | indent 4 }}
|
|
||||||
{{- end }}
|
|
||||||
@@ -1,340 +0,0 @@
|
|||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: {{ include "nextcloud-mcp-server.fullname" . }}
|
|
||||||
labels:
|
|
||||||
{{- include "nextcloud-mcp-server.labels" . | nindent 4 }}
|
|
||||||
spec:
|
|
||||||
strategy:
|
|
||||||
type: Recreate
|
|
||||||
{{- 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 or (eq .Values.auth.mode "oauth") (eq .Values.auth.mode "login-flow") }}
|
|
||||||
- "--oauth"
|
|
||||||
{{- end }}
|
|
||||||
{{- if eq .Values.auth.mode "oauth" }}
|
|
||||||
- "--oauth-token-type"
|
|
||||||
- "{{ .Values.auth.oauth.tokenType }}"
|
|
||||||
{{- end }}
|
|
||||||
{{- with .Values.mcp.extraArgs }}
|
|
||||||
{{- toYaml . | nindent 12 }}
|
|
||||||
{{- end }}
|
|
||||||
ports:
|
|
||||||
- name: http
|
|
||||||
containerPort: {{ include "nextcloud-mcp-server.port" . }}
|
|
||||||
protocol: TCP
|
|
||||||
{{- if .Values.observability.metrics.enabled }}
|
|
||||||
- name: metrics
|
|
||||||
containerPort: {{ .Values.observability.metrics.port }}
|
|
||||||
protocol: TCP
|
|
||||||
{{- end }}
|
|
||||||
env:
|
|
||||||
# Nextcloud connection
|
|
||||||
- name: NEXTCLOUD_HOST
|
|
||||||
value: {{ .Values.nextcloud.host | quote }}
|
|
||||||
{{- if eq .Values.auth.mode "basic" }}
|
|
||||||
# Basic auth mode (single-user)
|
|
||||||
- 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 "multi-user-basic" }}
|
|
||||||
# Multi-user BasicAuth mode (pass-through)
|
|
||||||
- name: ENABLE_MULTI_USER_BASIC_AUTH
|
|
||||||
value: "true"
|
|
||||||
- name: NEXTCLOUD_MCP_SERVER_URL
|
|
||||||
value: {{ include "nextcloud-mcp-server.mcpServerUrl" . | quote }}
|
|
||||||
- name: NEXTCLOUD_PUBLIC_ISSUER_URL
|
|
||||||
value: {{ include "nextcloud-mcp-server.publicIssuerUrl" . | quote }}
|
|
||||||
{{- if .Values.auth.multiUserBasic.enableOfflineAccess }}
|
|
||||||
# Background operations with app passwords (replaces deprecated ENABLE_OFFLINE_ACCESS)
|
|
||||||
- name: ENABLE_BACKGROUND_OPERATIONS
|
|
||||||
value: "true"
|
|
||||||
- name: TOKEN_STORAGE_DB
|
|
||||||
value: {{ .Values.auth.multiUserBasic.tokenStorageDb | quote }}
|
|
||||||
- name: TOKEN_ENCRYPTION_KEY
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: {{ include "nextcloud-mcp-server.multiUserBasicSecretName" . }}
|
|
||||||
key: {{ .Values.auth.multiUserBasic.tokenEncryptionKeyKey }}
|
|
||||||
- name: NEXTCLOUD_OIDC_SCOPES
|
|
||||||
value: {{ .Values.auth.multiUserBasic.scopes | quote }}
|
|
||||||
{{- if or .Values.auth.multiUserBasic.clientId .Values.auth.multiUserBasic.existingSecret }}
|
|
||||||
# Static OAuth credentials (optional - uses DCR if not provided)
|
|
||||||
- name: NEXTCLOUD_OIDC_CLIENT_ID
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: {{ include "nextcloud-mcp-server.multiUserBasicSecretName" . }}
|
|
||||||
key: {{ .Values.auth.multiUserBasic.clientIdKey }}
|
|
||||||
- name: NEXTCLOUD_OIDC_CLIENT_SECRET
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: {{ include "nextcloud-mcp-server.multiUserBasicSecretName" . }}
|
|
||||||
key: {{ .Values.auth.multiUserBasic.clientSecretKey }}
|
|
||||||
{{- end }}
|
|
||||||
{{- end }}
|
|
||||||
{{- 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_SCOPES
|
|
||||||
value: {{ .Values.auth.oauth.scopes | quote }}
|
|
||||||
{{- if or .Values.auth.oauth.clientId .Values.auth.oauth.existingSecret }}
|
|
||||||
- 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 }}
|
|
||||||
{{- else if eq .Values.auth.mode "login-flow" }}
|
|
||||||
# Login Flow v2 mode (ADR-022)
|
|
||||||
- name: ENABLE_LOGIN_FLOW
|
|
||||||
value: "true"
|
|
||||||
- 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: TOKEN_STORAGE_DB
|
|
||||||
value: {{ .Values.auth.loginFlow.tokenStorageDb | quote }}
|
|
||||||
- name: TOKEN_ENCRYPTION_KEY
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: {{ include "nextcloud-mcp-server.loginFlowSecretName" . }}
|
|
||||||
key: {{ .Values.auth.loginFlow.tokenEncryptionKeyKey }}
|
|
||||||
{{- 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 }}
|
|
||||||
# Semantic Search (replaces deprecated VECTOR_SYNC_ENABLED)
|
|
||||||
- name: ENABLE_SEMANTIC_SEARCH
|
|
||||||
value: {{ .Values.semanticSearch.enabled | quote }}
|
|
||||||
{{- if .Values.semanticSearch.enabled }}
|
|
||||||
- name: VECTOR_SYNC_SCAN_INTERVAL
|
|
||||||
value: {{ .Values.semanticSearch.scanInterval | quote }}
|
|
||||||
- name: VECTOR_SYNC_PROCESSOR_WORKERS
|
|
||||||
value: {{ .Values.semanticSearch.processorWorkers | quote }}
|
|
||||||
- name: VECTOR_SYNC_QUEUE_MAX_SIZE
|
|
||||||
value: {{ .Values.semanticSearch.queueMaxSize | quote }}
|
|
||||||
{{- end }}
|
|
||||||
# Document Chunking (always set, used by vector sync processor)
|
|
||||||
- name: DOCUMENT_CHUNK_SIZE
|
|
||||||
value: {{ .Values.documentChunking.chunkSize | quote }}
|
|
||||||
- name: DOCUMENT_CHUNK_OVERLAP
|
|
||||||
value: {{ .Values.documentChunking.chunkOverlap | quote }}
|
|
||||||
# Qdrant Vector Database
|
|
||||||
{{- if eq .Values.qdrant.mode "network" }}
|
|
||||||
# Network mode: Use dedicated Qdrant service
|
|
||||||
{{- if .Values.qdrant.networkMode.deploySubchart }}
|
|
||||||
- name: QDRANT_URL
|
|
||||||
value: "http://{{ .Release.Name }}-qdrant:6333"
|
|
||||||
{{- else if .Values.qdrant.networkMode.externalUrl }}
|
|
||||||
- name: QDRANT_URL
|
|
||||||
value: {{ .Values.qdrant.networkMode.externalUrl | quote }}
|
|
||||||
{{- end }}
|
|
||||||
{{- if or .Values.qdrant.networkMode.apiKey .Values.qdrant.networkMode.existingSecret }}
|
|
||||||
- name: QDRANT_API_KEY
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: {{ .Values.qdrant.networkMode.existingSecret | default (printf "%s-qdrant" .Release.Name) }}
|
|
||||||
key: {{ .Values.qdrant.networkMode.secretKey }}
|
|
||||||
{{- end }}
|
|
||||||
{{- else if eq .Values.qdrant.mode "persistent" }}
|
|
||||||
# Persistent local mode: File-based storage
|
|
||||||
- name: QDRANT_LOCATION
|
|
||||||
value: {{ .Values.qdrant.localPersistence.dataPath | quote }}
|
|
||||||
{{- else }}
|
|
||||||
# In-memory mode (default): Ephemeral storage
|
|
||||||
- name: QDRANT_LOCATION
|
|
||||||
value: ":memory:"
|
|
||||||
{{- end }}
|
|
||||||
- name: QDRANT_COLLECTION
|
|
||||||
value: {{ .Values.qdrant.collection | quote }}
|
|
||||||
# Ollama Embedding Service
|
|
||||||
{{- if or .Values.ollama.enabled .Values.ollama.url }}
|
|
||||||
- name: OLLAMA_BASE_URL
|
|
||||||
value: {{ .Values.ollama.url | default (printf "http://%s-ollama:11434" .Release.Name) | quote }}
|
|
||||||
- name: OLLAMA_EMBEDDING_MODEL
|
|
||||||
value: {{ .Values.ollama.embeddingModel | quote }}
|
|
||||||
- name: OLLAMA_VERIFY_SSL
|
|
||||||
value: {{ .Values.ollama.verifySsl | quote }}
|
|
||||||
{{- end }}
|
|
||||||
# OpenAI Embedding Provider (alternative to Ollama)
|
|
||||||
{{- if .Values.openai.enabled }}
|
|
||||||
- name: OPENAI_API_KEY
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: {{ .Values.openai.existingSecret | default (printf "%s-openai" (include "nextcloud-mcp-server.fullname" .)) }}
|
|
||||||
key: {{ .Values.openai.secretKey }}
|
|
||||||
{{- if .Values.openai.baseUrl }}
|
|
||||||
- name: OPENAI_BASE_URL
|
|
||||||
value: {{ .Values.openai.baseUrl | quote }}
|
|
||||||
{{- end }}
|
|
||||||
{{- end }}
|
|
||||||
# Observability
|
|
||||||
- name: METRICS_ENABLED
|
|
||||||
value: {{ .Values.observability.metrics.enabled | quote }}
|
|
||||||
- name: METRICS_PORT
|
|
||||||
value: {{ .Values.observability.metrics.port | quote }}
|
|
||||||
{{- if .Values.observability.tracing.enabled }}
|
|
||||||
- name: OTEL_EXPORTER_OTLP_ENDPOINT
|
|
||||||
value: {{ .Values.observability.tracing.endpoint | quote }}
|
|
||||||
- name: OTEL_SERVICE_NAME
|
|
||||||
value: {{ .Values.observability.tracing.serviceName | quote }}
|
|
||||||
- name: OTEL_TRACES_SAMPLER_ARG
|
|
||||||
value: {{ .Values.observability.tracing.samplingRate | quote }}
|
|
||||||
{{- end }}
|
|
||||||
- name: LOG_FORMAT
|
|
||||||
value: {{ .Values.observability.logging.format | quote }}
|
|
||||||
- name: LOG_LEVEL
|
|
||||||
value: {{ .Values.observability.logging.level | quote }}
|
|
||||||
- name: LOG_INCLUDE_TRACE_CONTEXT
|
|
||||||
value: {{ .Values.observability.logging.includeTraceContext | quote }}
|
|
||||||
{{- 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 or (and (eq .Values.auth.mode "oauth") .Values.auth.oauth.persistence.enabled) (eq .Values.auth.mode "login-flow") }}
|
|
||||||
- name: oauth-storage
|
|
||||||
mountPath: /app/.oauth
|
|
||||||
{{- end }}
|
|
||||||
- name: data-storage
|
|
||||||
mountPath: /app/data
|
|
||||||
{{- with .Values.volumeMounts }}
|
|
||||||
{{- toYaml . | nindent 12 }}
|
|
||||||
{{- end }}
|
|
||||||
volumes:
|
|
||||||
- name: tmp
|
|
||||||
emptyDir: {}
|
|
||||||
{{- if or (and (eq .Values.auth.mode "oauth") .Values.auth.oauth.persistence.enabled) (eq .Values.auth.mode "login-flow") }}
|
|
||||||
- name: oauth-storage
|
|
||||||
persistentVolumeClaim:
|
|
||||||
claimName: {{ include "nextcloud-mcp-server.oauthPvcName" . }}
|
|
||||||
{{- end }}
|
|
||||||
- name: data-storage
|
|
||||||
{{- if eq (include "nextcloud-mcp-server.dataStorageEnabled" .) "true" }}
|
|
||||||
persistentVolumeClaim:
|
|
||||||
claimName: {{ include "nextcloud-mcp-server.dataStoragePvcName" . }}
|
|
||||||
{{- else }}
|
|
||||||
emptyDir: {}
|
|
||||||
{{- 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 }}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
{{- 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 }}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
{{- 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 }}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{{- if and .Values.openai.enabled (not .Values.openai.existingSecret) }}
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Secret
|
|
||||||
metadata:
|
|
||||||
name: {{ include "nextcloud-mcp-server.fullname" . }}-openai
|
|
||||||
labels:
|
|
||||||
{{- include "nextcloud-mcp-server.labels" . | nindent 4 }}
|
|
||||||
type: Opaque
|
|
||||||
data:
|
|
||||||
{{ .Values.openai.secretKey }}: {{ .Values.openai.apiKey | b64enc | quote }}
|
|
||||||
{{- end }}
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
{{- if and .Values.observability.metrics.enabled .Values.prometheusRule.enabled }}
|
|
||||||
apiVersion: monitoring.coreos.com/v1
|
|
||||||
kind: PrometheusRule
|
|
||||||
metadata:
|
|
||||||
name: {{ include "nextcloud-mcp-server.fullname" . }}
|
|
||||||
namespace: {{ .Release.Namespace }}
|
|
||||||
labels:
|
|
||||||
{{- include "nextcloud-mcp-server.labels" . | nindent 4 }}
|
|
||||||
{{- with .Values.prometheusRule.labels }}
|
|
||||||
{{- toYaml . | nindent 4 }}
|
|
||||||
{{- end }}
|
|
||||||
spec:
|
|
||||||
groups:
|
|
||||||
- name: nextcloud-mcp-server.critical
|
|
||||||
interval: 30s
|
|
||||||
rules:
|
|
||||||
- alert: NextcloudMCPServerDown
|
|
||||||
expr: up{job="{{ include "nextcloud-mcp-server.fullname" . }}"} == 0
|
|
||||||
for: 5m
|
|
||||||
labels:
|
|
||||||
severity: critical
|
|
||||||
annotations:
|
|
||||||
summary: "Nextcloud MCP Server is down"
|
|
||||||
description: "{{ `{{` }} $labels.pod {{ `}}` }} has been down for more than 5 minutes."
|
|
||||||
|
|
||||||
- alert: NextcloudMCPHighErrorRate
|
|
||||||
expr: |
|
|
||||||
sum(rate(mcp_http_requests_total{status_code=~"5..", job="{{ include "nextcloud-mcp-server.fullname" . }}"}[5m]))
|
|
||||||
/ sum(rate(mcp_http_requests_total{job="{{ include "nextcloud-mcp-server.fullname" . }}"}[5m])) > 0.05
|
|
||||||
for: 5m
|
|
||||||
labels:
|
|
||||||
severity: critical
|
|
||||||
annotations:
|
|
||||||
summary: "High error rate on Nextcloud MCP Server"
|
|
||||||
description: "Error rate is {{ `{{` }} printf \"%.2f%%\" (mul $value 100) {{ `}}` }} (threshold: 5%)"
|
|
||||||
|
|
||||||
- alert: NextcloudMCPHighLatency
|
|
||||||
expr: |
|
|
||||||
histogram_quantile(0.95,
|
|
||||||
sum(rate(mcp_http_request_duration_seconds_bucket{job="{{ include "nextcloud-mcp-server.fullname" . }}"}[5m])) by (le, endpoint)
|
|
||||||
) > 1
|
|
||||||
for: 5m
|
|
||||||
labels:
|
|
||||||
severity: critical
|
|
||||||
annotations:
|
|
||||||
summary: "High latency on Nextcloud MCP Server"
|
|
||||||
description: "P95 latency is {{ `{{` }} printf \"%.2fs\" $value {{ `}}` }} on {{ `{{` }} $labels.endpoint {{ `}}` }} (threshold: 1s)"
|
|
||||||
|
|
||||||
- alert: NextcloudMCPDependencyDown
|
|
||||||
expr: mcp_dependency_health{job="{{ include "nextcloud-mcp-server.fullname" . }}"} == 0
|
|
||||||
for: 2m
|
|
||||||
labels:
|
|
||||||
severity: critical
|
|
||||||
annotations:
|
|
||||||
summary: "Nextcloud MCP dependency is down"
|
|
||||||
description: "Dependency {{ `{{` }} $labels.dependency {{ `}}` }} has been down for more than 2 minutes."
|
|
||||||
|
|
||||||
- name: nextcloud-mcp-server.warning
|
|
||||||
interval: 30s
|
|
||||||
rules:
|
|
||||||
- alert: NextcloudMCPTokenValidationErrors
|
|
||||||
expr: |
|
|
||||||
sum(rate(mcp_oauth_token_validations_total{result="error", job="{{ include "nextcloud-mcp-server.fullname" . }}"}[10m]))
|
|
||||||
/ sum(rate(mcp_oauth_token_validations_total{job="{{ include "nextcloud-mcp-server.fullname" . }}"}[10m])) > 0.01
|
|
||||||
for: 10m
|
|
||||||
labels:
|
|
||||||
severity: warning
|
|
||||||
annotations:
|
|
||||||
summary: "High token validation error rate"
|
|
||||||
description: "Token validation error rate is {{ `{{` }} printf \"%.2f%%\" (mul $value 100) {{ `}}` }} (threshold: 1%)"
|
|
||||||
|
|
||||||
- alert: NextcloudMCPVectorSyncQueueHigh
|
|
||||||
expr: mcp_vector_sync_queue_size{job="{{ include "nextcloud-mcp-server.fullname" . }}"} > 100
|
|
||||||
for: 15m
|
|
||||||
labels:
|
|
||||||
severity: warning
|
|
||||||
annotations:
|
|
||||||
summary: "Vector sync queue is high"
|
|
||||||
description: "Vector sync queue size is {{ `{{` }} $value {{ `}}` }} (threshold: 100)"
|
|
||||||
|
|
||||||
- alert: NextcloudMCPQdrantSlowQueries
|
|
||||||
expr: |
|
|
||||||
histogram_quantile(0.95,
|
|
||||||
sum(rate(mcp_db_operation_duration_seconds_bucket{db="qdrant", job="{{ include "nextcloud-mcp-server.fullname" . }}"}[10m])) by (le)
|
|
||||||
) > 0.5
|
|
||||||
for: 10m
|
|
||||||
labels:
|
|
||||||
severity: warning
|
|
||||||
annotations:
|
|
||||||
summary: "Qdrant queries are slow"
|
|
||||||
description: "P95 Qdrant query latency is {{ `{{` }} printf \"%.2fs\" $value {{ `}}` }} (threshold: 0.5s)"
|
|
||||||
{{- end }}
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
{{- 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 }}
|
|
||||||
---
|
|
||||||
{{- if eq .Values.auth.mode "login-flow" }}
|
|
||||||
apiVersion: v1
|
|
||||||
kind: PersistentVolumeClaim
|
|
||||||
metadata:
|
|
||||||
name: {{ include "nextcloud-mcp-server.fullname" . }}-oauth-storage
|
|
||||||
labels:
|
|
||||||
{{- include "nextcloud-mcp-server.labels" . | nindent 4 }}
|
|
||||||
spec:
|
|
||||||
accessModes:
|
|
||||||
- ReadWriteOnce
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
storage: 100Mi
|
|
||||||
{{- end }}
|
|
||||||
---
|
|
||||||
{{- if and (eq (include "nextcloud-mcp-server.dataStorageEnabled" .) "true") (not .Values.dataStorage.existingClaim) }}
|
|
||||||
{{- $legacyMultiUserBasic := eq (include "nextcloud-mcp-server.legacyMultiUserBasicPersistence" .) "true" }}
|
|
||||||
{{- $legacyQdrant := eq (include "nextcloud-mcp-server.legacyQdrantPersistence" .) "true" }}
|
|
||||||
{{- $accessMode := .Values.dataStorage.accessMode }}
|
|
||||||
{{- $storageClass := .Values.dataStorage.storageClass }}
|
|
||||||
{{- $size := .Values.dataStorage.size }}
|
|
||||||
{{- if $legacyMultiUserBasic }}
|
|
||||||
{{- $accessMode = .Values.auth.multiUserBasic.persistence.accessMode }}
|
|
||||||
{{- $storageClass = .Values.auth.multiUserBasic.persistence.storageClass }}
|
|
||||||
{{- $size = .Values.auth.multiUserBasic.persistence.size }}
|
|
||||||
{{- else if $legacyQdrant }}
|
|
||||||
{{- $accessMode = .Values.qdrant.localPersistence.accessMode }}
|
|
||||||
{{- $storageClass = .Values.qdrant.localPersistence.storageClass }}
|
|
||||||
{{- $size = .Values.qdrant.localPersistence.size }}
|
|
||||||
{{- end }}
|
|
||||||
apiVersion: v1
|
|
||||||
kind: PersistentVolumeClaim
|
|
||||||
metadata:
|
|
||||||
name: {{ include "nextcloud-mcp-server.fullname" . }}-data-storage
|
|
||||||
labels:
|
|
||||||
{{- include "nextcloud-mcp-server.labels" . | nindent 4 }}
|
|
||||||
spec:
|
|
||||||
accessModes:
|
|
||||||
- {{ $accessMode }}
|
|
||||||
{{- if $storageClass }}
|
|
||||||
storageClassName: {{ $storageClass }}
|
|
||||||
{{- end }}
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
storage: {{ $size }}
|
|
||||||
{{- end }}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
{{- 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 "multi-user-basic" }}
|
|
||||||
{{- if and .Values.auth.multiUserBasic.enableOfflineAccess (not .Values.auth.multiUserBasic.existingSecret) }}
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Secret
|
|
||||||
metadata:
|
|
||||||
name: {{ include "nextcloud-mcp-server.fullname" . }}-multi-user-basic
|
|
||||||
labels:
|
|
||||||
{{- include "nextcloud-mcp-server.labels" . | nindent 4 }}
|
|
||||||
type: Opaque
|
|
||||||
data:
|
|
||||||
{{ .Values.auth.multiUserBasic.tokenEncryptionKeyKey }}: {{ .Values.auth.multiUserBasic.tokenEncryptionKey | b64enc | quote }}
|
|
||||||
{{- if .Values.auth.multiUserBasic.clientId }}
|
|
||||||
{{ .Values.auth.multiUserBasic.clientIdKey }}: {{ .Values.auth.multiUserBasic.clientId | b64enc | quote }}
|
|
||||||
{{ .Values.auth.multiUserBasic.clientSecretKey }}: {{ .Values.auth.multiUserBasic.clientSecret | b64enc | quote }}
|
|
||||||
{{- end }}
|
|
||||||
{{- 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 }}
|
|
||||||
---
|
|
||||||
{{- if eq .Values.auth.mode "login-flow" }}
|
|
||||||
{{- if not .Values.auth.loginFlow.existingSecret }}
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Secret
|
|
||||||
metadata:
|
|
||||||
name: {{ include "nextcloud-mcp-server.fullname" . }}-login-flow
|
|
||||||
labels:
|
|
||||||
{{- include "nextcloud-mcp-server.labels" . | nindent 4 }}
|
|
||||||
type: Opaque
|
|
||||||
data:
|
|
||||||
{{ .Values.auth.loginFlow.tokenEncryptionKeyKey }}: {{ .Values.auth.loginFlow.tokenEncryptionKey | b64enc | quote }}
|
|
||||||
{{- end }}
|
|
||||||
{{- end }}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
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
|
|
||||||
{{- if .Values.observability.metrics.enabled }}
|
|
||||||
- port: {{ .Values.observability.metrics.port }}
|
|
||||||
targetPort: metrics
|
|
||||||
protocol: TCP
|
|
||||||
name: metrics
|
|
||||||
{{- end }}
|
|
||||||
selector:
|
|
||||||
{{- include "nextcloud-mcp-server.selectorLabels" . | nindent 4 }}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
{{- 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 }}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
{{- if and .Values.observability.metrics.enabled .Values.serviceMonitor.enabled }}
|
|
||||||
apiVersion: monitoring.coreos.com/v1
|
|
||||||
kind: ServiceMonitor
|
|
||||||
metadata:
|
|
||||||
name: {{ include "nextcloud-mcp-server.fullname" . }}
|
|
||||||
namespace: {{ .Release.Namespace }}
|
|
||||||
labels:
|
|
||||||
{{- include "nextcloud-mcp-server.labels" . | nindent 4 }}
|
|
||||||
{{- with .Values.serviceMonitor.labels }}
|
|
||||||
{{- toYaml . | nindent 4 }}
|
|
||||||
{{- end }}
|
|
||||||
spec:
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
{{- include "nextcloud-mcp-server.selectorLabels" . | nindent 6 }}
|
|
||||||
endpoints:
|
|
||||||
- port: metrics
|
|
||||||
path: {{ .Values.observability.metrics.path }}
|
|
||||||
interval: {{ .Values.serviceMonitor.interval }}
|
|
||||||
scrapeTimeout: {{ .Values.serviceMonitor.scrapeTimeout }}
|
|
||||||
scheme: http
|
|
||||||
relabelings:
|
|
||||||
# Add namespace label
|
|
||||||
- sourceLabels: [__meta_kubernetes_namespace]
|
|
||||||
targetLabel: namespace
|
|
||||||
# Add pod label
|
|
||||||
- sourceLabels: [__meta_kubernetes_pod_name]
|
|
||||||
targetLabel: pod
|
|
||||||
# Add service label
|
|
||||||
- sourceLabels: [__meta_kubernetes_service_name]
|
|
||||||
targetLabel: service
|
|
||||||
{{- end }}
|
|
||||||
@@ -1,553 +0,0 @@
|
|||||||
# 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
|
|
||||||
# Image tag is automatically set to chart appVersion
|
|
||||||
|
|
||||||
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 browser-accessible OAuth authorization endpoints (OAuth mode only)
|
|
||||||
# ONLY used to make authorization endpoints accessible to users' browsers
|
|
||||||
# All server-to-server communication (token endpoint, JWKS, introspection, userinfo)
|
|
||||||
# uses URLs from OIDC discovery without any rewriting
|
|
||||||
#
|
|
||||||
# Use case: When MCP server accesses Nextcloud at one URL but browsers need a different
|
|
||||||
# public URL for OAuth login (e.g., server uses internal DNS, browsers use public domain)
|
|
||||||
#
|
|
||||||
# If not specified, defaults to nextcloud.host (works when MCP server and browsers
|
|
||||||
# both access Nextcloud at the same URL)
|
|
||||||
# Example: https://cloud.example.com
|
|
||||||
publicIssuerUrl: ""
|
|
||||||
|
|
||||||
# Authentication configuration
|
|
||||||
# Choose one mode: "basic", "multi-user-basic", "oauth", or "login-flow"
|
|
||||||
auth:
|
|
||||||
# Authentication mode: "basic", "multi-user-basic", "oauth", or "login-flow"
|
|
||||||
# basic: Single-user with username/password (recommended for personal use)
|
|
||||||
# multi-user-basic: Multi-user with BasicAuth pass-through (credentials in request headers)
|
|
||||||
# oauth: Uses OAuth2/OIDC (experimental, requires patches)
|
|
||||||
# login-flow: Multi-user via Nextcloud Login Flow v2 (experimental, ADR-022)
|
|
||||||
mode: basic
|
|
||||||
|
|
||||||
# Basic authentication settings (single-user mode)
|
|
||||||
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"
|
|
||||||
|
|
||||||
# Multi-user BasicAuth settings (pass-through mode)
|
|
||||||
# Users provide credentials in request headers (Authorization: Basic ...)
|
|
||||||
# Server optionally stores app passwords for background operations
|
|
||||||
multiUserBasic:
|
|
||||||
# Enable offline access (background operations using app passwords via Astrolabe)
|
|
||||||
# When enabled, requires token encryption key. OAuth client credentials are optional (uses DCR if not provided)
|
|
||||||
enableOfflineAccess: false
|
|
||||||
# Token encryption key (required if enableOfflineAccess: true, ignored if existingSecret is set)
|
|
||||||
# Generate with: python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
|
|
||||||
tokenEncryptionKey: ""
|
|
||||||
# Token storage database path
|
|
||||||
tokenStorageDb: "/app/data/tokens.db"
|
|
||||||
# OAuth client credentials (optional - uses Dynamic Client Registration if not provided)
|
|
||||||
# Only needed if enableOfflineAccess: true
|
|
||||||
clientId: ""
|
|
||||||
clientSecret: ""
|
|
||||||
# OAuth scopes to request (space-separated)
|
|
||||||
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"
|
|
||||||
# Use existing secret for multi-user basic auth credentials
|
|
||||||
# If set, tokenEncryptionKey, clientId, and clientSecret above are ignored
|
|
||||||
# Secret should contain keys specified in the *Key fields below
|
|
||||||
# Example:
|
|
||||||
# kubectl create secret generic my-multiuser-creds \
|
|
||||||
# --from-literal=token_encryption_key=ESF1BvEQ... \
|
|
||||||
# --from-literal=client_id=my-client-id \
|
|
||||||
# --from-literal=client_secret=my-client-secret
|
|
||||||
existingSecret: ""
|
|
||||||
# Keys in the existing secret
|
|
||||||
tokenEncryptionKeyKey: "token_encryption_key"
|
|
||||||
clientIdKey: "client_id"
|
|
||||||
clientSecretKey: "client_secret"
|
|
||||||
# Persistent storage for token database
|
|
||||||
persistence:
|
|
||||||
enabled: true
|
|
||||||
# Storage class (leave empty for default)
|
|
||||||
storageClass: ""
|
|
||||||
accessMode: ReadWriteOnce
|
|
||||||
size: 100Mi
|
|
||||||
# Use existing PVC
|
|
||||||
existingClaim: ""
|
|
||||||
|
|
||||||
# OAuth2/OIDC settings (experimental)
|
|
||||||
oauth:
|
|
||||||
# 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: ""
|
|
||||||
|
|
||||||
# Login Flow v2 settings (experimental, ADR-022)
|
|
||||||
# Uses Nextcloud's native Login Flow v2 to obtain app passwords per user.
|
|
||||||
# No OAuth patches required — works with stock Nextcloud.
|
|
||||||
# See: docs/ADR-022-deployment-mode-consolidation.md
|
|
||||||
loginFlow:
|
|
||||||
# Token encryption key (required, ignored if existingSecret is set)
|
|
||||||
# Generate with: python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
|
|
||||||
tokenEncryptionKey: ""
|
|
||||||
# Token storage database path
|
|
||||||
tokenStorageDb: "/app/data/tokens.db"
|
|
||||||
# Use existing secret instead of creating one
|
|
||||||
existingSecret: ""
|
|
||||||
# Key in the existing secret
|
|
||||||
tokenEncryptionKeyKey: "token_encryption_key"
|
|
||||||
|
|
||||||
# Data Storage Configuration
|
|
||||||
# Persistent volume for /app/data directory
|
|
||||||
# Used for: token databases, qdrant persistent storage, and any app data
|
|
||||||
# When disabled, uses emptyDir (non-persistent, but still writable)
|
|
||||||
dataStorage:
|
|
||||||
# Enable persistent storage for /app/data
|
|
||||||
# Set to true when using:
|
|
||||||
# - Multi-user basic auth with offline access (stores tokens.db)
|
|
||||||
# - Login flow mode (stores app passwords in tokens.db)
|
|
||||||
# - Qdrant persistent mode (stores vector database)
|
|
||||||
# - Any feature requiring persistent app data
|
|
||||||
# Set to false for basic auth without persistence (uses emptyDir)
|
|
||||||
enabled: false
|
|
||||||
# Storage class (leave empty for default)
|
|
||||||
storageClass: ""
|
|
||||||
accessMode: ReadWriteOnce
|
|
||||||
# Size for data storage (should accommodate tokens.db and/or qdrant data)
|
|
||||||
# Recommended: 1Gi minimum, 5Gi for production with qdrant
|
|
||||||
size: 1Gi
|
|
||||||
# Use existing PVC
|
|
||||||
existingClaim: ""
|
|
||||||
|
|
||||||
# MCP server configuration
|
|
||||||
mcp:
|
|
||||||
# Transport mode (default: streamable-http for SSE)
|
|
||||||
transport: "streamable-http"
|
|
||||||
# Port for MCP server (both basic auth and OAuth modes)
|
|
||||||
port: 8000
|
|
||||||
# Additional command-line arguments to pass to nextcloud-mcp-server
|
|
||||||
# Example: ["--log-level", "debug", "--enable-app", "notes"]
|
|
||||||
extraArgs: []
|
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
# Observability Configuration
|
|
||||||
observability:
|
|
||||||
# Prometheus metrics
|
|
||||||
metrics:
|
|
||||||
enabled: true
|
|
||||||
port: 9090
|
|
||||||
path: /metrics
|
|
||||||
|
|
||||||
# OpenTelemetry tracing
|
|
||||||
tracing:
|
|
||||||
enabled: false
|
|
||||||
endpoint: "" # e.g., "http://opentelemetry-collector:4317"
|
|
||||||
serviceName: "nextcloud-mcp-server"
|
|
||||||
samplingRate: 1.0
|
|
||||||
|
|
||||||
# Logging configuration
|
|
||||||
logging:
|
|
||||||
format: json # "json" or "text"
|
|
||||||
level: INFO
|
|
||||||
includeTraceContext: true
|
|
||||||
|
|
||||||
# Prometheus ServiceMonitor (requires Prometheus Operator)
|
|
||||||
serviceMonitor:
|
|
||||||
enabled: false
|
|
||||||
interval: 30s
|
|
||||||
scrapeTimeout: 10s
|
|
||||||
labels: {}
|
|
||||||
# Additional labels for ServiceMonitor (e.g., for Prometheus selector)
|
|
||||||
# Example: { prometheus: kube-prometheus }
|
|
||||||
|
|
||||||
# Prometheus alert rules (requires Prometheus Operator)
|
|
||||||
prometheusRule:
|
|
||||||
enabled: false
|
|
||||||
labels: {}
|
|
||||||
# Additional labels for PrometheusRule (e.g., for Prometheus selector)
|
|
||||||
# Example: { prometheus: kube-prometheus }
|
|
||||||
|
|
||||||
# Grafana dashboards (requires Grafana with sidecar enabled)
|
|
||||||
dashboards:
|
|
||||||
# Enable automatic dashboard provisioning via ConfigMap
|
|
||||||
enabled: false
|
|
||||||
# Grafana folder name where dashboards will be imported
|
|
||||||
# The grafana-sidecar looks for ConfigMaps with label "grafana_dashboard: 1"
|
|
||||||
# and reads the folder name from annotation "grafana_folder" (supports spaces)
|
|
||||||
grafanaFolder: "Nextcloud MCP"
|
|
||||||
# Additional labels for dashboard ConfigMap
|
|
||||||
# These will be added alongside the required "grafana_dashboard: 1" label
|
|
||||||
labels: {}
|
|
||||||
# Additional annotations for dashboard ConfigMap
|
|
||||||
annotations: {}
|
|
||||||
|
|
||||||
service:
|
|
||||||
type: ClusterIP
|
|
||||||
port: 8000
|
|
||||||
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
|
|
||||||
|
|
||||||
# Semantic Search Configuration
|
|
||||||
# Enable semantic search with BM25 hybrid search and background synchronization
|
|
||||||
# of Nextcloud content into vector database
|
|
||||||
semanticSearch:
|
|
||||||
# Enable semantic search and background vector synchronization
|
|
||||||
enabled: false
|
|
||||||
# Scan interval in seconds (how often to check for changes)
|
|
||||||
scanInterval: 3600
|
|
||||||
# Number of concurrent processor workers
|
|
||||||
processorWorkers: 3
|
|
||||||
# Maximum queue size for documents pending indexing
|
|
||||||
queueMaxSize: 10000
|
|
||||||
|
|
||||||
# Document Chunking Configuration
|
|
||||||
# Controls how documents are split into chunks before embedding
|
|
||||||
# Only relevant when semanticSearch.enabled is true
|
|
||||||
documentChunking:
|
|
||||||
# Number of words per chunk (default: 512)
|
|
||||||
# Smaller chunks (256-384): Better for precise searches, more chunks to store
|
|
||||||
# Medium chunks (512-768): Balanced approach (recommended for most use cases)
|
|
||||||
# Larger chunks (1024+): Better for context, less precise matching
|
|
||||||
chunkSize: 512
|
|
||||||
# Number of overlapping words between chunks (default: 50)
|
|
||||||
# Recommended: 10-20% of chunkSize for context preservation across boundaries
|
|
||||||
# Must be less than chunkSize
|
|
||||||
chunkOverlap: 50
|
|
||||||
|
|
||||||
# Qdrant Vector Database Configuration
|
|
||||||
# Three deployment modes available:
|
|
||||||
# 1. Local In-Memory: Fast, ephemeral, zero-config (mode: "memory")
|
|
||||||
# 2. Local Persistent: File-based, survives restarts (mode: "persistent")
|
|
||||||
# 3. Network: Dedicated Qdrant service, production-ready (mode: "network")
|
|
||||||
qdrant:
|
|
||||||
# Qdrant mode: "memory", "persistent", or "network"
|
|
||||||
# - memory: In-memory storage (:memory:) - default, zero config, data lost on restart
|
|
||||||
# - persistent: Local file storage - data persists across restarts, suitable for small/medium deployments
|
|
||||||
# - network: Dedicated Qdrant service (see networkMode below)
|
|
||||||
mode: "memory"
|
|
||||||
|
|
||||||
# Collection name for vector data
|
|
||||||
collection: "nextcloud_content"
|
|
||||||
|
|
||||||
# Local persistent mode configuration (only used when mode: "persistent")
|
|
||||||
localPersistence:
|
|
||||||
# Enable persistent volume for local Qdrant data
|
|
||||||
enabled: true
|
|
||||||
# Storage class (leave empty for default)
|
|
||||||
storageClass: ""
|
|
||||||
accessMode: ReadWriteOnce
|
|
||||||
# Size for local Qdrant storage
|
|
||||||
size: 1Gi
|
|
||||||
# Path where Qdrant data is stored (relative to /app/data)
|
|
||||||
# Default: /app/data/qdrant
|
|
||||||
dataPath: "/app/data/qdrant"
|
|
||||||
# Use existing PVC
|
|
||||||
existingClaim: ""
|
|
||||||
|
|
||||||
# Network mode configuration (only used when mode: "network")
|
|
||||||
networkMode:
|
|
||||||
# Deploy Qdrant as a subchart (if true) or use external Qdrant (if false)
|
|
||||||
deploySubchart: false
|
|
||||||
# External Qdrant URL (used when deploySubchart: false)
|
|
||||||
# Example: "http://qdrant.default.svc.cluster.local:6333"
|
|
||||||
externalUrl: ""
|
|
||||||
# Optional API key for Qdrant authentication
|
|
||||||
apiKey: ""
|
|
||||||
# Use existing secret for API key
|
|
||||||
existingSecret: ""
|
|
||||||
secretKey: "api-key"
|
|
||||||
|
|
||||||
# Qdrant subchart configuration (only used when mode: "network" and networkMode.deploySubchart: true)
|
|
||||||
# All values are passed through to the qdrant/qdrant chart.
|
|
||||||
# See https://github.com/qdrant/qdrant-helm for full configuration options.
|
|
||||||
subchart:
|
|
||||||
# Number of Qdrant replicas
|
|
||||||
replicaCount: 1
|
|
||||||
image:
|
|
||||||
# Qdrant version
|
|
||||||
tag: v1.12.5
|
|
||||||
config:
|
|
||||||
cluster:
|
|
||||||
# Enable distributed cluster mode
|
|
||||||
enabled: false
|
|
||||||
# Persistent storage for vector data
|
|
||||||
persistence:
|
|
||||||
size: 10Gi
|
|
||||||
storageClass: ""
|
|
||||||
accessModes:
|
|
||||||
- ReadWriteOnce
|
|
||||||
# Resource limits and requests
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: 200m
|
|
||||||
memory: 512Mi
|
|
||||||
limits:
|
|
||||||
cpu: 1000m
|
|
||||||
memory: 2Gi
|
|
||||||
|
|
||||||
# Ollama Embedding Service
|
|
||||||
# Deployed as a subchart when enabled. All values are passed through to the ollama/ollama chart.
|
|
||||||
# See https://github.com/otwld/ollama-helm for full configuration options.
|
|
||||||
ollama:
|
|
||||||
# Enable Ollama subchart deployment
|
|
||||||
# Set to true to deploy Ollama as a subchart, or false to use an external Ollama instance
|
|
||||||
enabled: false
|
|
||||||
# External Ollama URL (use this if you have Ollama deployed elsewhere)
|
|
||||||
# When set, use enabled: false to prevent deploying the subchart
|
|
||||||
# Example: "http://ollama.default.svc.cluster.local:11434"
|
|
||||||
url: ""
|
|
||||||
# Embedding model to use
|
|
||||||
embeddingModel: "nomic-embed-text"
|
|
||||||
# Verify SSL certificates when connecting to Ollama
|
|
||||||
verifySsl: true
|
|
||||||
# Number of Ollama replicas (only used when subchart is deployed)
|
|
||||||
replicaCount: 1
|
|
||||||
# Ollama configuration (only used when subchart is deployed)
|
|
||||||
ollama:
|
|
||||||
# Models to automatically pull on startup
|
|
||||||
models:
|
|
||||||
pull:
|
|
||||||
- nomic-embed-text
|
|
||||||
# Persistent storage for models (only used when subchart is deployed)
|
|
||||||
persistentVolume:
|
|
||||||
enabled: true
|
|
||||||
size: 20Gi
|
|
||||||
storageClass: ""
|
|
||||||
# Resource limits and requests (only used when subchart is deployed)
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: 500m
|
|
||||||
memory: 1Gi
|
|
||||||
limits:
|
|
||||||
cpu: 2000m
|
|
||||||
memory: 4Gi
|
|
||||||
|
|
||||||
# OpenAI-compatible Embedding Provider
|
|
||||||
# Alternative to Ollama for embedding generation. Can be used with OpenAI or any compatible API.
|
|
||||||
openai:
|
|
||||||
# Enable OpenAI embedding provider
|
|
||||||
enabled: false
|
|
||||||
# OpenAI API key (only used if existingSecret is not set)
|
|
||||||
apiKey: ""
|
|
||||||
# Name of existing secret containing the API key
|
|
||||||
existingSecret: ""
|
|
||||||
# Key in the secret that contains the API key
|
|
||||||
secretKey: "api-key"
|
|
||||||
# Optional custom API endpoint (e.g., for Azure OpenAI or local compatible services)
|
|
||||||
baseUrl: ""
|
|
||||||
+3
-32
@@ -27,15 +27,7 @@ Access metrics at: `http://localhost:9090/metrics`
|
|||||||
|
|
||||||
### Kubernetes Deployment
|
### Kubernetes Deployment
|
||||||
|
|
||||||
Metrics are automatically scraped if you have Prometheus Operator installed:
|
For Kubernetes deployments with Helm, see the [Helm chart repository](https://github.com/cbcoutinho/helm-charts) which includes ServiceMonitor and PrometheusRule support.
|
||||||
|
|
||||||
```bash
|
|
||||||
helm install nextcloud-mcp charts/nextcloud-mcp-server \
|
|
||||||
--set observability.metrics.enabled=true \
|
|
||||||
--set observability.tracing.enabled=true \
|
|
||||||
--set observability.tracing.endpoint=http://opentelemetry-collector:4317 \
|
|
||||||
--set serviceMonitor.enabled=true
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
@@ -55,28 +47,7 @@ helm install nextcloud-mcp charts/nextcloud-mcp-server \
|
|||||||
|
|
||||||
### Helm Chart Configuration
|
### Helm Chart Configuration
|
||||||
|
|
||||||
```yaml
|
The Helm chart has moved to a [separate repository](https://github.com/cbcoutinho/helm-charts). See its `values.yaml` for observability configuration options including metrics, tracing, logging, and ServiceMonitor settings.
|
||||||
observability:
|
|
||||||
metrics:
|
|
||||||
enabled: true
|
|
||||||
port: 9090
|
|
||||||
path: /metrics
|
|
||||||
|
|
||||||
tracing:
|
|
||||||
enabled: true
|
|
||||||
endpoint: "http://opentelemetry-collector:4317"
|
|
||||||
samplingRate: 1.0
|
|
||||||
|
|
||||||
logging:
|
|
||||||
format: json
|
|
||||||
level: INFO
|
|
||||||
includeTraceContext: true
|
|
||||||
|
|
||||||
serviceMonitor:
|
|
||||||
enabled: true
|
|
||||||
interval: 30s
|
|
||||||
scrapeTimeout: 10s
|
|
||||||
```
|
|
||||||
|
|
||||||
## Metrics
|
## Metrics
|
||||||
|
|
||||||
@@ -206,7 +177,7 @@ sum(rate(mcp_nextcloud_api_requests_total{status_code!~"2.."}[5m])) by (app)
|
|||||||
- Vector sync queue >100 for >15min
|
- Vector sync queue >100 for >15min
|
||||||
- Qdrant slow (p95 >500ms) for >10min
|
- Qdrant slow (p95 >500ms) for >10min
|
||||||
|
|
||||||
See `charts/nextcloud-mcp-server/templates/prometheusrule.yaml` for complete definitions.
|
See the [Helm chart repository](https://github.com/cbcoutinho/helm-charts) for PrometheusRule definitions.
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -385,7 +385,7 @@ services:
|
|||||||
|
|
||||||
### Scaling with Multiple Replicas
|
### Scaling with Multiple Replicas
|
||||||
|
|
||||||
For higher load, use Docker Swarm or Kubernetes. See the [Helm Chart](../helm/) for Kubernetes deployments.
|
For higher load, use Docker Swarm or Kubernetes. See the [Helm chart](https://github.com/cbcoutinho/helm-charts) for Kubernetes deployments.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -93,21 +93,6 @@ version_provider = "uv"
|
|||||||
update_changelog_on_bump = true
|
update_changelog_on_bump = true
|
||||||
major_version_zero = true
|
major_version_zero = true
|
||||||
|
|
||||||
# MCP server version files + Helm appVersion
|
|
||||||
version_files = [
|
|
||||||
"charts/nextcloud-mcp-server/Chart.yaml:^appVersion:",
|
|
||||||
]
|
|
||||||
|
|
||||||
# Ignore tags from other components
|
|
||||||
ignored_tag_formats = [
|
|
||||||
"nextcloud-mcp-server-*", # Helm chart tags
|
|
||||||
]
|
|
||||||
|
|
||||||
# Filter commits by scope (all scopes except helm)
|
|
||||||
[tool.commitizen.customize]
|
|
||||||
changelog_pattern = "^(feat|fix|docs|refactor|perf|test|build|ci|chore)(?!\\((?:helm)\\))(\\([^)]+\\))?(!)?:"
|
|
||||||
schema_pattern = "^(feat|fix|docs|refactor|perf|test|build|ci|chore)(?!\\((?:helm)\\))(\\([^)]+\\))?(!)?:\\s.+"
|
|
||||||
|
|
||||||
[tool.ruff.lint]
|
[tool.ruff.lint]
|
||||||
extend-select = ["I", "PLC0415"]
|
extend-select = ["I", "PLC0415"]
|
||||||
|
|
||||||
|
|||||||
@@ -1,86 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# Bump Helm chart version
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
# Parse optional --increment flag
|
|
||||||
INCREMENT=""
|
|
||||||
while [[ $# -gt 0 ]]; do
|
|
||||||
case $1 in
|
|
||||||
--increment)
|
|
||||||
INCREMENT="$2"
|
|
||||||
shift 2
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "❌ Error: Unknown option: $1" >&2
|
|
||||||
echo "Usage: $0 [--increment PATCH|MINOR|MAJOR]" >&2
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
# Validate dependencies
|
|
||||||
command -v uv >/dev/null 2>&1 || {
|
|
||||||
echo "❌ Error: uv not found" >&2
|
|
||||||
echo " Install from https://docs.astral.sh/uv/" >&2
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# Validate Helm chart directory exists
|
|
||||||
if [ ! -d "charts/nextcloud-mcp-server" ]; then
|
|
||||||
echo "❌ Error: Must run from repository root (charts/ not found)" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
cd charts/nextcloud-mcp-server
|
|
||||||
|
|
||||||
# Validate Chart.yaml exists
|
|
||||||
if [ ! -f "Chart.yaml" ]; then
|
|
||||||
echo "❌ Error: Chart.yaml not found" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Bumping Helm chart version..."
|
|
||||||
if [ -n "$INCREMENT" ]; then
|
|
||||||
echo " Forcing $INCREMENT bump"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Build commitizen command
|
|
||||||
CZ_CMD="uv run cz --config .cz.toml bump --yes"
|
|
||||||
if [ -n "$INCREMENT" ]; then
|
|
||||||
CZ_CMD="$CZ_CMD --increment $INCREMENT"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Run commitizen bump and capture output
|
|
||||||
if ! output=$($CZ_CMD 2>&1); then
|
|
||||||
cd ../..
|
|
||||||
|
|
||||||
# Check if this is the expected "no commits to bump" case
|
|
||||||
if echo "$output" | grep -q "\[NO_COMMITS_TO_BUMP\]"; then
|
|
||||||
echo "ℹ️ No commits eligible for version bump" >&2
|
|
||||||
echo "$output" >&2
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Otherwise, this is an actual error
|
|
||||||
echo "❌ Error: Version bump failed" >&2
|
|
||||||
echo "$output" >&2
|
|
||||||
echo "" >&2
|
|
||||||
echo "Common causes:" >&2
|
|
||||||
echo " - No commits with scope 'helm' since last version" >&2
|
|
||||||
echo " - No conventional commits found (use feat(helm):, fix(helm):, etc.)" >&2
|
|
||||||
echo " - Git working directory not clean" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "$output"
|
|
||||||
echo ""
|
|
||||||
echo "✓ Helm chart version bumped successfully"
|
|
||||||
echo " Updated: Chart.yaml:version"
|
|
||||||
echo " Tag format: nextcloud-mcp-server-\${version}"
|
|
||||||
echo " Note: appVersion stays at MCP server version"
|
|
||||||
echo ""
|
|
||||||
echo "Next steps:"
|
|
||||||
echo " cd ../.."
|
|
||||||
echo " git push --follow-tags"
|
|
||||||
|
|
||||||
cd ../..
|
|
||||||
+1
-1
@@ -65,7 +65,7 @@ fi
|
|||||||
echo "$output"
|
echo "$output"
|
||||||
echo ""
|
echo ""
|
||||||
echo "✓ MCP server version bumped successfully"
|
echo "✓ MCP server version bumped successfully"
|
||||||
echo " Updated: pyproject.toml, Chart.yaml:appVersion"
|
echo " Updated: pyproject.toml"
|
||||||
echo " Tag format: v\${version}"
|
echo " Tag format: v\${version}"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Next steps:"
|
echo "Next steps:"
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ echo "Testing commitizen scope filtering patterns..."
|
|||||||
echo
|
echo
|
||||||
|
|
||||||
# Regex patterns from configs
|
# Regex patterns from configs
|
||||||
MCP_PATTERN='^(feat|fix|docs|refactor|perf|test|build|ci|chore)(?!\((?:helm|astrolabe)\))(\([^)]+\))?(!)?:'
|
MCP_PATTERN='^(feat|fix|docs|refactor|perf|test|build|ci|chore)(?!\((?:astrolabe)\))(\([^)]+\))?(!)?:'
|
||||||
HELM_PATTERN='^(feat|fix|docs|refactor|perf|test|build|ci|chore)\(helm\)(!)?:'
|
|
||||||
ASTROLABE_PATTERN='^(feat|fix|docs|refactor|perf|test|build|ci|chore)\(astrolabe\)(!)?:'
|
ASTROLABE_PATTERN='^(feat|fix|docs|refactor|perf|test|build|ci|chore)\(astrolabe\)(!)?:'
|
||||||
|
|
||||||
test_pattern() {
|
test_pattern() {
|
||||||
@@ -31,9 +30,6 @@ run_test() {
|
|||||||
if test_pattern "$message" "$MCP_PATTERN"; then
|
if test_pattern "$message" "$MCP_PATTERN"; then
|
||||||
matched_components+=("mcp")
|
matched_components+=("mcp")
|
||||||
fi
|
fi
|
||||||
if test_pattern "$message" "$HELM_PATTERN"; then
|
|
||||||
matched_components+=("helm")
|
|
||||||
fi
|
|
||||||
if test_pattern "$message" "$ASTROLABE_PATTERN"; then
|
if test_pattern "$message" "$ASTROLABE_PATTERN"; then
|
||||||
matched_components+=("astrolabe")
|
matched_components+=("astrolabe")
|
||||||
fi
|
fi
|
||||||
@@ -62,7 +58,7 @@ run_test() {
|
|||||||
failed=0
|
failed=0
|
||||||
passed=0
|
passed=0
|
||||||
|
|
||||||
# MCP server commits (any scope except helm/astrolabe)
|
# MCP server commits (any scope except astrolabe)
|
||||||
run_test "feat: add new feature" "mcp" && passed=$((passed+1)) || failed=$((failed+1))
|
run_test "feat: add new feature" "mcp" && passed=$((passed+1)) || failed=$((failed+1))
|
||||||
run_test "feat(mcp): add API endpoint" "mcp" && passed=$((passed+1)) || failed=$((failed+1))
|
run_test "feat(mcp): add API endpoint" "mcp" && passed=$((passed+1)) || failed=$((failed+1))
|
||||||
run_test "fix(mcp): resolve authentication bug" "mcp" && passed=$((passed+1)) || failed=$((failed+1))
|
run_test "fix(mcp): resolve authentication bug" "mcp" && passed=$((passed+1)) || failed=$((failed+1))
|
||||||
@@ -71,11 +67,6 @@ run_test "fix(ci): update workflow" "mcp" && passed=$((passed+1)) || failed=$((f
|
|||||||
run_test "feat(api): add endpoint" "mcp" && passed=$((passed+1)) || failed=$((failed+1))
|
run_test "feat(api): add endpoint" "mcp" && passed=$((passed+1)) || failed=$((failed+1))
|
||||||
run_test "ci: configure GitHub Actions" "mcp" && passed=$((passed+1)) || failed=$((failed+1))
|
run_test "ci: configure GitHub Actions" "mcp" && passed=$((passed+1)) || failed=$((failed+1))
|
||||||
|
|
||||||
# Helm chart commits
|
|
||||||
run_test "feat(helm): add resource limits" "helm" && passed=$((passed+1)) || failed=$((failed+1))
|
|
||||||
run_test "fix(helm): correct values schema" "helm" && passed=$((passed+1)) || failed=$((failed+1))
|
|
||||||
run_test "docs(helm): update deployment guide" "helm" && passed=$((passed+1)) || failed=$((failed+1))
|
|
||||||
|
|
||||||
# Astrolabe commits
|
# Astrolabe commits
|
||||||
run_test "feat(astrolabe): add dark mode" "astrolabe" && passed=$((passed+1)) || failed=$((failed+1))
|
run_test "feat(astrolabe): add dark mode" "astrolabe" && passed=$((passed+1)) || failed=$((failed+1))
|
||||||
run_test "fix(astrolabe): resolve UI bug" "astrolabe" && passed=$((passed+1)) || failed=$((failed+1))
|
run_test "fix(astrolabe): resolve UI bug" "astrolabe" && passed=$((passed+1)) || failed=$((failed+1))
|
||||||
@@ -83,11 +74,10 @@ run_test "perf(astrolabe): optimize rendering" "astrolabe" && passed=$((passed+1
|
|||||||
|
|
||||||
# Breaking changes
|
# Breaking changes
|
||||||
run_test "feat(mcp)!: breaking API change" "mcp" && passed=$((passed+1)) || failed=$((failed+1))
|
run_test "feat(mcp)!: breaking API change" "mcp" && passed=$((passed+1)) || failed=$((failed+1))
|
||||||
run_test "feat(helm)!: rename values" "helm" && passed=$((passed+1)) || failed=$((failed+1))
|
|
||||||
run_test "feat(astrolabe)!: remove deprecated feature" "astrolabe" && passed=$((passed+1)) || failed=$((failed+1))
|
run_test "feat(astrolabe)!: remove deprecated feature" "astrolabe" && passed=$((passed+1)) || failed=$((failed+1))
|
||||||
|
|
||||||
# Edge cases
|
# Edge cases
|
||||||
run_test "feat(invalid): test" "mcp" && passed=$((passed+1)) || failed=$((failed+1)) # Any scope except helm/astrolabe → MCP
|
run_test "feat(invalid): test" "mcp" && passed=$((passed+1)) || failed=$((failed+1)) # Any scope except astrolabe → MCP
|
||||||
run_test "random commit message" "none" && passed=$((passed+1)) || failed=$((failed+1)) # Not conventional commit
|
run_test "random commit message" "none" && passed=$((passed+1)) || failed=$((failed+1)) # Not conventional commit
|
||||||
run_test "feat (mcp): space before scope" "none" && passed=$((passed+1)) || failed=$((failed+1)) # Invalid format
|
run_test "feat (mcp): space before scope" "none" && passed=$((passed+1)) || failed=$((failed+1)) # Invalid format
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user