diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000..786d169 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,7 @@ +{ + "enabledMcpjsonServers": [ + "kubernetes", + "flux", + "playwright" + ] +} diff --git a/.mcp.json b/.mcp.json index 5c07e52..6532f8a 100644 --- a/.mcp.json +++ b/.mcp.json @@ -8,6 +8,10 @@ "type": "sse", "url": "http://localhost:8081/sse" }, + "homeassistant": { + "type": "sse", + "url": "http://localhost:8087/sse" + }, "playwright": { "type": "sse", "url": "http://playwright-mcp.playwright.svc.cluster.local:3000/sse" diff --git a/CLAUDE.md b/CLAUDE.md index ae3c26f..f4c3e5f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -79,26 +79,33 @@ Container start ### MCP Sidecars -Kubernetes and Flux MCP servers run as sidecar containers in the pod, inheriting its ServiceAccount RBAC permissions: +MCP (Model Context Protocol) servers run as sidecar containers in the pod, enabling AI assistants to interact with various services: -| Sidecar | Image | Port | Endpoint | -|---------|-------|------|----------| -| `kubernetes-mcp` | `quay.io/containers/kubernetes_mcp_server` | 8080 | `http://localhost:8080/sse` | -| `flux-mcp` | `ghcr.io/controlplaneio-fluxcd/flux-operator-mcp` | 8081 | `http://localhost:8081/sse` | +| Sidecar | Image | Port | Endpoint | Default | +|---------|-------|------|----------|---------| +| `kubernetes-mcp` | `quay.io/containers/kubernetes_mcp_server` | 8080 | `http://localhost:8080/sse` | Enabled | +| `flux-mcp` | `ghcr.io/controlplaneio-fluxcd/flux-operator-mcp` | 8081 | `http://localhost:8081/sse` | Enabled | +| `homeassistant-mcp` | `ghcr.io/homeassistant-ai/ha-mcp` | 8087 | `http://localhost:8087/sse` | Disabled | -Both are enabled by default and configurable via `mcpSidecars` in `values.yaml`. Playwright MCP remains an external service. +**Note:** +- Kubernetes and Flux sidecars require `clusterAccess` != `none` to be deployed (they need RBAC permissions) +- Kubernetes and Flux sidecars inherit the pod's ServiceAccount RBAC permissions +- Home Assistant sidecar requires `HOMEASSISTANT_URL` and `HOMEASSISTANT_TOKEN` in the env secret +- Playwright MCP remains an external service #### Enabling/Disabling MCP Servers To control MCP sidecars, set the `enabled` flag in your values override: ```yaml -# Disable both MCP sidecars +# Disable all MCP sidecars mcpSidecars: kubernetes: enabled: false flux: enabled: false + homeassistant: + enabled: false # Or selectively enable/disable mcpSidecars: @@ -106,6 +113,8 @@ mcpSidecars: enabled: true # Keep Kubernetes MCP enabled flux: enabled: false # Disable Flux MCP + homeassistant: + enabled: true # Enable Home Assistant MCP (requires secrets) ``` When deploying via Helm: diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index 9057944..aebfc59 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -124,7 +124,11 @@ ssh -p 2222 user@localhost ### MCP Sidecar Configuration -Control MCP servers for AI-assisted operations: +Control MCP servers for AI-assisted operations. + +**Important:** Kubernetes and Flux MCP sidecars are only deployed when: +1. They are enabled in values (`mcpSidecars..enabled: true`) +2. AND `clusterAccess` is not `none` (they need RBAC permissions to function) ```bash # Disable all MCP sidecars @@ -132,7 +136,8 @@ helm install mydev ./chart \ --set name=mydev \ --set githubRepo=https://github.com/youruser/yourrepo \ --set mcpSidecars.kubernetes.enabled=false \ - --set mcpSidecars.flux.enabled=false + --set mcpSidecars.flux.enabled=false \ + --set mcpSidecars.homeassistant.enabled=false # Enable only Kubernetes MCP helm install mydev ./chart \ @@ -140,6 +145,16 @@ helm install mydev ./chart \ --set githubRepo=https://github.com/youruser/yourrepo \ --set mcpSidecars.kubernetes.enabled=true \ --set mcpSidecars.flux.enabled=false + +# Enable Home Assistant MCP (requires credentials) +kubectl create secret generic devcontainer-mydev-secrets-env \ + --from-literal=homeassistant-url='http://homeassistant.local:8123' \ + --from-literal=homeassistant-token='your_long_lived_token' + +helm install mydev ./chart \ + --set name=mydev \ + --set githubRepo=https://github.com/youruser/yourrepo \ + --set mcpSidecars.homeassistant.enabled=true ``` ### Cluster Access Levels @@ -340,9 +355,15 @@ kubectl get pod -l app.kubernetes.io/instance=mydev -o jsonpath='{.items[0].spec # Check MCP container logs kubectl logs deployment/devcontainer-mydev -c kubernetes-mcp kubectl logs deployment/devcontainer-mydev -c flux-mcp +kubectl logs deployment/devcontainer-mydev -c homeassistant-mcp -# Verify RBAC permissions +# Verify RBAC permissions (for Kubernetes/Flux MCP) kubectl auth can-i --list --as system:serviceaccount:default:devcontainer-mydev + +# Check Home Assistant MCP credentials +kubectl get secret devcontainer-mydev-secrets-env -o jsonpath='{.data.homeassistant-url}' | base64 -d +# Verify the URL is accessible from the pod +kubectl exec deployment/devcontainer-mydev -- curl -s http://homeassistant.local:8123/api/ ``` ### Storage Issues diff --git a/README.md b/README.md index ccbe5a5..b07e693 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,8 @@ The secret is picked up automatically via `envFrom`. Keys recognised: | `VNC_PASSWORD` | Password for the VNC web UI | | `ANTHROPIC_API_KEY` | API key — alternative to browser-based Claude login | | `SSH_AUTHORIZED_KEYS` | Public key(s) for SSH access (required when `ssh: true`) | +| `homeassistant-url` | Home Assistant URL (required when `mcpSidecars.homeassistant.enabled: true`) | +| `homeassistant-token` | Home Assistant long-lived access token (required when `mcpSidecars.homeassistant.enabled: true`) | ```bash kubectl create secret generic devcontainer-mydev-secrets-env \ @@ -152,14 +154,18 @@ With any non-`none` value, a `ServiceAccount` named `devcontainer-{name}` is cre ### MCP Sidecars -The devcontainer includes MCP (Model Context Protocol) servers as sidecar containers that enable AI assistants to interact with Kubernetes and Flux: +The devcontainer includes MCP (Model Context Protocol) servers as sidecar containers that enable AI assistants to interact with various services: | Sidecar | Default | Purpose | |---------|---------|---------| | `mcpSidecars.kubernetes.enabled` | `true` | Kubernetes API access via MCP | | `mcpSidecars.flux.enabled` | `true` | Flux GitOps operations via MCP | +| `mcpSidecars.homeassistant.enabled` | `false` | Home Assistant smart home control via MCP | -These sidecars inherit the pod's ServiceAccount RBAC permissions (controlled by `clusterAccess`). +**Notes:** +- Kubernetes and Flux sidecars require `clusterAccess` != `none` to be deployed (automatically disabled when no cluster access) +- Kubernetes and Flux sidecars inherit the pod's ServiceAccount RBAC permissions (controlled by `clusterAccess`) +- Home Assistant sidecar requires additional configuration (see below) **Disable MCP sidecars:** ```bash @@ -177,6 +183,21 @@ helm install mydev ./chart \ --set mcpSidecars.flux.enabled=false # Disable only Flux MCP ``` +**Enable Home Assistant MCP:** +```bash +# Create secret with Home Assistant credentials +kubectl create secret generic devcontainer-mydev-secrets-env \ + --from-literal=GITHUB_TOKEN='ghp_...' \ + --from-literal=homeassistant-url='http://homeassistant.local:8123' \ + --from-literal=homeassistant-token='your_long_lived_access_token' + +# Deploy with Home Assistant MCP enabled +helm install mydev ./chart \ + --set name=mydev \ + --set githubRepo=https://github.com/youruser/yourrepo \ + --set mcpSidecars.homeassistant.enabled=true +``` + **Custom MCP configuration:** ```yaml # values.yaml override @@ -196,6 +217,19 @@ mcpSidecars: cpu: "500m" flux: enabled: false # Disabled in this example + homeassistant: + enabled: true + image: + repository: ghcr.io/homeassistant-ai/ha-mcp + tag: stable # or 'latest' for dev builds + port: 8087 + resources: + requests: + memory: "128Mi" + cpu: "100m" + limits: + memory: "512Mi" + cpu: "500m" ``` ### Display and resources diff --git a/VARIABLES.md b/VARIABLES.md index c06e38f..2a76cbb 100644 --- a/VARIABLES.md +++ b/VARIABLES.md @@ -175,6 +175,8 @@ Complete reference for all configurable values in the Antigravity Dev Container - `VNC_PASSWORD` — Password for VNC web UI - `ANTHROPIC_API_KEY` — API key for Claude - `SSH_AUTHORIZED_KEYS` — Public keys for SSH access + - `homeassistant-url` — Home Assistant base URL (e.g., http://homeassistant.local:8123) + - `homeassistant-token` — Home Assistant long-lived access token ## MCP Sidecars @@ -244,6 +246,41 @@ Complete reference for all configurable values in the Antigravity Dev Container ``` - **Description:** Resource limits for Flux MCP sidecar +### mcpSidecars.homeassistant.enabled +- **Type:** Boolean +- **Default:** `false` +- **Description:** Enable Home Assistant MCP server sidecar +- **Note:** Requires `homeassistant-url` and `homeassistant-token` in env secret + +### mcpSidecars.homeassistant.image.repository +- **Type:** String +- **Default:** `ghcr.io/homeassistant-ai/ha-mcp` +- **Description:** Home Assistant MCP server image + +### mcpSidecars.homeassistant.image.tag +- **Type:** String +- **Default:** `stable` +- **Description:** Home Assistant MCP server image tag +- **Options:** `stable` (recommended), `latest` (dev builds), `v{version}` (specific version) + +### mcpSidecars.homeassistant.port +- **Type:** Integer +- **Default:** `8087` +- **Description:** Port for Home Assistant MCP server (SSE mode) + +### mcpSidecars.homeassistant.resources +- **Type:** Object +- **Default:** + ```yaml + requests: + memory: "64Mi" + cpu: "50m" + limits: + memory: "256Mi" + cpu: "500m" + ``` +- **Description:** Resource limits for Home Assistant MCP sidecar + ## Usage Examples ### Minimal Configuration @@ -306,6 +343,30 @@ happyServerUrl: https://happy.internal.company.com happyWebappUrl: https://happy-app.internal.company.com ``` +### Smart Home Development Configuration + +```yaml +name: smarthome-dev +githubRepo: https://github.com/user/home-automation +ide: vscode + +clusterAccess: readwritens + +mcpSidecars: + kubernetes: + enabled: true + flux: + enabled: false + homeassistant: + enabled: true + image: + tag: stable + +# Requires secrets: +# homeassistant-url: http://homeassistant.local:8123 +# homeassistant-token: +``` + ## Helm CLI Examples ### Using --set Flags diff --git a/chart/Chart.yaml b/chart/Chart.yaml index e56e502..b056963 100644 --- a/chart/Chart.yaml +++ b/chart/Chart.yaml @@ -2,5 +2,5 @@ apiVersion: v2 name: devcontainer description: Antigravity Dev Container with Happy Coder AI assistant type: application -version: 0.1.17 +version: 0.1.18 appVersion: "latest" diff --git a/chart/templates/deployment.yaml b/chart/templates/deployment.yaml index c825bd6..c1afabf 100644 --- a/chart/templates/deployment.yaml +++ b/chart/templates/deployment.yaml @@ -98,7 +98,7 @@ spec: initialDelaySeconds: 5 periodSeconds: 5 {{- end }} - {{- if .Values.mcpSidecars.kubernetes.enabled }} + {{- if and .Values.mcpSidecars.kubernetes.enabled (ne .Values.clusterAccess "none") }} - name: kubernetes-mcp image: "{{ .Values.mcpSidecars.kubernetes.image.repository }}:{{ .Values.mcpSidecars.kubernetes.image.tag }}" args: @@ -123,7 +123,7 @@ spec: resources: {{- toYaml .Values.mcpSidecars.kubernetes.resources | nindent 12 }} {{- end }} - {{- if .Values.mcpSidecars.flux.enabled }} + {{- if and .Values.mcpSidecars.flux.enabled (ne .Values.clusterAccess "none") }} - name: flux-mcp image: "{{ .Values.mcpSidecars.flux.image.repository }}:{{ .Values.mcpSidecars.flux.image.tag }}" args: @@ -147,6 +147,40 @@ spec: resources: {{- toYaml .Values.mcpSidecars.flux.resources | nindent 12 }} {{- end }} + {{- if .Values.mcpSidecars.homeassistant.enabled }} + - name: homeassistant-mcp + image: "{{ .Values.mcpSidecars.homeassistant.image.repository }}:{{ .Values.mcpSidecars.homeassistant.image.tag }}" + imagePullPolicy: Always + command: ["fastmcp", "run", "fastmcp-sse.json"] + ports: + - name: homeassistant + containerPort: {{ .Values.mcpSidecars.homeassistant.port }} + env: + - name: HOMEASSISTANT_URL + valueFrom: + secretKeyRef: + name: {{ include "antigravity.envSecretName" . }} + key: homeassistant-url + optional: true + - name: HOMEASSISTANT_TOKEN + valueFrom: + secretKeyRef: + name: {{ include "antigravity.envSecretName" . }} + key: homeassistant-token + optional: true + livenessProbe: + tcpSocket: + port: {{ .Values.mcpSidecars.homeassistant.port }} + initialDelaySeconds: 10 + periodSeconds: 10 + readinessProbe: + tcpSocket: + port: {{ .Values.mcpSidecars.homeassistant.port }} + initialDelaySeconds: 5 + periodSeconds: 5 + resources: + {{- toYaml .Values.mcpSidecars.homeassistant.resources | nindent 12 }} + {{- end }} volumes: - name: workspace emptyDir: {} diff --git a/chart/values.yaml b/chart/values.yaml index 149708b..5d3c560 100644 --- a/chart/values.yaml +++ b/chart/values.yaml @@ -95,3 +95,16 @@ mcpSidecars: limits: memory: "256Mi" cpu: "500m" + homeassistant: + enabled: false # Disabled by default, requires HOMEASSISTANT_URL and HOMEASSISTANT_TOKEN + image: + repository: ghcr.io/homeassistant-ai/ha-mcp + tag: stable + port: 8087 + resources: + requests: + memory: "64Mi" + cpu: "50m" + limits: + memory: "256Mi" + cpu: "500m" diff --git a/memory/MEMORY.md b/memory/MEMORY.md index 0f533f0..504492f 100644 --- a/memory/MEMORY.md +++ b/memory/MEMORY.md @@ -2,19 +2,28 @@ ## Key Architecture Facts - Image: `ghcr.io/cpfarhood/devcontainer:latest` (repo name is `devcontainer`, not `antigravity`) -- `imagePullPolicy: Always` in statefulset (set during initial deployment debugging) +- Deployed via Helm chart (`chart/`), not kustomize anymore - Service must NOT be headless (`clusterIP: None`) — Cilium gateway can't route to headless services - `SECURE_CONNECTION=0` — TLS is terminated at the gateway, not the app - Container user is `user` (UID 1000) — baseimage-gui runs startapp.sh as `app` user, sudo is not available -- HTTPRoute is managed by Authentik outpost, not in kustomization -## Cluster Patterns -- External gateway: `external` in `gateway-system`, handles `*.farh.net` on port 443 HTTPS only -- Hostnames must be exactly `*.farh.net` (not `*.subdomain.farh.net`) to match gateway listener -- Authentik outpost Terraform lives in `../kubernetes/terraform/authentik-*-proxy/` -- Outpost config uses `external` gateway for public apps, `internal` for internal apps +## Deployment Method +- **Primary**: Helm chart in `chart/` directory +- **Makefile targets**: `helm-deploy`, `helm-delete`, `helm-logs`, `helm-shell`, `helm-port-forward` +- **Old kustomize** (`k8s/` directory) has been removed — all deployments use Helm now +- Chart published as OCI artifact to GHCR, reconciled by Flux + +## MCP Sidecars +- **Kubernetes MCP** (port 8080): Only deployed when enabled AND `clusterAccess` != `none` +- **Flux MCP** (port 8081): Only deployed when enabled AND `clusterAccess` != `none` +- **Home Assistant MCP** (port 8087): Disabled by default, requires secrets: + - `homeassistant-url`: Base URL like `http://homeassistant.local:8123` + - `homeassistant-token`: Long-lived access token +- **Playwright MCP**: External service, not a sidecar +- Configure via `mcpSidecars..enabled` in values ## Common Gotchas - `baseimage-gui` creates user dynamically — don't hardcode usernames in scripts, use numeric UID/GID - `chown /home` fails (PVC root not owned by container) — only chown subdirectories - `sudo` not available in startapp.sh — script already runs as correct user +- MCP sidecars need appropriate secrets and RBAC permissions to function