Kubernetes (MCP)¶
Built-in Kubernetes toolsets are recommended for most users
Holmes includes built-in Kubernetes toolsets (kubernetes/core, kubernetes/logs) that provide comprehensive cluster access — it uses bash to run kubectl commands directly, no additional setup required when deployed in-cluster.
The Kubernetes MCP addon is for advanced scenarios: enterprise environments requiring OAuth/OIDC authentication or centralized access control via identity providers.
The Kubernetes MCP server gives Holmes access to Kubernetes clusters via the MCP protocol, with support for OAuth/OIDC authentication.
In-Cluster Setup (ServiceAccount)¶
The simplest setup — the MCP server runs in the same cluster it monitors, using a ServiceAccount for authentication.
Step 1: Deploy¶
Add the following to your values.yaml:
# Disable built-in k8s toolsets to avoid overlap
toolsets:
kubernetes/core:
enabled: false
kubernetes/logs:
enabled: false
bash:
enabled: false
mcpAddons:
kubernetes:
enabled: true
serviceAccount:
create: true
name: "k8s-mcp-sa"
createClusterRoleBinding: true
clusterRole: "view"
config:
readOnly: true
Add the following to your generated_values.yaml:
holmes:
# Disable built-in k8s toolsets to avoid overlap
toolsets:
kubernetes/core:
enabled: false
kubernetes/logs:
enabled: false
bash:
enabled: false
mcpAddons:
kubernetes:
enabled: true
serviceAccount:
create: true
name: "k8s-mcp-sa"
createClusterRoleBinding: true
clusterRole: "view"
config:
readOnly: true
Step 2: Verify¶
OAuth / OIDC Setup (Microsoft Entra ID)¶
Use OAuth/OIDC when cluster access is managed through Microsoft Entra ID (Azure AD) — for example, enterprise environments with centralized SSO.
In this mode the MCP server validates OAuth tokens and passes them through to the Kubernetes API server, so each user's calls hit the API with their own identity. The ServiceAccount ClusterRoleBinding is not needed — permissions come from the OAuth token.
Two pieces of config drive the flow:
- Server-side (
mcpAddons.kubernetes.config.serverConfig) — TOML that the MCP server itself uses to validate incoming bearer tokens. - Holmes-side (
mcpAddons.kubernetes.config.oauth) — tells Holmes which OAuth endpoints to send users to. Without this, Holmes can't drive the browser login flow.
Step 1: Enable Azure AD on your AKS cluster¶
Your AKS cluster must be configured for Azure AD authentication. Follow the Microsoft guide to enable Azure AD integration on AKS.
Step 2: Create an Entra ID App Registration¶
- In the Azure portal, go to Microsoft Entra ID > App Registrations > New Registration
- Enter a name (e.g.,
holmes-k8s-mcp), select Accounts in this organizational directory only, and click Register - Under Authentication > Platform configurations, add a Web platform with redirect URI:
https://platform.robusta.dev/oauth/callback.html - Under API Permissions, add the following delegated permissions:
- Azure Kubernetes Service AAD Server (
6dae42f8-4368-4678-94ff-3960e28e3630):user.read - Microsoft Graph:
email,openid,profile
- Azure Kubernetes Service AAD Server (
- Click Grant admin consent for your tenant
- Under Certificates & Secrets, create a new client secret and copy the value
- From the Overview page, note your Application (client) ID and Directory (tenant) ID
Step 3: Store the client secret¶
Create a Kubernetes Secret with the Entra ID client secret you copied in Step 2.6, then expose it on the Holmes pod as MCP_OAUTH_CLIENT_SECRET. The Helm values in Step 4 reference it via {{ env.MCP_OAUTH_CLIENT_SECRET }} so the secret never appears in your values file.
kubectl create secret generic mcp-oauth-credentials \
--from-literal=client-secret='<CLIENT_SECRET>' \
-n YOUR_NAMESPACE \
--dry-run=client -o yaml | kubectl apply -f -
Step 4: Deploy¶
Add the following to your values.yaml (replace <TENANT_ID> and <CLIENT_ID>):
# Inject the OAuth client secret as an env var that the chart reads via Jinja.
additionalEnvVars:
- name: MCP_OAUTH_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: mcp-oauth-credentials
key: client-secret
# Disable built-in k8s toolsets to avoid overlap
toolsets:
kubernetes/core:
enabled: false
kubernetes/logs:
enabled: false
bash:
enabled: false
mcpAddons:
kubernetes:
enabled: true
serviceAccount:
create: true
name: "k8s-mcp-sa"
createClusterRoleBinding: false # No RBAC — OAuth token provides permissions
config:
readOnly: true
# Server-side: how the MCP server validates incoming JWTs.
# The chart bakes this into a Secret mounted at /etc/kubernetes-mcp/config.toml.
serverConfig: |
require_oauth = true
authorization_url = "https://login.microsoftonline.com/<TENANT_ID>/v2.0"
oauth_audience = "6dae42f8-4368-4678-94ff-3960e28e3630"
oauth_scopes = ["6dae42f8-4368-4678-94ff-3960e28e3630/.default", "openid", "profile"]
issuer_url = "https://sts.windows.net/<TENANT_ID>/"
# Holmes-side: how Holmes drives the browser OAuth flow for end users.
oauth:
enabled: true
client_id: "<CLIENT_ID>"
client_secret: "{{ env.MCP_OAUTH_CLIENT_SECRET }}"
Add the following to your generated_values.yaml (replace <TENANT_ID> and <CLIENT_ID>):
holmes:
additionalEnvVars:
- name: MCP_OAUTH_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: mcp-oauth-credentials
key: client-secret
# Disable built-in k8s toolsets to avoid overlap
toolsets:
kubernetes/core:
enabled: false
kubernetes/logs:
enabled: false
bash:
enabled: false
mcpAddons:
kubernetes:
enabled: true
serviceAccount:
create: true
name: "k8s-mcp-sa"
createClusterRoleBinding: false # No RBAC — OAuth token provides permissions
config:
readOnly: true
serverConfig: |
require_oauth = true
authorization_url = "https://login.microsoftonline.com/<TENANT_ID>/v2.0"
oauth_audience = "6dae42f8-4368-4678-94ff-3960e28e3630"
oauth_scopes = ["6dae42f8-4368-4678-94ff-3960e28e3630/.default", "openid", "profile"]
issuer_url = "https://sts.windows.net/<TENANT_ID>/"
oauth:
enabled: true
client_id: "<CLIENT_ID>"
client_secret: "{{ env.MCP_OAUTH_CLIENT_SECRET }}"
Step 5: Verify¶
When you ask Holmes a Kubernetes question for the first time, the Robusta UI will open a Microsoft login window. After signing in, Holmes uses your Azure-issued token for every kubernetes_* call — RBAC is enforced per user on the API server.