Model Context Protocol doczekał się nowej specyfikacji, MCP 2026-07-28. To największa zmiana w protokole od czasu Streamable HTTP: zniknęło uzgadnianie sesji, każde żądanie niesie ze sobą wszystko, czego serwer potrzebuje, aby na nie odpowiedzieć, a praca długotrwała lub interaktywna zyskuje pełnoprawne wsparcie. sgcWebSockets 2026.10.0 implementuje tę specyfikację zarówno w serwerze MCP, jak i w kliencie MCP dla Delphi i C++Buildera.
Najważniejsze dla każdego, kto już prowadzi serwer MCP w Delphi: nic się nie psuje. Serwer obsługuje obie ery jednocześnie. Klienci mówiący w wersji 2025-11-25, tacy jak VS Code czy Claude, nadal wywołują initialize i otrzymują sesję dokładnie tak jak wcześniej, podczas gdy klienci 2026-07-28 korzystają z nowego, bezstanowego modelu na tym samym punkcie końcowym. Nie potrzeba żadnego przełącznika ani drugiego serwera.
Co zmieniło się w MCP 2026-07-28
Mówiąc wprost, oto zmiany, które mają znaczenie przy budowie serwera lub klienta:
- Żądania bezstanowe. Nie ma uzgadniania
initializeaniMcp-Session-Id. Każde żądanie niesie w polu_metaswoją wersję protokołu, informacje o kliencie i jego możliwości, dzięki czemu odpowiedzieć na nie może dowolna instancja serwera za load balancerem. - server/discover. Klient, który chce się dowiedzieć, co oferuje serwer, wywołuje
server/discoveri otrzymuje w odpowiedzi obsługiwane wersje, możliwości i instrukcje. Wynik można buforować, a odpowiedzi niosą wskazówki dotyczące buforowania (ttlMs,cacheScope). - subscriptions/listen. Jedno żądanie strumieniowe zastępuje dawny strumień GET. Klient określa, jakich powiadomień chce (zmian list narzędzi, promptów i zasobów oraz aktualizacji zasobów), a serwer wysyła je tym strumieniem.
- Żądania wieloetapowe (Multi round-trip requests, MRTR). Uchwyt narzędzia, promptu lub zasobu nie wywołuje już klienta z powrotem, aby poprosić o dane wejściowe. Zwraca
input_requiredwraz z pytaniami, klient udziela odpowiedzi i ponawia wywołanie z odpowiedziami oraz podpisanymrequestState. - Rozszerzenie Tasks. Długo trwające wywołanie narzędzia może od razu odpowiedzieć uchwytem zadania. Klient odpytuje je za pomocą
tasks/get, odpowiada na pytania przeztasks/updatei zatrzymuje je przeztasks/cancel. - Nagłówki trasowania. Żądania HTTP niosą nagłówki
MCP-Protocol-Version,Mcp-MethodiMcp-Name(a takżeMcp-Param-*dla argumentów narzędzi oznaczonychx-mcp-header), dzięki czemu bramy mogą trasować i filtrować ruch bez analizowania treści żądania. Serwer sprawdza je względem treści żądania. - Autoryzacja klienta. Przepływ OAuth po stronie klienta został zaostrzony: wykrywanie metadanych chronionego zasobu, dokumenty metadanych Client ID, PKCE ze wskaźnikiem zasobu oraz walidacja
isszgodna z RFC 9207.
Dwuerowy serwer MCP w Delphi
Komponent TsgcWSAPIServer_MCP wykrywa erę każdego żądania. Żądanie, którego _meta wskazuje 2026-07-28, trafia na ścieżkę bezstanową, natomiast initialize i starsze wersje trafiają na znaną już ścieżkę sesyjną. Uchwyty narzędzi, promptów i zasobów są współdzielone przez obie ścieżki. Nowe opcje wypełniają jedynie to, co klienci 2026-07-28 odczytują z server/discover oraz ze wskazówek buforowania.
procedure TMainForm.FormCreate(Sender: TObject);
begin
MCPServer.MCPOptions.ServerInfo.Name := 'tickets-mcp';
// returned by server/discover to 2026-07-28 clients
MCPServer.MCPOptions.Instructions := 'Use search_tickets before opening a ticket.';
// cache hints returned with 2026-07-28 results (ttlMs, cacheScope)
MCPServer.MCPOptions.Cache.TTLMs := 60000;
MCPServer.MCPOptions.Cache.Scope := aimcpcsPublic;
MCPServer.Active := True;
end;
Gdy ustawiona jest wartość ServerInfo.Name, odpowiedzi 2026-07-28 niosą również informacje o serwerze w _meta. Błędy protokołu korzystają z nowych kodów: nagłówek niezgodny z treścią żądania zwraca -32020, brakująca możliwość klienta -32021, a nieobsługiwana wersja -32022, przy czym lista obsługiwanych wersji znajduje się w danych błędu.
Subskrypcje za pomocą subscriptions/listen
Subskrypcje są domyślnie włączone. Klient 2026-07-28 otwiera jeden strumień subscriptions/listen, a powiadomienia wysyłane już wcześniej za pomocą SendNotificationToolsListChanged lub SendNotificationResourcesUpdated docierają zarówno do tych odbiorców, jak i do sesji 2025-11-25. Serwer wysyła komentarz keep-alive na bezczynnych strumieniach, a przy dezaktywacji serwera każda otwarta subskrypcja jest zamykana w sposób kontrolowany, wraz z wynikiem końcowym.
procedure TMainForm.FormCreate(Sender: TObject);
begin
MCPServer.MCPOptions.Subscriptions.Enabled := True;
MCPServer.MCPOptions.Subscriptions.KeepAliveInterval := 15000; // 0 disables it
end;
procedure TMainForm.ToolsChanged;
begin
// delivered to legacy sessions and to every subscriptions/listen stream
MCPServer.SendNotificationToolsListChanged;
end;
Żądania wieloetapowe: pytanie użytkownika o imię
Dzięki MRTR uchwyt prosi o dodatkowe dane wejściowe, wypełniając aResponse.InputRequired i kończąc działanie. Klient zbiera odpowiedzi i ponownie wywołuje narzędzie. W drugiej rundzie odpowiedzi odczytywane są za pomocą aRequest.InputResponse. Poniższy przykład to narzędzie ask_name z dema serwera.
procedure TMainForm.MCPServerMCPRequestTool(Sender: TObject;
const aSession: TsgcAI_MCP_Session;
const aRequest: TsgcAI_MCP_Request_ToolsCall;
const aResponse: TsgcAI_MCP_Response_ToolsCall);
begin
if aRequest.Params.Name = 'ask_name' then
begin
if not aRequest.HasInputResponse('name') then
begin
// first round: ask the client for the user's name
aResponse.InputRequired.AddElicitation('name', 'What is your name?',
'{"type":"object","properties":{"name":{"type":"string"}},' +
'"required":["name"]}');
Exit;
end;
// second round: the answer arrives as raw JSON
aResponse.Result.Content.AddText('Hello, ' + aRequest.InputResponse('name'));
end;
end;
requestState jest przesyłany podpisany za pomocą HMAC-SHA256 z użyciem MCPOptions.MRTR.Secret i wygasa po MCPOptions.MRTR.StateTTL sekundach, dzięki czemu klient nie może go zmodyfikować. Elicitation przez URL (AddElicitationURL), sampling (AddSampling) i roots (AddRoots) działają według tego samego wzorca. Przed odpowiedzią input_required serwer sprawdza, czy klient zadeklarował odpowiednią możliwość.
Rozszerzenie Tasks: narzędzie long_job
Włącz zadania w MCPOptions.Tasks i wywołaj CreateTask z poziomu uchwytu narzędzia. Gdy klient zadeklarował w danym żądaniu rozszerzenie io.modelcontextprotocol/tasks, wywołanie odpowiada od razu identyfikatorem zadania, a Twój kod kończy pracę we własnym wątku. CreateTask zwraca nil, gdy zadania są wyłączone, żądanie pochodzi z wersji 2025-11-25 lub klient nie zadeklarował rozszerzenia, dlatego dla takich klientów warto zachować ścieżkę synchroniczną.
procedure TMainForm.FormCreate(Sender: TObject);
begin
MCPServer.MCPOptions.Tasks.Enabled := True;
MCPServer.MCPOptions.Tasks.TTL := 3600000;
MCPServer.MCPOptions.Tasks.PollInterval := 1000;
end;
procedure TMainForm.MCPServerMCPRequestTool(Sender: TObject;
const aSession: TsgcAI_MCP_Session;
const aRequest: TsgcAI_MCP_Request_ToolsCall;
const aResponse: TsgcAI_MCP_Response_ToolsCall);
var
oTask: TsgcAI_MCP_Task;
begin
if aRequest.Params.Name = 'long_job' then
begin
oTask := MCPServer.CreateTask(aSession, aResponse);
if Assigned(oTask) then
TLongJobThread.Create(oTask) // runs the job, see below
else
aResponse.Result.Content.AddText(RunLongJob);
end;
end;
procedure TLongJobThread.Execute;
var
i: Integer;
oResponse: TsgcAI_MCP_Response_ToolsCall;
begin
for i := 1 to 3 do
begin
if FTask.IsCancelled then
Break;
DoStep(i);
FTask.SetStatusMessage(Format('long_job step %d of 3', [i]));
end;
if FTask.IsCancelled then
FTask.Fail(CS_AI_MCP_INTERNAL_ERROR, 'Cancelled by the client')
else
begin
oResponse := TsgcAI_MCP_Response_ToolsCall.Create;
try
oResponse.Result.Content.AddText('long_job finished after 3 steps');
FTask.Complete(oResponse);
finally
oResponse.Free;
end;
end;
end;
Obiekt zadania jest bezpieczny wątkowo. OnMCPTaskCancel uruchamia się, gdy klient wywołuje tasks/cancel, a OnMCPTaskUpdate, gdy odpowiada na zadanie oczekujące na dane wejściowe. Zadania są powiązane z podmiotem, który je utworzył, i wygasają po TTL milisekundach.
Klient MCP: ProtocolEra i Discover
Komponent TsgcWSAPIClient_MCP otrzymuje nową właściwość MCPOptions.ProtocolEra. Pozostawiona jako aimcpeAuto sprawia, że klient sonduje serwer za pomocą server/discover, korzysta z 2026-07-28, gdy serwer je obsługuje, i wraca do uzgadniania initialize, gdy nie. Wynegocjowana era jest buforowana dla każdego punktu końcowego. Ustaw aimcpeModern lub aimcpeLegacy, aby wymusić konkretną wersję.
procedure TMainForm.Connect;
begin
MCPClient.MCPOptions.ProtocolEra := aimcpeAuto;
if MCPClient.Initialize then
begin
MemoLog.Lines.Add('protocol: ' + MCPClient.NegotiatedProtocolVersion);
if MCPClient.NegotiatedEra = aimcpeModern then
MCPClient.Discover; // result in OnMCPDiscover and ServerDiscover
MCPClient.ListTools;
end;
end;
Na ścieżce 2026-07-28 klient zapisuje _meta w każdym żądaniu, wysyła nagłówki trasowania, w tym Mcp-Param-* dla narzędzi deklarujących x-mcp-header, oraz otwiera subskrypcje za pomocą SubscriptionsListen. Zmiany list docierają w OnMCPListChanged, a aktualizacje zasobów w OnMCPResourcesUpdated. Przy MCPOptions.Tasks.Enabled klient deklaruje rozszerzenie Tasks i samodzielnie odpytuje zadania, wyzwalając OnMCPTaskCreated, OnMCPTaskStatus i OnMCPTaskCompleted.
Odpowiadanie na pytania MRTR za pomocą OnMCPInputRequired
Gdy serwer odpowiada input_required, klient wyzwala OnMCPInputRequired z oczekującymi pytaniami. Odpowiedz na każdy klucz i pozostaw Accept jako True. Klient następnie ponawia wywołanie z odpowiedziami i podpisanym requestState, maksymalnie przez MCPOptions.MRTR.MaxRounds rund. Ustaw MCPOptions.MRTR.Elicitation, Sampling lub Roots, aby zadeklarować te możliwości.
procedure TMainForm.MCPClientMCPInputRequired(Sender: TObject;
const aMethod: string;
const aInputRequests: TsgcAI_MCP_Client_InputRequests;
var Accept: Boolean);
var
i: Integer;
begin
for i := 0 to aInputRequests.Count - 1 do
if aInputRequests.Methods[i] = 'elicitation/create' then
aInputRequests.SetElicitationAccept(aInputRequests.Keys[i],
'{"name":"Sergio"}');
Accept := True;
end;
To samo zdarzenie obejmuje zadania oczekujące na dane wejściowe, w tym przypadku aMethod ma wartość tasks/update. Starsze serwery, które nadal wysyłają żądania roots, sampling czy elicitation bezpośrednio, otrzymują odpowiedzi z OnMCPListRoots, OnMCPSamplingCreateMessage i OnMCPElicitationCreate.
Autoryzacja klienta za pomocą MCPOptions.Authorization
Ustaw MCPOptions.Authorization.Enabled, a odpowiedź 401 lub 403 z serwera uruchomi przepływ autoryzacji MCP: wykrywanie metadanych chronionego zasobu, rejestrację (wstępnie zarejestrowany ClientId, dokument metadanych Client ID pobrany z ClientMetadataURL lub rejestrację dynamiczną), PKCE ze wskaźnikiem zasobu, logowanie w przeglądarce pod lokalnym adresem RedirectURL, walidację iss oraz żądanie tokenu. Oryginalne żądanie jest następnie ponawiane, a tokeny są odświeżane przed wygaśnięciem.
procedure TMainForm.FormCreate(Sender: TObject);
begin
MCPClient.MCPOptions.Authorization.Enabled := True;
MCPClient.MCPOptions.Authorization.ClientMetadataURL :=
'https://app.example.com/oauth/client.json';
MCPClient.MCPOptions.Authorization.AllowDynamicRegistration := True;
MCPClient.MCPOptions.Authorization.RedirectURL := 'http://127.0.0.1:33418/callback';
MCPClient.OnMCPAuthorizationURL := MCPClientAuthorizationURL;
end;
procedure TMainForm.MCPClientAuthorizationURL(Sender: TObject;
const aURL: string; var Handled: Boolean);
begin
MemoLog.Lines.Add('Sign in at ' + aURL);
Handled := False; // False: the default browser is opened
end;
Poświadczenia są przechowywane osobno dla każdego wystawcy. Obsłuż OnMCPAuthorizationCredentials, aby je zapisać i wczytać przy kolejnym uruchomieniu, dzięki czemu użytkownik loguje się tylko raz.
Również w sgcWebSockets .NET
Wszystko, co opisano w tym artykule, jest dostępne również w sgcWebSockets .NET, z tymi samymi nazwami klas, właściwości i zdarzeń: TsgcWSAPIServer_MCP i TsgcWSAPIClient_MCP udostępniają MCPOptions.Subscriptions, MCPOptions.MRTR, MCPOptions.Tasks, ProtocolEra, Discover oraz MCPOptions.Authorization, dzięki czemu aplikacja w C# może rozmawiać z serwerem MCP w Delphi w dowolnej erze, i odwrotnie.
Dema i dokumentacja
Dema w Demos\15.AI\03.MCP pokazują wszystkie funkcje. Demo serwera (01.MCP_Server) rejestruje erę każdego żądania, ma przełączniki dla zadań i subskrypcji oraz implementuje narzędzia ask_name i long_job. Demo klienta (02.MCP_Client) pozwala wybrać erę, wywołać Discover, subskrybować powiadomienia, odpowiadać na pytania MRTR i uruchamiać zadania.
Pełny opis znajduje się w dokumentacji serwera MCP oraz dokumentacji klienta MCP, a strona Komponenty MCP dla Delphi zawiera przegląd wszystkiego, co robią te komponenty. sgcWebSockets 2026.10.0 można pobrać ze strony pobierania sgcWebSockets.
Masz pytania, uwagi lub potrzebujesz pomocy przy migracji? Skontaktuj się z nami, odpowiedź otrzymasz od osób, które napisały ten kod.
