Von stdio zu Streamable HTTP: Unser MCP Server
TL;DR: Unser erster MCP Server lief lokal via npx und stdio. Das war ein guter Start – aber für die Integration in Claude.ai und andere Clients brauchten wir einen gehosteten Server mit Streamable HTTP Transport. In diesem Artikel zeigen wir, wie wir den Wechsel vollzogen haben, welche Spec-Details wichtig sind und warum Session-Management der eigentliche Knackpunkt war.
Wo wir herkamen: Der stdio-Server
In unserem ersten MCP-Artikel haben wir gezeigt, wie man mit dem @modelcontextprotocol/sdk und ein paar Zeilen Code einen MCP Server baut, der über stdio kommuniziert. Das Setup war simpel:
{
"mcpServers": {
"workshops-de": {
"command": "npx",
"args": ["-y", "@workshops.de/mcp"]
}
}
}
Claude Desktop startet den Prozess lokal, kommuniziert über stdin/stdout – fertig. Für den Einstieg perfekt. Aber stdio hat Grenzen:
- Nur lokal nutzbar – der Server muss auf dem Rechner des Users laufen
- Keine Web-Integration – Claude.ai kann keine lokalen Prozesse starten
- Kein Auth – jeder, der den Prozess startet, hat vollen Zugriff
- Kein Session-Management – jede Verbindung ist ein neuer Prozess
Für ein öffentliches Produkt wie workshops.de, das direkt in Claude.ai funktionieren soll, brauchten wir mehr.
Das Ziel: Ein gehosteter MCP Server
Die MCP-Spec definiert neben stdio einen zweiten Transport: Streamable HTTP. Statt lokalem Prozess-Start kommuniziert der Client über HTTP-Requests mit einem gehosteten Server. Das eröffnet:
- Cloud-Deployment – der Server läuft zentral, nicht auf dem Client
- OAuth 2.1 Integration – Authentifizierung und Autorisierung
- Session-Management – mehrere Clients, mehrere Sessions
- Direkte Claude.ai Integration – als “Connected App”
Streamable HTTP: Was ist anders?
Beim stdio-Transport ist die Kommunikation simpel: JSON-RPC Nachrichten über stdin/stdout, ein Prozess pro Verbindung. Streamable HTTP funktioniert grundlegend anders.
Der Request-Flow
Client Server
| |
| POST /mcp (initialize) |
| ----------------------------->|
| |
| 200 OK + Mcp-Session-Id |
| <-----------------------------|
| |
| POST /mcp (tools/list) |
| Mcp-Session-Id: abc-123 |
| ----------------------------->|
| |
| 200 OK (tool definitions) |
| <-----------------------------|
| |
| POST /mcp (tools/call) |
| Mcp-Session-Id: abc-123 |
| ----------------------------->|
| |
| 200 OK (tool result) |
| <-----------------------------|
Jeder Request ist ein normaler HTTP POST auf einen einzelnen Endpoint (/mcp). Der Server antwortet entweder mit einer direkten JSON Response oder – bei langlebigen Operationen – mit einem SSE-Stream.
Die Session-ID: Dein Lebensfaden
Das Herzstück des Streamable HTTP Transports ist die Mcp-Session-Id. Beim initialize-Request generiert der Server eine Session-ID und schickt sie als Response-Header zurück. Der Client schickt sie bei jedem weiteren Request mit.
Klingt einfach? Ist es auch – bis Sessions ablaufen oder der Server restartet.
Session-Management nach Spec
Die MCP-Spec definiert einen klaren, eleganten Mechanismus für das Session-Handling. Wenn man ihn korrekt implementiert, funktioniert der gesamte Lifecycle – inklusive Recovery – automatisch.
Der Session-Lifecycle
Der Flow besteht aus drei Szenarien:
- Stale Session-ID → Server antwortet mit HTTP 404 → Client re-initialisiert automatisch
- Frischer initialize → Server erstellt neue Session, gespeichert mit Session-ID + OAuth Token
- Bekannte Session → wird direkt wiederverwendet
Das Entscheidende: Die 404-Response bei unbekannten Sessions ist kein Fehler – sie ist der vorgesehene Recovery-Mechanismus. Die Spec verlangt genau dieses Verhalten. Der Server MUSS mit 404 antworten, wenn er eine Session-ID nicht kennt. Der Client MUSS daraufhin eine neue initialize-Sequenz starten.
Warum das so elegant ist
Dieses Design hat einen großen Vorteil: Du brauchst keine Session-Persistence. Sessions können im Speicher leben. Bei einem Deployment gehen sie verloren – und das ist okay. Der Client erkennt die unbekannte Session via 404 und initialisiert neu.
Statt komplexe Persistence mit Redis oder einer Datenbank zu bauen, verlässt du dich auf den Spec-konformen Recovery-Mechanismus. Einfacher, robuster, weniger Fehlerquellen.
Was die Spec klar regelt
Falls ein Client nach einer 404 trotzdem nicht recovered, ist das ein Client-Bug – nicht ein Server-Problem. Die Spec sagt klar, dass es die Verantwortung des Clients ist, auf eine 404 mit Re-Initialization zu reagieren. Serverseitig kann man nichts weiter tun, ohne die Spec zu verletzen.
OAuth 2.1: Der Türsteher
Für die Claude.ai Integration braucht der MCP Server OAuth 2.1. Im Gegensatz zum lokalen stdio-Server, wo der User physischen Zugriff auf seinen Rechner hat, muss ein gehosteter Server wissen, wer da anklopft.
Der Auth-Flow
1. User klickt "Connect" in Claude.ai
2. Redirect zu workshops.de OAuth Login
3. User autorisiert den Zugriff
4. Redirect zurück zu Claude.ai mit Auth-Code
5. Claude.ai tauscht Code gegen Access Token
6. Alle MCP-Requests enthalten das Token
Für uns bedeutet das: Jede Session ist an einen OAuth Token gebunden. Das Session-Management speichert also nicht nur die Session-ID, sondern auch den zugehörigen Token – und damit den User-Kontext.
Von 6 zu 40+ Tools: Der Feature-Sprung
Der ursprüngliche npm-Server hatte 6 Tools: Kurse auflisten, Events finden, Trainer anzeigen. Nett, aber limitiert.
Mit dem gehosteten Server konnten wir die Tool-Palette massiv erweitern – denn jetzt haben wir User-Kontext durch OAuth:
Öffentliche Tools (ohne Auth):
-
search– Volltextsuche über alle Inhalte -
list_courses,list_events– Kurs- und Terminkatalog -
get_course,get_event– Detailinformationen -
list_posts,get_post– Blog-Artikel
Authentifizierte Tools:
-
get_current_user– Wer bin ich? -
get_my_bookings– Meine Buchungen -
subscribe_course– Terminradar abonnieren
Der Unterschied ist fundamental: Mit stdio war der Server ein read-only Nachschlagewerk. Mit Streamable HTTP + OAuth ist er ein vollwertiges, interaktives Tool, das auch Schreiboperationen unterstützt.
Architektur: Node.js MCP Server + Elixir/Phoenix API
Unser Setup besteht aus zwei getrennten Services:
- MCP Server – eine eigenständige Node.js-Anwendung, die das MCP-Protokoll (Streamable HTTP Transport), Session-Management und OAuth handhabt
- workshops.de API – unser bestehendes Elixir/Phoenix Backend, das die eigentliche Business-Logik, Datenbank-Zugriffe und Content-Verwaltung bereitstellt
Der MCP Server ist im Grunde ein Adapter: Er empfängt JSON-RPC Requests von Claude, übersetzt sie in API-Calls an unser Elixir-Backend und formatiert die Responses zurück ins MCP-Format.
Diese Trennung hat Vorteile: Der MCP Server bleibt schlank und fokussiert auf Protokoll-Logik. Die gesamte Domain-Logik lebt weiterhin in der bestehenden API – kein Duplizieren von Business-Rules, kein zweites Datenmodell.
Lessons Learned
1. Die Spec ist dein Freund
Lies die MCP-Spec. Dann lies sie nochmal. Die meisten Fragen, die beim Implementieren aufkommen, sind bereits in der Spec adressiert.
2. Session-Management ist nicht optional
Auch wenn es verlockend ist, Sessions zu ignorieren und jeden Request stateless zu behandeln – die Spec verlangt Sessions, und Clients wie Claude.ai verlassen sich darauf.
3. 404 ist kein Fehler, sondern ein Feature
Die 404-Response bei unbekannten Sessions ist der vorgesehene Recovery-Mechanismus. Korrekt implementieren, nicht drum herum arbeiten.
4. OAuth Token + Session-ID = User-Kontext
Die Kombination aus OAuth Token und Session-ID gibt dir alles, was du brauchst, um personalisierte Responses zu liefern. Nutze das.
5. Start simple, iterate fast
Unser erster Streamable HTTP Endpoint war ein einziger Handler mit einem riesigen switch-Statement. Nicht schön, aber funktional. Refactoring kam später – nachdem der Flow stabil lief.
Was kommt als Nächstes?
Der MCP Server für workshops.de ist jetzt live und direkt in Claude.ai nutzbar. Aber wir sind noch lange nicht fertig:
- Mehr Write-Operations – Buchungen, Bewertungen, Kursmaterial-Zugriff
- Streaming Responses – für langlebige Operationen wie Volltextsuche
- Rate Limiting – damit Claude nicht unsere API in die Knie zwingt
- Analytics – welche Tools werden wie oft genutzt?
- Multi-Client Support – neben Claude.ai auch andere MCP-kompatible Clients
Fazit
Der Weg von stdio zu Streamable HTTP war kein einfaches “Transport austauschen”. Es war ein fundamentaler Architekturwechsel – von einem lokalen Tool zu einem Cloud-Service mit Auth, Session-Management und User-Kontext.
Das Session-Handling ist dabei der Knackpunkt. Die Interaktion zwischen Server und Client muss präzise der Spec folgen – ein falscher Status-Code, und der Client weiß nicht mehr, was er tun soll. Aber genau das macht MCP stark: Es ist ein durchdachter Standard, der diese Edge Cases adressiert. Wenn Server und Client sich an die Spec halten, funktioniert der Recovery-Flow elegant und automatisch.
Unser Tipp: Fang mit stdio an, um MCP zu verstehen. Aber wenn du einen Server für die echte Welt bauen willst – investiere die Zeit in Streamable HTTP. Es lohnt sich.
