El Model Context Protocol tiene una nueva especificación, MCP 2026-07-28. Es el mayor cambio al protocolo desde Streamable HTTP: el handshake de sesión desaparece, cada solicitud lleva lo que el servidor necesita para responderla, y el trabajo largo o interactivo obtiene soporte de primera clase. sgcWebSockets 2026.10.0 lo implementa tanto en el servidor MCP como en el cliente MCP para Delphi y C++Builder.
Lo importante para quien ya tenga un servidor MCP en Delphi en producción: nada se rompe. El servidor es de doble era. Los clientes que hablan 2025-11-25, como VS Code o Claude, siguen llamando a initialize y obtienen una sesión exactamente como antes, mientras que los clientes 2026-07-28 usan el nuevo modelo sin estado en el mismo endpoint. No necesita un interruptor ni un segundo servidor.
Qué cambió en MCP 2026-07-28
En términos sencillos, estos son los cambios que importan al construir un servidor o un cliente:
- Solicitudes sin estado. No hay handshake de
initializeniMcp-Session-Id. Cada solicitud lleva su versión de protocolo, la información del cliente y las capacidades del cliente dentro de_meta, de modo que cualquier instancia de servidor detrás de un balanceador de carga puede responderla. - server/discover. Un cliente que quiere saber qué ofrece un servidor llama a
server/discovery recibe las versiones soportadas, las capacidades y las instrucciones. El resultado se puede cachear, y los resultados llevan indicaciones de caché (ttlMs,cacheScope). - subscriptions/listen. Una única solicitud de streaming reemplaza el antiguo stream GET. El cliente indica qué notificaciones quiere (cambios de lista de tools, prompts y resources, y actualizaciones de resource) y el servidor las envía por ese stream.
- Multi round-trip requests (MRTR). Un handler de tool, prompt o resource ya no vuelve a llamar al cliente para pedir información. Devuelve
input_requiredcon las preguntas, el cliente responde y reintenta la llamada con las respuestas y unrequestStatefirmado. - Extensión Tasks. Una llamada de tool larga puede responder de inmediato con un identificador de task. El cliente lo consulta con
tasks/get, responde preguntas contasks/updatey lo detiene contasks/cancel. - Cabeceras de enrutamiento. Las solicitudes HTTP llevan las cabeceras
MCP-Protocol-Version,Mcp-MethodyMcp-Name(además deMcp-Param-*para los argumentos de tool marcados conx-mcp-header), de modo que las gateways pueden enrutar y filtrar sin analizar el cuerpo. El servidor las verifica contra el cuerpo. - Autorización de cliente. El flujo OAuth para clientes se refuerza: descubrimiento de metadatos de protected resource, Client ID Metadata Documents, PKCE con el indicador de recurso y validación de
isssegún RFC 9207.
Un servidor MCP de doble era en Delphi
El componente TsgcWSAPIServer_MCP detecta la era de cada solicitud. Una solicitud cuyo _meta indica 2026-07-28 sigue el camino sin estado, mientras que initialize y las versiones anteriores siguen el camino de sesión que ya conoce. Sus handlers de tool, prompt y resource son compartidos por ambos. Las nuevas opciones solo completan lo que los clientes 2026-07-28 leen desde server/discover y desde las indicaciones de caché.
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;
Cuando se establece ServerInfo.Name, los resultados 2026-07-28 también llevan la información del servidor en _meta. Los errores de protocolo usan los nuevos códigos: una cabecera que no coincide con el cuerpo responde -32020, una capacidad de cliente ausente -32021 y una versión no soportada -32022, con la lista de versiones soportadas en los datos del error.
Subscriptions con subscriptions/listen
Subscriptions está activado de forma predeterminada. Un cliente 2026-07-28 abre un stream subscriptions/listen, y las notificaciones que ya envía con SendNotificationToolsListChanged o SendNotificationResourcesUpdated llegan a esos listeners además de a las sesiones 2025-11-25. El servidor envía un comentario keep-alive en los streams inactivos, y cuando el servidor se desactiva, cada subscription abierta se cierra de forma ordenada con su resultado 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;
Multi round-trip requests: pedir el nombre al usuario
Con MRTR, un handler pide más información completando aResponse.InputRequired y retornando. El cliente recoge las respuestas y vuelve a llamar a la tool. En la segunda ronda las respuestas se leen con aRequest.InputResponse. El ejemplo siguiente es la tool ask_name del demo de servidor.
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;
El requestState viaja firmado con HMAC-SHA256 usando MCPOptions.MRTR.Secret y expira tras MCPOptions.MRTR.StateTTL segundos, de modo que un cliente no puede manipularlo. La elicitation por URL (AddElicitationURL), sampling (AddSampling) y roots (AddRoots) siguen el mismo patrón. Antes de responder input_required, el servidor verifica que el cliente declaró la capacidad correspondiente.
La extensión Tasks: una tool long_job
Active tasks en MCPOptions.Tasks y llame a CreateTask desde el handler de la tool. Cuando el cliente declaró la extensión io.modelcontextprotocol/tasks en esa solicitud, la llamada responde de inmediato con un id de task y su código termina el trabajo en su propio thread. CreateTask devuelve nil cuando tasks está desactivado, la solicitud es 2025-11-25 o el cliente no declaró la extensión, así que mantenga un camino síncrono para esos clientes.
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;
El objeto task es thread safe. OnMCPTaskCancel se dispara cuando el cliente llama a tasks/cancel, y OnMCPTaskUpdate cuando responde a una task que espera información. Las tasks están vinculadas al principal que las creó y expiran tras TTL milisegundos.
El cliente MCP: ProtocolEra y Discover
El componente TsgcWSAPIClient_MCP obtiene una nueva propiedad MCPOptions.ProtocolEra. Déjela en aimcpeAuto y el cliente sondea el servidor con server/discover, usa 2026-07-28 cuando el servidor lo soporta y recurre al handshake de initialize cuando no lo soporta. La era negociada se cachea por endpoint. Establézcala en aimcpeModern o aimcpeLegacy para forzar una de ellas.
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;
En el camino 2026-07-28 el cliente escribe _meta en cada solicitud, envía las cabeceras de enrutamiento, incluida Mcp-Param-* para las tools que declaran x-mcp-header, y abre subscriptions con SubscriptionsListen. Los cambios de lista llegan en OnMCPListChanged y las actualizaciones de resource en OnMCPResourcesUpdated. Con MCPOptions.Tasks.Enabled el cliente declara la extensión Tasks y consulta las tasks por su cuenta, disparando OnMCPTaskCreated, OnMCPTaskStatus y OnMCPTaskCompleted.
Responder preguntas de MRTR con OnMCPInputRequired
Cuando un servidor responde input_required, el cliente dispara OnMCPInputRequired con las preguntas pendientes. Responda cada clave y deje Accept en True. El cliente entonces reintenta la llamada con las respuestas y el requestState firmado, hasta MCPOptions.MRTR.MaxRounds rondas. Establezca MCPOptions.MRTR.Elicitation, Sampling o Roots para declarar esas capacidades.
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;
El mismo evento cubre las tasks que esperan información, en cuyo caso aMethod es tasks/update. Los servidores heredados que todavía envían solicitudes de roots, sampling o elicitation directamente obtienen sus respuestas desde OnMCPListRoots, OnMCPSamplingCreateMessage y OnMCPElicitationCreate.
Autorización de cliente con MCPOptions.Authorization
Establezca MCPOptions.Authorization.Enabled y un 401 o 403 del servidor inicia el flujo de autorización MCP: descubrimiento de metadatos de protected resource, registro (un ClientId preregistrado, un Client ID Metadata Document desde ClientMetadataURL, o registro dinámico), PKCE con el indicador de recurso, el inicio de sesión en el navegador en el RedirectURL de loopback, validación de iss y la solicitud del token. La solicitud original se reintenta después, y los tokens se renuevan antes de expirar.
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;
Las credenciales se guardan por issuer. Gestione OnMCPAuthorizationCredentials para almacenarlas y cargarlas en la siguiente ejecución, de modo que el usuario inicie sesión una sola vez.
También en sgcWebSockets .NET
Todo lo de este artículo también está disponible en sgcWebSockets .NET, con los mismos nombres de clase, propiedad y evento: TsgcWSAPIServer_MCP y TsgcWSAPIClient_MCP exponen MCPOptions.Subscriptions, MCPOptions.MRTR, MCPOptions.Tasks, ProtocolEra, Discover y MCPOptions.Authorization, de modo que una aplicación C# puede hablar con un servidor MCP en Delphi en cualquiera de las dos eras, y viceversa.
Demos y documentación
Los demos en Demos\15.AI\03.MCP muestran todas las funciones. El demo de servidor (01.MCP_Server) registra la era de cada solicitud, tiene interruptores para tasks y subscriptions e implementa las tools ask_name y long_job. El demo de cliente (02.MCP_Client) le permite elegir la era, llamar a Discover, suscribirse a notificaciones, responder preguntas de MRTR y ejecutar tasks.
La referencia completa está en la documentación del servidor MCP y la documentación del cliente MCP, y la página de componentes MCP para Delphi tiene una descripción general de todo lo que hacen los componentes. Puede descargar sgcWebSockets 2026.10.0 desde la página de descarga de sgcWebSockets.
¿Preguntas, comentarios o ayuda con la migración? Contáctenos, recibirá respuesta de las personas que escribieron el código.
