Delphi MCP sunucusu ve istemcisi: yeni MCP 2026-07-28 spesifikasyonu

· Bileşenler
Delphi MCP sunucusu ve istemcisi: yeni MCP 2026-07-28 spesifikasyonu

Model Context Protocol'ün yeni bir spesifikasyonu var, MCP 2026-07-28. Bu, Streamable HTTP'den bu yana protokolde yapılan en büyük değişiklik, oturum el sıkışması kaldırıldı, her istek sunucunun yanıt vermesi için ihtiyaç duyduğu her şeyi taşıyor ve uzun süren veya etkileşimli işler artık birinci sınıf destek görüyor. sgcWebSockets 2026.10.0, bunu hem Delphi hem de C++Builder için MCP sunucusunda ve MCP istemcisinde uyguluyor.

Zaten bir Delphi MCP sunucusu çalıştıran herkes için önemli olan kısım şu, hiçbir şey bozulmuyor. Sunucu çift dönemli çalışıyor. VS Code veya Claude gibi 2025-11-25 dilini konuşan istemciler initialize çağırmaya devam ediyor ve öncekiyle tıpatıp aynı şekilde bir oturum alıyor, 2026-07-28 istemcileri ise aynı uç noktada yeni durumsuz modeli kullanıyor. Bir anahtara veya ikinci bir sunucuya ihtiyacınız yok.

MCP 2026-07-28'de neler değişti

Basitçe söylemek gerekirse, bir sunucu ya da istemci geliştirirken önem taşıyan değişiklikler şunlar:

Delphi'de çift dönemli bir MCP sunucusu

TsgcWSAPIServer_MCP bileşeni her isteğin dönemini algılar. _meta alanı 2026-07-28 diyen bir istek durumsuz yoldan ilerlerken, initialize ve daha eski sürümler zaten bildiğiniz oturum yolundan ilerler. Araç, prompt ve kaynak işleyicileriniz her iki yol tarafından da paylaşılır. Yeni seçenekler yalnızca 2026-07-28 istemcilerinin server/discover üzerinden ve önbellek ipuçlarından okuduğu bilgileri doldurur.

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;

ServerInfo.Name ayarlandığında, 2026-07-28 sonuçları da _meta içinde sunucu bilgisini taşır. Protokol hataları yeni kodları kullanır, gövdeyle eşleşmeyen bir başlık -32020 ile, eksik bir istemci yeteneği -32021 ile ve desteklenmeyen bir sürüm -32022 ile yanıtlanır, desteklenen sürümlerin listesi hata verisinde yer alır.

subscriptions/listen ile abonelikler

Abonelikler varsayılan olarak açıktır. Bir 2026-07-28 istemcisi tek bir subscriptions/listen akışı açar ve SendNotificationToolsListChanged veya SendNotificationResourcesUpdated ile zaten gönderdiğiniz bildirimler hem bu dinleyicilere hem de 2025-11-25 oturumlarına ulaşır. Sunucu, boşta kalan akışlarda bir keep-alive yorumu gönderir ve sunucu devre dışı bırakıldığında her açık abonelik nihai sonucuyla birlikte düzgün bir şekilde kapatılır.

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;

Çok adımlı istekler: kullanıcıdan isim istemek

MRTR ile bir işleyici, aResponse.InputRequired alanını doldurup dönerek daha fazla girdi ister. İstemci yanıtları toplar ve aracı yeniden çağırır. İkinci turda yanıtlar aRequest.InputResponse ile okunur. Aşağıdaki örnek, sunucu demosundaki ask_name aracıdır.

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, MCPOptions.MRTR.Secret kullanılarak HMAC-SHA256 ile imzalanmış olarak yol alır ve MCPOptions.MRTR.StateTTL saniye sonra süresi dolar, böylece bir istemci onunla oynayamaz. URL elicitation (AddElicitationURL), sampling (AddSampling) ve roots (AddRoots) da aynı örüntüyü izler. Sunucu, input_required ile yanıt vermeden önce istemcinin ilgili yeteneği bildirip bildirmediğini kontrol eder.

Tasks eklentisi: bir long_job aracı

MCPOptions.Tasks içinde görevleri etkinleştirin ve araç işleyicisinden CreateTask çağırın. İstemci o istekte io.modelcontextprotocol/tasks eklentisini bildirdiğinde, çağrı hemen bir görev kimliğiyle yanıt verir ve kodunuz işi kendi thread'inde tamamlar. Görevler devre dışıyken, istek 2025-11-25 ise ya da istemci eklentiyi bildirmediyse CreateTask nil döndürür, bu yüzden bu istemciler için eşzamanlı bir yol tutmaya devam edin.

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;

Görev nesnesi thread güvenlidir. İstemci tasks/cancel çağırdığında OnMCPTaskCancel tetiklenir, girdi bekleyen bir göreve yanıt verdiğinde ise OnMCPTaskUpdate tetiklenir. Görevler onu oluşturan asıl kimliğe bağlıdır ve TTL milisaniye sonra süresi dolar.

MCP istemcisi: ProtocolEra ve Discover

TsgcWSAPIClient_MCP bileşeni yeni bir MCPOptions.ProtocolEra özelliği kazandı. Onu aimcpeAuto olarak bırakırsanız istemci sunucuyu server/discover ile yoklar, sunucu destekliyorsa 2026-07-28'i kullanır ve desteklemiyorsa initialize el sıkışmasına geri döner. Üzerinde anlaşılan dönem uç nokta başına önbelleğe alınır. Birini zorlamak için aimcpeModern veya aimcpeLegacy olarak ayarlayın.

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;

2026-07-28 yolunda istemci her istekte _meta yazar, yönlendirme başlıklarını gönderir (x-mcp-header bildiren araçlar için Mcp-Param-* dahil) ve SubscriptionsListen ile abonelikler açar. Liste değişiklikleri OnMCPListChanged içinde, kaynak güncellemeleri ise OnMCPResourcesUpdated içinde gelir. MCPOptions.Tasks.Enabled etkinken istemci Tasks eklentisini bildirir ve görevleri kendi başına yoklayarak OnMCPTaskCreated, OnMCPTaskStatus ve OnMCPTaskCompleted olaylarını tetikler.

OnMCPInputRequired ile MRTR sorularını yanıtlamak

Bir sunucu input_required ile yanıt verdiğinde, istemci bekleyen sorularla birlikte OnMCPInputRequired tetikler. Her anahtarı yanıtlayın ve Accept değerini True olarak bırakın. İstemci daha sonra çağrıyı yanıtlarla ve imzalı requestState ile, MCPOptions.MRTR.MaxRounds tura kadar yeniden dener. Bu yetenekleri bildirmek için MCPOptions.MRTR.Elicitation, Sampling veya Roots değerlerini ayarlayın.

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;

Aynı olay, girdi bekleyen görevleri de kapsar, bu durumda aMethod tasks/update olur. Roots, sampling veya elicitation isteklerini hâlâ doğrudan gönderen eski sunucular, yanıtlarını OnMCPListRoots, OnMCPSamplingCreateMessage ve OnMCPElicitationCreate üzerinden alır.

MCPOptions.Authorization ile istemci yetkilendirmesi

MCPOptions.Authorization.Enabled değerini ayarlayın, sunucudan gelen bir 401 veya 403 MCP yetkilendirme akışını başlatır, korunan kaynak meta veri keşfi, kayıt (önceden kayıtlı bir ClientId, ClientMetadataURL üzerinden bir İstemci Kimliği Meta Veri Belgesi veya dinamik kayıt), kaynak göstergesiyle PKCE, geri döngü RedirectURL üzerinde tarayıcı girişi, iss doğrulaması ve token isteği. Ardından orijinal istek yeniden denenir ve tokenlar süresi dolmadan önce yenilenir.

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;

Kimlik bilgileri her yayıncı için ayrı tutulur. Bunları saklamak ve bir sonraki çalıştırmada yüklemek için OnMCPAuthorizationCredentials olayını işleyin, böylece kullanıcı yalnızca bir kez giriş yapar.

sgcWebSockets .NET'te de mevcut

Bu yazıdaki her şey sgcWebSockets .NET'te de aynı sınıf, özellik ve olay adlarıyla mevcuttur, TsgcWSAPIServer_MCP ve TsgcWSAPIClient_MCP MCPOptions.Subscriptions, MCPOptions.MRTR, MCPOptions.Tasks, ProtocolEra, Discover ve MCPOptions.Authorization özelliklerini sunar, böylece bir C# uygulaması her iki dönemde de bir Delphi MCP sunucusuyla konuşabilir, ve bunun tersi de geçerlidir.

Demolar ve dokümantasyon

Demos\15.AI\03.MCP içindeki demolar her özelliği gösterir. Sunucu demosu (01.MCP_Server) her isteğin dönemini günlüğe kaydeder, görevler ve abonelikler için anahtarlara sahiptir ve ask_name ile long_job araçlarını uygular. İstemci demosu (02.MCP_Client) dönemi seçmenize, Discover çağırmanıza, bildirimlere abone olmanıza, MRTR sorularını yanıtlamanıza ve görevleri çalıştırmanıza olanak tanır.

Tam referans MCP sunucusu dokümantasyonunda ve MCP istemcisi dokümantasyonunda yer alıyor, Delphi için MCP bileşenleri sayfasında ise bileşenlerin yaptığı her şeyin genel bir bakışı bulunuyor. sgcWebSockets 2026.10.0'ı sgcWebSockets indirme sayfasından indirebilirsiniz.

Sorularınız mı var, geri bildiriminiz mi var ya da geçiş konusunda yardıma mı ihtiyacınız var? Bizimle iletişime geçin, kodu yazan kişilerden bir yanıt alacaksınız.

İlgili