MCP assumes your server is still there for the next call. A serverless function assumes the opposite: it spins up per request, answers, and forgets everything in between. Those two models do not fit, and the mismatch is why MCP servers on per-request platforms produce broken sessions, dropped streams and slow tool chains. The fix is not cleverer code, it is a different runtime shape: one process that stays up.
Why do MCP sessions break on serverless?
A Model Context Protocol session is a conversation, not a one-off request. The client connects, negotiates capabilities in an initialize handshake, lists your tools, and then keeps calling them over the same session, often dozens of times as an agent works through a task. Three things go wrong when each of those steps lands on a function that was born for that request:
- Torn-down transports. MCP transports can be long-lived: stdio pipes, or an HTTP connection the server holds open to stream. A per-request platform recycles the instance when the response ends, so the connection the client is counting on is gone mid-session, and the client sees a broken transport rather than a clean answer.
- Per-request state loss. Anything the server kept in memory, session context from the handshake, a warm cache, an open connection to a backing API, evaporates between invocations. The next call may land on a fresh instance that has never seen this client.
- Cold starts, multiplied. An agent does not make one request, it makes a chain of them. Every cold start adds its latency to a single link in that chain, and a ten-tool-call task pays the toll ten times. The user experiences this as an assistant that thinks in slow motion.
None of this is a defect in serverless platforms. They are built for stateless request handling and are good at it. MCP simply is not that workload.
What does an always-on slot change?
On DenkOps, an MCP server runs in one always-on slot: a process that starts once and keeps running. That single property resolves all three failures. There is no cold start, because the process is already warm when the next tool call arrives. The transport is not torn down underneath the client, because nothing is recycled between requests. And in-memory state survives from one call to the next, so caches stay warm and connections stay open. For state that must also survive redeploys, the slot mounts a durable disk at /persist, covered in state that survives redeploys.
Doesn't stateless streamable HTTP solve this?
It helps, and the DenkOps SDK uses it: servers built with defineMcp speak stateless streamable HTTP, POST-only at /mcp, so any MCP client that supports remote servers can connect without the server holding a stream. But statelessness at the protocol layer does not change the runtime economics underneath. A per-request platform still pays a cold start per call in the worst case, still gives your handlers nowhere to keep a warm cache or a pooled connection, and still cannot host the many MCP servers that do hold sessions or streams. A stateless protocol on top of an always-on process gives you both halves: maximum client compatibility, and a runtime that is simply there.
If you want to feel the difference rather than read about it, deploy the starter: one file, one command, and every tool call after the first hits a process that never left.