MCP credentials — operator setup
This page is for operators running AIFactory in Kubernetes (or any environment where the standard cloud-CLI credential files aren't already on disk). For the user-facing view of what the default MCP servers do, see Default MCP servers.
There are three credential delivery paths. Prefer cloud-native identity where you can. Mount Secrets when you can't.
Path 1: cloud-native identity (preferred)
Each major cloud has a way to give a Kubernetes pod identity without a long-lived secret. Use them.
AWS — IRSA (IAM Roles for Service Accounts)
Annotate the AIFactory ServiceAccount with the IAM role ARN you want pods to assume. The AWS SDK in the MCP subprocess automatically uses the projected token at AWS_WEB_IDENTITY_TOKEN_FILE. Leave mcpCredentials.providers.aws = false — no Secret mount needed.
# values.yaml
serviceAccount:
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/aifactory-readonly
The role's trust policy must allow your EKS OIDC provider to assume it:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"Federated": "arn:aws:iam::123456789012:oidc-provider/oidc.eks.us-east-1.amazonaws.com/id/EXAMPLED539D4633E53DE1B716D3041E"},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {"StringEquals": {
"oidc.eks.us-east-1.amazonaws.com/id/EXAMPLED539D4633E53DE1B716D3041E:sub": "system:serviceaccount:aifactory:aifactory"
}}
}]
}
Attach ReadOnlyAccess (AWS-managed) or a tighter custom policy with only the Describe* / List* / Get* actions you want the agent to use.
Azure — AKS Pod Identity / Managed Identity
On AKS with workload identity enabled, annotate the ServiceAccount + set the env var that tells the Azure SDK to look for the projected token.
# values.yaml
serviceAccount:
annotations:
azure.workload.identity/client-id: "00000000-0000-0000-0000-000000000000"
# Tell the Azure MCP server to use Managed Identity
extraEnv:
- name: AZURE_USE_MSI
value: "true"
Assign the Reader role on the subscription / resource group the agent should see. Leave mcpCredentials.providers.azure = false.
GCP — Workload Identity Federation
On GKE Autopilot or any cluster with Workload Identity enabled:
serviceAccount:
annotations:
iam.gke.io/gcp-service-account: aifactory-readonly@PROJECT_ID.iam.gserviceaccount.com
Bind the K8s ServiceAccount to the GCP service account via gcloud iam service-accounts add-iam-policy-binding. Grant the GCP SA the Viewer role at the appropriate scope. Leave mcpCredentials.providers.gcp = false.
Path 2: mounted Secret (fallback)
For bare-metal / on-prem K8s where cloud-native identity isn't available, AIFactory mounts credentials from one Kubernetes Secret with well-known keys.
One-time setup
From a logged-in workstation (run aws configure, az login, gcloud auth, gh auth login first so the local files exist):
kubectl create secret generic aifactory-mcp-credentials \
--from-file=aws-credentials=$HOME/.aws/credentials \
--from-file=kubeconfig=$HOME/.kube/config \
--from-file=gcp-service-account.json=/path/to/sa.json \
--from-literal=github-token=ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx \
--from-literal=gitlab-token=glpat-xxxxxxxxxxxxxxxxxxxx \
--from-literal=gitlab-instance-url=https://gitlab.example.com \
--from-literal=azure-devops-token=adopat_xxxxxxxxxx \
--from-literal=azure-devops-org-url=https://dev.azure.com/myorg \
--from-literal=azure-tenant-id=00000000-0000-0000-0000-000000000000 \
--from-literal=azure-client-id=00000000-0000-0000-0000-000000000000 \
--from-literal=azure-client-secret=NOT_A_REAL_SECRET \
-n aifactory
You don't need every key — populate only the providers you actually use. Missing keys for unmounted providers don't cause failures; the chart only references keys when the corresponding providers.<name> toggle is true.
Enable in values.yaml
mcpCredentials:
enabled: true
secretName: aifactory-mcp-credentials
providers:
aws: true # → /home/nonroot/.aws/credentials (0400)
kubernetes: true # → /home/nonroot/.kube/config (0400)
gcp: true # → /etc/aifactory/gcp-sa.json (0400)
azure: true # → AZURE_TENANT_ID + CLIENT_ID + SECRET env
github: true # → GITHUB_TOKEN env
gitlab: true # → GITLAB_TOKEN + GITLAB_INSTANCE_URL env
azureDevOps: true # → AZURE_DEVOPS_PERSONAL_ACCESS_TOKEN + ORG_URL env
What gets mounted where
| Provider | Secret key | Pod path / env var | Mode |
|---|---|---|---|
| aws | aws-credentials | /home/nonroot/.aws/credentials (file) | 0400 |
| kubernetes | kubeconfig | /home/nonroot/.kube/config (file) | 0400 |
| gcp | gcp-service-account.json | /etc/aifactory/gcp-sa.json (file) + GOOGLE_APPLICATION_CREDENTIALS env points at it | 0400 |
| azure | azure-tenant-id, azure-client-id, azure-client-secret | AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET env | — |
| github | github-token | GITHUB_TOKEN env | — |
| gitlab | gitlab-token (+ optional gitlab-instance-url) | GITLAB_TOKEN + GITLAB_INSTANCE_URL env | — |
| azureDevOps | azure-devops-token, azure-devops-org-url | AZURE_DEVOPS_PERSONAL_ACCESS_TOKEN, AZURE_DEVOPS_ORG_SERVICE_URL env | — |
File mounts use Kubernetes' subPath projection — the Secret key lands at exactly the documented path, not in a directory of projected files. defaultMode: 0400 matches the cloud CLIs' refusal-to-read-loose-perms posture.
Path 3: operator config file (laptop only)
For dev / single-host deployments where you want a single place to register credentials without populating cloud CLI files:
chmod 0700 ~/.aifactory
cat > ~/.aifactory/mcp-credentials.json <<EOF
{
"github": { "tokenEnv": "GITHUB_TOKEN" },
"aws": { "profile": "aifactory-readonly" },
"azure": {
"tenantId": "00000000-0000-0000-0000-000000000000",
"clientId": "00000000-0000-0000-0000-000000000000",
"clientSecretEnv": "AZURE_CLIENT_SECRET"
},
"kubernetes": { "kubeconfigPath": "/opt/secrets/kubeconfig" },
"gcp": { "credentialsPath": "/opt/secrets/gcp-sa.json" },
"gitlab": {
"tokenEnv": "GITLAB_TOKEN",
"instanceUrl": "https://gitlab.example.com"
},
"azureDevOps": {
"tokenEnv": "ADO_PAT",
"orgUrl": "https://dev.azure.com/myorg"
}
}
EOF
chmod 0600 ~/.aifactory/mcp-credentials.json
The file never holds secrets in plain text — only references to env vars (*Env) or absolute file paths (*Path). AIFactory refuses to read it with looser-than-0600 perms.
Rotation
Secret-mounted credentials
Update the K8s Secret:
kubectl create secret generic aifactory-mcp-credentials \
--from-file=aws-credentials=$HOME/.aws/credentials \
... \
--dry-run=client -o yaml | kubectl apply -f - -n aifactory
The pod sees the new content within ~60 seconds (Kubernetes' default Secret refresh window). MCP subprocesses spawn fresh per task, so the next task picks up the rotated value. No pod restart needed.
Token-bearing env-var providers (GitHub / GitLab / ADO / Azure SP)
Same rotation flow — update the Secret, wait for kubelet refresh, next task picks it up.
Cloud-native identity (IRSA / Workload Identity)
Rotation happens server-side by the cloud provider. AIFactory doesn't see the rotation.
Troubleshooting
Verify credentials are being detected
Run aifactory --mcp-doctor on the host (laptop) or kubectl exec into the pod:
kubectl exec -n aifactory deploy/aifactory -- aifactory --mcp-doctor
The output shows each catalog entry's marker status + credential probe result + a hint command for missing creds.
"Operator config refused with permissions ..."
chmod 0600 ~/.aifactory/mcp-credentials.json
Server starts but tool calls return auth errors
The MCP server itself is reporting bad credentials. AIFactory's probe only checks presence, not validity. Common causes:
- Expired AWS access key — run
aws sts get-caller-identityto confirm - Expired Azure service-principal secret — check Microsoft Entra
- Stale kubeconfig pointing at a deleted cluster
- GitHub PAT scoped too narrowly — fine-grained tokens need explicit grants per repo
The task's agent log shows the MCP-side error verbatim.
Server doesn't show up at all
aifactory --mcp-doctor --project-dir <path> will tell you which of the three checks failed:
✗ markers: none of: has_kubernetes→ the project has nok8s//charts// etc. AddAGENT_MCP_coder_ADD=kubernetesto.aifactory/.envif you want it anyway.✗ creds: none→ see the hint line for the right CLI to run.
kubernetes-mcp-server version warning
AIFactory pins kubernetes-mcp-server@>=3.6.0 because CVE-2026-46519 (CVSS 8.8) in earlier versions lets the --read-only flag be bypassed at the execution layer. If mcp-doctor warns about an older version, your npm cache may be serving a stale package — clear it (rm -rf ~/.npm/_npx) and let AIFactory's npx re-fetch.
GCP (Cloud AI Companion MCP)
The GCP catalog entry (V2 — issue #168) uses remote-first HTTP transport rather than a local subprocess. It connects to Google's Cloud AI Companion MCP server, which went GA in March 2026 and provides code context, GCP resource enumeration, and IAM query tools.
When to use
Use the GCP catalog entry when:
- Your project deploys to GCP (the
has_gcpmarker fires on projects withgcp/,app.yaml,cloudbuild.yaml, etc.) - You want the agent to query GCP resource state (Cloud Run services, GKE clusters, IAM policies, etc.)
- You are a Cloud AI Companion / Gemini Code Assist subscriber
The GCP server is architecturally different from the stdio-based entries: it runs remotely on Google's infrastructure, so there is no local subprocess to spawn. The GOOGLE_APPLICATION_CREDENTIALS env var is the only credential the pod needs to pass; the Cloud AI Companion server performs its own Application Default Credentials exchange.
Operator setup — Workload Identity (preferred on GKE)
Annotate the ServiceAccount and leave mcpCredentials.providers.gcp = false. No Secret mount is needed.
# values.yaml
serviceAccount:
annotations:
iam.gke.io/gcp-service-account: aifactory-readonly@PROJECT_ID.iam.gserviceaccount.com
Bind the K8s SA to the GCP SA:
gcloud iam service-accounts add-iam-policy-binding \
aifactory-readonly@PROJECT_ID.iam.gserviceaccount.com \
--role roles/iam.workloadIdentityUser \
--member "serviceAccount:PROJECT_ID.svc.id.goog[aifactory/aifactory]"
Grant roles/viewer (or a tighter custom role) on the GCP project / resource hierarchy the agent should see.
Operator setup — service-account JSON (bare-metal / on-prem)
Create a GCP service account, download its JSON key, and upload it as a Kubernetes Secret:
kubectl create secret generic aifactory-gcp-mcp-creds \
--from-file=gcp-service-account.json=/path/to/sa-key.json \
-n aifactory
Enable in values.yaml:
mcpCredentials:
enabled: true
secretName: aifactory-mcp-credentials # shared Secret for other providers
providers:
gcp: true
gcp:
secretName: aifactory-gcp-mcp-creds # GCP-only Secret (overrides shared secretName for the SA JSON mount)
The pod mounts the JSON at /etc/aifactory/gcp-sa.json (mode 0400) and sets GOOGLE_APPLICATION_CREDENTIALS to that path. The Cloud AI Companion server picks it up automatically via the standard ADC discovery chain.
Endpoint override
The default endpoint is the Cloud AI Companion GA URL:
https://cloudaicompanion.googleapis.com/v1/extensions/default/mcp
Override it for VPC Service Controls perimeters, staging projects, or when Google releases additional GCP MCP servers (BigQuery, Cloud Run, etc.) that you want to use before the catalog updates:
mcpCredentials:
gcp:
endpointOverride: "https://cloudaicompanion-staging.googleapis.com/v1/extensions/default/mcp"
This injects GCP_MCP_ENDPOINT into the pod environment, which mcp_catalog._get_gcp_mcp_endpoint() reads at call time (after module import, so the env var can be set after the process starts).
GCP MCP server troubleshooting
"gcp not in required servers" — check both conditions:
- The project must have a GCP marker (
gcp/,app.yaml,cloudbuild.yaml, orcloudbuild.ymlin the project root). - Credentials must be detectable: either Workload Identity (automatic) or
GOOGLE_APPLICATION_CREDENTIALSpointing at a readable file.
"403 Forbidden" from the endpoint — the service account lacks the required Cloud AI Companion IAM role. Check:
gcloud projects get-iam-policy PROJECT_ID \
--flatten="bindings[].members" \
--filter="bindings.members:serviceAccount:SA_EMAIL"
The minimum required role is roles/cloudaicompanion.user (or roles/viewer which includes it).
"endpoint unreachable" — VPC Service Controls may be blocking egress. Either add cloudaicompanion.googleapis.com to your VPC-SC perimeter or use mcpCredentials.gcp.endpointOverride to point at a Private Service Connect endpoint.
See also Google's Cloud AI Companion MCP documentation.
See also
- Default MCP servers — user-facing concept page
- Remote Control credentials — same Secret-mounting pattern for a different credential file