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-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).
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.
TsgcHTMLComponent_MCPApp, met de verzameling TsgcHTMLMCPApps van TsgcHTMLMCPApp-items
Eén HTML-document dat de host toont in een frame in een sandbox, met de assets inline
Delphi, C++ Builder, .NET
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();
De members die je het vaakst gebruikt.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
| Online helpGebruikshandleiding voor MCP Apps: de tool, de resource, de fragmenttool en de bridge. | Openen | |
| Alle sgcHTML-componentenBlader door de volledige functiematrix van 80+ componenten. | Openen | |
| Download gratis proefversieDe proefversie van 30 dagen bevat de 60.HTML-demoprojecten. | Openen | |
| PrijzenSingle-, Team- en Site-licenties met volledige broncode. | Openen |