MCPApp

TsgcHTMLComponent_MCPApp: een MCP-tool die antwoordt met een pagina in plaats van tekst, zodat dezelfde Delphi-componenten die elke webpagina renderen ook binnen ChatGPT, Claude en VS Code renderen (MCP Apps).

TsgcHTMLComponent_MCPApp

Een MCP App is een tool waarvan het resultaat door de host wordt gerenderd in plaats van alleen gelezen. Het component publiceert de tool, de resource die de pagina bevat en een begeleidende tool die de pagina zelf aanroept, op de MCP-server die je al hebt, en de pagina wordt opgebouwd uit dezelfde sgcHTML-componenten als elke andere. Vereist de Enterprise- of All-Access-editie van sgcWebSockets, of het sgcAI-pack, omdat het op de MCP-server draait.

Componentklasse

TsgcHTMLComponent_MCPApp, met de verzameling TsgcHTMLMCPApps van TsgcHTMLMCPApp-items

Rendert

Eén HTML-document dat de host toont in een frame in een sandbox, met de assets inline

Familie

Chat en AI

Talen

Delphi, C++ Builder, .NET

Declareer een app, beantwoord drie gebeurtenissen

Geef het component de MCPServer van een TsgcWSAPIServer_MCP, voeg een app toe met een toolnaam en een beschrijving, handel OnRender, OnToolCall en OnFragment af en roep dan RegisterApps aan.

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();

Belangrijkste eigenschappen en methoden

De members die je het vaakst gebruikt.

Apps

Apps is een TsgcHTMLMCPApps-verzameling van TsgcHTMLMCPApp-items. ToolName is verplicht: een app zonder toolnaam registreert niets. Description is wat het model leest om te beslissen of het de tool aanroept, en InputSchema is het JSON-schema van de argumenten. Title, ResourceURI, PageBuilder, Enabled, PreferredWidth en PreferredHeight maken een app compleet, waarbij de laatste twee de grootte zijn waarop de host wordt gevraagd het frame te openen.

Wat RegisterApps declareert

Drie dingen per app op de server: de tool, benoemd door ToolName, waarvan _meta.ui.resourceUri de host vertelt welke resource de pagina bevat; de begeleidende tool <ToolName>.fragment, die de pagina aanroept en het model nooit; en de resource ui://sgchtml/<toolname>, geserveerd als text/html;profile=mcp-app. In Delphi draait RegisterApps vanuit Loaded en roept code die een app toevoegt het opnieuw aan; in .NET roep je het zelf aan. UnregisterApps verwijdert ze.

Twee helften van een resultaat

Een toolresultaat heeft twee doelgroepen. In OnToolCall is aText de zin die het model leest en aStructured de JSON die de pagina leest. Beide zijn optioneel, en als de tekst leeg is, wordt de Title van de app gebruikt. Vul beide in wanneer ze verschillen, wat meestal het geval is: het model wil een zin en de pagina wil getallen.

De pagina

OnRender levert de pagina als gewone sgcHTML-markup. aHTML komt binnen met de pagina die de PageBuilder van de app bouwt, als er een is toegewezen, zodat een eventhandler die kan vervangen of laten staan. GetAppHTML geeft het hele document van één app terug, dat is wat de resource beantwoordt en wat een test leest.

Fragmenten en de bridge

Een frame in een sandbox heeft geen HTTP-server om een hx-get te beantwoorden. Elk document bevat window.sgcMCPApp: het stuurt ui/initialize wanneer de pagina klaar is, past het thema toe waarmee de host antwoordt, meldt de hoogte van de pagina zodat de host het frame kan bemeten, en registreert een htmx-extensie die elke hx-get en hx-post omzet in een tools/call van de fragmenttool. OnFragment beantwoordt die: aTarget is wat het element vroeg, aArguments de JSON van het formulier of de query, en aHTML de markup die wordt geswapt. Dezelfde markup die via HTTP wordt geserveerd, is een gewoon htmx-verzoek.

Eén document

Het frame zit in een sandbox en heeft meestal geen eigen netwerk, dus er kan niets worden gelinkt. InlineAssets staat standaard aan en zorgt ervoor dat de stylesheet, de scripts en de pagina in één bestand meereizen. Zet het alleen uit voor een host op dezelfde machine die de assets kan serveren.

De MCP-server

MCPServer is de TsgcAI_MCP_Server waarop de apps worden gedeclareerd. TsgcWSAPIServer_MCP maakt die beschikbaar als publieke eigenschap, dus FApps.MCPServer := FMCP.MCPServer zet de apps op het endpoint van een webserver. Het component abonneert zich op de tool- en resource-gebeurtenissen en ketent de eventhandlers die er al waren, zodat de tools die je al publiceert blijven werken. Een host die die gebeurtenissen zelf afhandelt, roept ProcessToolsCall en ProcessResourcesRead aan, die False teruggeven wanneer het verzoek niet van een app was. Geef het in .NET de TsgcAI_MCP_Server die je hebt, bijvoorbeeld die welke eigendom is van TsgcAI_MCP_Server_Stdio.

Geen state

Het component houdt zelf geen state bij. Wat een toolaanroep betekent, wie hem mag aanroepen en wat een fragmentverzoek mag teruggeven zijn beslissingen van je applicatie, precies zoals voor een route in een webapplicatie. Wat het model kan activeren is een tool die je hebt geregistreerd, die antwoordt met markup die je zelf hebt gebouwd.

Bekijk het zonder assistent

Demos\60.HTML\01.RunTime\19.MCPApp is een consoleserver op poort 5725 die twee apps, orders.board en sales.summary, op één endpoint publiceert en een referentiehost meelevert. Open http://localhost:5725/ en het linkerpaneel doet wat een echte host doet: het roept de tool aan, leest de resource, rendert die in een frame in een sandbox en stuurt de aanroepen van de pagina door. Richt Claude, ChatGPT of VS Code op http://localhost:5725/mcp voor het echte werk.

Beschikbaarheid

De unit compileert waar zowel SGC_HTML als SGC_AI_MCP zijn gedefinieerd. SGC_AI_MCP wordt geleverd met de Enterprise- en All-Access-edities en met het sgcAI-pack. SGC_HTML wordt geleverd met sgcHTML, dat los van sgcWebSockets wordt verkocht, op elk platform behalve Android en iOS.

Blijf ontdekken

Online helpGebruikshandleiding voor MCP Apps: de tool, de resource, de fragmenttool en de bridge.
Alle sgcHTML-componentenBlader door de volledige functiematrix van 80+ componenten.
Download gratis proefversieDe proefversie van 30 dagen bevat de 60.HTML-demoprojecten.
PrijzenSingle-, Team- en Site-licenties met volledige broncode.
De beste deal: All-AccessElk eSeGeCe-product, inclusief Premium-ondersteuning, vanaf €1,059 per jaar.
Bekijk de All-Access-prijzen

Klaar om te beginnen?

Download de gratis proefversie en begin met het bouwen van web-UI's in Delphi, C++ Builder en .NET.