From d590b25a8c2aa16acafd588dfd93b0a4172062ef Mon Sep 17 00:00:00 2001 From: Steven Welch Date: Sat, 12 Sep 2026 15:39:52 -0600 Subject: [PATCH 1/4] feat: add direct MCP Access applications --- cf-access-mcp.tf | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/cf-access-mcp.tf b/cf-access-mcp.tf index 155f59d..00cf673 100644 --- a/cf-access-mcp.tf +++ b/cf-access-mcp.tf @@ -43,26 +43,31 @@ resource "cloudflare_zero_trust_access_application" "mcp_gateway" { ] } -# Per-backend MCP endpoints (mcp-.makeitwork.cloud) terminate on the -# same toolhive proxyrunners behind the same tunnel. One application per FQDN: -# Access domains cannot wildcard a name prefix, and first-level names are -# required anyway because Universal SSL only covers one subdomain level. +# Individual ToolHive backend endpoints use one first-level hostname each so +# Universal SSL covers them. Cloudflare Access applications are explicit per +# hostname because Access does not wildcard a name prefix. The shared service +# token is an owner-approved solo-developer trust boundary; the cluster keeps +# backend credentials and generated proxy Services internal. # -# This list controls Access applications only. Tunnel DNS is exclusively owned -# by TunnelBinding resources in kustomize-cluster. +# Tunnel DNS and routes remain exclusively owned by the corresponding +# TunnelBinding subjects in kustomize-cluster. Keep the aggregate application +# until all external clients have migrated to direct endpoints. locals { mcp_backends = [ - "makeitwork-apify", - "makeitwork-argocd", - "makeitwork-aws-docs", - "makeitwork-cloudflare", - "makeitwork-context7", - "makeitwork-github", - "makeitwork-github-xnoto", - "makeitwork-grafana", - "makeitwork-kubernetes", - "makeitwork-parallel-search", - "makeitwork-terraform-docs", + "apify", + "argocd", + "aws", + "aws-docs", + "cloudflare", + "context7", + "gcp", + "grafana", + "kubernetes", + "parallel-search", + "playwright", + "slidespeak", + "terraform-docs", + "twilio-docs", ] } From 3d2c72cef9d2d60ebf078be1654a70ca6e6d5d90 Mon Sep 17 00:00:00 2001 From: Steven Welch Date: Sat, 12 Sep 2026 15:50:14 -0600 Subject: [PATCH 2/4] refactor: retire aggregate Access application --- cf-access-mcp.tf | 53 ++++++++---------------------------------------- 1 file changed, 8 insertions(+), 45 deletions(-) diff --git a/cf-access-mcp.tf b/cf-access-mcp.tf index 00cf673..6353891 100644 --- a/cf-access-mcp.tf +++ b/cf-access-mcp.tf @@ -1,7 +1,8 @@ -# Gates mcp.makeitwork.cloud (ToolHive VirtualMCPServer in kustomize-cluster -# workloads/mcp-gateway). MCP clients are headless HTTP agents, so machine -# access uses a dedicated service token (CF-Access-Client-* headers) rather -# than the browser OIDC flow; the admins policy stays for interactive debug. +# MCP clients are headless HTTP agents, so every direct endpoint uses the +# existing Cloudflare Access service token (CF-Access-Client-* headers) rather +# than browser OIDC. The shared token is an owner-approved solo-developer trust +# boundary; individual ToolHive proxy Services and their backend credentials +# remain cluster-internal. resource "cloudflare_zero_trust_access_service_token" "mcp_gateway" { account_id = local.account_id name = "mcp-gateway" @@ -9,49 +10,11 @@ resource "cloudflare_zero_trust_access_service_token" "mcp_gateway" { duration = "forever" } -resource "cloudflare_zero_trust_access_application" "mcp_gateway" { - account_id = local.account_id - name = "MCP Gateway" - type = "self_hosted" - domain = "mcp.makeitwork.cloud" - session_duration = "24h" - - allowed_idps = [ - cloudflare_zero_trust_access_identity_provider.github.id, - ] - - policies = [ - { - name = "mcp-gateway-clients" - decision = "non_identity" - include = [{ - service_token = { - token_id = cloudflare_zero_trust_access_service_token.mcp_gateway.id - } - }] - }, - { - name = "makeitworkcloud-admins" - decision = "allow" - session_duration = "24h" - include = [{ - group = { - id = cloudflare_zero_trust_access_group.admins.id - } - }] - } - ] -} - # Individual ToolHive backend endpoints use one first-level hostname each so # Universal SSL covers them. Cloudflare Access applications are explicit per -# hostname because Access does not wildcard a name prefix. The shared service -# token is an owner-approved solo-developer trust boundary; the cluster keeps -# backend credentials and generated proxy Services internal. -# -# Tunnel DNS and routes remain exclusively owned by the corresponding -# TunnelBinding subjects in kustomize-cluster. Keep the aggregate application -# until all external clients have migrated to direct endpoints. +# hostname because Access does not wildcard a name prefix. Tunnel DNS and routes +# are exclusively owned by the corresponding TunnelBinding subjects in +# kustomize-cluster. locals { mcp_backends = [ "apify", From 19afc4e7ff5b186bf5f8863c775d0cde7c766ccd Mon Sep 17 00:00:00 2001 From: Steven Welch Date: Sat, 12 Sep 2026 16:00:42 -0600 Subject: [PATCH 3/4] fix: retain aggregate Access until route removal --- cf-access-mcp.tf | 71 ++++++++++++++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 27 deletions(-) diff --git a/cf-access-mcp.tf b/cf-access-mcp.tf index 6353891..387f31c 100644 --- a/cf-access-mcp.tf +++ b/cf-access-mcp.tf @@ -10,6 +10,44 @@ resource "cloudflare_zero_trust_access_service_token" "mcp_gateway" { duration = "forever" } +# Retain this Access application until Argo CD has removed the aggregate +# TunnelBinding route. Removing edge Access before that route reconciles away +# would briefly expose the aggregate without authentication. A later scoped +# cleanup PR removes this now-unrouted application after verification. +resource "cloudflare_zero_trust_access_application" "mcp_gateway" { + account_id = local.account_id + name = "MCP Gateway" + type = "self_hosted" + domain = "mcp.makeitwork.cloud" + session_duration = "24h" + + allowed_idps = [ + cloudflare_zero_trust_access_identity_provider.github.id, + ] + + policies = [ + { + name = "mcp-gateway-clients" + decision = "non_identity" + include = [{ + service_token = { + token_id = cloudflare_zero_trust_access_service_token.mcp_gateway.id + } + }] + }, + { + name = "makeitworkcloud-admins" + decision = "allow" + session_duration = "24h" + include = [{ + group = { + id = cloudflare_zero_trust_access_group.admins.id + } + }] + } + ] +} + # Individual ToolHive backend endpoints use one first-level hostname each so # Universal SSL covers them. Cloudflare Access applications are explicit per # hostname because Access does not wildcard a name prefix. Tunnel DNS and routes @@ -17,20 +55,9 @@ resource "cloudflare_zero_trust_access_service_token" "mcp_gateway" { # kustomize-cluster. locals { mcp_backends = [ - "apify", - "argocd", - "aws", - "aws-docs", - "cloudflare", - "context7", - "gcp", - "grafana", - "kubernetes", - "parallel-search", - "playwright", - "slidespeak", - "terraform-docs", - "twilio-docs", + "apify", "argocd", "aws", "aws-docs", "cloudflare", "context7", "gcp", + "grafana", "kubernetes", "parallel-search", "playwright", "slidespeak", + "terraform-docs", "twilio-docs", ] } @@ -42,29 +69,19 @@ resource "cloudflare_zero_trust_access_application" "mcp_gateway_backend" { domain = "mcp-${each.key}.makeitwork.cloud" session_duration = "24h" - allowed_idps = [ - cloudflare_zero_trust_access_identity_provider.github.id, - ] + allowed_idps = [cloudflare_zero_trust_access_identity_provider.github.id] policies = [ { name = "mcp-gateway-clients" decision = "non_identity" - include = [{ - service_token = { - token_id = cloudflare_zero_trust_access_service_token.mcp_gateway.id - } - }] + include = [{service_token = {token_id = cloudflare_zero_trust_access_service_token.mcp_gateway.id}}] }, { name = "makeitworkcloud-admins" decision = "allow" session_duration = "24h" - include = [{ - group = { - id = cloudflare_zero_trust_access_group.admins.id - } - }] + include = [{group = {id = cloudflare_zero_trust_access_group.admins.id}}] } ] } From 3d8af656fd74119890ed355f45ce5dcc642c375d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 13 Sep 2026 02:35:35 +0000 Subject: [PATCH 4/4] chore: apply pre-commit fixes --- cf-access-mcp.tf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cf-access-mcp.tf b/cf-access-mcp.tf index 387f31c..2038056 100644 --- a/cf-access-mcp.tf +++ b/cf-access-mcp.tf @@ -75,13 +75,13 @@ resource "cloudflare_zero_trust_access_application" "mcp_gateway_backend" { { name = "mcp-gateway-clients" decision = "non_identity" - include = [{service_token = {token_id = cloudflare_zero_trust_access_service_token.mcp_gateway.id}}] + include = [{ service_token = { token_id = cloudflare_zero_trust_access_service_token.mcp_gateway.id } }] }, { name = "makeitworkcloud-admins" decision = "allow" session_duration = "24h" - include = [{group = {id = cloudflare_zero_trust_access_group.admins.id}}] + include = [{ group = { id = cloudflare_zero_trust_access_group.admins.id } }] } ] }