Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions operators/kustomization.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ resources:
- forgejo
- grafana
- kube-prometheus-stack
- toolhive
- tor-controller
6 changes: 6 additions & 0 deletions operators/toolhive/kustomization.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- toolhive-crds-app.yaml
- toolhive-operator-app.yaml
33 changes: 33 additions & 0 deletions operators/toolhive/toolhive-crds-app.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: toolhive-operator-crds
namespace: argocd
annotations:
argocd.argoproj.io/sync-wave: "0"
spec:
ignoreDifferences:
- group: apiextensions.k8s.io
kind: CustomResourceDefinition
jqPathExpressions:
- .metadata.annotations
- .spec.conversion
- .spec.preserveUnknownFields
- .status
project: default
destination:
server: https://kubernetes.default.svc
source:
chart: toolhive-operator-crds
repoURL: ghcr.io/stacklok/toolhive
targetRevision: 0.44.0
helm:
releaseName: toolhive-operator-crds
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- ServerSideApply=true
- RespectIgnoreDifferences=true
34 changes: 34 additions & 0 deletions operators/toolhive/toolhive-operator-app.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: toolhive-operator
namespace: argocd
annotations:
# Operator crashes on startup if its CRDs are missing.
argocd.argoproj.io/sync-wave: "1"
spec:
project: default
destination:
server: https://kubernetes.default.svc
namespace: toolhive-system
source:
chart: toolhive-operator
repoURL: ghcr.io/stacklok/toolhive
targetRevision: 0.44.0
helm:
releaseName: toolhive-operator
values: |
operator:
# Chart defaults carry requests/limits; empty keeps single-node policy.
resources: {}
env:
- name: TOOLHIVE_SKIP_UPDATE_CHECK
value: "true"
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- ServerSideApply=true
- CreateNamespace=true
1 change: 1 addition & 0 deletions workloads/apps/kustomization.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ resources:
- arc-app.yaml
- grafana-app.yaml
- kubectl-tunnel-app.yaml
- mcp-gateway-app.yaml
- opencode-app.yaml
- ../argocd
29 changes: 29 additions & 0 deletions workloads/apps/mcp-gateway-app.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: mcp-gateway
namespace: argocd
annotations:
argocd.argoproj.io/sync-wave: "1"
spec:
project: default
source:
repoURL: https://github.com/makeitworkcloud/kustomize-cluster.git
path: workloads/mcp-gateway
targetRevision: main
destination:
server: https://kubernetes.default.svc
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
- SkipDryRunOnMissingResource=true
retry:
limit: 5
backoff:
duration: 30s
maxDuration: 5m
factor: 2
5 changes: 5 additions & 0 deletions workloads/apps/wait-for-crds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ spec:
grafanadashboards.grafana.integreatly.org
servicemonitors.monitoring.coreos.com
probes.monitoring.coreos.com
mcpservers.toolhive.stacklok.dev
mcpremoteproxies.toolhive.stacklok.dev
mcpgroups.toolhive.stacklok.dev
virtualmcpservers.toolhive.stacklok.dev
mcpexternalauthconfigs.toolhive.stacklok.dev
)

for crd in "${CRDS[@]}"; do
Expand Down
26 changes: 26 additions & 0 deletions workloads/mcp-gateway/github-secret.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
apiVersion: v1
kind: Secret
metadata:
name: mcp-github
namespace: mcp
annotations:
argocd.argoproj.io/sync-wave: "0"
type: Opaque
stringData:
# Value must be "Bearer <pat>"; set the real token with `sops` before relying on the github proxy.
github_token: ENC[AES256_GCM,data:ZFkkbMtBs+T1WOqkR34eLVFvfcXXA2kb9bgYo0+5,iv:jfQFwykT9AtNXvVZZAHG2EI7yGh8K1xhKBQoiZxPZAI=,tag:wzeWjm30uliTyp9VDFZiRQ==,type:str]
sops:
age:
- enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBhZ29ZazZIbGNGQk5ISmNo
ci9JTXJEOVZsbExtRXRacFRlWXVnNnB2eVN3CkRudXFnS2J0VlFzTUJ2U1NOeXll
NmU3WVM5aXhxWllaRi93NWNsb3VRWlEKLS0tIEkrNm94TzRVRXhoa0NwZUtJUmJS
anJiUWdCNkdOSjcwRUJjcUUyci9WVEkKlRM+6XutijQ1DhMowYFB5NjNzjyKzLHd
IK/4zKTGd3N/iMld4f3eNefoDAvzhXcjNtTBjy1G5tc3kx6qBpRthQ==
-----END AGE ENCRYPTED FILE-----
recipient: age152ek83tm4fj5u70r3fecytn4kg7c5xca24erjchxexx4pfqg6das7q763l
encrypted_regex: ^(token|api-token|apiToken|clientID|clientSecret|client_id|client_secret|password|secret|github_token|CLOUDFLARE_API_TOKEN|KIMI_API_KEY|credentials\.json|.*_SERVICE_KEY|GF_AUTH_(GITHUB|GENERIC_OAUTH)_CLIENT_SECRET|GF_SECURITY_ADMIN_PASSWORD|dex\.[a-z]+\.clientID|dex\.[a-z]+\.clientSecret|OIDC_CLIENT_(ID|SECRET))$
lastmodified: "2026-08-26T02:46:44Z"
mac: ENC[AES256_GCM,data:DJvovnhPN4xjHSjGkJj64hyyMZGhbS7eawmRYn3LaTV0Q8Lxhg5AsNUnREKkOFIhv5pvgWL0dN0Yr8MnIjg0qxpP+ckqbFZgErDQoLV8A5V7nrU+W9CHxdZ9HLCJ53ZjPzp/pALAzBENODWKTqkOhcBHADnUUqQT2fZVcd1gHpc=,iv:BK64OlZmt5M2Ls166Yn2s+IXic/R632F5x8eThBgBXg=,tag:vtfRfXZHiCbLNyYS8TPWiQ==,type:str]
version: 3.13.3
11 changes: 11 additions & 0 deletions workloads/mcp-gateway/ksops-mcp-gateway-secrets.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
apiVersion: viaduct.ai/v1
kind: ksops
metadata:
name: ksops-mcp-gateway-secrets
annotations:
config.kubernetes.io/function: |
exec:
path: ksops
files:
- github-secret.yaml
91 changes: 91 additions & 0 deletions workloads/mcp-gateway/kubernetes-mcpserver.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: mcp-kubernetes
namespace: mcp
annotations:
argocd.argoproj.io/sync-wave: "0"
automountServiceAccountToken: false
---
# Read-only across the cluster, but Secrets stay excluded: KSOPS-decrypted
# secrets live in-cluster and the gateway must not become a read path to them.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: mcp-kubernetes-readonly
annotations:
argocd.argoproj.io/sync-wave: "0"
rules:
- apiGroups: [""]
resources:
- configmaps
- endpoints
- events
- limitranges
- namespaces
- nodes
- persistentvolumeclaims
- pods
- pods/log
- replicationcontrollers
- resourcequotas
- serviceaccounts
- services
verbs: ["get", "list", "watch"]
- apiGroups:
- actions.github.com
- apiextensions.k8s.io
- apps
- argoproj.io
- autoscaling
- batch
- cert-manager.io
- grafana.integreatly.org
- metrics.k8s.io
- monitoring.coreos.com
- networking.cfargotunnel.com
- networking.k8s.io
- policy
- rbac.authorization.k8s.io
- storage.k8s.io
- toolhive.stacklok.dev
- tor.k8s.torproject.org
resources: ["*"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: mcp-kubernetes-readonly
annotations:
argocd.argoproj.io/sync-wave: "0"
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: mcp-kubernetes-readonly
subjects:
- kind: ServiceAccount
name: mcp-kubernetes
namespace: mcp
---
# --read-only is belt-and-braces: even an RBAC mistake cannot make this
# server perform writes through the MCP tool surface.
apiVersion: toolhive.stacklok.dev/v1beta1
kind: MCPServer
metadata:
name: kubernetes
namespace: mcp
annotations:
argocd.argoproj.io/sync-wave: "1"
spec:
groupRef:
name: gateway
image: ghcr.io/containers/kubernetes-mcp-server:v0.0.66@sha256:6d650f4bd6ac303ad82713c997e73a2d001602f9bf17392c9b9a0e30e29c6423
transport: streamable-http
mcpPort: 8080
serviceAccount: mcp-kubernetes
args:
- --read-only
- --port
- "8080"
11 changes: 11 additions & 0 deletions workloads/mcp-gateway/kustomization.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- namespace.yaml
- mcpgroup.yaml
- kubernetes-mcpserver.yaml
- remote-proxies.yaml
- vmcp.yaml
generators:
- ksops-mcp-gateway-secrets.yaml
9 changes: 9 additions & 0 deletions workloads/mcp-gateway/mcpgroup.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
apiVersion: toolhive.stacklok.dev/v1beta1
kind: MCPGroup
metadata:
name: gateway
namespace: mcp
annotations:
argocd.argoproj.io/sync-wave: "0"
spec: {}
7 changes: 7 additions & 0 deletions workloads/mcp-gateway/namespace.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
apiVersion: v1
kind: Namespace
metadata:
name: mcp
annotations:
argocd.argoproj.io/sync-wave: "0"
44 changes: 44 additions & 0 deletions workloads/mcp-gateway/remote-proxies.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
apiVersion: toolhive.stacklok.dev/v1beta1
kind: MCPRemoteProxy
metadata:
name: context7
namespace: mcp
annotations:
argocd.argoproj.io/sync-wave: "1"
spec:
groupRef:
name: gateway
remoteUrl: https://mcp.context7.com/mcp
transport: streamable-http
---
apiVersion: toolhive.stacklok.dev/v1beta1
kind: MCPRemoteProxy
metadata:
name: github
namespace: mcp
annotations:
argocd.argoproj.io/sync-wave: "1"
spec:
groupRef:
name: gateway
remoteUrl: https://api.githubcopilot.com/mcp/
transport: streamable-http
externalAuthConfigRef:
name: github-token
---
# The header value is injected verbatim, so the Secret holds "Bearer <pat>".
apiVersion: toolhive.stacklok.dev/v1beta1
kind: MCPExternalAuthConfig
metadata:
name: github-token
namespace: mcp
annotations:
argocd.argoproj.io/sync-wave: "1"
spec:
type: headerInjection
headerInjection:
headerName: Authorization
valueSecretRef:
name: mcp-github
key: github_token
16 changes: 16 additions & 0 deletions workloads/mcp-gateway/vmcp.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
# ClusterIP-only trust boundary: edge auth is Cloudflare Access on
# mcp.makeitwork.cloud (tfroot-cloudflare); flannel does not enforce
# NetworkPolicy on this cluster, so anonymous here is a deliberate choice.
apiVersion: toolhive.stacklok.dev/v1beta1
kind: VirtualMCPServer
metadata:
name: gateway
namespace: mcp
annotations:
argocd.argoproj.io/sync-wave: "2"
spec:
groupRef:
name: gateway
incomingAuth:
type: anonymous
8 changes: 8 additions & 0 deletions workloads/opencode/configmap.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,13 @@ data:
"apiKey": "{env:KIMI_API_KEY}"
}
}
},
"mcp": {
"mcp-gateway": {
"type": "remote",
"url": "http://vmcp-gateway.mcp.svc:4483/mcp",
"enabled": true,
"oauth": false
}
}
}
Loading