From 469a078252395f833f7fac610e6fde37e0e6fd9d Mon Sep 17 00:00:00 2001 From: Steven Welch Date: Sat, 12 Sep 2026 15:39:52 -0600 Subject: [PATCH 1/7] feat: expose direct MCP endpoints --- workloads/mcp-gateway/tunnel-binding.yaml | 67 +++++++++++++++++++++-- 1 file changed, 61 insertions(+), 6 deletions(-) diff --git a/workloads/mcp-gateway/tunnel-binding.yaml b/workloads/mcp-gateway/tunnel-binding.yaml index 20f9f77..bee6cf4 100644 --- a/workloads/mcp-gateway/tunnel-binding.yaml +++ b/workloads/mcp-gateway/tunnel-binding.yaml @@ -8,12 +8,67 @@ metadata: argocd.argoproj.io/sync-wave: "2" makeitwork.cloud/dns-reconcile: "2026-09-02" subjects: - # Single aggregate route: every integration is addressed through the - # vmcp-gateway aggregate (in-cluster http://vmcp-gateway.mcp.svc:4483/mcp, - # external mcp.makeitwork.cloud) with member-name-prefixed tools. The former - # per-integration subjects were retired when OpenCode moved to the internal - # aggregate endpoint; reintroduce per-backend routes only with a new owner - # decision, and retire their DNS through this binding when doing so. + # Each ToolHive proxy is externally reachable only through its dedicated + # Cloudflare Access application. The direct routes preserve backend-specific + # tool discovery for clients while proxy Services remain ClusterIP-only. + - name: apify + spec: + fqdn: mcp-apify.makeitwork.cloud + target: http://mcp-apify-remote-proxy.mcp.svc:8080 + - name: argocd + spec: + fqdn: mcp-argocd.makeitwork.cloud + target: http://mcp-argocd-proxy.mcp.svc:8080 + - name: aws + spec: + fqdn: mcp-aws.makeitwork.cloud + target: http://mcp-aws-proxy.mcp.svc:8080 + - name: aws-docs + spec: + fqdn: mcp-aws-docs.makeitwork.cloud + target: http://mcp-aws-docs-proxy.mcp.svc:8080 + - name: cloudflare + spec: + fqdn: mcp-cloudflare.makeitwork.cloud + target: http://mcp-cloudflare-read-remote-proxy.mcp.svc:8080 + - name: context7 + spec: + fqdn: mcp-context7.makeitwork.cloud + target: http://mcp-context7-remote-proxy.mcp.svc:8080 + - name: gcp + spec: + fqdn: mcp-gcp.makeitwork.cloud + target: http://mcp-gcp-proxy.mcp.svc:8080 + - name: grafana + spec: + fqdn: mcp-grafana.makeitwork.cloud + target: http://mcp-grafana-remote-proxy.mcp.svc:8080 + - name: kubernetes + spec: + fqdn: mcp-kubernetes.makeitwork.cloud + target: http://mcp-kubernetes-proxy.mcp.svc:8080 + - name: parallel-search + spec: + fqdn: mcp-parallel-search.makeitwork.cloud + target: http://mcp-parallel-search-remote-proxy.mcp.svc:8080 + - name: playwright + spec: + fqdn: mcp-playwright.makeitwork.cloud + target: http://mcp-playwright-proxy.mcp.svc:8080 + - name: slidespeak + spec: + fqdn: mcp-slidespeak.makeitwork.cloud + target: http://mcp-slidespeak-remote-proxy.mcp.svc:8080 + - name: terraform-docs + spec: + fqdn: mcp-terraform-docs.makeitwork.cloud + target: http://mcp-terraform-docs-proxy.mcp.svc:8080 + - name: twilio-docs + spec: + fqdn: mcp-twilio-docs.makeitwork.cloud + target: http://mcp-twilio-docs-remote-proxy.mcp.svc:8080 + # Retain the aggregate during client migration. Remove this subject with the + # VirtualMCPServer only after every external client uses direct endpoints. - name: vmcp-gateway spec: fqdn: mcp.makeitwork.cloud From 68cb2d16e75a8fdb55a50ef167c033b326f326ac Mon Sep 17 00:00:00 2001 From: Steven Welch Date: Sat, 12 Sep 2026 15:47:27 -0600 Subject: [PATCH 2/7] fix: align MCP route subjects with services --- workloads/mcp-gateway/tunnel-binding.yaml | 28 +++++++++++------------ 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/workloads/mcp-gateway/tunnel-binding.yaml b/workloads/mcp-gateway/tunnel-binding.yaml index bee6cf4..d55bf1e 100644 --- a/workloads/mcp-gateway/tunnel-binding.yaml +++ b/workloads/mcp-gateway/tunnel-binding.yaml @@ -11,59 +11,59 @@ subjects: # Each ToolHive proxy is externally reachable only through its dedicated # Cloudflare Access application. The direct routes preserve backend-specific # tool discovery for clients while proxy Services remain ClusterIP-only. - - name: apify + - name: mcp-apify-remote-proxy spec: fqdn: mcp-apify.makeitwork.cloud target: http://mcp-apify-remote-proxy.mcp.svc:8080 - - name: argocd + - name: mcp-argocd-proxy spec: fqdn: mcp-argocd.makeitwork.cloud target: http://mcp-argocd-proxy.mcp.svc:8080 - - name: aws + - name: mcp-aws-proxy spec: fqdn: mcp-aws.makeitwork.cloud target: http://mcp-aws-proxy.mcp.svc:8080 - - name: aws-docs + - name: mcp-aws-docs-proxy spec: fqdn: mcp-aws-docs.makeitwork.cloud target: http://mcp-aws-docs-proxy.mcp.svc:8080 - - name: cloudflare + - name: mcp-cloudflare-read-remote-proxy spec: fqdn: mcp-cloudflare.makeitwork.cloud target: http://mcp-cloudflare-read-remote-proxy.mcp.svc:8080 - - name: context7 + - name: mcp-context7-remote-proxy spec: fqdn: mcp-context7.makeitwork.cloud target: http://mcp-context7-remote-proxy.mcp.svc:8080 - - name: gcp + - name: mcp-gcp-proxy spec: fqdn: mcp-gcp.makeitwork.cloud target: http://mcp-gcp-proxy.mcp.svc:8080 - - name: grafana + - name: mcp-grafana-remote-proxy spec: fqdn: mcp-grafana.makeitwork.cloud target: http://mcp-grafana-remote-proxy.mcp.svc:8080 - - name: kubernetes + - name: mcp-kubernetes-proxy spec: fqdn: mcp-kubernetes.makeitwork.cloud target: http://mcp-kubernetes-proxy.mcp.svc:8080 - - name: parallel-search + - name: mcp-parallel-search-remote-proxy spec: fqdn: mcp-parallel-search.makeitwork.cloud target: http://mcp-parallel-search-remote-proxy.mcp.svc:8080 - - name: playwright + - name: mcp-playwright-proxy spec: fqdn: mcp-playwright.makeitwork.cloud target: http://mcp-playwright-proxy.mcp.svc:8080 - - name: slidespeak + - name: mcp-slidespeak-remote-proxy spec: fqdn: mcp-slidespeak.makeitwork.cloud target: http://mcp-slidespeak-remote-proxy.mcp.svc:8080 - - name: terraform-docs + - name: mcp-terraform-docs-proxy spec: fqdn: mcp-terraform-docs.makeitwork.cloud target: http://mcp-terraform-docs-proxy.mcp.svc:8080 - - name: twilio-docs + - name: mcp-twilio-docs-remote-proxy spec: fqdn: mcp-twilio-docs.makeitwork.cloud target: http://mcp-twilio-docs-remote-proxy.mcp.svc:8080 From 076f619528793ecf72795c45b13107de0a5d39cc Mon Sep 17 00:00:00 2001 From: Steven Welch Date: Sat, 12 Sep 2026 15:47:56 -0600 Subject: [PATCH 3/7] docs: record direct MCP endpoint rollout --- workloads/mcp-gateway/README.md | 75 ++++++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 21 deletions(-) diff --git a/workloads/mcp-gateway/README.md b/workloads/mcp-gateway/README.md index 49c6571..b2d2806 100644 --- a/workloads/mcp-gateway/README.md +++ b/workloads/mcp-gateway/README.md @@ -1,30 +1,63 @@ -# MCP Gateway — `gcp` ToolHive Backend +# ToolHive MCP endpoints -This directory deploys the local `gcp` backend for the MCP gateway. MCPGroup -members carry clean integration keys (`gcp`, `kubernetes`, `argocd`, `apify`, -`aws`, `aws-docs`, `cloudflare-read`, `context7`, `grafana`, `parallel-search`, -`playwright`, `slidespeak`, `terraform-docs`, `twilio-docs`); the `vmcp-gateway` -aggregate exposes their tools name-prefixed with those keys, in-cluster at -`http://vmcp-gateway.mcp.svc:4483/mcp` and externally at `mcp.makeitwork.cloud` -behind Cloudflare Access. `github`, `hero-ssh`, and `codebase-memory` remain -intentionally non-aggregated per-backend Services. +This directory deploys the ToolHive backends used by OpenCode and external MCP +clients. OpenCode connects directly to the ClusterIP proxy Services. External +clients use one Cloudflare Access-protected endpoint per selected backend; the +`vmcp-gateway` aggregate remains available only during external-client +migration. + +## External endpoints + +Each direct endpoint is `https://mcp-.makeitwork.cloud/mcp` for: + +`apify`, `argocd`, `aws`, `aws-docs`, `cloudflare`, `context7`, `gcp`, +`grafana`, `kubernetes`, `parallel-search`, `playwright`, `slidespeak`, +`terraform-docs`, and `twilio-docs`. + +The `TunnelBinding` owns workload DNS and routes each hostname to its +ToolHive-generated ClusterIP proxy Service. `tfroot-cloudflare` owns the +matching Cloudflare Access applications. The proxy Services remain internal; +Cloudflare Access is the only external authentication boundary. + +## Authentication and security boundary + +Owner decision, 2026-09-12: for the solo-developer external-MCP use case, every +direct endpoint may use the existing shared MCP Gateway Cloudflare Access +service token. Clients provide its `CF-Access-Client-*` headers at the edge; +no Cloudflare Access credential is stored in these manifests, and backend API +credentials remain in their existing cluster-owned Secrets. + +`github`, `hero-ssh`, and `codebase-memory` remain ClusterIP-only and have no +external TunnelBinding subject. The Cloudflare and provider credentials attached +to other ToolHive proxies remain separate backend authorization boundaries. ## Workload -- **Image:** `ghcr.io/makeitworkcloud/gcloud-mcp@sha256:1a2e38cf1f1855f445b17a1fe6669045b5a3fa794ca54ccf9a22c0aee68dd1d2`. -- **Identity:** dedicated Kubernetes ServiceAccount `mcp/gcloud-mcp` with a 3600-second projected token for the Google Workload Identity Federation (WIF) provider. -- **Configuration:** non-secret, content-addressed ConfigMaps carry the external-account configuration and restricted `gcloud` command allowlist. +- **GCP image:** `ghcr.io/makeitworkcloud/gcloud-mcp@sha256:1a2e38cf1f1855f445b17a1fe6669045b5a3fa794ca54ccf9a22c0aee68dd1d2`. +- **GCP identity:** dedicated Kubernetes ServiceAccount `mcp/gcloud-mcp` with a 3600-second projected token for the Google Workload Identity Federation (WIF) provider. +- **GCP configuration:** non-secret, content-addressed ConfigMaps carry the external-account configuration and restricted `gcloud` command allowlist. - **Kustomize:** `gcloud-mcp-namereference.yaml` is required because ToolHive stores `podTemplateSpec` as a `RawExtension`, outside Kustomize's default name-reference rules. -## Ownership and Security Boundary +`tfroot-gcp` owns and applies the Google Cloud WIF provider. The provider +accepts only `system:serviceaccount:mcp:gcloud-mcp` and impersonates +`gcloud-mcp@makeitworkcloud.iam.gserviceaccount.com`. + +## Delivery and verification + +The Cloudflare Access applications must be applied successfully from +`tfroot-cloudflare` **before** this GitOps change is merged: adding a +`TunnelBinding` subject first would expose a route without its required edge +Access application. This is a cross-repository ordering requirement. -- `tfroot-gcp` owns and applies the Google Cloud WIF provider; this repository consumes it. -- The provider accepts only `system:serviceaccount:mcp:gcloud-mcp` and impersonates `gcloud-mcp@makeitworkcloud.iam.gserviceaccount.com`. -- The command allowlist and GCP IAM roles are independent read boundaries. No credentials or token values are committed. -- `groupRef: gateway` exposes the backend through the existing `mcp.makeitwork.cloud` Cloudflare Access path under the `gcp` integration key; this workload creates no dedicated TunnelBinding or DNS record. The per-integration TunnelBinding subjects were retired when OpenCode moved to the single internal aggregate endpoint; only the aggregate root route remains. -- Owner waiver, 2026-09-09: the aggregate is anonymous to in-cluster callers. The single developer consumer holds the external Cloudflare Access pre-shared key. -- Owner approval, 2026-09-11: the browser-capable `playwright` member is exposed through the external aggregate; the shared MCP Gateway Cloudflare Access service token remains the edge authentication boundary. +After the Access apply, merge the GitOps route change and separately verify: -## Delivery and Verification +1. the `mcp-gateway` Argo CD Application and every generated proxy Service are healthy; +2. each expected CNAME, ownership TXT record, and tunnel route exists; +3. each direct endpoint rejects a request without Cloudflare Access headers; +4. each direct endpoint accepts an authenticated MCP `tools/list` request; and +5. no mutating MCP tool is used as a rollout probe. -The WIF provider must apply before this workload can authenticate. After a GitOps merge, verify the rendered ConfigMap references, GCP WIF startup, Gateway/Argo reconciliation, and an allowed read-only MCP request. Do not use a write probe without separate approval. +Retain `mcp.makeitwork.cloud` and `vmcp-gateway` until every external client +uses direct endpoints. Roll back in reverse order: first remove the direct +TunnelBinding subjects and verify their operator-owned DNS cleanup, then revert +the Cloudflare Access application change through its environment-gated apply. From fa7e169990cb7d248bcc96e504d847e35ce07f1c Mon Sep 17 00:00:00 2001 From: Steven Welch Date: Sat, 12 Sep 2026 15:50:53 -0600 Subject: [PATCH 4/7] refactor: remove aggregate MCP route --- workloads/mcp-gateway/tunnel-binding.yaml | 62 +++++------------------ 1 file changed, 14 insertions(+), 48 deletions(-) diff --git a/workloads/mcp-gateway/tunnel-binding.yaml b/workloads/mcp-gateway/tunnel-binding.yaml index d55bf1e..5862104 100644 --- a/workloads/mcp-gateway/tunnel-binding.yaml +++ b/workloads/mcp-gateway/tunnel-binding.yaml @@ -12,67 +12,33 @@ subjects: # Cloudflare Access application. The direct routes preserve backend-specific # tool discovery for clients while proxy Services remain ClusterIP-only. - name: mcp-apify-remote-proxy - spec: - fqdn: mcp-apify.makeitwork.cloud - target: http://mcp-apify-remote-proxy.mcp.svc:8080 + spec: {fqdn: mcp-apify.makeitwork.cloud, target: http://mcp-apify-remote-proxy.mcp.svc:8080} - name: mcp-argocd-proxy - spec: - fqdn: mcp-argocd.makeitwork.cloud - target: http://mcp-argocd-proxy.mcp.svc:8080 + spec: {fqdn: mcp-argocd.makeitwork.cloud, target: http://mcp-argocd-proxy.mcp.svc:8080} - name: mcp-aws-proxy - spec: - fqdn: mcp-aws.makeitwork.cloud - target: http://mcp-aws-proxy.mcp.svc:8080 + spec: {fqdn: mcp-aws.makeitwork.cloud, target: http://mcp-aws-proxy.mcp.svc:8080} - name: mcp-aws-docs-proxy - spec: - fqdn: mcp-aws-docs.makeitwork.cloud - target: http://mcp-aws-docs-proxy.mcp.svc:8080 + spec: {fqdn: mcp-aws-docs.makeitwork.cloud, target: http://mcp-aws-docs-proxy.mcp.svc:8080} - name: mcp-cloudflare-read-remote-proxy - spec: - fqdn: mcp-cloudflare.makeitwork.cloud - target: http://mcp-cloudflare-read-remote-proxy.mcp.svc:8080 + spec: {fqdn: mcp-cloudflare.makeitwork.cloud, target: http://mcp-cloudflare-read-remote-proxy.mcp.svc:8080} - name: mcp-context7-remote-proxy - spec: - fqdn: mcp-context7.makeitwork.cloud - target: http://mcp-context7-remote-proxy.mcp.svc:8080 + spec: {fqdn: mcp-context7.makeitwork.cloud, target: http://mcp-context7-remote-proxy.mcp.svc:8080} - name: mcp-gcp-proxy - spec: - fqdn: mcp-gcp.makeitwork.cloud - target: http://mcp-gcp-proxy.mcp.svc:8080 + spec: {fqdn: mcp-gcp.makeitwork.cloud, target: http://mcp-gcp-proxy.mcp.svc:8080} - name: mcp-grafana-remote-proxy - spec: - fqdn: mcp-grafana.makeitwork.cloud - target: http://mcp-grafana-remote-proxy.mcp.svc:8080 + spec: {fqdn: mcp-grafana.makeitwork.cloud, target: http://mcp-grafana-remote-proxy.mcp.svc:8080} - name: mcp-kubernetes-proxy - spec: - fqdn: mcp-kubernetes.makeitwork.cloud - target: http://mcp-kubernetes-proxy.mcp.svc:8080 + spec: {fqdn: mcp-kubernetes.makeitwork.cloud, target: http://mcp-kubernetes-proxy.mcp.svc:8080} - name: mcp-parallel-search-remote-proxy - spec: - fqdn: mcp-parallel-search.makeitwork.cloud - target: http://mcp-parallel-search-remote-proxy.mcp.svc:8080 + spec: {fqdn: mcp-parallel-search.makeitwork.cloud, target: http://mcp-parallel-search-remote-proxy.mcp.svc:8080} - name: mcp-playwright-proxy - spec: - fqdn: mcp-playwright.makeitwork.cloud - target: http://mcp-playwright-proxy.mcp.svc:8080 + spec: {fqdn: mcp-playwright.makeitwork.cloud, target: http://mcp-playwright-proxy.mcp.svc:8080} - name: mcp-slidespeak-remote-proxy - spec: - fqdn: mcp-slidespeak.makeitwork.cloud - target: http://mcp-slidespeak-remote-proxy.mcp.svc:8080 + spec: {fqdn: mcp-slidespeak.makeitwork.cloud, target: http://mcp-slidespeak-remote-proxy.mcp.svc:8080} - name: mcp-terraform-docs-proxy - spec: - fqdn: mcp-terraform-docs.makeitwork.cloud - target: http://mcp-terraform-docs-proxy.mcp.svc:8080 + spec: {fqdn: mcp-terraform-docs.makeitwork.cloud, target: http://mcp-terraform-docs-proxy.mcp.svc:8080} - name: mcp-twilio-docs-remote-proxy - spec: - fqdn: mcp-twilio-docs.makeitwork.cloud - target: http://mcp-twilio-docs-remote-proxy.mcp.svc:8080 - # Retain the aggregate during client migration. Remove this subject with the - # VirtualMCPServer only after every external client uses direct endpoints. - - name: vmcp-gateway - spec: - fqdn: mcp.makeitwork.cloud - target: http://vmcp-gateway.mcp.svc:4483 + spec: {fqdn: mcp-twilio-docs.makeitwork.cloud, target: http://mcp-twilio-docs-remote-proxy.mcp.svc:8080} tunnelRef: kind: ClusterTunnel name: cluster-apps From 595d7ee4f2a056d501f2c3a5da87b04d07359e40 Mon Sep 17 00:00:00 2001 From: Steven Welch Date: Sat, 12 Sep 2026 15:56:45 -0600 Subject: [PATCH 5/7] refactor: remove ToolHive aggregate membership Drop spec.groupRef gateway from every aggregated MCPServer/MCPRemoteProxy, remove the vmcp.yaml and mcpgroup.yaml kustomization entries, and refresh aggregate-specific comments and the README. The 14 direct TunnelBinding routes are unchanged. --- workloads/mcp-gateway/README.md | 11 ++++------- workloads/mcp-gateway/argocd-mcpserver.yaml | 9 ++++----- workloads/mcp-gateway/aws-docs-mcpserver.yaml | 2 -- workloads/mcp-gateway/aws-mcpserver.yaml | 2 -- workloads/mcp-gateway/cloudflare-remote-proxy.yaml | 11 +++++------ workloads/mcp-gateway/gcp-mcpserver.yaml | 9 ++++----- workloads/mcp-gateway/grafana-remote-proxy.yaml | 5 ++--- workloads/mcp-gateway/kubernetes-mcpserver.yaml | 6 ++---- workloads/mcp-gateway/kustomization.yaml | 2 -- workloads/mcp-gateway/playwright-mcpserver.yaml | 14 ++++++-------- workloads/mcp-gateway/remote-proxies.yaml | 12 +++--------- workloads/mcp-gateway/slidespeak-remote-proxy.yaml | 2 -- .../mcp-gateway/terraform-docs-mcpserver.yaml | 2 -- workloads/mcp-gateway/twilio-remote-proxy.yaml | 4 +--- 14 files changed, 31 insertions(+), 60 deletions(-) diff --git a/workloads/mcp-gateway/README.md b/workloads/mcp-gateway/README.md index b2d2806..4113a13 100644 --- a/workloads/mcp-gateway/README.md +++ b/workloads/mcp-gateway/README.md @@ -2,9 +2,7 @@ This directory deploys the ToolHive backends used by OpenCode and external MCP clients. OpenCode connects directly to the ClusterIP proxy Services. External -clients use one Cloudflare Access-protected endpoint per selected backend; the -`vmcp-gateway` aggregate remains available only during external-client -migration. +clients use one Cloudflare Access-protected endpoint per selected backend. ## External endpoints @@ -57,7 +55,6 @@ After the Access apply, merge the GitOps route change and separately verify: 4. each direct endpoint accepts an authenticated MCP `tools/list` request; and 5. no mutating MCP tool is used as a rollout probe. -Retain `mcp.makeitwork.cloud` and `vmcp-gateway` until every external client -uses direct endpoints. Roll back in reverse order: first remove the direct -TunnelBinding subjects and verify their operator-owned DNS cleanup, then revert -the Cloudflare Access application change through its environment-gated apply. +Roll back in reverse order: first remove the direct TunnelBinding subjects and +verify their operator-owned DNS cleanup, then revert the Cloudflare Access +application change through its environment-gated apply. diff --git a/workloads/mcp-gateway/argocd-mcpserver.yaml b/workloads/mcp-gateway/argocd-mcpserver.yaml index 71b10a5..0ccd85f 100644 --- a/workloads/mcp-gateway/argocd-mcpserver.yaml +++ b/workloads/mcp-gateway/argocd-mcpserver.yaml @@ -6,8 +6,6 @@ metadata: annotations: argocd.argoproj.io/sync-wave: "1" spec: - groupRef: - name: gateway image: ghcr.io/argoproj-labs/mcp-for-argocd:v0.9.0@sha256:dffc6c719d8635be2d5f2c8ad1c0178540325da4b40da0b373307e12b4c48f86 transport: streamable-http mcpPort: 3000 @@ -17,9 +15,10 @@ spec: - "3000" # The proxyrunner runs in a separate pod, so the server must leave # loopback. Unauthenticated + skipped Host check (no --allowed-host-header) - # is acceptable: the backend svc is ClusterIP-only behind the gateway, the - # same trust boundary as the vmcp anonymous decision in vmcp.yaml, and the - # real credential (the ArgoCD mcp account token) stays server-side. + # is acceptable: the backend svc is ClusterIP-only behind the ToolHive + # proxy, external callers must pass the direct endpoint's Cloudflare + # Access application, and the real credential (the ArgoCD mcp account + # token) stays server-side. - --bind-address - 0.0.0.0 - --allow-unauthenticated diff --git a/workloads/mcp-gateway/aws-docs-mcpserver.yaml b/workloads/mcp-gateway/aws-docs-mcpserver.yaml index 780cc6c..a098215 100644 --- a/workloads/mcp-gateway/aws-docs-mcpserver.yaml +++ b/workloads/mcp-gateway/aws-docs-mcpserver.yaml @@ -6,8 +6,6 @@ metadata: annotations: argocd.argoproj.io/sync-wave: "1" spec: - groupRef: - name: gateway # Upstream publishes sha-* tags only; the digest pins the current latest. image: public.ecr.aws/awslabs-mcp/awslabs/aws-documentation-mcp-server:latest@sha256:1e6bcdbe2a9085f304f9f6f8c39765883df2d900cf03970a81959f62db755ce1 transport: stdio diff --git a/workloads/mcp-gateway/aws-mcpserver.yaml b/workloads/mcp-gateway/aws-mcpserver.yaml index 33de07f..ab64558 100644 --- a/workloads/mcp-gateway/aws-mcpserver.yaml +++ b/workloads/mcp-gateway/aws-mcpserver.yaml @@ -6,8 +6,6 @@ metadata: annotations: argocd.argoproj.io/sync-wave: "1" spec: - groupRef: - name: gateway # Pinned to the upstream AWS MCP Proxy v1.6.4 release. image: public.ecr.aws/mcp-proxy-for-aws/mcp-proxy-for-aws:1.6.4 transport: stdio diff --git a/workloads/mcp-gateway/cloudflare-remote-proxy.yaml b/workloads/mcp-gateway/cloudflare-remote-proxy.yaml index 3153d3b..5f6831f 100644 --- a/workloads/mcp-gateway/cloudflare-remote-proxy.yaml +++ b/workloads/mcp-gateway/cloudflare-remote-proxy.yaml @@ -1,10 +1,11 @@ --- # Cloudflare's hosted API MCP server remains the upstream. This in-cluster # ToolHive proxy keeps the dedicated API token in the mcp namespace instead of -# in OpenCode. groupRef adds it to the externally exposed VirtualMCPServer -# aggregate: external callers still authenticate through the shared MCP -# Gateway Cloudflare Access service token, never with the Cloudflare API -# token, which the proxy injects only on outbound upstream requests. +# in OpenCode. It is externally reachable only through the dedicated direct +# route (mcp-cloudflare.makeitwork.cloud, tunnel-binding.yaml): external +# callers authenticate through that endpoint's Cloudflare Access application, +# never with the Cloudflare API token, which the proxy injects only on +# outbound upstream requests. # # Required credential contract: a separate, account-wide token scoped only to # Cloudflare API read permission groups for the account containing xnoto.dev, @@ -38,8 +39,6 @@ metadata: annotations: argocd.argoproj.io/sync-wave: "1" spec: - groupRef: - name: gateway # https://github.com/cloudflare/mcp documents this Streamable HTTP endpoint. remoteUrl: https://mcp.cloudflare.com/mcp transport: streamable-http diff --git a/workloads/mcp-gateway/gcp-mcpserver.yaml b/workloads/mcp-gateway/gcp-mcpserver.yaml index d2ae908..eb1705b 100644 --- a/workloads/mcp-gateway/gcp-mcpserver.yaml +++ b/workloads/mcp-gateway/gcp-mcpserver.yaml @@ -1,7 +1,8 @@ --- -# Cluster-internal backend aggregated by the existing Access-protected gateway; -# no dedicated TunnelBinding or public hostname is created. Owner waived the -# anonymous in-cluster aggregate path on 2026-09-09: the single developer +# Externally reachable only through the dedicated direct route +# (mcp-gcp.makeitwork.cloud, tunnel-binding.yaml) behind that endpoint's +# Cloudflare Access application; no anonymous in-cluster path exists. Owner +# waived the in-cluster aggregate path on 2026-09-09: the single developer # consumer holds the Cloudflare Access pre-shared key for external access. apiVersion: toolhive.stacklok.dev/v1beta1 kind: MCPServer @@ -11,8 +12,6 @@ metadata: annotations: argocd.argoproj.io/sync-wave: "1" spec: - groupRef: - name: gateway image: ghcr.io/makeitworkcloud/gcloud-mcp@sha256:1a2e38cf1f1855f445b17a1fe6669045b5a3fa794ca54ccf9a22c0aee68dd1d2 transport: stdio proxyMode: streamable-http diff --git a/workloads/mcp-gateway/grafana-remote-proxy.yaml b/workloads/mcp-gateway/grafana-remote-proxy.yaml index c33361f..f6712a9 100644 --- a/workloads/mcp-gateway/grafana-remote-proxy.yaml +++ b/workloads/mcp-gateway/grafana-remote-proxy.yaml @@ -1,5 +1,6 @@ # The Grafana MCP backend runs with its operator-owned token in the grafana -# namespace. This proxy preserves the existing external gateway exposure. +# namespace. This proxy exposes it externally through the dedicated direct +# route (mcp-grafana.makeitwork.cloud, tunnel-binding.yaml). apiVersion: toolhive.stacklok.dev/v1beta1 kind: MCPRemoteProxy metadata: @@ -8,8 +9,6 @@ metadata: annotations: argocd.argoproj.io/sync-wave: "1" spec: - groupRef: - name: gateway # This is a deliberately selected in-cluster ToolHive Service. ToolHive # blocks private endpoints by default, so permit this single upstream. allowPrivateEndpoint: true diff --git a/workloads/mcp-gateway/kubernetes-mcpserver.yaml b/workloads/mcp-gateway/kubernetes-mcpserver.yaml index 83d3335..0229445 100644 --- a/workloads/mcp-gateway/kubernetes-mcpserver.yaml +++ b/workloads/mcp-gateway/kubernetes-mcpserver.yaml @@ -11,8 +11,8 @@ metadata: automountServiceAccountToken: true --- # Operator access across the cluster. Secrets stay excluded: KSOPS-decrypted -# secrets live in-cluster and the gateway must not become a read or write path -# to them. The Kubernetes MCP server's generic resource operations use +# secrets live in-cluster and this MCP backend must not become a read or write +# path to them. The Kubernetes MCP server's generic resource operations use # server-side apply, which requires patch in addition to create/update/delete. apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -90,8 +90,6 @@ metadata: 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 diff --git a/workloads/mcp-gateway/kustomization.yaml b/workloads/mcp-gateway/kustomization.yaml index 17746b7..300338b 100644 --- a/workloads/mcp-gateway/kustomization.yaml +++ b/workloads/mcp-gateway/kustomization.yaml @@ -2,7 +2,6 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - namespace.yaml - - mcpgroup.yaml - kubernetes-mcpserver.yaml - argocd-mcpserver.yaml - grafana-remote-proxy.yaml @@ -25,7 +24,6 @@ resources: - remote-proxies.yaml - slidespeak-remote-proxy.yaml - twilio-remote-proxy.yaml - - vmcp.yaml - tunnel-binding.yaml configMapGenerator: # A content-addressed ConfigMap name changes the MCPServer pod template, diff --git a/workloads/mcp-gateway/playwright-mcpserver.yaml b/workloads/mcp-gateway/playwright-mcpserver.yaml index d7240a3..35e92cf 100644 --- a/workloads/mcp-gateway/playwright-mcpserver.yaml +++ b/workloads/mcp-gateway/playwright-mcpserver.yaml @@ -4,12 +4,12 @@ # print-to-PDF for OpenCode agents (career-agent research thread, see # agent-knowledge docs/agents/career/work/2026-09-02-pdf-generation-mcp-landscape.md). # -# Aggregated gateway member: groupRef exposes this backend through the -# VirtualMCPServer aggregate under the `playwright` integration key. The -# credentialed GitHub proxy (makeitwork-github-remote-proxy.yaml) remains -# intentionally non-aggregated. Owner approved the external aggregate exposure -# of this browser-capable member on 2026-09-11; the shared MCP Gateway -# Cloudflare Access service token remains the edge authentication boundary. +# Externally reachable only through the dedicated direct route +# (mcp-playwright.makeitwork.cloud, tunnel-binding.yaml) behind that endpoint's +# Cloudflare Access application. The credentialed GitHub proxy +# (makeitwork-github-remote-proxy.yaml) remains ClusterIP-only. Owner approved +# the external exposure of this browser-capable backend on 2026-09-11; +# Cloudflare Access remains the edge authentication boundary. # # Image digest resolved against the registry manifest API (OCI image index, # amd64/linux present): sha256 of the v0.0.80 index. @@ -21,8 +21,6 @@ metadata: annotations: argocd.argoproj.io/sync-wave: "1" spec: - groupRef: - name: gateway image: mcr.microsoft.com/playwright/mcp:v0.0.80@sha256:dda1f7f9b812e22946635c8af7df9288b96d3b9e3f0f1b8576d6823e2031c1de transport: streamable-http mcpPort: 8080 diff --git a/workloads/mcp-gateway/remote-proxies.yaml b/workloads/mcp-gateway/remote-proxies.yaml index 437cdbb..617be87 100644 --- a/workloads/mcp-gateway/remote-proxies.yaml +++ b/workloads/mcp-gateway/remote-proxies.yaml @@ -1,6 +1,6 @@ -# Aggregate members use clean integration keys; the vmcp aggregate exposes -# member tools name-prefixed with those keys. Credential-bearing members keep -# their cluster-owned Secrets, referenced by unchanged field names. +# Hosted remote MCP backends, each externally reachable through its dedicated +# direct route in tunnel-binding.yaml. Credential-bearing backends keep their +# cluster-owned Secrets, referenced by unchanged field names. --- apiVersion: toolhive.stacklok.dev/v1beta1 kind: MCPRemoteProxy @@ -10,8 +10,6 @@ metadata: annotations: argocd.argoproj.io/sync-wave: "1" spec: - groupRef: - name: gateway remoteUrl: https://mcp.context7.com/mcp transport: streamable-http --- @@ -23,8 +21,6 @@ metadata: annotations: argocd.argoproj.io/sync-wave: "1" spec: - groupRef: - name: gateway remoteUrl: https://search.parallel.ai/mcp transport: streamable-http headerForward: @@ -43,8 +39,6 @@ metadata: annotations: argocd.argoproj.io/sync-wave: "1" spec: - groupRef: - name: gateway remoteUrl: "https://mcp.apify.com/?tools=actors,apify/facebook-marketplace-scraper,apify/google-maps-scraper&telemetry-enabled=false" transport: streamable-http headerForward: diff --git a/workloads/mcp-gateway/slidespeak-remote-proxy.yaml b/workloads/mcp-gateway/slidespeak-remote-proxy.yaml index f131f47..4e1df49 100644 --- a/workloads/mcp-gateway/slidespeak-remote-proxy.yaml +++ b/workloads/mcp-gateway/slidespeak-remote-proxy.yaml @@ -9,8 +9,6 @@ metadata: annotations: argocd.argoproj.io/sync-wave: "1" spec: - groupRef: - name: gateway remoteUrl: https://mcp.slidespeak.co/mcp transport: streamable-http headerForward: diff --git a/workloads/mcp-gateway/terraform-docs-mcpserver.yaml b/workloads/mcp-gateway/terraform-docs-mcpserver.yaml index bf3b8a1..f96040c 100644 --- a/workloads/mcp-gateway/terraform-docs-mcpserver.yaml +++ b/workloads/mcp-gateway/terraform-docs-mcpserver.yaml @@ -6,8 +6,6 @@ metadata: annotations: argocd.argoproj.io/sync-wave: "1" spec: - groupRef: - name: gateway image: docker.io/hashicorp/terraform-mcp-server:1.2.0@sha256:bd095e2b442a2cb61255fe4db52f9e824f35d307a2044784c95d37a93f18d324 transport: streamable-http mcpPort: 8080 diff --git a/workloads/mcp-gateway/twilio-remote-proxy.yaml b/workloads/mcp-gateway/twilio-remote-proxy.yaml index d0b0d16..78a7553 100644 --- a/workloads/mcp-gateway/twilio-remote-proxy.yaml +++ b/workloads/mcp-gateway/twilio-remote-proxy.yaml @@ -1,5 +1,5 @@ --- -# Public-docs-only Twilio member: proxies Twilio's hosted documentation MCP +# Public-docs-only Twilio backend: proxies Twilio's hosted documentation MCP # (https://www.twilio.com/docs/ai/mcp) with no MCPExternalAuthConfig, no # forwarded Authorization header, and no secret references, so it cannot # reach Twilio account state even if upstream adds account-capable endpoints. @@ -14,7 +14,5 @@ metadata: annotations: argocd.argoproj.io/sync-wave: "1" spec: - groupRef: - name: gateway remoteUrl: https://mcp.twilio.com/docs transport: streamable-http From d5508038d162391c0ba0b7eaf28fed93164d9add Mon Sep 17 00:00:00 2001 From: Steven Welch Date: Sat, 12 Sep 2026 15:56:54 -0600 Subject: [PATCH 6/7] refactor: delete VirtualMCPServer gateway aggregate --- workloads/mcp-gateway/vmcp.yaml | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 workloads/mcp-gateway/vmcp.yaml diff --git a/workloads/mcp-gateway/vmcp.yaml b/workloads/mcp-gateway/vmcp.yaml deleted file mode 100644 index 9ac4c36..0000000 --- a/workloads/mcp-gateway/vmcp.yaml +++ /dev/null @@ -1,16 +0,0 @@ ---- -# 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 From 85791a78be06406234ece7f8843b0c811a311133 Mon Sep 17 00:00:00 2001 From: Steven Welch Date: Sat, 12 Sep 2026 15:57:03 -0600 Subject: [PATCH 7/7] refactor: delete MCPGroup gateway aggregate --- workloads/mcp-gateway/mcpgroup.yaml | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 workloads/mcp-gateway/mcpgroup.yaml diff --git a/workloads/mcp-gateway/mcpgroup.yaml b/workloads/mcp-gateway/mcpgroup.yaml deleted file mode 100644 index f071d81..0000000 --- a/workloads/mcp-gateway/mcpgroup.yaml +++ /dev/null @@ -1,9 +0,0 @@ ---- -apiVersion: toolhive.stacklok.dev/v1beta1 -kind: MCPGroup -metadata: - name: gateway - namespace: mcp - annotations: - argocd.argoproj.io/sync-wave: "0" -spec: {}