Was passiert, wenn ein MCP-Server auf Cloudflare Workers trifft – und warum stdio die bessere Antwort war
SerieMCP für Entwickler
Teil 7 von 8
Ausgangslage: Eine Registry für KI-Agenten
Webspire ist eine kuratierte Registry aus CSS-Effekten und UI-Patterns, die als Tailwind-kompatible Snippets bereitstehen. Jedes Snippet hat eine klare Struktur: HTML als Referenz, CSS Custom Properties für Konfiguration, automatisch generierte Astro- und Web-Component-Varianten. Kein Runtime-Overhead, keine Framework-Abhängigkeit.
Das Ziel: Webspire soll nicht nur für Menschen nutzbar sein, sondern auch für KI-Agenten. Über MCP sollen Tools wie Claude Code oder Cursor direkt auf die Registry zugreifen können – Snippets suchen, Empfehlungen bekommen, Code abrufen. Der Agent wird zum Komponentenberater.
Der MCP-Server stellt 7 Tools und 5 Resources bereit:
search_snippets– Volltextsuche mit Filtern nach Kategorie, Tags, Accessibilityrecommend_snippet– Use Case beschreiben, passendes Snippet bekommenget_snippet– Vollständigen CSS-Code samt Metadaten abrufenlist_categories– Alle Kategorien mit Anzahlsearch_patterns/get_pattern– UI-Patterns nach Intent oder Family durchsuchen
Ein typischer Workflow: Du sagst deinem Agenten “Ich brauche einen glassmorphism Login-Card”, der Agent ruft recommend_snippet auf, bekommt glass/frosted zurück, holt den Code via get_snippet und passt ihn an deinen Kontext an. Getesteter Code statt halluziniertes CSS.
Der erste Ansatz: Cloudflare Worker
Die naheliegende Idee war, den MCP-Server als Cloudflare Worker zu deployen. Der Worker lief bereits als REST-API, also warum nicht einfach einen /mcp-Endpoint hinzufügen?
const server = new McpServer({
name: 'webspire',
version: '0.1.0',
});
const transport = new WebStandardStreamableHTTPServerTransport({
sessionIdGenerator: undefined,
enableJsonResponse: true,
});
await server.connect(transport);
return transport.handleRequest(request);
Stateless, jeder Request erstellt eine frische Server-Instanz, registriert Tools und Resources, verarbeitet die Anfrage. Sauber, funktional, deployed in Sekunden.
Und es hat funktioniert. Technisch.
Das Problem: Architektur-Mismatch
Was wir nicht bedacht hatten: MCP-Clients und Cloudflare Workers haben fundamental unterschiedliche Erwartungen.
MCP-Clients (wie mcp-remote, Claude Desktop, Cursor) erwarten persistente Verbindungen. Sie bauen eine Session auf und halten sie am Leben – mit regelmäßigen Health-Checks, Reconnect-Logik und Session-Management. Das ist das richtige Verhalten für ein Protokoll, das auf kontinuierliche Interaktion ausgelegt ist.
Cloudflare Workers sind von Haus aus stateless. Jeder Request ist isoliert, kein Session-State zwischen Requests. Das lässt sich ändern – mit Durable Objects, KV Storage oder D1 kann man Sessions persistent machen. Aber genau das wollten wir nicht: Der MCP-Server sollte bewusst stateless bleiben, weil die Daten read-only sind und kein Session-Management nötig ist. Nur passt dieses Modell nicht zu MCP-Clients, die persistente Verbindungen erwarten. Jeder Health-Check ist dadurch ein vollständiger Cold Start: Server erstellen, Tools registrieren, Transport aufbauen, Response senden.
Das Ergebnis: Ein einzelner MCP-Client generierte im Leerlauf 250.000 Requests pro Tag – für einen Client. Nicht weil er etwas tat – sondern weil er die Verbindung aufrechterhalten wollte, die es nicht gab.
Die Kostenrechnung
Cloudflare Workers sind großzügig: 100.000 Requests pro Tag kostenlos, danach 0,50 Dollar pro Million. Klingt nach wenig. Aber:
| Szenario | Requests/Tag | Kosten/Monat |
|---|---|---|
| 1 Client idle | 250.000 | ca. 2,25 Dollar |
| 10 Clients | 2.500.000 | ca. 37,50 Dollar |
| 50 Clients | 12.500.000 | ca. 187 Dollar |
Und das alles für: nichts. Kein einziger sinnvoller Request. Jeder dieser Requests erstellt einen MCP-Server, registriert 7 Tools und 5 Resources, baut einen Transport auf und antwortet mit “ja, ich lebe noch”. Das ist architektonisch falsch.
Alternativen auf Worker-Seite
Wir haben verschiedene Ansätze evaluiert:
| Ansatz | Löst das Problem? | Trade-off |
|---|---|---|
| Rate Limiting | Teilweise | Client pollt trotzdem, bekommt 429er |
| API Key / Auth | Nein | Verhindert Missbrauch, nicht das Polling |
| Durable Objects | Ja | Session-State möglich, aber Extra-Kosten und Komplexität |
| SSE mit Sessions | Teilweise | Workers haben kein echtes Persistent-Streaming (30s Timeout) |
Durable Objects wären die technisch sauberste Lösung gewesen – ein Durable Object pro Client, das den Session-State hält und Health-Checks ohne neuen Server-Aufbau beantwortet. Aber für ein Projekt mit statischen, read-only Daten ist das Over-Engineering.
Die eigentliche Frage
Am Ende war die Frage einfach: Braucht Webspire überhaupt einen Remote-MCP?
Die Antwort: Nein.
- Die Registry ist statisch – sie ändert sich nur beim Deploy
- Alle Daten sind read-only – kein User-State, keine Mutationen
- Es gibt keine Echtzeit-Anforderung – Snippets ändern sich nicht zwischen Requests
- Die Registry ist klein – rund 600 Zeilen JSON, kein Problem für ein lokales Paket
Die Lösung: npm-Paket mit stdio
Also haben wir @casoon/webspire-mcp als npm-Paket gebaut:
npx @casoon/webspire-mcp
Das Paket bundled die komplette Registry bei Build-Zeit. Kein Netzwerk-Call, kein Server, keine Kosten. Der MCP-Server läuft lokal über stdio – der natürliche Transport für Desktop-Tools wie Claude Code oder Cursor.
Konfiguration in Claude Code
{
"mcpServers": {
"webspire": {
"command": "npx",
"args": ["@casoon/webspire-mcp"]
}
}
}
Drei Zeilen. Danach hat der Agent Zugriff auf die komplette Registry.
Der Vergleich
| Cloudflare Worker | npm-Paket | |
|---|---|---|
| Transport | HTTP (stateless) | stdio (persistent) |
| Session | Keine (Reconnect-Loop) | Natürlich (Prozess lebt) |
| Kosten | 0,50 Dollar/Mio Requests | 0 |
| Latenz | ca. 50ms (Netzwerk) | ca. 1ms (lokal) |
| Registry | Fetch + Cache | Bundled, instant |
| Offline | Nein | Ja |
| Setup | URL konfigurieren | npx ausführen |
Die stdio-Verbindung löst das Session-Problem elegant: Der Prozess startet einmal, bleibt am Leben, und die Kommunikation läuft über stdin/stdout. Kein Polling, kein Reconnect, kein Overhead.
Wann Remote-MCP trotzdem richtig ist
Cloudflare Workers sind nicht das Problem. Die REST-API läuft weiterhin dort – für JSON-Endpoints und maschinelle Konsumenten, die klassische HTTP-Requests machen.
Das Problem war spezifisch das Zusammenspiel von MCP-Protokoll und stateless Workers. MCP ist für persistente Verbindungen designed, Workers für kurzlebige Request-Response-Zyklen. Beides hat seinen Platz – nur nicht zusammen, wenn der Client Health-Checks in Dauerschleife sendet.
Remote-MCP ist die richtige Wahl bei:
- Dynamischen Daten – wenn sich der Inhalt zwischen Requests ändert
- Multi-Tenant – wenn verschiedene Nutzer unterschiedliche Daten sehen
- Authentifizierung – wenn der Zugriff kontrolliert werden muss
- Zentrale Updates – wenn alle Clients sofort die neueste Version brauchen
Für eine statische Snippet-Registry trifft nichts davon zu.
Projektstruktur Webspire MCP
Die Entscheidungsmatrix
Für alle, die vor einer ähnlichen Entscheidung stehen:
| Kriterium | Lokal (stdio/npm) | Remote (HTTP/SSE) |
|---|---|---|
| Daten ändern sich selten | Besser | Unnötig |
| Daten sind nutzerspezifisch | Ungeeignet | Besser |
| Offline-Fähigkeit wichtig | Ja | Nein |
| Kosten minimieren | Ja | Skaliert mit Nutzung |
| Einfaches Setup | npx reicht | URL + Auth konfigurieren |
| Zentrale Kontrolle | Nein | Ja |
Die Faustregel: Wenn deine Daten in ein npm-Paket passen und sich nicht zwischen Requests ändern, ist stdio fast immer die bessere Wahl. Remote-MCP löst Probleme, die du bei statischen Daten nicht hast.
Was ich daraus gelernt habe
- Die einfachste Lösung ist oft die richtige. Nicht jedes Problem braucht einen Server.
- stdio ist der native MCP-Transport – designed für genau diesen Use Case.
- Architektur-Entscheidungen kosten Geld. 250.000 sinnlose Requests pro Tag sind ein Warnsignal, kein Feature.
- Remote und lokal schließen sich nicht aus. Die REST-API bleibt, der MCP-Server wandert lokal. Jede Schnittstelle bekommt den Transport, der zu ihr passt.
Der MCP-Server läuft jetzt dort, wo er hingehört: auf deinem Rechner, neben deinem Editor, ohne Umweg über die Cloud.
Die vollständige Registry mit CSS-Snippets, UI-Patterns und Token-System gibt es auf der Webspire Landingpage.