Strona główna / Artykuły / MCP bez stanu: skalowanie serwerów bez sesji ani procedur handshakingowych

MCP bez stanu: skalowanie serwerów bez sesji ani procedur handshakingowych

Jak bezstanowy Model Context Protocol kończy sesje i procedury handshaking, oraz w jaki sposób _meta, wieloetapowe żądania, nagłówki routingu, cache i zadania zapewniają jego użyteczność.

1919 słów

Serwer protokołu Model Context Protocol, który działa bez zarzutu na jednym komputerze, może zacząć sprawiać problemy w momencie uruchomienia trzech kopii za load balancerem, ponieważ każda instancja pamięta tylko sesje, które sama stworzyła. Przejście protokołu w kierunku bezstanowej struktury jest właśnie skierowane na rozwiązanie tego problemu. Ten artykuł wyjaśnia, jakie zmiany następują po usunięciu sesji i procedury inicjalizacji, w jaki sposób żądania przenoszą swój własny kontekst, oraz jak interakcje wieloetapowe, routowanie oparte na nagłówkach, listy możliwe do zapisania w pamięci cache oraz zadania w tle pasują do nowego modelu, abyś mógł ocenić, jakie to ma znaczenie dla serwerów i bramek, które używasz.

Uwaga dotycząca terminologii: opisany tutaj projekt bez stanu należy do rewizji protokołu z 2026 roku. Szczegóły takie jak dokładne nazwy metod, nazwy nagłówków oraz typy wyników mogą się jeszcze zmienić, dlatego przed użyciem ich w kodzie należy je sprawdzić w aktualnej specyfikacji MCP. Jeśli potrzebujesz przypomnienia podstaw tego, jak klienci MCP odkrywają i wywołują narzędzia, wpis na blogu dotyczący sposobu, w jaki Model Context Protocol umożliwia agentom odkrywanie i wywoływanie narzędzi, omawia ten temat.

Co oznacza tutaj „stan”

System jest stanowy, gdy musi pamiętać coś między żądaniami, aby móc obsłużyć następne. Dobrym codziennym przykładem jest zamówienie jedzenia na dostawę: przechodzi ono od stanu przyjętego, przez przygotowywanie, do wysyłki, aż po dostarczenie, a usługa musi śledzić, w którym etapie znajduje się każde zamówienie, aby móc w każdej chwili odpowiedzieć na pytanie „Gdzie jest moje jedzenie?”.

Wcześniejsze wersje MCP funkcjonowały w ten sam sposób na poziomie połączenia. Gdy klient (aplikacja AI) po raz pierwszy łączył się z serwerem, obie strony przeprowadzały procedurę inicjalizacji. Następnie serwer wydawał identyfikator sesji, na przykład abc123, a klient dołączał go do każdego kolejnego żądania, aby serwer mógł powiązać każdą próbę połączenia z tym, co wydarzyło się wcześniej w rozmowie, włączając w to umiejętności uzgodnione przez obie strony.

Dlaczego sesje przestają działać przy skalowaniu poziomym

Z jedną instancją serwera i umiarkowanym ruchem taki projekt jest w pełni wystarczający. Problemy pojawiają się, gdy obciążenie rośnie i dodaje się instancje za balansem obciążeń:

  1. Klient użytkownika wysyła swój pierwszy żądanie, na przykład o listę narzędzi. Balans obciążeń kieruje je do Serwera 1, który tworzy sesję abc123.
  2. Ten sam klient wysyła drugie żądanie, na przykład do narzędzia pogodowego. Tym razem balans obciążeń wybiera Serwer 2.
  3. Serwer 2 nie zna abc123. Nie posiada żadnych danych ze stanu pamięci Serwera 1, więc żądanie zostaje odrzucone.

Systemy stanowe mają dwa standardowe rozwiązania. Sesje „sticky” przypinają każdego klienta do jednej instancji, co utrudnia równomierne rozłożenie obciążenia i komplikuje przejście na zapasową instancję. Alternatywnie wspólna baza danych, tak jak Redis, przechowuje dane sesji, które mogą być odczytywane przez wszystkie instancje. To działa, ale wymaga utrzymania dodatkowej infrastruktury, jej zabezpieczenia i zapewnienia dostępności, a także wykonywania zapytań sieciowych przy każdym wezwaniu – wszystko to wyłącznie po to, by zachować zgodność z protokołem.

Model bezstanowy

Wersja z 2026 roku obiera inną drogę: przekształca MCP w bezstanowy protokół zapytanie-odpowiedź i eliminuje sesje. Każde zapytanie jest niezależne i nie polega na żadnych informacjach przechowywanych przez serwer z poprzedniego zapytania. Ponieważ na serwerze nie ma pamięci przypisanej do konkretnego klienta, każda instancja może odpowiedzieć na dowolne zapytanie, a skalowanie polega jedynie na dodawaniu instancji za zwykłym balansem obciążenia.

To nie oznacza, że aplikacja w ogóle nie może mieć żadnego stanu. Narzędzie zarządzające koszykiem zakupowym lub edycją długiego dokumentu nadal potrzebuje danych w jakimś miejscu. Różnica polega na tym, że taki stan staje się jawnymi danymi aplikacji, przechowywanymi w wybranym miejscu i odwoływanymi za pomocą identyfikatorów w żądaniu, zamiast być ukrytym stanem protokołu powiązanym z połączeniem.

Jak żądanie przenosi swój własny kontekst

Bez ręcznego nawiązywania połączenia czy identyfikatora sesji serwer nadal musi wiedzieć, jaką wersję protokołu używa klient, kim jest klient i co potrafi. Odpowiedź brzmi, że każde żądanie przynosi ze sobą te informacje.

Obiekt _meta

Ładowarki żądań zawierają opcjonalny obiekt _meta do przechowywania tych metadanych. Może on zawierać:

  • Wersję protokołu, dzięki czemu serwer wie, jak interpretować wiadomość.
  • Tożsamość klienta, np. nazwa i wersja aplikacji, takie jak MyAIApp v1.0.
  • Zdolności klienta, aby serwer wiedział, jakie funkcje może wykorzystać w swojej odpowiedzi.
  • Ponieważ kontekst jest dostarczany w ramach żądania, serwer może je natychmiast przetworzyć, bez konieczności wyszukiwania w tabeli sesji. Wadą jest nieco większa objętość danych przy każdym wywołaniu, co zazwyczaj jest zaniedbywalne w porównaniu z kosztem wspólnego magazynu sesji.

    Interakcje wieloetapowe bez otwartej połączenia

    Projekty z zachowaniem stanu ułatwiają wymianę informacji: jeśli serwer potrzebuje dodatkowych danych, może o nie poprosić przez już otwarte połączenie. Weźmy użytkownika, który chce zarezerwować lot do Delhi, ale zapomina podać datę. Serwer z zachowaniem stanu mógłby po prostu poprosić o datę i czekać na odpowiedź przez to samo połączenie.

    Protokół bez stanu nie może utrzymywać otwartych połączeń w tym celu, dlatego MCP definiuje zamiast tego uporządkowany proces wieloetapowy:

    1. Gdy serwer otrzymuje żądanie bez niezbędnych parametrów, zwraca specjalny wynik input required zamiast napotkać błąd lub czekać.
    2. Aplikacja klienta prosi użytkownika o brakujące informacje.
    3. Klient umieszcza odpowiedzi w obiekcie input responses i wysyła nowe, całkowicie niezależne żądanie, które serwer może zrealizować.

    Ponieważ druga prośba zawiera wszystko, co jest potrzebne, może trafić na dowolną instancję serwera. Serwer nie musi pamiętać, że zadał pytanie – to klient kontynuuje proces. Jeśli serwer musi powiązać te dwie prośby (na przykład aby uniknąć powtarzania kosztownych operacji), może zwrócić klientowi nieprzejrzysty token do odsłuchania, zamiast przechowywać ukrytą pamięć.

    Ładowanie na podstawie nagłówków zamiast treści

    Podejście bez stanu otwiera również możliwości poprawy wydajności i operacyjności. Wyróżniają się dwie zmiany: Ładowanie oparte na nagłówkach oraz możliwość przechowywania w pamięci podręcznej wyników listy.

    Detale protokołu w nagłówkach HTTP

    Wcześniej infrastruktura znajdująca się przed serwerem MCP, taką jak brama API, firewall aplikacji internetowych czy balanser obciążenia, musiała analizować treść JSON każdej żądania tylko po to, aby ustalić, który metodę lub narzędzie jest wywoływane. Analiza treści żądań na poziomie bramy generuje obciążenie dla procesora, powoduje opóźnienia i jest trudna do skonfigurowania w wielu bramach.

    Zgodnie z nowymi zasadami żądania HTTP muszą zawierać kluczowe informacje protokołowe w nagłówkach:

    • MCP-Method, na przykład tools/call;
    • MCP-Name – nazwa konkretnego narzędzia, które jest wywoływane.

    Brama może odczytywać te nagłówki i kierować ruchem, ograniczać jego szybkość lub go blokować bez wpływania na treść przesyłanych danych. Dzięki temu proste zasady są łatwe do sformułowania – na przykład wysyłanie kosztownych narzędzi do dedykowanej grupy instancji, stosowanie bardziej restrykcyjnych ograniczeń szybkości dla jednego narzędzia lub całkowite zablokowanie narzędzia podczas incydentu. Jak zawsze w przypadku nagłówków, serwer powinien nadal sprawdzać, czy odpowiadają one treści przesyłanej, aby klient nie mógł obejść zasad poprzez wysłanie mylącego nagłówka.

    Listy narzędzi i poleceń nadających się do cache’owania

    Klienci nieustannie zadają serwerom te same pytania: jakie narzędzia są dostępne, które polecenia są obsługiwane. Wśród tysięcy użytkowników serwer może poświęcać zaskakująco dużo swojej mocy obliczeniowej na odpowiadanie na te identyczne żądania.

    Listy narzędzi i zapytań rzadko się zmieniają, dlatego nowa wersja umożliwia przechowywanie wyników list w pamięci podręcznej. Klient może pobrać listę narzędzi raz, przechować ją w pamięci i używać ponownie przy kolejnych żądaniach, zamiast pytać o nią ponownie. Ta sama cecha pozwala wspólnej infrastrukturze, takiej jak brama lub cache HTTP przed serwerami, odpowiadać na powtarzające się żądania list od wielu klientów jednocześnie. W obu przypadkach do serwera trafia znacznie mniej żądań. Podobnie jak w przypadku każdego cache’u, potrzebny jest sposób na unieważnienie jego zawartości, gdy aktualizacja zmienia listę, więc należy planować termin wygaśnięcia lub wersjonowanie zamiast trwałego przechowywania.

    Długotrwałe zadania z użyciem tła

    Część narzędzi odpowiada w milisekundach, np. przy wyszukiwaniu informacji pogodowych. Inne tak nie robią: poproszenie asystenta o analizę 10 000 dokumentów może zająć dwadzieścia minut. W prostym cyklu żądanie-odpowiedź klient musiałby utrzymywać połączenie otwarte przez cały czas, co zajmuje zasoby po obu stronach i powoduje, że interfejs użytkownika pozostaje w stanie oczekiwania.

    Aby to rozwiązać, wersja z 2026 roku zawiera przeprojektowany framework Tasks:

    1. Tworzenie. Gdy klient uruchamia intensywne narzędzie, serwer natychmiast odpowiada identyfikatorem zadania, na przykład task_abc123, a początkowe żądanie zostaje zakończone.
    2. Egzekucja w tle. Serwer przeprowadza analizę w tle, podczas gdy użytkownik może kontynuować korzystanie z pozostałych funkcji aplikacji.
  • Kontrole postępów. W dowolnym momencie klient może zapytać o stan i wyniki zadania za pomocą wywołania Task Get.
  • Zaktualizowane dane wejściowe. Jeśli w trakcie wykonywania zadania potrzebne są dodatkowe informacje, klient dostarcza je za pomocą Task Update.
  • To jest znany wzorzec asynchronicznego zadania z web API, zastosowany w MCP. Zapewnia to responsywność aplikacji niezależnie od stopnia złożoności wykonywanych działań. W środowisku z wieloma instancjami pamiętaj, że stan zadania musi być przechowywany w miejscu dostępnym dla wszystkich instancji, ponieważ wywołanie Task Get może trafić do innego serwera niż ten, który utworzył zadanie. Protokół nie wymaga już wspólnego stanu sesji, ale odpowiedzialność za trwałe przechowywanie zadań nadal spoczywa na Tobie.

    Co to oznacza dla twoich serwerów

    Jeśli zarządzasz lub budujesz serwery MCP, praktyczna lista kontrolna wygląda następująco:

    • Usuń ukrytą pamięć na poziomie pojedynczej połączenia. Wszystko, czego narzędzie potrzebuje pomiędzy wywołaniami, powinno znajdować się w jawnej przestrzeni przechowywania, oznaczonej identyfikatorami wysyłanymi przez klienta.
    • Czytaj kontekst z każdej prośby. Pobieraj wersję protokołu, tożsamość i możliwości klienta z pola _meta, a nie z sesji.
    • Projektuj narzędzia tak, aby pytały, a nie czekały. Zwracaj wynik wymagający danych wejściowych, gdy brakuje parametrów, i oczekuj odpowiedzi w nowej prośbie.
    • Wykorzystuj nagłówki routingu na poziomie brzegowym. Skonfiguruj bramy tak, aby kierowały ruch i ograniczały go według MCP-Method oraz MCP-Name, a także weryfikowały je w odniesieniu do treści żądania na serwerze.
  • Listy cache’u i planowanie unieważniania. Pozwól klientom i bramkom cache’ować listy narzędzi i poleceń, z wyraźną metodą odświeżania ich po wdrożeniach.
  • Przenoszenie wolnych narzędzi do zadań. Szybko zwracaj identyfikator zadania i przechowuj stan zadania w magazynie udostępnianym wszystkim instancjom.
  • Główne wnioski

    • Bezstanowa wersja zastępuje projekt MCP oparty na sesjach niezależnymi wywołaniami typu żądanie-odpowiedź, co eliminuje potrzebę sesji trwałych lub wspólnego magazynu sesji jedynie w celu skalowania.
    • Identyfikatory ręcznego nawiązania połączenia i sesji zniknęły; każde żądanie zawiera w polu _meta wersję protokołu, identyfikator klienta oraz jego możliwości.
    • Brakujące informacje są obsługiwane poprzez wynik input required oraz kolejne żądanie zawierające input responses, zamiast otwartego połączenia.
    • MCP-Method oraz MCP-Name to nagłówki umożliwiające bramkom kierowanie ruchem, ograniczanie jego szybkości i blokowanie go bez konieczności analizy treści w formacie JSON.
    • Stabilne listy narzędzi, zapytań i zasobów mogą być przechowywane w pamięci cache, co zmniejsza powtarzające się obciążenia serwerów.
    • Narzędzia działające przez długi czas natychmiast zwracają identyfikator zadania, a klienty korzystają z poleceń Task Get i Task Update, aby uzyskać aktualne informacje.
    • Podejście bezstanowe polega na przenoszeniu stanu zamiast jego eliminacji: dane aplikacji oraz postęp w realizacji zadań nadal wymagają trwałego miejsca przechowywania, do którego może uzyskać dostęp każda instancja. Przed ich wykorzystaniem sprawdź dokładne nazwy zgodnie z aktualną specyfikacją.

    Literatura pokrewna