Le Model Context Protocol a une nouvelle spécification, MCP 2026-07-28. C'est le plus grand changement du protocole depuis Streamable HTTP : la poignée de main de session disparaît, chaque requête transporte ce dont le serveur a besoin pour y répondre, et les travaux longs ou interactifs bénéficient d'un support de premier ordre. sgcWebSockets 2026.10.0 l'implémente à la fois dans le serveur MCP et dans le client MCP pour Delphi et C++Builder.
Le point important pour quiconque exploite déjà un serveur MCP Delphi : rien ne casse. Le serveur est bi-ère. Les clients qui parlent 2025-11-25, comme VS Code ou Claude, continuent d'appeler initialize et obtiennent une session exactement comme avant, tandis que les clients 2026-07-28 utilisent le nouveau modèle sans état sur le même point de terminaison. Vous n'avez besoin ni d'un commutateur ni d'un second serveur.
Ce qui a changé dans MCP 2026-07-28
En termes simples, voici les changements qui comptent quand vous construisez un serveur ou un client :
- Requêtes sans état. Il n'y a plus de poignée de main
initializeni deMcp-Session-Id. Chaque requête transporte sa version de protocole, les informations du client et ses capacités dans_meta, si bien que n'importe quelle instance de serveur derrière un répartiteur de charge peut y répondre. - server/discover. Un client qui veut savoir ce qu'offre un serveur appelle
server/discoveret reçoit en retour les versions prises en charge, les capacités et les instructions. Le résultat peut être mis en cache, et les résultats transportent des indications de cache (ttlMs,cacheScope). - subscriptions/listen. Une seule requête en flux continu remplace l'ancien flux GET. Le client indique quelles notifications il souhaite (changements de liste d'outils, de prompts et de ressources, mises à jour de ressources) et le serveur les pousse sur ce flux.
- Requêtes à plusieurs allers-retours (MRTR). Un gestionnaire d'outil, de prompt ou de ressource ne rappelle plus le client pour demander une saisie. Il renvoie
input_requiredavec les questions, le client répond et relance l'appel avec les réponses et unrequestStatesigné. - Extension Tasks. Un appel d'outil long peut répondre immédiatement avec un identifiant de tâche. Le client l'interroge avec
tasks/get, répond aux questions avectasks/updateet l'arrête avectasks/cancel. - En-têtes de routage. Les requêtes HTTP transportent les en-têtes
MCP-Protocol-Version,Mcp-MethodetMcp-Name(ainsi queMcp-Param-*pour les arguments d'outil marqués avecx-mcp-header), afin que les passerelles puissent router et filtrer sans analyser le corps. Le serveur les vérifie par rapport au corps. - Autorisation côté client. Le flux OAuth pour les clients est renforcé : découverte des métadonnées de ressource protégée, Client ID Metadata Documents, PKCE avec l'indicateur de ressource et validation
issselon la RFC 9207.
Un serveur MCP bi-ère en Delphi
Le composant TsgcWSAPIServer_MCP détecte l'ère de chaque requête. Une requête dont le _meta indique 2026-07-28 suit le chemin sans état, tandis que initialize et les versions plus anciennes suivent le chemin de session que vous connaissez déjà. Vos gestionnaires d'outils, de prompts et de ressources sont partagés par les deux. Les nouvelles options ne font que remplir ce que les clients 2026-07-28 lisent depuis server/discover et depuis les indications de cache.
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;
Quand ServerInfo.Name est défini, les résultats 2026-07-28 transportent aussi les informations du serveur dans _meta. Les erreurs de protocole utilisent les nouveaux codes : un en-tête qui ne correspond pas au corps répond -32020, une capacité client manquante -32021 et une version non prise en charge -32022, avec la liste des versions prises en charge dans les données d'erreur.
Les abonnements avec subscriptions/listen
Les abonnements sont activés par défaut. Un client 2026-07-28 ouvre un flux subscriptions/listen, et les notifications que vous envoyez déjà avec SendNotificationToolsListChanged ou SendNotificationResourcesUpdated atteignent ces auditeurs aussi bien que les sessions 2025-11-25. Le serveur envoie un commentaire de maintien de connexion sur les flux inactifs, et quand le serveur est désactivé, chaque abonnement ouvert est fermé proprement avec son résultat final.
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;
Requêtes à plusieurs allers-retours : demander un nom à l'utilisateur
Avec MRTR, un gestionnaire demande une saisie supplémentaire en remplissant aResponse.InputRequired puis en retournant. Le client recueille les réponses et rappelle l'outil. Au second tour, les réponses sont lues avec aRequest.InputResponse. L'exemple ci-dessous est l'outil ask_name de la démo serveur.
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;
Le requestState voyage signé avec HMAC-SHA256 en utilisant MCPOptions.MRTR.Secret et expire après MCPOptions.MRTR.StateTTL secondes, si bien qu'un client ne peut pas le falsifier. L'élicitation par URL (AddElicitationURL), l'échantillonnage (AddSampling) et les racines (AddRoots) suivent le même schéma. Avant de répondre à input_required, le serveur vérifie que le client a déclaré la capacité correspondante.
L'extension Tasks : un outil long_job
Activez les tâches dans MCPOptions.Tasks et appelez CreateTask depuis le gestionnaire d'outil. Quand le client a déclaré l'extension io.modelcontextprotocol/tasks sur cette requête, l'appel répond immédiatement avec un identifiant de tâche et votre code termine le travail sur son propre thread. CreateTask renvoie nil quand les tâches sont désactivées, que la requête est 2025-11-25 ou que le client n'a pas déclaré l'extension, alors gardez un chemin synchrone pour ces clients.
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;
L'objet tâche est thread-safe. OnMCPTaskCancel se déclenche quand le client appelle tasks/cancel, et OnMCPTaskUpdate quand il répond à une tâche en attente d'une saisie. Les tâches sont liées au principal qui les a créées et expirent après TTL millisecondes.
Le client MCP : ProtocolEra et Discover
Le composant TsgcWSAPIClient_MCP reçoit une nouvelle propriété MCPOptions.ProtocolEra. Laissez-la sur aimcpeAuto et le client sonde le serveur avec server/discover, utilise 2026-07-28 quand le serveur le prend en charge et retombe sur la poignée de main initialize sinon. L'ère négociée est mise en cache par point de terminaison. Réglez-la sur aimcpeModern ou aimcpeLegacy pour en forcer une.
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;
Sur le chemin 2026-07-28, le client écrit _meta sur chaque requête, envoie les en-têtes de routage, y compris Mcp-Param-* pour les outils qui déclarent x-mcp-header, et ouvre les abonnements avec SubscriptionsListen. Les changements de liste arrivent dans OnMCPListChanged et les mises à jour de ressources dans OnMCPResourcesUpdated. Avec MCPOptions.Tasks.Enabled, le client déclare l'extension Tasks et interroge les tâches par lui-même, en déclenchant OnMCPTaskCreated, OnMCPTaskStatus et OnMCPTaskCompleted.
Répondre aux questions MRTR avec OnMCPInputRequired
Quand un serveur répond input_required, le client déclenche OnMCPInputRequired avec les questions en attente. Répondez à chaque clé et laissez Accept à True. Le client relance alors l'appel avec les réponses et le requestState signé, jusqu'à MCPOptions.MRTR.MaxRounds tours. Réglez MCPOptions.MRTR.Elicitation, Sampling ou Roots pour déclarer ces capacités.
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;
Le même événement couvre les tâches en attente d'une saisie, auquel cas aMethod vaut tasks/update. Les serveurs anciens qui envoient encore directement des requêtes de racines, d'échantillonnage ou d'élicitation reçoivent leurs réponses depuis OnMCPListRoots, OnMCPSamplingCreateMessage et OnMCPElicitationCreate.
Autorisation côté client avec MCPOptions.Authorization
Définissez MCPOptions.Authorization.Enabled et un 401 ou 403 du serveur déclenche le flux d'autorisation MCP : découverte des métadonnées de ressource protégée, enregistrement (un ClientId préenregistré, un Client ID Metadata Document depuis ClientMetadataURL, ou un enregistrement dynamique), PKCE avec l'indicateur de ressource, la connexion dans le navigateur sur le RedirectURL local, la validation iss et la requête de jeton. La requête d'origine est alors relancée, et les jetons sont renouvelés avant leur expiration.
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;
Les identifiants sont conservés par émetteur. Gérez OnMCPAuthorizationCredentials pour les stocker et les recharger au prochain lancement, afin que l'utilisateur ne se connecte qu'une seule fois.
Également dans sgcWebSockets .NET
Tout ce qui est décrit dans cet article est aussi disponible dans sgcWebSockets .NET, avec les mêmes noms de classes, de propriétés et d'événements : TsgcWSAPIServer_MCP et TsgcWSAPIClient_MCP exposent MCPOptions.Subscriptions, MCPOptions.MRTR, MCPOptions.Tasks, ProtocolEra, Discover et MCPOptions.Authorization, si bien qu'une application C# peut dialoguer avec un serveur MCP Delphi dans l'une ou l'autre ère, et inversement.
Démos et documentation
Les démos dans Demos\15.AI\03.MCP montrent chaque fonctionnalité. La démo serveur (01.MCP_Server) journalise l'ère de chaque requête, dispose de commutateurs pour les tâches et les abonnements, et implémente les outils ask_name et long_job. La démo client (02.MCP_Client) vous permet de choisir l'ère, d'appeler Discover, de vous abonner aux notifications, de répondre aux questions MRTR et d'exécuter des tâches.
La référence complète se trouve dans la documentation du serveur MCP et la documentation du client MCP, et la page composants MCP pour Delphi présente un aperçu de tout ce que font les composants. Vous pouvez télécharger sgcWebSockets 2026.10.0 depuis la page de téléchargement de sgcWebSockets.
Des questions, des retours ou besoin d'aide pour la migration ? Contactez-nous, vous obtiendrez une réponse des personnes qui ont écrit le code.
