diff --git a/bootstrap/applicationset.yaml b/bootstrap/applicationset.yaml index 72b98e9..6f63dc8 100644 --- a/bootstrap/applicationset.yaml +++ b/bootstrap/applicationset.yaml @@ -38,6 +38,14 @@ spec: - repoURL: http://gitea-ha-http.apps:3000/olb42/toolhive.git targetRevision: main ref: values + - repoURL: http://gitea-ha-http.apps:3000/olb42/toolhive.git + targetRevision: main + path: 'manifest/overlays/{{ .overlay }}' + kustomize: + commonAnnotationsEnvsubst: true + commonAnnotations: + app-source: ${ARGOCD_APP_SOURCE_REPO_URL} + app-revision: ${ARGOCD_APP_SOURCE_TARGET_REVISION} destination: server: https://kubernetes.default.svc namespace: '{{ .namespace }}' diff --git a/manifest/overlays/production/kustomization.yaml b/manifest/overlays/production/kustomization.yaml new file mode 100644 index 0000000..79d6494 --- /dev/null +++ b/manifest/overlays/production/kustomization.yaml @@ -0,0 +1,11 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +# namespace: kube-system + + +resources: + - mcpserver-gitea.yaml + - mcpserver-argocd.yaml + - mcpserver-kubernetes.yaml + - mcp-group-homelab-core.yaml diff --git a/manifest/overlays/production/mcp-group-homelab-core.yaml b/manifest/overlays/production/mcp-group-homelab-core.yaml new file mode 100644 index 0000000..8cb6c20 --- /dev/null +++ b/manifest/overlays/production/mcp-group-homelab-core.yaml @@ -0,0 +1,5 @@ +apiVersion: toolhive.stacklok.dev/v1beta1 +kind: MCPGroup +metadata: + name: homelab-core + namespace: toolhive-system \ No newline at end of file diff --git a/manifest/overlays/production/mcpserver-argocd.yaml b/manifest/overlays/production/mcpserver-argocd.yaml new file mode 100644 index 0000000..c0df3d1 --- /dev/null +++ b/manifest/overlays/production/mcpserver-argocd.yaml @@ -0,0 +1,46 @@ +apiVersion: toolhive.stacklok.dev/v1beta1 +kind: MCPServer +metadata: + name: argocd-mcp + namespace: toolhive-system +spec: + # argoproj-labs MCP server for Argo CD. The image entrypoint already launches + # the server (default stdio transport); ToolHive proxies it to streamable-http + # at http://mcp-argocd-mcp-proxy.mcp-services:8080/mcp. + # NOTE: do NOT set args — the image ENTRYPOINT is `node` and any args replace + # the script path (e.g. "stdio" -> node stdio -> "Cannot find module /app/stdio"). + image: ghcr.io/argoproj-labs/mcp-for-argocd:v0.7.0 + transport: stdio + proxyMode: streamable-http + proxyPort: 8080 + env: + - name: ARGOCD_BASE_URL + value: https://argocd-server.argocd.svc.cluster.local + # argocd-server serves a self-signed cert in-cluster. + - name: NODE_TLS_REJECT_UNAUTHORIZED + value: "0" + # Start read-only; drop this to enable sync/write tools later. + - name: MCP_READ_ONLY + value: "true" + # ToolHive 0.28.3 does not translate spec.secrets into the workload, so inject + # the token natively on the `mcp` container via podTemplateSpec. + podTemplateSpec: + spec: + containers: + - name: mcp + env: + - name: ARGOCD_API_TOKEN + valueFrom: + secretKeyRef: + name: argocd-mcp-secret + key: token + permissionProfile: + type: builtin + name: network + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 500m + memory: 512Mi diff --git a/manifest/overlays/production/mcpserver-gitea.yaml b/manifest/overlays/production/mcpserver-gitea.yaml new file mode 100644 index 0000000..6263d1f --- /dev/null +++ b/manifest/overlays/production/mcpserver-gitea.yaml @@ -0,0 +1,39 @@ +apiVersion: toolhive.stacklok.dev/v1beta1 +kind: MCPServer +metadata: + name: gitea-mcp + namespace: toolhive-system +spec: + # Upstream Gitea MCP server. Speaks stdio; ToolHive's proxy runner wraps it and + # exposes streamable-http at http://mcp-gitea-mcp-proxy.mcp-services:8080/mcp. + image: docker.gitea.com/gitea-mcp-server:nightly + transport: stdio + proxyMode: streamable-http + proxyPort: 8080 + # No args: the image's default command already runs the binary in stdio mode. + # (Passing "-t stdio" replaced the command and execed "-t" directly.) + env: + - name: GITEA_HOST + value: http://gitea-ha-http.apps:3000 + # NOTE: ToolHive 0.28.3 does not translate spec.secrets into the workload, so + # inject the token natively on the `mcp` container via podTemplateSpec. + podTemplateSpec: + spec: + containers: + - name: mcp + env: + - name: GITEA_ACCESS_TOKEN + valueFrom: + secretKeyRef: + name: gitea-mcp-secret + key: token + permissionProfile: + type: builtin + name: network + resources: + requests: + cpu: 50m + memory: 64Mi + limits: + cpu: 300m + memory: 256Mi diff --git a/manifest/overlays/production/mcpserver-kubernetes.yaml b/manifest/overlays/production/mcpserver-kubernetes.yaml new file mode 100644 index 0000000..8fd2364 --- /dev/null +++ b/manifest/overlays/production/mcpserver-kubernetes.yaml @@ -0,0 +1,27 @@ +apiVersion: toolhive.stacklok.dev/v1beta1 +kind: MCPServer +metadata: + name: kubernetes-mcp + namespace: toolhive-system +spec: + # containers/kubernetes-mcp-server. Authenticates to the API in-cluster via the + # kubernetes-mcp ServiceAccount (read-only ClusterRole, see rbac file). Speaks + # stdio; ToolHive proxies to streamable-http at + # http://mcp-kubernetes-mcp-proxy.mcp-services:8080/mcp. + image: ghcr.io/containers/kubernetes-mcp-server:latest + transport: stdio + proxyMode: streamable-http + proxyPort: 8080 + # Pin the MCP server pod to our read-only ServiceAccount. The ClusterRole is + # the real guardrail: even if a write tool is invoked, the API rejects it. + serviceAccount: kubernetes-mcp + permissionProfile: + type: builtin + name: network + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 500m + memory: 512Mi