Comparison
Where should I host an MCP server?
Last verified: 10 August 2026 · spec 2026-07-28 · ruleset 1.4.2
What statelessness changed about hosting
“Servers MUST NOT rely on prior requests over the same connection to establish context (e.g., capabilities, protocol version, client identity). Every request supplies this metadata in its _meta field.”
Read that as an infrastructure statement and the consequences fall out immediately. Under the session-based revisions, a client established a session with one instance and had to keep reaching that instance — which meant sticky routing, or a shared session store, or both. Neither is a natural fit for serverless.
| Before (session-based) | 2026-07-28 (stateless) | |
|---|---|---|
| Load balancing | Sticky sessions required | Any instance, any request |
| Shared state | Session store between instances | None needed |
| Scale to zero | Drops live sessions | Safe |
| Deploys | Drain sessions first | Roll freely |
| Serverless | Awkward | Natural fit |
If you are carrying hosting decisions made for a session-based MCP server — a Redis session store, sticky routing rules, a minimum instance count to avoid dropping sessions — those are now cost and complexity with nothing behind them.
What the spec actually requires of a host
Five things, each traceable to a rule. Everything else is preference.
1. HTTPS with a valid certificate
“All the OAuth protocol URLs (URLs exposed by the AS, RS and Client) MUST use the https scheme except for loopback interface redirect URIs, which MAY use the http scheme.”
Rules out plain-HTTP deployments and self-signed certificates for anything with authorization. Practically every platform gives you this now; the thing to check is that it also covers your custom domain, not just a platform subdomain — see point 4.
2. Control over response headers
“Servers MUST validate the Origin header on all incoming connections to prevent DNS rebinding attacks. If the Origin header is present and invalid, servers MUST respond with HTTP 403 Forbidden.”
You need to read Origin and return 403, and to set WWW-Authenticate on a 401. Any platform that lets you run a real handler does this. A platform where a CDN or WAF layer rewrites your response headers is worth checking before you commit.
3. Correct Content-Type, and unbuffered streams if you stream
“The server MUST return either Content-Type: application/json (a single JSON object) or Content-Type: text/event-stream (an SSE response stream).”
“When initiating an SSE stream, servers SHOULD include the X-Accel-Buffering: no header in the HTTP response.”
The X-Accel-Buffering: no recommendation exists because a proxy that buffers an SSE stream defeats the point of streaming. This is the requirement most likely to be violated by your platform rather than your code.
4. A stable canonical URI
“The resource parameter MUST identify the MCP server that the client intends to use the token with, using the canonical URI as defined in RFC 8707 Section 2.”
Your Protected Resource Metadata names a canonical URI, and your token audience check compares against it. Both break if the URL changes — which rules out deploying on per-deployment preview URLs and means you want a custom domain pinned to a stable address.
5. Static paths at /.well-known/
“MCP servers MUST implement OAuth 2.0 Protected Resource Metadata (RFC9728).”
If your server does authorization, it must serve a document at /.well-known/oauth-protected-resource on the same origin. Straightforward anywhere you control routing; awkward if your MCP server sits behind a gateway that owns the root path.
The one question that actually forks the decision
Everything above is satisfiable almost anywhere. The real fork is whether your server implements subscriptions/listen — the single long-lived stream that replaced resources/subscribe and the standalone GET endpoint.
“Replace the HTTP GET endpoint and resources/subscribe/resources/unsubscribe with subscriptions/listen: a single long-lived POST-response stream for opted-in server-to-client change notifications.”
- No subscriptions — every request is a short request/response cycle. Serverless is ideal, scale-to-zero is free, and request-duration limits never come up.
- With subscriptions — you hold a response open indefinitely. Now you need a platform that permits long-running responses and does not buffer them, which is a much smaller set.
Platform categories against those requirements
| Category | Fits stateless MCP | Long-lived streams | Watch out for |
|---|---|---|---|
| Edge runtimesCloudflare Workers, Deno Deploy | Excellent — no cold-start penalty | Usually supported | Restricted runtime; Node APIs and native deps may not work |
| Serverless functionsVercel, AWS Lambda, Cloud Run | Excellent — the change that unlocked it | Check the duration cap | Cold starts; per-request timeouts; response buffering |
| Managed containersFly.io, Railway, Render | Good — no constraints either way | Yes | You pay for idle unless it scales to zero |
| Self-hostedYour own VM or cluster | Good — full control | Yes | Proxy buffering is on by default in most nginx configs |
proxy_buffering is on by default, which will buffer an SSE stream and break subscriptions/listen in a way that looks like your application hanging. The X-Accel-Buffering: no header your server sets is exactly what nginx reads to disable it — which is why MCP-RES-006 exists.Questions to ask whichever platform you're evaluating
These are the ones that map to actual conformance rules, in the order they are likely to bite:
- Can I attach a custom domain with TLS? Needed for a stable canonical URI (
MCP-AUT-003). - Are my response headers passed through unmodified?
WWW-AuthenticateandX-Accel-Bufferingmust reach the client. - Is the response body buffered? Only matters if you stream — but it fails silently when it does.
- What is the maximum response duration? Only matters for
subscriptions/listen. - Can I serve arbitrary paths under
/.well-known/? Needed for RFC 9728 metadata. - Does anything inject or rewrite headers? A WAF that strips unknown request headers will remove
Mcp-MethodandMcp-Name— and then your own validation rejects every request as a mismatch.
-32020 for a mismatch it can see, and nothing in the logs says a header was removed upstream.Cold starts, and why caching metadata is the answer
The honest downside of statelessness on serverless: a client that used to learn your tool list once per session may now ask far more often, and each ask can be a cold invocation. That is a real cost, and the spec anticipated it.
“Require ttlMs and cacheScope fields on results returned by tools/list, prompts/list, resources/list, resources/read, and resources/templates/list via a new CacheableResult interface.”
ttlMs and cacheScope are not paperwork — they are the mechanism that makes the stateless protocol affordable to host. A tools/list with a meaningful ttlMs and cacheScope: "public" can be served from a client or intermediary cache instead of waking your function.
// Conformant, but wakes your function on every list call
{ "resultType": "complete", "tools": [ … ], "ttlMs": 0, "cacheScope": "private" }
// Conformant, and cacheable by anything in the path
{ "resultType": "complete", "tools": [ … ], "ttlMs": 3600000, "cacheScope": "public" }Only claim "public" if the response genuinely contains nothing user-specific — the wrong value here is a data-leak class, not a tuning mistake. See caching metadata.
Check the deployment, not the platform
Most hosting-related conformance failures come from the layer between your code and the client — a stripped header, a buffered stream, a certificate on the wrong hostname. The validator tests the deployed endpoint from outside, which is where those show up.
Validate a server →Frequently asked
Where should I host an MCP server?
Since the 2026-07-28 revision removed protocol sessions, almost anywhere that serves HTTPS works — including serverless platforms that were previously awkward for MCP. The decision now turns on whether you need long-lived streaming for subscriptions/listen: if you do not, serverless is the natural fit; if you do, you need a platform that allows long-running responses without buffering them.
Can I run an MCP server on serverless?
Yes, and it is now the natural fit rather than a workaround. Statelessness means every request is independently processable with no session to pin a client to an instance, so there is nothing to share between invocations and no sticky routing to configure. This was the main thing that made serverless awkward under the session-based revisions.
Do I need sticky sessions for an MCP server?
Not under 2026-07-28. Sticky sessions existed because a client had to keep reaching the instance holding its Mcp-Session-Id. With sessions removed, any instance can serve any request, so ordinary round-robin load balancing is correct and sticky routing is unnecessary complexity.
Does an MCP server need to support SSE?
Only if it implements subscriptions/listen for server-to-client notifications. A server that only exposes tools and resources can answer every request with a single application/json response. If you do stream, the spec requires Content-Type: text/event-stream and recommends setting X-Accel-Buffering: no so proxies do not buffer the stream.
Does an MCP server need HTTPS?
In practice yes. The spec requires all OAuth protocol URLs to use HTTPS except loopback redirects, so any server with authorization must be served over TLS. Beyond that, the canonical server URI in your Protected Resource Metadata must match what clients actually connect to, which effectively means a stable custom domain with a valid certificate.
What about cold starts?
They matter more under 2026-07-28 than they used to, because a stateless client re-sends context on every request and may call tools/list far more often than a session-based one did. The mitigation is built into the spec: ttlMs and cacheScope let clients and intermediaries cache list responses, so a well-configured server sees far fewer cold invocations than a naive reading suggests.