MCPApp

TsgcHTMLComponent_MCPApp: un tool MCP che risponde con una pagina invece che con testo, così gli stessi componenti Delphi che renderizzano qualsiasi pagina web vengono renderizzati dentro ChatGPT, Claude e VS Code (MCP Apps).

TsgcHTMLComponent_MCPApp

Una MCP App è un tool il cui risultato viene renderizzato dall’host invece di essere solo letto. Il componente pubblica il tool, la risorsa che contiene la pagina e un tool di supporto che la pagina stessa richiama, sul server MCP che hai già, e la pagina è costruita con gli stessi componenti sgcHTML di qualsiasi altra. Richiede l’edizione Enterprise o All-Access di sgcWebSockets, oppure il pack sgcAI, perché viene eseguito sul server MCP.

Classe del componente

TsgcHTMLComponent_MCPApp, con la collezione TsgcHTMLMCPApps di elementi TsgcHTMLMCPApp

Renderizza

Un unico documento HTML che l’host mostra in un frame in sandbox, con le sue risorse inline

Famiglia

Chat e IA

Linguaggi

Delphi, C++ Builder, .NET

Dichiara un’app, rispondi a tre eventi

Assegna al componente il MCPServer di un TsgcWSAPIServer_MCP, aggiungi un’app con un nome di tool e una descrizione, rispondi a OnRender, OnToolCall e OnFragment, quindi chiama 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();

Proprietà e metodi principali

I membri che utilizzerai più spesso.

App

Apps è una collezione TsgcHTMLMCPApps di elementi TsgcHTMLMCPApp. ToolName è obbligatorio: un’app che ne è priva non registra nulla. Description è ciò che il modello legge per decidere se chiamare il tool, e InputSchema è lo schema JSON dei suoi argomenti. Title, ResourceURI, PageBuilder, Enabled, PreferredWidth e PreferredHeight completano un’app, gli ultimi due indicando la dimensione con cui si chiede all’host di aprire il frame.

Cosa dichiara RegisterApps

Tre elementi per ogni app sul server: il tool, chiamato con il nome in ToolName, il cui _meta.ui.resourceUri indica all’host quale risorsa contiene la pagina; il tool di supporto <ToolName>.fragment, che viene chiamato dalla pagina e mai dal modello; e la risorsa ui://sgchtml/<toolname>, servita come text/html;profile=mcp-app. In Delphi, RegisterApps viene eseguito da Loaded e il codice che aggiunge un’app lo richiama di nuovo; in .NET, chiamalo tu stesso. UnregisterApps li rimuove.

Le due metà di un risultato

Il risultato di un tool ha due destinatari. In OnToolCall, aText è la frase che legge il modello e aStructured è il JSON che legge la pagina. Entrambi sono facoltativi e, quando il testo è vuoto, viene usato il Title dell’app. Rispondi con entrambi quando differiscono, cosa che di solito accade: il modello vuole una frase e la pagina vuole numeri.

La pagina

OnRender risponde con la pagina come normale markup sgcHTML. aHTML arriva già contenente la pagina che costruisce il PageBuilder dell’app, quando ne è assegnato uno, così un gestore può sostituirla oppure lasciarla. GetAppHTML restituisce l’intero documento di un’app, che è ciò che risponde la risorsa e ciò che legge un test.

I frammenti e il bridge

Un frame in sandbox non ha alcun server HTTP che risponda a un hx-get. Ogni documento include window.sgcMCPApp: invia ui/initialize quando la pagina è pronta, applica il tema con cui risponde l’host, comunica l’altezza della pagina perché l’host possa dimensionare il frame, e registra un’estensione htmx che trasforma ogni hx-get e hx-post in una chiamata tools/call al tool dei frammenti. OnFragment vi risponde: aTarget è ciò che l’elemento ha richiesto, aArguments il JSON del modulo o della query, e aHTML il markup inserito. Lo stesso markup servito via HTTP è una normale richiesta htmx.

Un solo documento

Il frame è in sandbox e di solito non ha una rete propria, quindi nulla può essere collegato. InlineAssets è attivo per impostazione predefinita e fa viaggiare il foglio di stile, gli script e la pagina in un unico file. Disattivalo solo per un host sulla stessa macchina che possa servire le risorse.

Il server MCP

MCPServer è il TsgcAI_MCP_Server su cui vengono dichiarate le app. TsgcWSAPIServer_MCP lo espone come proprietà pubblica, quindi FApps.MCPServer := FMCP.MCPServer colloca le app sull’endpoint di un web server. Il componente si iscrive agli eventi di tool e risorse e concatena i gestori che c’erano già, così i tool che pubblichi già continuano a funzionare. Un host che gestisce direttamente questi eventi chiama ProcessToolsCall e ProcessResourcesRead, che rispondono False quando la richiesta non era di un’app. In .NET, passagli il TsgcAI_MCP_Server che hai, per esempio quello posseduto da TsgcAI_MCP_Server_Stdio.

Nessuno stato

Il componente non conserva alcuno stato proprio. Che cosa significa una chiamata a un tool, chi può effettuarla e che cosa può restituire una richiesta di frammento sono decisioni della tua applicazione, esattamente come per una route in un’applicazione web. Ciò che il modello può attivare è un tool che hai registrato, che risponde con markup costruito da te.

Vederlo senza un assistente

Demos\60.HTML\01.RunTime\19.MCPApp è un server console sulla porta 5725 che pubblica due app, orders.board e sales.summary, su un unico endpoint, e include un host di riferimento. Apri http://localhost:5725/ e il pannello di sinistra fa ciò che fa un host reale: chiama il tool, legge la risorsa, la renderizza in un frame in sandbox e inoltra le chiamate della pagina. Punta Claude, ChatGPT o VS Code a http://localhost:5725/mcp per provare quello vero.

Disponibilità

L’unit compila dove sono definiti sia SGC_HTML sia SGC_AI_MCP. SGC_AI_MCP è incluso nelle edizioni Enterprise e All-Access e nel pack sgcAI. SGC_HTML è incluso in sgcHTML, che si vende indipendentemente da sgcWebSockets, su tutte le piattaforme tranne Android e iOS.

Continua a esplorare

Guida in lineaGuida all’uso per le MCP App: il tool, la risorsa, il tool dei frammenti e il bridge.
Tutti i componenti sgcHTMLEsplora la matrice completa delle funzionalità di oltre 80 componenti.
Scarica la Prova GratuitaLa prova di 30 giorni include i progetti demo 60.HTML.
PrezziLicenze Single, Team e Site con codice sorgente completo.
La scelta più conveniente: All-AccessTutti i prodotti eSeGeCe, con Supporto Premium incluso, a partire da €1,059/anno.
Vedi i prezzi All-Access

Pronto a Iniziare?

Scarica la versione di prova gratuita e inizia a creare interfacce web in Delphi, C++ Builder e .NET.