Diese Woche sind zwei Dinge passiert, die aus „kann ich einen MCP-Server gratis betreiben“ einen klaren Ja-Fall machen, mit einer messbaren Bedingung. Heute hat das Model Context Protocol seine Revision 2026-07-28 veröffentlicht, und die Schlagzeilen-Änderung ist ein zustandsloser Protokollkern: Sessions, der Mcp-Session-Id-Header und der initialize-Handshake sind weg. Der Release-Post nennt die Konsequenz direkt:

Any request can now land on any server instance behind a plain round-robin load balancer without needing shared storage.

Und gestern, einen Tag vor dem Release, drehte sich Cloudflares eigene Dokumentation. Die Klasse McpAgent — der offizielle Pfad, der jeden MCP-Server mit einem Durable Object unterlegte, weil der Transport einen Ort für seine Session brauchte — ist jetzt als deprecated und eingefroren markiert, mit einem zustandslosen Request-Handler als empfohlenem Ersatz für neue Server.

Beide Züge schließen dieselbe Lücke von zwei Seiten. Ein Worker auf dem Free-Plan war immer der natürliche Ort für einen kleinen MCP-Server, bis auf eines: der Transport verlangte Zustand, und Zustand hieß Durable Object und klebrige Instanz. Jetzt verspricht das Protokoll selbst, dass jede Anfrage in sich geschlossen ist. Was bleibt, ist die eine harte Decke des Free-Plans — 10 ms CPU pro Anfrage —, und ob ein echter Server darunter passt, ist keine Frage zum Diskutieren. Es ist eine Frage zum Messen. Also haben wir einen gebaut, auf dieser Website, und nachgemessen.

Was die Revision tatsächlich entfernt hat

Der alte Lebenszyklus eröffnete jede Verbindung mit einer initialize-Runde, prägte eine Session-ID und verlangte, dass der Client sie bei jedem Aufruf mitführt. All das ist gestrichen. Version, Client-Identität und Capabilities reisen jetzt in den _meta-Feldern jeder Anfrage, und das eine, was ein Server implementieren muss, ist server/discover: es meldet jedem, der fragt, unterstützte Versionen und Capabilities.

Der Transport wurde im Gegenzug strenger. Jeder POST muss einen MCP-Protocol-Version-Header tragen, der zur Version im Body passt, dazu einen Mcp-Method-Header als Spiegel der JSON-RPC-Methode — und Mcp-Name bei Tool-Aufrufen —, damit Load Balancer und Gateways routen können, ohne Bodies zu parsen. Eine Abweichung ist ein hartes 400. Batching bleibt draußen, ping ist komplett entfernt, eine unbekannte Methode ist jetzt ein HTTP-404, und der HTTP+SSE-Transport der 2024er-Ära ist förmlich als deprecated eingestuft. Ein Server, der nie von sich aus Nachrichten schickt, darf jede Anfrage mit reinem JSON beantworten und den Streaming-Pfad mit 405 verweigern.

Bis 2025: zustandsbehaftet

initialize

Session-ID geprägt

jeder Aufruf trägt die Session

dieselbe Instanz muss antworten

2026-07-28: zustandslos

ein POST
Version + Identität in _meta

jede Instanz antwortet

Was ein Client früher tun musste, und was eine Anfrage jetzt ist

Für einen Server, der nur liest, ist die verbleibende Protokollfläche klein: server/discover, tools/list, tools/call und die Header-Disziplin. Das ist die ganze Liste.

Der Server, den diese Website jetzt betreibt

Dieses Blog veröffentlicht bereits eine Agent-Oberfläche — Markdown-Spiegel jedes Artikels und einen posts.json-Feed. Der MCP-Server ist dieselben Dateien hinter zwei Tools: list_articles liefert den Feed, get_article das volle Markdown eines Artikels. Er lebt unter https://301.sh/mcp auf demselben Worker, der die Inhaltsverhandlung der Seite macht, ohne Abhängigkeiten — nach der Revision ist die verbleibende Protokollfläche kurz genug, um sie von Hand auszuschreiben. Cloudflare liefert für denselben Job einen SDK-Weg (createMcpHandler plus Protokoll-SDK), und der ist die richtige Antwort, sobald man Resources, Prompts oder Elicitation braucht; ein Nur-Lese-Server mit zwei Tools braucht sie nicht.

Der Dispatch, auf seine Form verdichtet:

switch (message.method) {
  case 'server/discover':
    return reply(id, { resultType: 'complete', supportedVersions: ['2026-07-28'],
      capabilities: { tools: {} }, ttlMs: 3_600_000, cacheScope: 'public' });
  case 'tools/list':
    return reply(id, { resultType: 'complete', tools: TOOLS, ttlMs: 3_600_000,
      cacheScope: 'public' });
  case 'tools/call': {
    // Mcp-Name header must equal params.name, or 400 + HeaderMismatch.
    const mirror = await env.ASSETS.fetch(new URL(`/${slug}.md`, origin));
    return reply(id, { resultType: 'complete',
      content: [{ type: 'text', text: await mirror.text() }], isError: false });
  }
  default:
    return fail(id, 404, -32601, `Method not found: ${message.method}`);
}

Nichts darin rechnet. Jede Antwort ist ein bereits deploytes statisches Asset, geholt über das Assets-Binding, und genau diese Entscheidung belohnt die Messung.

Die Messung

Cloudflares Definition des Limits ist der Teil, den die meisten überlesen:

CPU time measures how long the CPU spends executing your Worker code. Waiting on network requests (such as fetch() calls, KV reads, or database queries) does not count toward CPU time.

Das 10-ms-Budget geht also für Parsen, Validieren und JSON-Bauen drauf — nicht für das Holen des Artikeltexts. Wir haben 40 Anfragen an den Produktiv-Endpunkt geschickt und die cpuTime pro Aufruf aus den Workers Logs gelesen:

Methode CPU, ms (min / Median / max) Wall, ms (min / Median / max)
server/discover 0 / 0 / 1 0 / 1 / 2
tools/list 0 / 0 / 0 0 / 1 / 2
list_articles 0 / 1 / 1 10 / 13 / 23
get_article, größter Artikel 0 / 1 / 2 6 / 10 / 88

Der schlechteste Fall verbraucht 2 ms vom 10-ms-Budget, und die langsamste Antwort — 88 ms Wanduhrzeit für den längsten Artikel der Seite — ist fast vollständig Warten auf den Asset-Fetch, das der Zähler ignoriert. Ein ausliefernd arbeitendes Tool läuft bei einem Fünftel der freien Decke mit Luft nach oben, und die andere Zahl des Free-Plans, 100.000 Anfragen am Tag, ist für einen Endpunkt, den Agents ein paarmal pro Gespräch aufrufen, eine ganz andere Klasse von Problem.

Wo die 10 ms wirklich beißen

Die Decke ist real; sie wohnt nur woanders als im Transport. Beim Audit der Agent-Oberfläche dieser Seite haben wir eine Template-Engine vermessen, die wir als Service erwogen hatten, und sie riss dieselben 10 ms um den Faktor acht. Das ist die ehrliche Trennlinie: ein Tool, das Bytes ausliefert, kostet ein bis zwei Millisekunden; ein Tool, das rechnet — Dokumente parst, Templates rendert, in Menge hasht —, frisst das Budget fast sofort. Cloudflare erlaubt gelegentliche Überschreitungen („Each isolate has some built-in flexibility“), aber ein Worker, der das Limit dauerhaft reißt, wird mit Fehler 1102 beendet, und Burst-Toleranz ist kein Plan.

Die Aufhebung hat einen Preis, und nach der Hausregel dieser Seite gehört er neben das Limit: der Workers-Paid-Tarif für $5 im Monat hebt die Kappe pro Anfrage auf standardmäßig 30 Sekunden (konfigurierbar bis 5 Minuten) und enthält 30 Millionen CPU-Millisekunden und 10 Millionen Anfragen im Monat. Wenn Ihre Tools rechnen, ist das die Zahl zum Vergleichen, nicht die 10 ms des Free-Tarifs.

Probieren Sie es, und was das alles nicht braucht

Der Endpunkt ist live, und eine Anfrage zeigt die ganze neue Form des Protokolls:

curl https://301.sh/mcp -X POST \
  -H 'Content-Type: application/json' \
  -H 'MCP-Protocol-Version: 2026-07-28' \
  -H 'Mcp-Method: server/discover' \
  -d '{"jsonrpc":"2.0","id":1,"method":"server/discover",
       "params":{"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28"}}}'

Keine Session zu öffnen, kein Zustand zu halten, kein Durable Object auf der Rechnung. Wenn Ihre Seite bereits Markdown-Spiegel oder einen Feed veröffentlicht — und nach dem Agent-Readiness-Audit tat unsere das —, ist die Distanz von „statische Dateien“ zu „MCP-Server“ eine Route auf einem Worker, den Sie womöglich schon betreiben. Die Spezifikation passt endlich zu dem, was ein kleiner Nur-Lese-Server immer sein wollte: ein schlichter HTTP-Endpunkt, der Fragen zu Inhalten beantwortet, die Sie längst gebaut haben. Der Free-Plan trägt das mühelos. Es sind die rechnenden Tools, die nie gratis sein würden — und jetzt kennen Sie die Zahl, die das entscheidet.