Blogs / MCP-Server erklärt: Was ich beim Deployment auf AWS EC2 gelernt habe

MCP-Server erklärt: Was ich beim Deployment auf AWS EC2 gelernt habe

Veröffentlicht
26. Juli 2026
Autor
Faizan Nadeem
Tags
MCP AI Agent Tools Backend Development AWS
Diagramm einer Serverinfrastruktur, das einen bereitgestellten API-Endpunkt hinter einem Reverse Proxy darstellt
Foto von Google Deep Mind auf Unsplash

Die meisten MCP-Inhalte sind theoretisch — Diagramme eines Clients, der mit einem Server spricht, eine Analogie zu USB-C, ein fünfzeiliges „Hello World“, das auf localhost läuft und nie deinen Laptop verlässt. Nichts davon bereitet dich auf die eigentliche Arbeit vor: ein lokal gebautes Tool auf einen echten Server zu bringen, hinter einen echten Reverse Proxy zu setzen und für einen echten Client über das öffentliche Internet erreichbar zu machen. Das ist ein anderes Problem — und genau darüber schreibt fast niemand.

Ich habe einen solchen Server deployed. Ein lokaler MCP-Server, verschoben auf eine EC2-Instanz, bereitgestellt über den Streamable-HTTP-Transport mit Bearer-Token-Auth davor via nginx. Es funktionierte. Und dann änderte sich, fast sobald ich mich auf dieses Setup eingeschossen hatte, das Protokoll selbst darunter — die größte Überarbeitung der MCP-Spezifikation seit dem Start ging am 28. Juli 2026 von Release Candidate zu Final über und schreibt den Kern des Protokolls auf Statelessness um. Die meisten behandeln „MCP-Support hinzufügen“ wie einen einzelnen Haken auf einer Liste. Ist es nicht. Es ist eine Transportentscheidung, eine Auth-Entscheidung und jetzt — mit diesem Spec-Update — eine Skalierungsentscheidung, und wenn du nur eine davon falsch triffst, ist das der Unterschied zwischen einem Tool, das in einer Demo funktioniert, und einem, dem du in Produktion tatsächlich vertrauen kannst.

Du wirst lernen:

  • Was MCP tatsächlich standardisiert und was es bewusst dir überlässt
  • Wie der Streamable-HTTP-Transport funktioniert und warum er den älteren SSE-basierten Ansatz ersetzt hat
  • Wie ich einen lokalen MCP-Server auf EC2 mit nginx und Bearer-Token-Auth veröffentlicht habe
  • Was die Stateless-Umschreibung vom 28. Juli 2026 auf Protokollebene verändert
  • Warum ein statischer Bearer-Token die Hürde für „hat Auth“ nimmt, aber nicht für „spec-konforme Auth“
  • Was ich in meinem eigenen Deployment tatsächlich ändern musste, um mit dem Update Schritt zu halten

Inhaltsverzeichnis

  1. Die Grundlagen
  2. Die vollständige Architektur
  3. Die Kernschichten erklärt
  4. Ende-zu-Ende-Durchgang
  5. Sonderfälle
  6. Skalierungs- & Produktionsherausforderungen
  7. Codebeispiele
  8. Häufige Fallstricke
  9. Best Practices für Produktion

Die Grundlagen

Was MCP tatsächlich abdeckt

Das Model Context Protocol standardisiert genau eine Sache: wie eine AI-Anwendung Tools entdeckt und aufruft sowie Ressourcen liest, die von einem separaten Serverprozess bereitgestellt werden — mithilfe eines definierten Nachrichtenformats (JSON-RPC) über einen definierten Transport. Es sagt dir nicht, wie du deine Tool-Logik schreibst, wie du deinen Agenten entwirfst oder wie du irgendetwas deployst. Es ist ein Verbindungsstandard, kein Framework. Bevor es existierte, brauchte jede AI-Anwendung, die zum Beispiel mit deiner internen API sprechen wollte, eine individuelle Einmal-Integration. MCP bedeutet, dass du den Server einmal baust und jeder konforme Client mit ihm sprechen kann.

Diese Einordnung ist wichtig, weil sie auch genau erklärt, warum das Deployment der schwierige Teil ist. Das Protokoll gibt dir einen Vertrag dafür, wie Nachrichten strukturiert sind — es sagt nichts darüber aus, ob dein Server nur von deinem Laptop oder aus dem Internet erreichbar ist, ob er durch irgendetwas geschützt ist oder ob er mehr als einen gleichzeitig zugreifenden Client verkraftet. All das liegt bei dir.

Warum Deployment-Details ein wertvolles Problem sind

  • Ein lokal laufender MCP-Server hat keine echte Angriffsfläche. Ein öffentlich bereitgestellter schon. In dem Moment, in dem dein Server eine öffentliche URL hat, ist er eine authentifizierte API wie jede andere — mit allem, was das für Token-Handling und Eingabevalidierung bedeutet.
  • Session- und Skalierungsverhalten waren unter der älteren Spec nicht offensichtlich. Wenn man das falsch machte, bekam man entweder einen Server, der unter paralleler Last stillschweigend kaputtging, oder einen überengineerten mit Sticky Sessions, die er gar nicht brauchte.
  • Das Protokoll selbst bewegt sich noch. Ein Deployment, das auf Annahmen von vor sechs Monaten basiert, kann still und leise aufhören, spec-konform zu sein, ohne einen einzigen Fehler zu werfen — Clients beginnen stattdessen einfach, auf eine ältere Protokollversion herunterzuhandeln.

Die vollständige Architektur

AI Client ── HTTPS POST ──▶ nginx (TLS termination, auth check, reverse proxy)
                                    │
                                    ▼
                          MCP Server Process
                          (Streamable HTTP transport)
                                    │
                       ┌────────────┴────────────┐
                    Tools                    Resources

Das Leitprinzip: Sobald dein MCP-Server eine öffentliche URL hat, behandle ihn exakt wie jede andere authentifizierte API, die du von EC2 aus veröffentlichen würdest — denn strukturell ist er genau das. Der „MCP“-Teil ist das Nachrichtenformat obendrauf; die Sicherheits- und Skalierungsüberlegungen darunter sind dieselben, die du auf jeden Backend-Service anwenden würdest.

Die Kernschichten erklärt

1. Transport: Streamable HTTP

Was es ist: Ein einzelner HTTP-Endpunkt, der POST-Anfragen mit JSON-RPC-Nachrichten akzeptiert. Er ersetzte den früheren HTTP+SSE-Transport, der neben normalen HTTP-Aufrufen auf einen dauerhaften Server-Sent-Events-Stream setzte.

Warum es wichtig ist: Ein einzelner POST-Endpunkt ist etwas, womit jeder Reverse Proxy, Load Balancer und API Gateway ohnehin umgehen kann. Du kämpfst nicht mit deiner Infrastruktur, um einen langlebigen Stream durch nginx oder einen ALB am Leben zu halten — es verhält sich wie eine normale HTTP-API.

location /mcp {
    proxy_pass http://127.0.0.1:8000;
    proxy_set_header Host $host;
    proxy_set_header Authorization $http_authorization;
    proxy_http_version 1.1;
    proxy_read_timeout 300s;
}

Produktionstipp: Selbst bei einem Single-Endpoint-Transport solltest du den Read-Timeout deines Proxys großzügig setzen. Einzelne Tool-Aufrufe können legitimerweise länger dauern als eine typische API-Anfrage, und ein zu knapp gesetzter Timeout beendet gültige langlaufende Aufrufe — nicht nur hängende Verbindungen.

2. Authentifizierung: Bearer Tokens (ein Startpunkt, kein Ziel)

Was es ist: Ein statischer Token im Authorization-Header, der geprüft wird, bevor eine Anfrage den MCP-Serverprozess erreichen darf — entweder auf nginx-Ebene oder in Application-Middleware.

Warum es wichtig ist: Es ist das minimal tragfähige Sicherheitsniveau für alles mit öffentlicher URL, und genau dort habe ich angefangen. Aber ein statischer Bearer-Token hat keine Ablaufsemantik, keine Bindung an einen bestimmten Server und keinen Schutz, wenn er irgendwo in ein Log gerät. Er beantwortet die Frage „Ist diese Anfrage überhaupt authentifiziert?“, aber nicht viel mehr.

from fastapi import Request, HTTPException

VALID_TOKEN = "your-rotated-secret-token"

async def verify_bearer(request: Request):
    auth = request.headers.get("authorization", "")
    if auth != f"Bearer {VALID_TOKEN}":
        raise HTTPException(status_code=401, detail="Unauthorized")

Produktionstipp: Lass niemals den rohen Authorization-Header in deine Access-Logs gelangen. Das übersieht man leicht in einem Standard-Logformat von nginx oder der App — und schon wird eine Logdatei zu einem Credential-Leak.

3. Statelessness — der Kern der Umschreibung vom Juli 2026

Was es ist: Die Spezifikation vom 2026-07-28 entfernt Protokoll-Sessions vollständig. Es gibt keinen Mcp-Session-Id-Header mehr, und der Transport lässt auch den lang gehaltenen GET-Stream-Endpunkt fallen, den frühere Versionen nutzten, um Client und Server über eine Konversation hinweg synchron zu halten. Jede Anfrage ist jetzt in sich abgeschlossen.

Warum es wichtig ist: Im älteren Modell musste sich ein Server oft etwas über einen Client zwischen Aufrufen merken — genau die Art von Zustand, die dich bei mehr als einer Serverinstanz hinter einem Load Balancer in Sticky Sessions zwingt. Statelessness auf Protokollebene bedeutet, dass jede Instanz legitimerweise jede Anfrage bearbeiten kann — echtes horizontales Skalieren, ohne einen Client an eine bestimmte Box zu binden.

Produktionstipp: Wenn dein Server auf In-Memory-State gesetzt hat, um sich irgendetwas über einen Client zwischen Aufrufen zu merken, bricht dieses Muster unter der neuen Spec auseinander. Verschiebe jeden relevanten Zustand entweder direkt in den Request-Payload oder in einen externen Store wie Redis, den jede Instanz lesen kann.

4. Multi Round-Trip Requests (den Benutzer mitten im Aufruf etwas fragen)

Was es ist: Ein zustandsloses Protokoll braucht trotzdem eine Möglichkeit, dass ein Server einen Client mitten in einem Tool-Aufruf um Input bittet — eine Bestätigung, einen fehlenden Parameter. Die neue Spec löst das mit sogenannten Multi Round-Trip Requests: Statt einen Stream offen zu halten, gibt der Server ein InputRequiredResult zurück, das die Fragen und ein opakes requestState-Blob enthält. Der Client sammelt die Antworten und stellt den ursprünglichen Aufruf erneut aus — mit sowohl den Antworten als auch dem zurückgesendeten State im Gepäck.

Warum es wichtig ist: Das ist der Baustein, der echte Statelessness praktikabel macht. Weil alles, was der Server zum Fortsetzen braucht, im Payload enthalten ist, den der Client zurücksendet, kann jede Serverinstanz — nicht notwendigerweise die, die den Aufruf gestartet hat — den Retry aufnehmen und abschließen.

Produktionstipp: Behandle requestState clientseitig als opak. Untersuche oder verändere es nicht — speichere es einfach und sende es exakt wie empfangen zurück, sonst riskierst du, einen Fortsetzungsablauf zu brechen, der darauf angewiesen ist, dass es unverändert bleibt.

5. Härtung der Autorisierung: OAuth 2.1, PKCE und Resource Indicators

Was es ist: Die aktualisierte Spec drängt Remote-MCP-Server in Richtung OAuth 2.1 mit PKCE für die Autorisierung und verlangt Resource Indicators (gemäß RFC 8707), die einen ausgestellten Token an genau den Server binden, für den er gedacht war.

Warum es wichtig ist: Ein statischer Bearer-Token kann gegen jeden Server wiederverwendet werden, der ihn zufällig akzeptiert, falls er jemals geleakt wird. Einen Token an eine bestimmte Resource zu binden, schließt genau diese Lücke — ein Token, der für einen MCP-Server ausgestellt wurde, ist gegen einen anderen nicht mehr nutzbar, selbst wenn beide demselben Identity Provider vertrauen.

Produktionstipp: Ein Bearer-Token-Setup wie das, mit dem die meisten von uns angefangen haben, erfüllt „dieser Endpunkt erfordert Authentifizierung“. Es erfüllt nicht die Interoperabilitäts- und Replay-Schutz-Hürde, die die neue Spec für einen korrekt konformen Remote-Server setzt, und diese Lücke sollte man schließen, bevor man die URL an irgendwen außerhalb des eigenen Teams weitergibt.

6. Deprecation Policy und das Extensions Framework

Was es ist: Die Spec bewegt Capabilities jetzt durch einen formalen Lebenszyklus von Active → Deprecated → Removed, mit einem Mindestabstand von zwölf Monaten zwischen Deprecation und Removal. Neue Capabilities — Dinge wie serverseitig gerendertes UI oder Unterstützung für langlaufende Tasks — erscheinen zuerst als opt-in Extensions, statt direkt im Protokollkern zu landen. Eine Handvoll älterer Capabilities, darunter roots, sampling und logging in ihrer ursprünglich spezifizierten Form, bewegen sich zugunsten ihrer Nachfolger durch diesen Deprecation-Pfad.

Warum es wichtig ist: Das bedeutet, dass eine funktionierende Integration nicht über Nacht durch eine zukünftige Revision kaputtgeht, und dass du neue Capabilities bewusst übernehmen kannst, statt gezwungen zu sein, bei jeder Erweiterung der Spec sofort mitzuhalten.

Ende-zu-Ende-Durchgang

Verfolge den tatsächlichen Weg vom lokalen Prototyp zu einem EC2-Deployment auf aktuellem Spec-Stand:

  1. Lokales Testen. Der MCP-Server läuft über stdio gegen einen lokalen Client — kein Netzwerk, keine Auth, schnelle Iteration.
  2. Umstieg auf Streamable HTTP. Der Server wird so umkonfiguriert, dass er statt stdio den Streamable-HTTP-Transport bereitstellt, und auf eine EC2-Instanz deployed.
  3. nginx davor. TLS termination passiert bei nginx, das auch den Bearer-Token prüft, bevor irgendetwas den MCP-Prozess erreicht, und die Anfrage dann per Reverse Proxy weiterleitet.
  4. Ein normaler Tool-Aufruf. Der Client sendet eine JSON-RPC-Anfrage als einzelnes POST; der Server verarbeitet sie und gibt im selben Request-Response-Zyklus ein Ergebnis zurück.
  5. Aushandlung der Protokollversion. Da die Spec vom 2026-07-28 jetzt final ist, handeln Client und Server aus, welche Protokollrevision verwendet wird. Ein Client, der noch nicht aktualisiert wurde, handelt automatisch auf 2025-11-25 herunter, statt komplett zu scheitern.
  6. Ein Tool braucht mehr Input. Statt die Verbindung offenzuhalten, gibt der Server ein InputRequiredResult mit den Fragen zurück, die beantwortet werden müssen.
  7. Der Client setzt fort. Er stellt den Aufruf erneut mit den Antworten und dem zurückgesendeten requestState aus. Weil diese Fortsetzung nicht davon abhängt, dieselbe Serverinstanz zu treffen, kann sie auf jeder Box hinter nginx landen.
  8. Pfad bei Auth-Fehlern. Ein fehlender oder ungültiger Token wird auf nginx-Ebene abgewiesen, bevor er überhaupt den MCP-Prozess erreicht — der Application-Code sieht die fehlerhafte oder nicht authentifizierte Anfrage gar nicht erst.

Sonderfälle

Langlaufende Tools. Alles, was sich früher bei einer langsamen Operation auf einen offengehaltenen Stream stützte, sollte auf das Tasks-Extension-Muster umsteigen, statt zu versuchen, auf einem Transport, der so etwas nicht mehr unterstützt, wieder eine dauerhafte Verbindung nachzubauen.

Tools ohne Plan für Remote-Betrieb. Wenn ein Server nur lokal neben seinem Client laufen muss, ist stdio-Transport weiterhin die einfachste Option, und fast keine der hier besprochenen Deployment-Fragen ist relevant.

Clients mit gemischten Versionen während des Übergangs. Da ältere Clients automatisch auf 2025-11-25 zurückfallen, ist es realistisch, eine Zeit lang ein einziges Deployment zu betreiben, das beide Protokollrevisionen korrekt bedient, statt jeden Client im Gleichschritt mit dem Server aktualisieren zu müssen.

Skalierungs- & Produktionsherausforderungen

Mehrere Instanzen hinter einem Load Balancer. Unter dem alten sitzungsbasierten Modell bedeutete das Sticky Sessions oder gemeinsamen Session-Speicher, damit ein Client an die Instanz gebunden blieb, die über ihn Bescheid wusste. Statelessness entfernt diese Anforderung vollständig — jede Instanz kann jede Anfrage bedienen, und genau das ist für alle mit mehr als einer Box der eigentliche Durchbruch dieser Umschreibung.

TLS- und Zertifikatsmanagement bei wachsender Instanzzahl. Ein Zertifikat pro EC2-Instanz zu verwalten skaliert schlecht; einen verwalteten Load Balancer mit ACM-gestütztem Zertifikat vor eine Flotte von Instanzen zu setzen, verursacht weit weniger operativen Aufwand als nginx-TLS-Konfigurationen pro Instanz.

Token-Ausstellung und Rotation im großen Maßstab. Statische Bearer-Tokens haben nicht nur eine Sicherheitsobergrenze — sie werden auch schnell zum operativen Ärgernis, sobald du mehr als nur ein paar Clients hast, weil jede Rotation ein manuelles Koordinationsproblem ist. Der Umstieg auf OAuth-2.1-basierte Ausstellung schließt die Sicherheitslücke und beseitigt gleichzeitig den Rotationsschmerz.

Tracing von Multi-Call-Interaktionen ohne serverseitigen Session-State. Wenn auf dem Server keine Session mehr eine Folge von Aufrufen zusammenhält, werden Correlation IDs, die durch den Request-Payload mitgeführt werden, zum einzigen verlässlichen Mittel, um zu rekonstruieren, was eine einzelne Agent-Interaktion über mehrere Aufrufe hinweg tatsächlich getan hat.

Codebeispiele

Die obige nginx-Konfiguration und die Middleware zur Bearer-Prüfung decken die Transport- und grundlegenden Auth-Schichten ab. Hier ist ein minimales Muster dafür, wie der Fortsetzungs-Teil eines Multi Round-Trip Request auf Client-Seite behandelt wird:

async def call_tool_with_resume(client, tool_name, params):
    result = await client.call_tool(tool_name, params)
    if result.get("type") == "InputRequiredResult":
        answers = collect_answers(result["questions"])
        return await client.call_tool(
            tool_name,
            {**params, "answers": answers, "requestState": result["requestState"]},
        )
    return result

Das wichtige Detail ist die letzte Zeile: requestState wird direkt durchgereicht, unberührt, exakt so, wie der Server es gesendet hat.

Häufige Fallstricke

Fehler: „Es ist deployed“ mit „Es ist produktionsreif“ gleichsetzen. nginx und einen Bearer-Token vor einen MCP-Server zu setzen, nimmt nur die niedrigste Hürde, nicht die ganze. Lösung: Miss deinen MCP-Endpunkt an demselben Standard wie jede andere öffentliche API — ordentliche Auth, diszipliniertes Logging und Rate Limiting, bevor du die URL breit teilst.

Fehler: Sich zwischen Aufrufen auf Server-Memory verlassen. Manche frühen Deployments hingen stillschweigend davon ab, dass der Server sich etwas über einen Client aus einer vorherigen Anfrage merkte. Lösung: Behandle jede Anfrage so, als könnte sie auf einer anderen Instanz landen als die letzte — denn unter der aktuellen Spec kann sie das.

Fehler: Eine einzelne Protokollversion im Client hart zu kodieren. Ein Client, der auf genau eine Revision festgenagelt ist, bricht in dem Moment, in dem ein Server aktualisiert wird. Lösung: Implementiere Version Negotiation mit einem vernünftigen Fallback, damit ein Upgrade auf der einen Seite nicht die andere Seite mit herunterzieht.

Fehler: Die Auth-Hürde für ein „nur internes“ Tool unterschätzen. Was heute nur intern ist, wird morgen oft mit einem Partnerteam geteilt — und ab diesem Punkt reicht ein statischer Token nicht mehr. Lösung: Baue von Anfang an in Richtung OAuth 2.1 und Resource Indicators, wenn es irgendeine realistische Chance gibt, dass der Server deinen Laptop oder deine VPC verlässt.

Best Practices für Produktion

  • Halte keinen Zustand vor, den deine Infrastruktur nicht skalieren kann. Wenn sich ein Client zwischen Aufrufen etwas merken muss, pack es in den Payload oder einen externen Store — nicht in den Server-Memory.
  • Binde Tokens an die Resource, für die sie gedacht sind. Ein Token, der gegen jeden Server funktioniert, dem du zufällig vertraust, ist ein Token, der irgendwann irgendwo verwendet wird, wo du das nie beabsichtigt hast.
  • Handle Protokollversionen aus, statt eine anzunehmen. Deine Clients und dein Server werden nicht immer am selben Tag upgraden — baue bewusst für diese Lücke.
  • Halte Auth-Checks am Edge. Eine schlechte Anfrage schon bei nginx abzuweisen, bevor sie deinen Application-Code erreicht, hält deinen MCP-Prozess einfacher und deine Angriffsfläche kleiner.
  • Behandle jedes opake Protokollfeld als opak. requestState und ähnliche Felder existieren, damit der Server dem vertrauen kann, was zurückkommt — untersuche oder verändere sie clientseitig nicht.

Zum Abschluss

Die Theorie rund um MCP ist einfach; im Deployment liegen die eigentlichen Entscheidungen, und genau diese Entscheidungen haben sich mit dem Spec-Update vom 28. Juli 2026 für alle geändert. Streamable HTTP wurde einfacher hinter Standard-Infrastruktur zu betreiben, Statelessness machte „eine weitere EC2-Instanz hinzufügen“ zu einer echten Option statt zu einem Session-Management-Projekt, und das Autorisierungsmodell holte endlich zu dem auf, was ein öffentlich erreichbarer Server braucht. Nichts davon ist kompliziert, wenn man es einmal gesehen hat — es ist nur nicht der Teil, den irgendeiner der theoretischen Artikel abdeckt.

Wenn du einen MCP-Server irgendwo anders als auf localhost deployed hast: Was musstest du ändern, als echte Clients und echte Last dazukamen?

Weitere Artikel