Deploying the OpenShift MCP Server on Openshift
Let’s set the stage
This is the last installment of my short MCP series for the homelab. I already covered the overview, deploying aap-mcp on my operator-managed platform, and deploying rhel-mcp for Linux host diagnostics. This post is about openshift-mcp: the bridge that lets an assistant in Cursor talk to my Openshift cluster API.
In the overview I said I would write up token hardening for Openshift MCP. What I am really doing here is walking through the full deployment: manifests, auth, and how I connect Cursor. The auth piece is the important part.
What you’re deploying
The OpenShift MCP Server is based on the upstream Kubernetes MCP server work. It is a tech-preview style project that exposes cluster tools over HTTP so clients like Cursor can list pods, pull logs, query metrics, work with routes, and do a lot of other API-backed tasks without you pasting oc output into chat.
In my lab the server runs as a pod in the openshift-mcp namespace on ocp.bk.lab. Cursor hits a route like openshift-mcp-server-openshift-mcp.apps.ocp.bk.lab/mcp. I do not run it with npx and a local kubeconfig on my laptop, though that is a valid option if you prefer.
There are two auth ideas to keep straight:
- Who can call the MCP URL — the HTTP endpoint on the route
- What the tools can do on the cluster — which Kubernetes/OpenShift APIs get used under the hood
I use token passthrough for the second one. That means when I put my Openshift bearer token in Cursor, the MCP server uses my RBAC for API calls, not some shared super-user built into the pod.
Prerequisites
Before you apply manifests, you should have:
- An Openshift cluster and
oclogged in with permission to create a namespace, deployment, route, and a ClusterRoleBinding - A cluster admin or someone who can grant
viewto the MCP service account (needed for parts of in-cluster setup) - A token for your user when you configure Cursor (
oc whoami -tworks for lab use)
The manifest layout
I keep a directory of YAML files that create the openshift-mcp namespace and roll out the server. Here is what each file is for.
namespace.yaml
Creates the openshift-mcp project, separate from aap and rhel-mcp.
apiVersion: v1
kind: Namespace
metadata:
name: openshift-mcp
serviceaccount.yaml
The identity the pod runs as. Even with passthrough auth, the deployment still mounts a service account token for in-cluster provider detection.
apiVersion: v1
kind: ServiceAccount
metadata:
name: openshift-mcp-server
namespace: openshift-mcp
clusterrolebinding.yaml
Grants the built-in view ClusterRole to the MCP service account. This matters when cluster_auth_mode is kubeconfig (the default). I still apply it in my lab even though passthrough is what I actually use day to day.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: openshift-mcp-server-view
roleRef:
kind: ClusterRole
name: view
subjects:
- kind: ServiceAccount
name: openshift-mcp-server
namespace: openshift-mcp
configmap.yaml
The main config.toml for the server. Mine enables several toolsets and is not read-only. That was a deliberate lab choice so the assistant can do more than look around.
port = "8080"
cluster_provider_strategy = "in-cluster"
read_only = false
disable_destructive = false
toolsets = ["core", "config", "openshift", "kubevirt", "metrics", "traces"]
trust_proxy_headers = true
Setting read_only = false means write-style tools can be exposed. What actually happens still depends on your user token’s RBAC. If your account cannot delete a namespace, the assistant cannot either. disable_destructive = false is another knob on the server side; combine that with passthrough and you need to be honest about risk.
For a safer starting point, set read_only = true and leave destructive tools disabled until you know what you need.
auth-configmap.yaml
A small overlay mounted at /etc/kubernetes-mcp-server/conf.d that switches API auth to passthrough.
cluster_auth_mode = "passthrough"
With this in place, the MCP server forwards the agent’s Authorization: Bearer header to the Openshift API. Tools run as you, not as the pod service account.
I did not turn on require_oauth in the lab. That would force JWT validation on every MCP HTTP call. Openshift tokens from oc whoami -t are opaque, not JWTs, so that path is a better fit when you wire up a full OIDC flow. For my homelab I rely on network isolation plus sending a user token from Cursor.
deployment.yaml
Runs the container image quay.io/redhat-user-workloads/crt-nshift-lightspeed-tenant/openshift-mcp-server:latest, mounts the main config and auth overlay, and exposes port 8080.
containers:
- name: openshift-mcp-server
image: quay.io/redhat-user-workloads/crt-nshift-lightspeed-tenant/openshift-mcp-server:latest
args:
- "--config"
- "/etc/kubernetes-mcp-server/config.toml"
volumeMounts:
- name: config
mountPath: /etc/kubernetes-mcp-server
readOnly: true
- name: auth-config
mountPath: /etc/kubernetes-mcp-server/conf.d
readOnly: true
service.yaml
Exposes the deployment inside the cluster on port 8080. The route and health probes target this service.
apiVersion: v1
kind: Service
metadata:
name: openshift-mcp-server
namespace: openshift-mcp
spec:
selector:
app: openshift-mcp-server
ports:
- name: http
port: 8080
targetPort: http
protocol: TCP
route.yaml
Creates an Openshift Route so MCP clients outside the cluster can connect over HTTPS. TLS terminates at the router with edge termination.
apiVersion: route.openshift.io/v1
kind: Route
metadata:
name: openshift-mcp-server
namespace: openshift-mcp
annotations:
haproxy.router.openshift.io/timeout: 5m
spec:
to:
kind: Service
name: openshift-mcp-server
weight: 100
port:
targetPort: http
tls:
termination: edge
insecureEdgeTerminationPolicy: Redirect
In my lab the MCP URL is https://openshift-mcp-server-openshift-mcp.apps.ocp.bk.lab/mcp.
Deploy on Openshift
-
Change into the directory with your manifests and apply them.
oc apply -f . -
Confirm the pod is running.
oc get pods -n openshift-mcp -
Grab the route host.
oc get route openshift-mcp-server -n openshift-mcp -o jsonpath='https://{.spec.host}/mcp{"\n"}' -
Optional health check.
curl -sk "$(oc get route openshift-mcp-server -n openshift-mcp -o jsonpath='https://{.spec.host}')/healthz"
Agent token and Cursor
For passthrough mode the assistant needs a real Openshift token in every MCP request.
-
Get a token (lab shortcut):
oc whoami -tTokens expire. When tools start failing with auth errors, run it again and update Cursor.
-
Add the server to
~/.cursor/mcp.json:
{
"mcpServers": {
"openshift-mcp": {
"type": "http",
"url": "https://openshift-mcp-server-openshift-mcp.apps.ocp.example.com/mcp",
"headers": {
"Authorization": "Bearer <token-from-oc-whoami-t>"
}
}
}
}
- Test with something simple:
List pods in the aap namespace
If the token and route are good, you get real data back. If you get forbidden errors, check whether your user actually has permission for that namespace or verb.
What passthrough buys you
When I use my normal admin-capable lab account, the assistant can do anything I could do in the API within the tools exposed by the server. That is powerful and a little scary.
When I use a tighter account, the assistant is tightened too. Same idea as the AAP MCP post: the server is not a bypass around RBAC.
Security implications (plain version)
- Anyone who can hit the route without a token — In my lab I did not enable
require_oauthon the HTTP endpoint. If someone on my network could reach the URL, they might talk to MCP without your user token depending on server behavior. I count on lab network boundaries. For anything stricter, look at OAuth-protected MCP in the upstream docs. - Your token in Cursor — Treat it like a password. Do not commit
mcp.jsonwith a real bearer token in git. - Write tools enabled —
read_only = falseplus passthrough means a confused or overly eager assistant could try mutating things you are allowed to change. I accept that in the lab because it helps me move faster. I would tighten both server config and RBAC for production. - Token lifetime — Refresh tokens when they expire. Stale tokens look like mysterious MCP failures.
The upstream repo has a detailed auth guide (AGENT-AUTH.md in the project) covering lab mode, passthrough, and full OAuth. I leaned on passthrough because it maps cleanly to how I already use oc.
How this fits with the other MCP servers
On ocp.bk.lab I now have three HTTP MCP endpoints:
- openshift-mcp — cluster API, pods, metrics, resources
- aap-mcp — automation controller
- rhel-mcp — SSH diagnostics on individual hosts
I turn on whichever ones match the problem. Cluster question? Openshift MCP. Job template or dispatch? AAP MCP. Service failing on idm01.bk.lab? RHEL MCP. Often two at once, occasionally all three when OpenShift Virtualization is involved.
Things that tripped me up
- Forgot the Bearer header — Without
Authorizationin Cursor, passthrough mode has nothing to forward and tools fail in confusing ways. - Expired token — Run
oc whoami -tagain and update the config. - Wrong URL — The MCP path is
/mcpon the openshift-mcp route, not the main console route. - RBAC surprise — The assistant can only do what your user can do. If you use a limited account, expect limited tools results.
- Read-only vs write in config.toml — Server-level
read_onlygates which tools exist; your token still decides what API calls succeed.
Troubleshooting
- Pod running?
oc get pods -n openshift-mcp - Route matches
mcp.json?oc get route -n openshift-mcp - Fresh token?
oc whoami -t - Can you do it with oc?
oc auth can-i <verb> <resource> -n <namespace> - Auth overlay mounted? Check the deployment has the
auth-configvolume atconf.d
Final Thoughts
Deploying openshift-mcp closed the loop on the MCP series for my homelab. AAP covers automation, RHEL covers hosts, and Openshift MCP covers the cluster itself. The setup is not hard, but the auth choices matter more than the YAML. Passthrough with a bearer token in Cursor was the right trade for me: the assistant acts as me, and I can narrow that identity whenever I want.
If you have been following along since the overview, this is the Openshift piece I kept promising. All three posts together are how I actually run MCP today on ocp.bk.lab.
For more detail see the OpenShift MCP Server on GitHub and Red Hat’s article on MCP for root-cause analysis in VS Code and Cursor.
As with everything I write about my lab, this is how I run things. Your auth model, toolsets, and appetite for write access may differ. Keep secret data out of git.