MCPApp
TsgcHTMLComponent_MCPApp: una herramienta MCP que responde con una página en lugar de texto, de modo que los mismos componentes de Delphi que renderizan cualquier página web se renderizan dentro de ChatGPT, Claude y VS Code (MCP Apps).
TsgcHTMLComponent_MCPApp: una herramienta MCP que responde con una página en lugar de texto, de modo que los mismos componentes de Delphi que renderizan cualquier página web se renderizan dentro de ChatGPT, Claude y VS Code (MCP Apps).
Una MCP App es una herramienta cuyo resultado el host renderiza en lugar de limitarse a leerlo. El componente publica la herramienta, el recurso que lleva la página y una herramienta auxiliar a la que llama la propia página, en el servidor MCP que ya tienes, y la página se construye con los mismos componentes sgcHTML que cualquier otra. Requiere la edición Enterprise o All-Access de sgcWebSockets, o el pack sgcAI, porque se ejecuta en el servidor MCP.
TsgcHTMLComponent_MCPApp, con la colección TsgcHTMLMCPApps de elementos TsgcHTMLMCPApp
Un documento HTML que el host muestra en un marco aislado, con sus recursos incrustados
Delphi, C++ Builder, .NET
Dale al componente el MCPServer de un TsgcWSAPIServer_MCP, añade una app con un nombre de herramienta y una descripción, responde a OnRender, OnToolCall y OnFragment, y luego llama a RegisterApps.
uses
Classes, SysUtils, StrUtils,
// sgc
sgcWebSocket_Server, sgcAI, sgcHTML_Nodes, sgcHTML_Nodes_Bootstrap,
sgcHTML_MCPApp;
// The MCP endpoint a host connects to
FMCP := TsgcWSAPIServer_MCP.Create(nil);
FMCP.Server := FHTTPServer;
FMCP.EndpointOptions.Endpoint := '/mcp';
// The apps, declared on the MCP server the endpoint owns
FApps := TsgcHTMLComponent_MCPApp.Create(nil);
FApps.MCPServer := FMCP.MCPServer;
FApps.OnRender := DoRender;
FApps.OnToolCall := DoToolCall;
FApps.OnFragment := DoFragment;
with FApps.Apps.Add do
begin
ToolName := 'orders.board';
Title := 'Orders board';
Description := 'Shows the orders of the company as a board that ' +
'can be filtered by status.';
InputSchema.Text := '{"type":"object","properties":{"status":' +
'{"type":"string","description":"open, shipped or closed"}}}';
PreferredWidth := 720;
PreferredHeight := 460;
end;
FApps.RegisterApps; // the tool, the fragment tool and the resource
// What the model reads, and what the page reads
procedure TMyLogic.DoToolCall(Sender: TObject; aApp: TsgcHTMLMCPApp;
const aArguments: string; var aText, aStructured: string);
begin
aText := '3 open orders, 1420.50 in total.';
aStructured := '{"orders":3,"total":1420.5}';
end;
// The page: ordinary sgcHTML components
procedure TMyLogic.DoRender(Sender: TObject; aApp: TsgcHTMLMCPApp;
const aArguments: string; var aHTML: string);
var
oCard: TsgcHTMLCard;
oButton: TsgcHTMLContainer;
begin
oCard := TsgcHTMLCard.Create;
try
oCard.Body.Add(TsgcHTMLHeading.Create('Orders', 5));
oButton := TsgcHTMLContainer.Create('button');
oButton.CSSClass := 'btn btn-outline-secondary';
// inside a host, the bridge turns this hx-get into a call of the fragment tool
oButton.Attributes := 'type="button" hx-get="orders?status=open" ' +
'hx-target="#orders" hx-swap="outerHTML"';
oButton.AddText('Open');
oCard.Body.Add(oButton);
oCard.Body.AddRaw(BuildOrdersTable('')); // your markup: the element whose id is orders
aHTML := oCard.HTML;
finally
oCard.Free;
end;
end;
// The markup a control of the page asked for
procedure TMyLogic.DoFragment(Sender: TObject; aApp: TsgcHTMLMCPApp;
const aTarget, aArguments: string; var aHTML: string);
begin
// aTarget is what the element asked for, for instance orders?status=open
if StartsText('orders', aTarget) then
aHTML := BuildOrdersTable(StatusOf(aTarget));
end;
// includes: sgcAI.hpp, sgcHTML_MCPApp.hpp
// The MCP endpoint a host connects to
FMCP = new TsgcWSAPIServer_MCP(NULL);
FMCP->Server = FHTTP;
FMCP->EndpointOptions->Endpoint = "/mcp";
// The apps, declared on the MCP server the endpoint owns
FApps = new TsgcHTMLComponent_MCPApp(NULL);
FApps->MCPServer = FMCP->MCPServer;
FApps->OnRender = DoRender;
FApps->OnToolCall = DoToolCall;
FApps->OnFragment = DoFragment;
TsgcHTMLMCPApp *app = FApps->Apps->Add();
app->ToolName = "orders.board";
app->Title = "Orders board";
app->Description = "Shows the orders of the company as a board that can be filtered by status.";
app->InputSchema->Text = "{\"type\":\"object\",\"properties\":{\"status\":{\"type\":\"string\"}}}";
app->PreferredWidth = 720;
app->PreferredHeight = 460;
FApps->RegisterApps(); // the tool, the fragment tool and the resource
// What the model reads, and what the page reads
void __fastcall TMyLogic::DoToolCall(TObject *Sender, TsgcHTMLMCPApp *aApp,
const String aArguments, String &aText, String &aStructured)
{
aText = "3 open orders, 1420.50 in total.";
aStructured = "{\"orders\":3,\"total\":1420.5}";
}
// The markup a control of the page asked for
void __fastcall TMyLogic::DoFragment(TObject *Sender, TsgcHTMLMCPApp *aApp,
const String aTarget, const String aArguments, String &aHTML)
{
// aTarget is what the element asked for, for instance orders?status=open
if (aTarget.Pos("orders") == 1)
aHTML = BuildOrdersTable(StatusOf(aTarget));
}
// DoRender answers the page with sgcHTML components, as in the Delphi tab.
using esegece.sgcWebSockets;
// An MCP server over stdio, for a host that starts your program as a process
var host = new TsgcAI_MCP_Server_Stdio();
var apps = new TsgcHTMLComponent_MCPApp();
apps.MCPServer = host.MCPServer;
var app = apps.Apps.Add();
app.ToolName = "orders.board";
app.Title = "Orders board";
app.Description = "Shows the orders of the company as a board that can be filtered by status.";
app.InputSchema.Add("{\"type\":\"object\",\"properties\":{\"status\":{\"type\":\"string\"}}}");
app.PreferredWidth = 720;
app.PreferredHeight = 460;
// What the model reads, and what the page reads
apps.OnToolCall += (object sender, TsgcHTMLMCPApp a, string args,
ref string text, ref string structured) =>
{
text = "3 open orders, 1420.50 in total.";
structured = "{\"orders\":3,\"total\":1420.5}";
};
// The page: ordinary sgcHTML components
apps.OnRender += (object sender, TsgcHTMLMCPApp a, string args, ref string html) =>
{
var card = new TsgcHTMLCard();
card.Body.Add(new TsgcHTMLHeading("Orders", 5));
var button = new TsgcHTMLContainer("button");
button.CSSClass = "btn btn-outline-secondary";
// inside a host, the bridge turns this hx-get into a call of the fragment tool
button.Attributes = "type=\"button\" hx-get=\"orders?status=open\" "
+ "hx-target=\"#orders\" hx-swap=\"outerHTML\"";
button.AddText("Open");
card.Body.Add(button);
card.Body.AddRaw(BuildOrdersTable("")); // your markup: the element whose id is orders
html = card.HTML;
};
// The markup a control of the page asked for
apps.OnFragment += (object sender, TsgcHTMLMCPApp a, string target,
string args, ref string html) =>
{
// target is what the element asked for, for instance orders?status=open
if (target.StartsWith("orders"))
html = BuildOrdersTable(StatusOf(target));
};
apps.RegisterApps(); // the tool, the fragment tool and the resource
host.Run();
Los miembros que usarás con más frecuencia.
Apps es una colección TsgcHTMLMCPApps de elementos TsgcHTMLMCPApp. ToolName es obligatorio: una app sin él no registra nada. Description es lo que lee el modelo para decidir si llama a la herramienta; InputSchema es el esquema JSON de sus argumentos. Title, ResourceURI, PageBuilder, Enabled, PreferredWidth y PreferredHeight completan una app, siendo los dos últimos el tamaño con el que se pide al host que abra el marco.
Tres cosas por cada app en el servidor: la herramienta, con el nombre indicado en ToolName, cuyo _meta.ui.resourceUri indica al host qué recurso lleva la página; la herramienta auxiliar <ToolName>.fragment, a la que llama la página y nunca el modelo; y el recurso ui://sgchtml/<toolname>, servido como text/html;profile=mcp-app. En Delphi, RegisterApps se ejecuta desde Loaded y el código que añade una app vuelve a llamarlo; en .NET, llámalo tú mismo. UnregisterApps las elimina.
Un resultado de herramienta tiene dos destinatarios. En OnToolCall, aText es la frase que lee el modelo y aStructured es el JSON que lee la página. Ambos son opcionales, y cuando el texto está vacío se usa el Title de la app. Responde a ambos cuando difieran, lo que suele ocurrir: el modelo quiere una frase y la página quiere números.
OnRender responde con la página como marcado sgcHTML normal. aHTML llega con la página que construye el PageBuilder de la app, cuando hay uno asignado, de modo que un manejador puede reemplazarla o dejarla. GetAppHTML devuelve el documento completo de una app, que es lo que responde el recurso y lo que lee una prueba.
Un marco aislado no tiene servidor HTTP que responda a un hx-get. Cada documento lleva window.sgcMCPApp: envía ui/initialize cuando la página está lista, aplica el tema con el que responde el host, informa de la altura de la página para que el host pueda dimensionar el marco, y registra una extensión de htmx que convierte cada hx-get y hx-post en un tools/call de la herramienta de fragmentos. OnFragment responde a esa llamada: aTarget es lo que el elemento pidió, aArguments el JSON del formulario o de la consulta, y aHTML el marcado que se intercambia. El mismo marcado servido por HTTP es una petición htmx normal.
El marco está aislado y normalmente no tiene red propia, así que no se puede enlazar nada. InlineAssets está activado por defecto y hace que la hoja de estilos, los scripts y la página viajen en un solo archivo. Desactívalo solo para un host en la misma máquina que pueda servir los recursos.
MCPServer es el TsgcAI_MCP_Server en el que se declaran las apps. TsgcWSAPIServer_MCP lo expone como propiedad pública, de modo que FApps.MCPServer := FMCP.MCPServer coloca las apps en el endpoint de un servidor web. El componente se suscribe a los eventos de herramientas y recursos y encadena los manejadores que ya había, así que las herramientas que ya publicas siguen funcionando. Un host que gestiona esos eventos por sí mismo llama a ProcessToolsCall y ProcessResourcesRead, que responden False cuando la petición no era de una app. En .NET, pásale el TsgcAI_MCP_Server que tengas, por ejemplo el que es propiedad de TsgcAI_MCP_Server_Stdio.
El componente no guarda ningún estado propio. Qué significa una llamada a una herramienta, quién puede hacerla y qué puede devolver una petición de fragmento son decisiones de tu aplicación, exactamente igual que con una ruta en una aplicación web. Lo que el modelo puede desencadenar es una herramienta que tú registraste, que responde con marcado que tú construiste.
Demos\60.HTML\01.RunTime\19.MCPApp es un servidor de consola en el puerto 5725 que publica dos apps, orders.board y sales.summary, en un mismo endpoint, e incluye un host de referencia. Abre http://localhost:5725/ y el panel izquierdo hace lo que hace un host real: llama a la herramienta, lee el recurso, lo renderiza en un marco aislado y reenvía las llamadas de la página. Apunta Claude, ChatGPT o VS Code a http://localhost:5725/mcp para probarlo de verdad.
La unidad se compila donde están definidos SGC_HTML y SGC_AI_MCP. SGC_AI_MCP viene con las ediciones Enterprise y All-Access y con el pack sgcAI. SGC_HTML viene con sgcHTML, que se vende de forma independiente de sgcWebSockets, en todas las plataformas excepto Android e iOS.
| Ayuda en líneaGuía de uso de las MCP Apps: la herramienta, el recurso, la herramienta de fragmentos y el puente. | Abrir | |
| Todos los componentes de sgcHTMLExplora la matriz completa de más de 80 componentes. | Abrir | |
| Descargar prueba gratuitaLa prueba de 30 días incluye los proyectos de demostración 60.HTML. | Abrir | |
| PreciosLicencias Single, Team y Site con código fuente completo. | Abrir |