Startseite / Artikel

Bereitstellung eines MCP-Servers auf EC2 nach der Änderung der Stateless-Spezifikation

Ein MCP-Server von localhost auf EC2 verschieben: streambares HTTP, nginx und Trägerauthentifizierung – was bedeuten dann stateless Sessions und OAuth 2.1 für eine echte Implementierung?

Verlag: REACTAPP.TOP11 Min. Lesezeit2446 Wörter

Quellen: MCP Servers Explained: A Complete Guide to What I Learned Deploying One to AWS EC2

Von localhost zu einem echten Server

Tutorials zu MCP bleiben in der Regel abstrakt. Sie zeigen einen Client neben einem Server, vergleichen das Protokoll mit einem USB-C-Anschluss oder führen ein fünfzeiliges Beispiel auf localhost aus, das niemals den Laptop des Entwicklers verlässt. Die eigentliche Arbeit beginnt danach: Ein lokal geschriebenes Tool muss auf einem echten Server unter einem echten Reverse-Proxy laufen, damit ein echter Client es über das öffentliche Internet aufrufen kann. Das ist ein eigenständiges Problem, dem nur wenig Aufmerksamkeit geschenkt wird.

Die hier beschriebene Implementierung beginnt mit einem lokalen MCP-Server, verschiebt ihn auf eine EC2-Instanz und stellt ihn über den streambaren HTTP-Transport zur Verfügung, wobei nginx im Voraus einen Berechtigungstoken verlangt. Es funktionierte einwandfrei. Kurz darauf wurde das Protokoll weiterentwickelt. Am 28. Juli 2026 wurde die bisher größte Überarbeitung der MCP-Spezifikation vom Release Candidate zur endgültigen Version, wodurch der Kern des Protokolls zustandslos wurde. Teams checken oft „MCP-Unterstützung“ als einzige Option ab, doch es handelt sich dabei um drei separate Aspekte: Transport, Authentifizierung sowie, seit diesem Update, Skalierbarkeit. Eine falsche Entscheidung in einem dieser Bereiche ist es, was ein Demo-Tool von einem Tool unterscheidet, dem in der Produktion Vertrauen entgegengebracht werden kann.

Der Überblick befasst sich mit mehreren Fragen: Welche Teile des Problems definiert MCP und welche liegen ungelöst, warum das streamfähige HTTP den auf SSE basierenden Transport verdrängt hat, wie nginx und ein Trägertoken einen lokalen Server auf EC2 freigeben, welche Änderungen durch die stateless Rewrite-Logik entstehen, warum ein statischer Token zwar als authentifiziert gilt, aber nicht den Anforderungen einer spezifikationskonformen Authentifizierung entspricht, und welche Teile der Implementierung geändert werden mussten, um mit dem Fortschritt Schritt zu halten.

Was MCP standardisiert und was nicht

Auf dem Papier legt das Model Context Protocol nur eine Sache fest: die Art und Weise, wie eine KI-Anwendung über JSON-RPC-Nachrichten, die über einen definierten Transport übertragen werden, Tools findet und aufruft sowie Ressourcen von einem separaten Serverprozess abruft. Tool-Logik, Agentenentwurf und -einsatz liegen außerhalb dessen. Es handelt sich um ein Standard zur Verbindung, nicht um ein Framework. Früher benötigte jede KI-Anwendung, die Zugang zu einer internen API haben wollte, eine maßgeschneiderte Integration. Unter MCP dient ein einziger Server allen kompatiblen Clients.

Wenn man es so betrachtet, wird klar, warum die Implementierung Schwierigkeiten verursacht. Das Protokoll beschreibt die Nachrichtenstruktur, schweigt jedoch darüber, ob der Server nur von einem Laptop aus oder von überall aus erreichbar ist, ob etwas ihn schützt und ob er mehrere Clients gleichzeitig bewältigen kann. Der Betreiber ist für alle diese Fragen verantwortlich.

Warum eine öffentliche Bereitstellung anders ist

  • Lokalen Servern werden fast nichts preisgegeben, öffentlichen Servern hingegen viel. Mit einer öffentlichen URL wird der Server zu einer authentifizierten API wie jeder andere, weshalb die Handhabung von Tokens und die Eingabenvalidierung genauso wichtig sind.
  • Früher waren die Regeln bezüglich Sessions und Skalierung unklar. Falsche Annahmen führten entweder dazu, dass der Server unter gleichzeitiger Last stillschweigend versagte, oder dass er mit unnötigen „sticky sessions“ belastet wurde.
  • Die Spezifikation entwickelt sich weiter. Eine Implementierung, die auf vor sechs Monaten gemachten Annahmen beruht, kann ohne jeglichen Fehler aus der Konformität geraten, weil die Clients einfach auf eine ältere Protokollversion zurückgreifen.

Die Architektur sieht so aus.

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

Ein Prinzip leitet all das. Sobald ein MCP-Server eine öffentliche URL erhält, sollte er wie jede über EC2 bereitgestellte, authentifizierte API behandelt werden, da er strukturell einer ist. MCP ist lediglich das darüberliegende Nachrichtenformat, während die Sicherheits- und Skalierungsaspekte identisch mit denen jedes Backends sind.

1. Streambarer HTTP-Transport

Streambarer HTTP ist ein einziger HTTP-Endpunkt, der POST-Anfragen entgegennimmt, die JSON-RPC-Nachrichten enthalten. Er hat den früheren HTTP+SSE-Transport abgelöst, der neben regulären HTTP-Aufrufen einen persistenten server-sent-events-Stream benötigte.

Ein POST-Endpunkt ist für jeden Reverse-Proxy, Load Balancer oder API-Gateway vertrautes Terrain, wodurch es kein Problem gibt, einen langanhaltenden Stream über nginx oder einen ALB offen zu halten. Der Traffic sieht aus wie bei einer regulären HTTP-API, und der nginx-Location-Block bleibt kurz.

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;
}

Ein Tipp für die Produktion: Selbst bei einem Transport mit einem einzigen Endpunkt sollte die Lesezeitüberschreitung des Proxys großzügig gewählt werden. Einzelne Toolaufrufe können durchaus länger dauern als eine typische API-Anfrage, und eine zu strenge Zeitüberschreitung führt dazu, dass auch gültige, lang andauernde Aufrufe zusammen mit den tatsächlich hängengebliebenen abgebrochen werden.

2. Bearer-Token-Authentifizierung

Bei diesem Verfahren befindet sich ein festes Token im Authorization-Header und wird vor dem Erreichen des MCP-Server-Prozesses – egal ob in nginx oder im Anwendungs-Middleware – überprüft. Für Inhalte auf öffentlichen URLs ist dies die am wenigsten sichere, aber dennoch eine akzeptable und sinnvolle erste Lösung. Allerdings läuft das Token niemals ab, ist nicht an einen bestimmten Server gebunden und bietet keinen Schutz, falls es in Logs landet. Es gibt lediglich Auskunft darüber, dass eine Anfrage über Berechtigungsdaten verfügt.

Ein Entwurf für ein FastAPI-Middleware beginnt mit den Importen.

from fastapi import Request, HTTPException

Dann kommt die Überprüfung selbst.

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")

Noch ein Tipp: Halten Sie den rohen Authorization-Header aus den Zugriffsprotokollen raus. Die Standardformate für nginx- und Anwendungsprotokolle führen dazu, dass dieser leicht übersehen wird, und sobald er darin enthalten ist, wird die Protokolldatei zu einer leckenden Zugangsdatenquelle.

3. Statelessness – der Kern der Überarbeitung vom Juli 2026

Durch die Spezifikation vom 2026-07-28 verschwinden Sessions auf Protokollebene. Es gibt keinen Mcp-Session-Id-Header mehr, und der Transportweg verzichtet auch auf den lang lebenden GET-Stream, den frühere Versionen nutzten, um Client und Server während einer Konversation synchron zu halten. Jeder Anfrage enthält alles, was sie benötigt.

Skalierbarkeit ist das Wichtigste. Das ältere Modell ermöglichte es einem Server, sich über mehrere Anfragen hinweg an Informationen über einen Client zu erinnern, und sobald mehrere Instanzen hinter einem Load Balancer standen, erforderte diese Erinnerung sogenannte „sticky sessions“. Ohne Zustand auf Protokollebene kann jede Instanz jede Anfrage bearbeiten, was eine echte horizontale Skalierung ermöglicht und die Clients davon befreit, an ein bestimmtes Gerät gebunden zu sein.

In der Praxis wird ein Server, der zwischen Anfragen Zustände über einen Client im Arbeitsspeicher speichert, unter den neuen Vorgaben nicht mehr funktionieren. Alle wichtigen Zustände sollten in den Anfrage-Body oder in eine externe Speicherlösung wie Redis übertragen werden, die von jeder Instanz gelesen werden kann.

4. Mehrfache Round-Trip-Anfragen

Auch ein staatloses Protokoll benötigt eine Möglichkeit, damit der Server mitten in einem Toolaufruf eine Frage an den Client stellen kann – beispielsweise um eine Bestätigung oder einen fehlenden Parameter anzufragen. Die neue Spezifikation löst dieses Problem mit Mehrfach-Round-Trip-Anfragen. Anstatt einen Datenstrom offen zu halten, antwortet der Server mit einem InputRequiredResult, der seine Fragen zusammen mit einem unklaren requestState-Wert auflistet. Der Client sammelt die Antworten, sendet den ursprünglichen Aufruf erneut, fügt diese Antworten hinzu und gibt den Zustand unverändert zurück.

Deshalb ist eine echte Zustandslosigkeit umsetzbar. Da alles, was der Server zum Fortsetzen benötigt, im Payload enthalten ist, den der Client zurücksendet, kann jede Instanz – nicht unbedingt die, die den Aufruf initiiert hat – den Versuch wiederholen und ihn abschließen.

Kunden sollten requestState unberührt lassen: weder inspizieren noch bearbeiten. Bewahren Sie ihn bei und geben Sie ihn genau so zurück, wie er angekommen ist – andernfalls kann eine auf seine Integrität angewiesene Fortsetzung fehlschlagen.

5. Verschärfung der Autorisierung

Die überarbeitete Spezifikation lenkt entfernte MCP-Server auf OAuth 2.1 mit PKCE und erfordert Ressourcenindikatoren (RFC 8707), die ein ausgestelltes Token mit dem Server verknüpfen, für den es bestimmt ist.

Ein Problem stellt das Wiederholen von Anfragen dar. Wenn ein statisches Tragetoken durchsickert, kann jedes Server, das es akzeptiert, ins Visier geraten. Ein mit einer Ressource verknüpftes Token schließt dieses Sicherheitsloch – daher funktioniert ein für einen MCP-Server ausgestelltes Token nicht auf einem anderen, selbst wenn beide auf denselben Identitätsanbieter verweisen.

Eine Setup mit Träger-Token, bei dem viele Anwender beginnen, erfüllt zwar die Anforderung „dieser Endpunkt erfordert Authentifizierung“, reicht aber nicht aus, um die Interoperabilität sowie den Schutz vor Wiederabspielung zu gewährleisten, die die neue Spezifikation von einem konformen Remote-Server erwartet. Dieser Unterschied muss geschlossen werden, bevor die URL an Personen außerhalb des Kernteams weitergeleitet wird.

6. Deprecierungsrichtlinie und Erweiterungen

Die Funktionen in der Spezifikation folgen nun einem formalen Lebenszyklus mit den Phasen Aktiv, Depreciert und Entfernt, wobei mindestens zwölf Monate zwischen Deprecierung und Entfernung liegen. Neue Funktionen wie serverseitig generierte Benutzeroberflächen oder langlaufende Aufgaben werden zunächst als optional verfügbare Erweiterungen eingeführt, anstatt direkt in den Kern aufgenommen zu werden. Einige ältere Funktionen, darunter roots, Sampling und Logging in ihrer ursprünglichen Form, werden aufgrund neuerer Alternativen in Richtung Deprecierung geführt.

Für Anbieter bedeutet das, dass eine funktionierende Integration durch zukünftige Überarbeitungen nicht über Nacht gestört wird, und neue Funktionen können bewusst implementiert werden, anstatt unter Druck zu geraten, mit allen Neuerungen der Spezifikation Schritt zu halten.

Ein End-to-End-Überblick

Der Weg von einem lokalen Prototypen bis zu einer mit der aktuellen Spezifikation übereinstimmenden EC2-Implementierung sieht wie folgt aus:

  1. lokale Tests. Der Server läuft über stdio gegen einen lokalen Client – ohne Netzwerk und ohne Authentifizierung – wodurch die Iterationsgeschwindigkeit hoch ist.
  2. Wechsel auf streambaren HTTP. Der Server wird neu konfiguriert, um den streambaren HTTP-Transport zu verwenden, und auf eine EC2-Instanz bereitgestellt.
  3. nginx als Proxy. nginx beendet den TLS-Verkehr, überprüft den Token, bevor etwas den MCP-Prozess erreicht, und leitet die Anfrage anschließend weiter.
  • Einfacher Tool-Aufruf. Ein POST-Anfragen enthält eine JSON-RPC-Anfrage, und das Ergebnis kommt in derselben Anfrage und Antwort zurück.
  • Versionenverhandlung. Mit der endgültigen Spezifikation vom 28.07.2026 verhandeln Client und Server eine Protokollversion. Ein Client, der noch nicht aktualisiert wurde, wechselt automatisch auf die Version vom 25.11.2025, ohne komplett zu versagen.
  • Mehr Eingaben erforderlich. Anstatt die Verbindung offen zu lassen, antwortet der Server mit einem InputRequiredResult, in dem aufgeführt wird, was noch gefragt werden muss.
  • Fortsetzen. Der Client wiederholt den Aufruf mit den beigefügten Antworten sowie dem zurückgesendeten requestState. Der erneute Versuch hängt nicht von einer bestimmten Instanz ab, sodass jede Maschine hinter nginx ihn ausführen kann.
  • Authentifizierungsfehler. Anfragen ohne Token oder mit einem ungültigen Token werden bei nginx abgewiesen. Der Anwendungscode kommt nie mit ihnen in Berührung.
  • Sonderfälle

    Schleppende Operationen. Anwendungen, die früher von einem während der gesamten Laufzeit offenen Stream abhängig waren, sollten auf die Tasks-Erweiterung umgestellt werden. Die Erstellung einer persistenten Verbindung auf einem Transportmechanismus, der ohne solche Verbindung konzipiert ist, führt in die falsche Richtung.

    Tools, die niemals remote genutzt werden. Ein Server, der ausschließlich lokal neben seinem Client läuft, wird am besten mit stdio bedient, und fast keine der hier genannten Bereitstellungsprobleme gelten in diesem Fall.

    Klienten mit gemischten Versionen. Da ältere Klienten automatisch auf 2025-11-25 zurückgreifen, kann eine einzige Bereitstellung eine Weile lang beide Protokollversionen bedienen, sodass nicht jeder Klient im Gleichschritt mit dem Server aktualisiert werden muss.

    Skalierung und Herausforderungen in der Produktion

    Mehrere Instanzen. Im Session-Modell bedeutete es, einen Client an die Instanz zu binden, die ihn kannte, entweder durch „sticky sessions“ oder gemeinsame Session-Speicherung. Dieser Bedarf besteht nicht mehr, und für alle, die mehr als einen Rechner betreiben, ist das der Hauptvorteil der Überarbeitung.

    TLS in einer wachsenden Infrastruktur. Das Verwalten eines Zertifikats auf jeder EC2-Instanz wird schnell unübersichtlich. Ein verwalteter Load Balancer, der ein ACM-Zertifikat vor der Infrastruktur hält, erfordert weitaus weniger Aufwand als die TLS-Konfiguration mit nginx auf jedem einzelnen Rechner.

    Ausstellung und Rotation von Tokens. Statische Träger-Token haben ihre Sicherheitsgrenzen und stellen außerdem bei mehr als ein paar Clients ein betriebliches Problem dar, da jede Rotation eine manuelle Koordination erfordert. Die auf OAuth 2.1 basierende Ausstellung behebt sowohl die Sicherheitslücken als auch die Belastung durch die Rotation gleichzeitig.

    Aufspüren mehrerer Anrufe ohne Serverseitige Sessions. Da keine Session eine Abfolge von Anrufen auf dem Server verbindet, werden die in der Anfrage-Payload enthaltenen Korrelations-IDs zur einzigen zuverlässigen Methode, herauszufinden, was bei einer einzelnen Interaktion mit einem Agenten über mehrere Anrufe hinweg geschehen ist.

    Verarbeitung der Wiederaufnahme auf der Client-Seite

    Die nginx-Konfiguration sowie das bearer-check-Middleware kümmern sich um den Transport und die grundlegende Authentifizierung. Hier ist ein minimales Muster für die Client-Seite einer Multi-Round-Trip-Anfrage.

    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
    

    Wichtig ist die letzte Zeile, in der requestState genau so zurückgegeben wird, wie es der Server bereitgestellt hat.

    Häufige Fehlerquellen

    • „Deployed“ als „produktionsreif“ zu betrachten. nginx zusammen mit einem Token erfüllt nur die grundlegendsten Anforderungen, nicht alle. Halten Sie den MCP-Endpunkt an die Standards jeder öffentlichen API – mit angemessener Authentifizierung, sorgfältigem Logging und Rate Limiting – bevor die URL weit verbreitet wird.
    • Verlass auf Servergedächtnis. Bei einigen frühen Implementierungen wurde stillschweigend davon ausgegangen, dass ein Client aus einer vorherigen Anfrage in Erinnerung bleibt. Planen Sie damit, dass jede Anfrage eine andere Instanz als die vorherige erreicht, da die aktuelle Spezifikation genau das zulässt.
  • Fixierung auf eine einzige Protokollversion. Ein Client, der auf eine bestimmte Revision festgelegt ist, funktioniert nicht mehr, wenn der Server aktualisiert wird. Implementieren Sie Verhandlungsmechanismen mit einer sinnvollen Rückfalllösung, damit ein Upgrade einer Seite die andere nicht lahmlegt.
  • Auslass der Authentifizierung für ein internes Tool. Ein heute intern genutztes Tool könnte später an ein Partnerteam übergeben werden, und dann reicht ein statischer Token nicht aus. Wenn das Gerät möglicherweise den Laptop oder die VPC verlässt, sollten von Anfang an OAuth 2.1 sowie Ressourcenindikatoren verwendet werden.
  • Praktiken, die in der Produktion funktionieren

    • Vermeiden Sie Zustände, bei denen Ihre Infrastruktur nicht skaliert werden kann. Alles, was zwischen den Aufrufen erhalten bleiben muss, sollte im Payload oder in einem externen Speicher gespeichert werden.
    • Verknüpfen Sie jeden Token mit seiner vorgesehenen Ressource, denn ein Token, das von jedem Server akzeptiert wird, dem Sie vertrauen, wird früher oder später an einem falschen Ort verwendet.
    • Verhandeln Sie die Protokollversionen und gehen Sie nicht von einer bestimmten aus. Clients und Server aktualisieren sich selten am selben Tag, daher muss man auf diese Lücke Rücksicht nehmen.
    • Überprüfen Sie die Authentifizierung an der Peripherie. Wenn nginx eine fehlerhafte Anfrage ablehnt, bleibt der MCP-Prozess einfacher und zeigt weniger Informationen preis.
    • Lassen Sie unsichtbare Felder unberührt. requestState und ähnliche Felder existieren dafür, dass der Server sich auf die zurückgegebenen Werte verlassen kann; daher sollten Clients sie weder lesen noch ändern.

    Was dies für die Bereitstellung bedeutet

    Theoretisch ist MCP einfach. Die Bereitstellung umfasst die wichtigen Entscheidungen, und die Spezifikationsaktualisierung vom 28. Juli 2026 hat diese geändert. Streamable HTTP lässt sich leicht hinter herkömmlicher Infrastruktur betreiben, die Zustandslosigkeit macht das Hinzufügen einer EC2-Instanz zu einer realistischen Option anstelle eines Projekts zur Sitzungskontrolle, und das Autorisierungsmodell passt nun zu einem öffentlich zugänglichen Server. Nichts davon ist schwierig, sobald man es versteht – und genau das werden theoretische Beschreibungen oft weggelassen.

    Verwandte Artikel

  • Neue Messung der MCP-Token-Steuer: Zwei Tool-Beschreibungen verursachten den Anstieg — Eine 13-tägige erneute Ausführung von neun MCP-Servern zeigt, dass sich die Anzahl der Token in den Tool-Definitionen von 36.320 auf 38.738 erhöht hat, wobei fast alle Änderungen von einem Server stammen, der seine bereits vorhandenen Beschreibungen überschrieb.