How to Scale MCP Servers on Kubernetes with the Stateless 2026-07-28 Spec
Scale MCP servers on Kubernetes now that the 2026-07-28 spec is stateless: drop sticky sessions, use round-robin, autoscale with HPA, and run serverless.
The MCP 2026-07-28 specification made the protocol core stateless, and that single change rewrites how you run MCP servers on Kubernetes. There is no session handshake and no Mcp-Session-Id header anymore, so you can drop sticky sessions, delete the shared session store, put a plain round-robin load balancer in front, and autoscale with a normal HPA. Any pod serves any request.
If you have been operating a remote MCP server the old way - session affinity on the Ingress, a Redis session store, deep packet inspection at the gateway to keep a client pinned to the same pod - most of that scaffolding is now dead weight. This post is the operational guide for platform and DevOps teams: what actually changes on the cluster, and the manifests to change it.
What changed in the 2026-07-28 spec?
The headline for infra teams is that MCP moved from a bidirectional stateful model to plain request/response. Concretely:
- No
initializehandshake and no protocol-level session, so noMcp-Session-Idheader to track or route on. - Capabilities now come from a new
server/discovermethod instead of being negotiated once at session start. - Client metadata travels per-request, so each call is self-contained.
The release also shipped an Extensions framework, Tasks for long-running work, MCP Apps for server-rendered UIs, OAuth/OIDC auth hardening, and a formal deprecation policy. But the stateless core is the part that changes your Kubernetes topology. All four Tier-1 SDKs (Python, TypeScript, Go, C#) support 2026-07-28, with the Rust SDK in beta, so your server implementation almost certainly already has a compliant release to target.
Why did MCP servers need sticky sessions before?
Under the old stateful model, the client ran an initialize handshake, the server minted a session, and every subsequent request had to land on the pod that held that session state. On Kubernetes that forced a chain of workarounds:
- Session affinity (
sessionAffinity: ClientIP) on the Service, or cookie-based affinity on the Ingress, to pin a client to one pod. - A shared session store (usually Redis) if you wanted any resilience, because losing the pod meant losing the session.
- Gateway-level inspection to read the session header and route accordingly.
Every one of those fights Kubernetes rather than working with it. Sticky sessions skew load, so one hot pod runs at capacity while others idle. Rollouts and scale-downs kill live sessions. HPA is unreliable because you cannot freely move traffic between pods. It is the same class of problem stateful web apps had before they externalized session state - except now the protocol itself removes the need.
How do you deploy a stateless MCP server on Kubernetes?
Like any other stateless HTTP workload. No affinity, no session store, plain round-robin.
apiVersion: v1
kind: Service
metadata:
name: mcp-server
namespace: mcp
spec:
# No sessionAffinity - round-robin across all pods
selector:
app: mcp-server
ports:
- port: 80
targetPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: mcp-server
namespace: mcp
spec:
replicas: 3
selector:
matchLabels: {app: mcp-server}
template:
metadata:
labels: {app: mcp-server}
spec:
containers:
- name: mcp-server
image: registry.example.ae/mcp/server:2026.07.28
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /mcp # backs the server/discover method
port: 8080
initialDelaySeconds: 3
periodSeconds: 10
livenessProbe:
httpGet:
path: /healthz
port: 8080
periodSeconds: 15
resources:
requests: {cpu: 250m, memory: 256Mi}
limits: {memory: 512Mi}
Note what is missing: no sessionAffinity: ClientIP, no nginx.ingress.kubernetes.io/affinity annotations, no Redis dependency for session state. The readiness probe now maps cleanly to server/discover - if the pod can answer a discover call, it can serve traffic.
Stateful vs stateless MCP on Kubernetes
| Concern | Stateful (pre-2026-07-28) | Stateless (2026-07-28) |
|---|---|---|
| Session | initialize handshake + Mcp-Session-Id | None - each request self-contained |
| Load balancing | Sticky sessions (ClientIP / cookie affinity) | Plain round-robin |
| Session store | Redis or similar, shared across pods | Not needed |
| Gateway routing | Inspect session header, pin to pod | Route on Mcp-Method header |
| Autoscaling | Fragile - moving traffic breaks sessions | Clean HPA on CPU / memory / custom metrics |
| Rollouts / scale-down | Sever live sessions | Safe - drain and replace freely |
| Serverless / Knative | Impractical | Natural fit, scale to zero |
| Capability discovery | Negotiated once per session | server/discover per client, cacheable |
How do you route MCP traffic at the Ingress?
Because each request carries its method, you can route on the Mcp-Method header instead of parsing a session. That opens up clean traffic-management patterns without any mesh magic. A common split is to send heavy tools/call traffic to a beefier pool while lightweight tools/list and server/discover calls hit a small, cheap tier that clients can cache against.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: mcp-ingress
namespace: mcp
annotations:
# Example: route long tool calls to a separate backend
nginx.ingress.kubernetes.io/server-snippet: |
if ($http_mcp_method = "tools/call") {
set $backend "mcp-server-heavy";
}
spec:
rules:
- host: mcp.example.ae
http:
paths:
- path: /mcp
pathType: Prefix
backend:
service:
name: mcp-server
port: {number: 80}
Clients can also cache tools/list responses now, which cuts load on the discovery path and keeps your Ingress rules simple. If you already run a service mesh, header-based routing on Mcp-Method slots straight into your existing traffic rules - see our service mesh comparison for picking one.
How do you autoscale MCP servers now?
This is where the stateless core pays off. With no session to preserve, a standard HorizontalPodAutoscaler just works - scaling up adds real capacity because any pod can pick up any request, and scaling down never cuts a conversation.
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: mcp-server
namespace: mcp
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: mcp-server
minReplicas: 3
maxReplicas: 30
metrics:
- type: Resource
resource:
name: cpu
target: {type: Utilization, averageUtilization: 65}
behavior:
scaleDown:
stabilizationWindowSeconds: 120
For request-driven scaling rather than CPU, put KEDA in front and scale on requests-per-second from Prometheus, or on queue depth for the Tasks tier below. Add a PodDisruptionBudget with minAvailable: 2 so voluntary disruptions and rollouts never drop you below serving capacity. This is the same pattern we use for the stateless orchestration tier in our production RAG stack on Kubernetes.
Can MCP servers run serverless on Knative?
Yes, and now it is straightforward. Stateless request/response is precisely the contract Knative and serverless platforms expect, so an MCP server can scale to zero between bursts and cold-start back when a client calls. That is a good fit for internal-tool servers with spiky, unpredictable traffic - you stop paying for idle pods without breaking any client, because there is no session to lose on scale-down.
The one caveat is latency-sensitive or long-running tool calls, which do not want a cold start or a held-open request. Handle those with the Tasks pattern below rather than a synchronous serverless call.
How do you handle long-running tool calls?
Do not hold a request open. Use the Tasks extension: the server accepts the work, returns a task handle immediately, and the client polls for the result. On Kubernetes, back this with a queue and a separate worker pool so your request-serving tier stays fast and stateless.
Client ──tools/call──▶ MCP server (stateless, HPA on RPS)
│ enqueue task
▼
Queue (Redis / NATS / Kafka)
│
▼
Worker pods (KEDA scales on queue depth)
Client ◀──poll task handle──┘ result written back to store
The request-serving Deployment autoscales on RPS; the worker pods autoscale on queue depth with KEDA. The two tiers scale independently, which is exactly what you want when tool calls range from milliseconds to minutes. This is the same queue-plus-worker shape we use for RAG ingestion, just applied to MCP tool execution.
What this means for GCC platform teams
For teams building sovereign inference and agent platforms in the UAE and wider GCC, the stateless core removes a real operational tax. You can run MCP servers in-region on AKS, EKS, or a Core42 sovereign cluster with plain round-robin, no external session store to keep in-region and audit, and clean autoscaling that finance can actually reason about. Fewer moving parts also means a smaller attack surface and a simpler compliance story - one stateless Deployment behind an Ingress, rather than a stateful fleet with a shared session tier to secure and back up.
Pair the MCP server tier with a gateway like LiteLLM for provider routing and cost control (see deploy LiteLLM proxy on Kubernetes), and you have a clean, sovereign agent-tooling platform.
The migration checklist
If you are moving an existing server to the 2026-07-28 spec, here is the short list:
- Upgrade the server to an SDK release supporting 2026-07-28 (Python, TypeScript, Go, or C# are all Tier-1).
- Remove
sessionAffinityfrom the Service and any affinity annotations from the Ingress. - Delete the shared session store dependency once no client relies on it.
- Repoint health checks at
server/discover. - Add an HPA on CPU or RPS and a PodDisruptionBudget.
- Add
Mcp-Methodrouting if you want to split heavy tool calls from discovery traffic. - Move long-running tools to the Tasks extension with a queue and worker pods.
Getting help
NomadX Kubernetes deploys and operates stateless MCP server fleets, LLM gateways, and agent tooling for platform teams across the GCC - in-region, autoscaled, and sovereign where it needs to be. If you are migrating MCP servers to the 2026-07-28 spec or standing up a new agent platform, our AI/ML Infrastructure on Kubernetes engagement is the place to start. Book a free 30-minute discovery call to scope it with an engineer.
Frequently Asked Questions
Do MCP servers still need sticky sessions on Kubernetes in 2026?
No. The MCP 2026-07-28 spec removed the protocol-level session (no more Mcp-Session-Id header, no initialize handshake), so any server instance can handle any request. You can drop session affinity on the Service and Ingress and run behind a plain round-robin load balancer. Sticky sessions and shared session stores are no longer required for a compliant remote server.
How do you autoscale MCP servers now that the protocol is stateless?
Use a standard HorizontalPodAutoscaler on CPU or memory, or custom metrics like requests-per-second via KEDA or Prometheus Adapter. Because requests are independent, scaling up adds capacity immediately and scaling down never severs a live session. Set sensible requests and limits, a readiness probe on server/discover, and a PodDisruptionBudget so rollouts stay smooth.
What should Kubernetes health checks hit on an MCP server?
Point readiness and liveness probes at the server/discover method (or a lightweight health endpoint the server exposes). Because there is no session to establish, a successful discover response is a reliable signal the pod can serve traffic. Avoid probing tool-execution paths, which can be slow or have side effects.
Can MCP servers run on Knative or serverless now?
Yes. Stateless request/response is exactly what Knative and serverless platforms expect, so MCP servers scale to zero and back on demand without breaking clients. This suits spiky internal-tool traffic well. For long-running tool calls, use the Tasks extension with a queue and dedicated worker pods rather than holding a request open on a serverless instance.
How do you handle long-running tool calls on stateless MCP servers?
Use the Tasks extension from the 2026-07-28 spec. The server accepts the work, returns a task handle, and the client polls for completion. On Kubernetes, back this with a queue (Redis, NATS, or Kafka) and a separate pool of worker pods that you autoscale on queue depth with KEDA, keeping the request-serving tier fast and stateless.
Complementary NomadX Services
Related Articles
Get Started for Free
We would be happy to speak with you and arrange a free consultation with our Kubernetes Expert in Dubai, UAE. 30-minute call, actionable results in days.
Talk to an Expert