diff --git a/README.md b/README.md
index 3959aba..7ecfcff 100644
--- a/README.md
+++ b/README.md
@@ -34,9 +34,11 @@ No modules.
| [cloudflare_zero_trust_access_application.alertmanager](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_access_application) | resource |
| [cloudflare_zero_trust_access_application.grafana_alerts](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_access_application) | resource |
| [cloudflare_zero_trust_access_application.k3s](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_access_application) | resource |
+| [cloudflare_zero_trust_access_application.mcp_gateway](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_access_application) | resource |
| [cloudflare_zero_trust_access_application.warp](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_access_application) | resource |
| [cloudflare_zero_trust_access_group.admins](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_access_group) | resource |
| [cloudflare_zero_trust_access_identity_provider.github](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_access_identity_provider) | resource |
+| [cloudflare_zero_trust_access_service_token.mcp_gateway](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_access_service_token) | resource |
| [cloudflare_zero_trust_organization.main](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_organization) | resource |
| [cloudflare_zero_trust_tunnel_cloudflared.warp](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_tunnel_cloudflared) | resource |
| [cloudflare_zero_trust_tunnel_cloudflared_route.private_network](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/zero_trust_tunnel_cloudflared_route) | resource |
@@ -61,6 +63,8 @@ No inputs.
| Name | Description |
| ---- | ----------- |
+| [mcp\_gateway\_service\_token\_client\_id](#output\_mcp\_gateway\_service\_token\_client\_id) | CF-Access-Client-Id for MCP gateway clients |
+| [mcp\_gateway\_service\_token\_client\_secret](#output\_mcp\_gateway\_service\_token\_client\_secret) | CF-Access-Client-Secret for MCP gateway clients |
| [tunnel\_ids](#output\_tunnel\_ids) | Cloudflare Tunnel IDs for reference in kustomize-cluster ConfigMaps |
diff --git a/cf-access-mcp.tf b/cf-access-mcp.tf
new file mode 100644
index 0000000..ab2b5ca
--- /dev/null
+++ b/cf-access-mcp.tf
@@ -0,0 +1,44 @@
+# 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.
+resource "cloudflare_zero_trust_access_service_token" "mcp_gateway" {
+ account_id = local.account_id
+ name = "mcp-gateway"
+ # Non-expiring; rotate deliberately by bumping client_secret_version.
+ 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
+ }
+ }]
+ }
+ ]
+}
diff --git a/cf-tunnels.tf b/cf-tunnels.tf
index 239ce85..2692666 100644
--- a/cf-tunnels.tf
+++ b/cf-tunnels.tf
@@ -22,6 +22,7 @@ locals {
"forgejo",
"grafana",
"k3s",
+ "mcp",
"opencode",
"status",
"alertmanager",
diff --git a/outputs.tf b/outputs.tf
index c36825b..d5d16ac 100644
--- a/outputs.tf
+++ b/outputs.tf
@@ -8,3 +8,15 @@ output "tunnel_ids" {
warp = cloudflare_zero_trust_tunnel_cloudflared.warp.id
}
}
+
+# Clients send these as CF-Access-Client-Id / CF-Access-Client-Secret headers.
+output "mcp_gateway_service_token_client_id" {
+ description = "CF-Access-Client-Id for MCP gateway clients"
+ value = cloudflare_zero_trust_access_service_token.mcp_gateway.client_id
+}
+
+output "mcp_gateway_service_token_client_secret" {
+ description = "CF-Access-Client-Secret for MCP gateway clients"
+ value = cloudflare_zero_trust_access_service_token.mcp_gateway.client_secret
+ sensitive = true
+}