This guide explains how to deploy Frontegg's MCP Gateway Helm chart in your Kubernetes cluster. The chart packages two services that expose an MCP-compliant authorization and tool routing layer in front of your MCP servers.
| Component | Role |
|---|---|
mcp-auth | Serves OAuth, dynamic client registration, authorization, and callback endpoints for MCP clients. |
mcp-gw | Receives MCP requests and routes or authorizes them against Frontegg. |
Both components share a Redis backend for token and session caching and are configured against a Frontegg vendor using region, client credentials, and applicationId.
For each release, the chart renders:
Deployment/<release>-mcp-gateway-auth, which runs themcp-authimage.Deployment/<release>-mcp-gateway-gw, which runs themcp-gwimage.Service/<release>-mcp-gateway-auth, a ClusterIP service onmcpAuth.port.Service/<release>-mcp-gateway-gw, a ClusterIP service onmcpGw.port.- A shared
ServiceAccount, which is optional and enabled by default. - HorizontalPodAutoscaler resources per component, which are optional and disabled by default.
The chart does not ship an Ingress, API gateway, or other layer-7 router. You must front the two services with the path-matching ingress or API gateway you already use, such as NGINX Ingress, Traefik, Istio, Envoy, AWS ALB, GCP HTTPS LB, or Kong. See Routing for the path map you need to configure.
helm repo add frontegg https://frontegg.github.io/helm-charts/
helm repo update
helm upgrade --install mcp-gateway frontegg/mcp-gateway -f my-values.yamlEvery value under env must be filled in for a working deployment. Names are camelCase in values.yaml and are translated to UPPER_SNAKE_CASE environment variables on both containers. For example, vendorClientId becomes VENDOR_CLIENT_ID.
env:
# Redis shared cache for sessions and tokens.
redisHost: my-redis.example.com
redisPort: "6379"
redisPassword: "<redis-password>"
redisDb: "0"
redisTlsEnabled: "true"
cacheTtl: "300"
# Frontegg vendor.
fronteggRegion: "eu" # eu | us | ca | au | stg
vendorClientId: "<vendor-client-id>"
vendorClientSecret: "<vendor-client-secret>"
applicationId: "<application-id>"
# The hostname this gateway will be reached at, without scheme.
fronteggMcpGwHost: "tenant.mcp-gw.frontegg.com"
# The same host with scheme, used as the OAuth issuer and authorization URL.
externalAuthorizationUrl: "https://tenant.mcp-gw.frontegg.com"
# Your hybrid auth backend, with scheme.
hybridAuthHost: "https://hybrid-auth.customer.example.com"Optional environment values:
| Key | Purpose |
|---|---|
approvalFlowWebhookEndpoint | Webhook that receives tool-call approval requests. |
eventWebhookProvider | Where to forward audit events, such as datadog. |
eventWebhookUrl | Destination URL for the event webhook. |
eventWebhookSecret | Shared secret for signing event webhook deliveries. |
These are commented out in the default values.yaml. Uncomment and set them if needed.
The two services must sit behind a path-matching HTTP router, such as an Ingress controller, service mesh gateway, or cloud API gateway. Configure it so that the following auth-related paths land on mcp-auth and everything else lands on mcp-gw:
| Path | Target service |
|---|---|
/.well-known/* | <release>-mcp-gateway-auth |
/authorize | <release>-mcp-gateway-auth |
/integration-callback | <release>-mcp-gateway-auth |
/security-stepup-verify | <release>-mcp-gateway-auth |
/dcr/register | <release>-mcp-gateway-auth |
Everything else, / | <release>-mcp-gateway-gw |
Your router must satisfy these requirements:
- Order and specificity: The auth paths above must win over the catch-all
/route. Most ingress controllers do this automatically based on prefix length. On routers that match in declared order, declare the auth rules first. - Host header preservation: The
Hostheader of the incoming request must matchenv.fronteggMcpGwHost. Both services use it to validate issuer URLs. If your gateway rewrites the upstream host, configure it to pass the original host. - Single external hostname: Both services are reached on the same external host. Only the path differs.
Image tags are pinned in the chart and are bumped by Frontegg as part of releasing a new chart version. Do not override mcpAuth.tag or mcpGw.tag. To pick up a new image, upgrade to a newer chart version:
helm repo update
helm upgrade mcp-gateway frontegg/mcp-gateway -f my-values.yamlEach component has independent resources blocks under mcpAuth.resources and mcpGw.resources. Defaults are conservative, with 200m CPU and 256Mi memory requests and a 512Mi memory limit, and are suitable for low-traffic tenants.
Autoscaling is disabled by default. Enable it per component:
autoscaling:
mcpGw:
enabled: true
minReplicas: 2
maxReplicas: 10
targetCPUUtilizationPercentage: 70
mcpAuth:
enabled: true
minReplicas: 2
maxReplicas: 5
targetCPUUtilizationPercentage: 70When autoscaling.<component>.enabled is true, the Deployment's replicas field is omitted so the HPA can manage it.
All three probes, liveness, readiness, and startup, hit GET /health on port http. Override per component if your environment needs different timings:
mcpGw:
livenessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 10
periodSeconds: 10nodeSelector, tolerations, affinity, podAnnotations, podLabels, podSecurityContext, and securityContext are top-level values and apply to both deployments. There is no per-component override today. To schedule the two components differently, install the chart twice with a nameOverride.
A single ServiceAccount is shared by both deployments. To use an existing one:
serviceAccount:
create: false
name: my-existing-sa| Key | Default | Description |
|---|---|---|
mcpAuth.repository | 527305576865.dkr.ecr.us-east-1.amazonaws.com/docker-hub/frontegg/hybrid-agen-co-auth | mcp-auth image repository. Override only for private registry mirrors. |
mcpAuth.tag | Pinned by chart | Managed by Frontegg and bumped by chart release. Do not override. |
mcpAuth.pullPolicy | IfNotPresent | Image pull policy. |
mcpAuth.replicaCount | 1 | Used when autoscaling.mcpAuth.enabled is false. |
mcpAuth.port | 8080 | Service port. The container always listens on 8080. |
mcpAuth.resources | requests: {cpu: 200m, memory: 256Mi}, limits: {memory: 512Mi} | Default resource requests and limits. |
mcpAuth.{liveness,readiness,startup}Probe | GET /health on port http | Default health probes. |
mcpGw.* | Mirrors mcpAuth.* defaults | mcp-gw deployment configuration. |
imagePullSecrets | [] | List of name entries for private registry credentials. |
nameOverride and fullnameOverride | "" | Name override values. |
serviceAccount.create | true | Whether to create a ServiceAccount. |
serviceAccount.automount | true | Whether to automount the ServiceAccount token. |
serviceAccount.annotations | {} | Useful for IRSA or Workload Identity. |
serviceAccount.name | "" | If empty, defaults to the chart fullname. |
service.type | ClusterIP | Applied to both services. |
podAnnotations and podLabels | {} | Applied to both deployments' pod templates. |
podSecurityContext | {} | Pod-level security context. |
securityContext | capabilities.drop: [NET_RAW] | Container-level security context. |
autoscaling.mcpAuth.enabled | false | Whether autoscaling is enabled for mcp-auth. |
autoscaling.mcpAuth.minReplicas and maxReplicas | 1 and 100 | Replica bounds for mcp-auth. |
autoscaling.mcpAuth.targetCPUUtilizationPercentage | 80 | CPU utilization target. |
autoscaling.mcpAuth.targetMemoryUtilizationPercentage | Unset | Set to enable memory-based scaling. |
autoscaling.mcpGw.* | Mirrors autoscaling.mcpAuth.* | Autoscaling configuration for mcp-gw. |
volumes and volumeMounts | [] | Applied to both deployments. |
nodeSelector, tolerations, and affinity | {}, [], and {} | Applied to both deployments. |
env.redisHost | "" | Hostname of the shared Redis instance. |
env.redisPort | "6379" | Redis port. |
env.redisPassword | "" | Redis password. |
env.redisDb | "0" | Redis database. |
env.redisTlsEnabled | "true" | Set to "false" for plain Redis. |
env.cacheTtl | "300" | Token and session cache TTL in seconds. |
env.fronteggRegion | "eu" | Supported values are eu, us, ca, au, and stg. |
env.vendorClientId | "" | Frontegg vendor client ID. |
env.vendorClientSecret | "" | Frontegg vendor client secret. |
env.applicationId | "" | Frontegg application ID. |
env.fronteggMcpGwHost | "" | External hostname of the gateway, without scheme. |
env.externalAuthorizationUrl | "" | Same hostname with scheme, published as the OAuth issuer. |
env.hybridAuthHost | "" | URL of your hybrid auth service, with scheme. |
env.approvalFlowWebhookEndpoint | Commented | Webhook for approval-flow callbacks. |
env.eventWebhookProvider | Commented | Example: datadog. |
env.eventWebhookUrl | Commented | Destination URL for event webhooks. |
env.eventWebhookSecret | Commented | Shared secret for signing event webhooks. |
Every key under .Values.env is converted from camelCase to UPPER_SNAKE_CASE and emitted as a container environment variable on both containers. To add a new environment variable, add a key under env.
# Watch the rollout.
kubectl rollout status deploy/<release>-mcp-gateway-auth
kubectl rollout status deploy/<release>-mcp-gateway-gw
# Health checks. Both should return 200.
kubectl port-forward svc/<release>-mcp-gateway-auth 8080:8080 &
curl -fsS http://localhost:8080/health
kubectl port-forward svc/<release>-mcp-gateway-gw 8081:8080 &
curl -fsS http://localhost:8081/healthThere are no CRDs and no persistent state owned by the chart. Redis lives outside it.
Image versions are tied to chart versions. Each chart release pins the matching mcp-auth and mcp-gw images. To roll out a new image, upgrade the chart:
helm repo update
helm upgrade mcp-gateway frontegg/mcp-gateway --version <new-chart-version> -f my-values.yamlDo not override mcpAuth.tag or mcpGw.tag in your values file. Those are managed by Frontegg as part of the chart release.