MCPApp
TsgcHTMLComponent_MCPApp : un outil MCP qui répond par une page au lieu de texte, de sorte que les mêmes composants Delphi qui produisent n'importe quelle page web s'affichent dans ChatGPT, Claude et VS Code (MCP Apps).
TsgcHTMLComponent_MCPApp : un outil MCP qui répond par une page au lieu de texte, de sorte que les mêmes composants Delphi qui produisent n'importe quelle page web s'affichent dans ChatGPT, Claude et VS Code (MCP Apps).
Une MCP App est un outil dont l'hôte affiche le résultat au lieu de simplement le lire. Le composant publie l'outil, la ressource qui porte la page et un outil compagnon que la page appelle elle-même, sur le serveur MCP que vous avez déjà, et la page est construite avec les mêmes composants sgcHTML que toute autre. Il requiert l'édition Enterprise ou All-Access de sgcWebSockets, ou le pack sgcAI, car il s'exécute sur le serveur MCP.
TsgcHTMLComponent_MCPApp, avec la collection TsgcHTMLMCPApps d'éléments TsgcHTMLMCPApp
Un document HTML que l'hôte affiche dans un cadre isolé (sandbox), avec ses ressources intégrées
Delphi, C++ Builder, .NET
Donnez au composant le MCPServer d'un TsgcWSAPIServer_MCP, ajoutez une application avec un nom d'outil et une description, répondez à OnRender, OnToolCall et OnFragment, puis appelez 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();
Les membres que vous utilisez le plus souvent.
Apps est une collection TsgcHTMLMCPApps d'éléments TsgcHTMLMCPApp. ToolName est obligatoire : une application qui n'en a pas n'enregistre rien. Description est ce que le modèle lit pour décider s'il doit appeler l'outil, et InputSchema est le schéma JSON de ses arguments. Title, ResourceURI, PageBuilder, Enabled, PreferredWidth et PreferredHeight complètent une application, les deux derniers étant la taille à laquelle l'hôte est invité à ouvrir le cadre.
Trois éléments par application sur le serveur : l'outil, nommé par ToolName, dont _meta.ui.resourceUri indique à l'hôte quelle ressource porte la page ; l'outil compagnon <ToolName>.fragment, que la page appelle et que le modèle n'appelle jamais ; et la ressource ui://sgchtml/<toolname>, servie en text/html;profile=mcp-app. En Delphi, RegisterApps s'exécute depuis Loaded et le code qui ajoute une application l'appelle de nouveau ; en .NET, appelez-le vous-même. UnregisterApps les supprime.
Un résultat d'outil a deux destinataires. Dans OnToolCall, aText est la phrase que lit le modèle et aStructured le JSON que lit la page. Les deux sont facultatifs, et lorsque le texte est vide, le Title de l'application est utilisé. Répondez aux deux quand ils diffèrent, ce qui est généralement le cas : le modèle veut une phrase et la page veut des chiffres.
OnRender répond avec la page sous forme de balisage sgcHTML ordinaire. aHTML arrive contenant la page que construit le PageBuilder de l'application, s'il y en a un d'affecté, de sorte qu'un gestionnaire peut la remplacer ou la laisser. GetAppHTML renvoie le document complet d'une application, c'est ce que la ressource renvoie et ce que lit un test.
Un cadre isolé n'a aucun serveur HTTP pour répondre à un hx-get. Chaque document embarque window.sgcMCPApp : il envoie ui/initialize quand la page est prête, applique le thème avec lequel l'hôte répond, signale la hauteur de la page pour que l'hôte puisse dimensionner le cadre, et enregistre une extension htmx qui transforme chaque hx-get et hx-post en un tools/call de l'outil de fragment. OnFragment y répond : aTarget est ce que l'élément a demandé, aArguments le JSON du formulaire ou de la chaîne de requête, et aHTML le balisage inséré. Le même balisage servi en HTTP est une requête htmx ordinaire.
Le cadre est isolé et n'a généralement pas de réseau propre, donc rien ne peut être référencé par un lien. InlineAssets est activé par défaut et fait voyager la feuille de style, les scripts et la page dans un seul fichier. Ne le désactivez que pour un hôte situé sur la même machine et capable de servir les ressources.
MCPServer est le TsgcAI_MCP_Server sur lequel les applications sont déclarées. TsgcWSAPIServer_MCP l'expose comme propriété publique, de sorte que FApps.MCPServer := FMCP.MCPServer place les applications sur le point de terminaison d'un serveur web. Le composant s'abonne aux événements d'outil et de ressource et chaîne les gestionnaires qui s'y trouvaient, de sorte que les outils que vous publiez déjà continuent de fonctionner. Un hôte qui gère lui-même ces événements appelle ProcessToolsCall et ProcessResourcesRead, qui répondent False lorsque la requête n'était pas celle d'une application. En .NET, donnez-lui le TsgcAI_MCP_Server que vous avez, par exemple celui que possède TsgcAI_MCP_Server_Stdio.
Le composant ne conserve aucun état propre. Ce que signifie un appel d'outil, qui peut l'appeler et ce qu'une requête de fragment peut renvoyer sont des décisions de votre application, exactement comme pour une route d'une application web. Ce que le modèle peut déclencher est un outil que vous avez enregistré, qui répond avec un balisage que vous avez construit.
Demos\60.HTML\01.RunTime\19.MCPApp est un serveur console sur le port 5725 qui publie deux applications, orders.board et sales.summary, sur un même point de terminaison, et fournit un hôte de référence. Ouvrez http://localhost:5725/ et le panneau de gauche fait ce que fait un vrai hôte : il appelle l'outil, lit la ressource, l'affiche dans un cadre isolé et transmet les appels de la page. Pointez Claude, ChatGPT ou VS Code vers http://localhost:5725/mcp pour l'essayer en conditions réelles.
L'unité se compile là où SGC_HTML et SGC_AI_MCP sont tous deux définis. SGC_AI_MCP est fourni avec les éditions Enterprise et All-Access et avec le pack sgcAI. SGC_HTML est fourni avec sgcHTML, qui est vendu indépendamment de sgcWebSockets, sur toutes les plateformes sauf Android et iOS.
| Aide en ligneGuide d'utilisation des MCP Apps : l'outil, la ressource, l'outil de fragment et le pont. | Ouvrir | |
| Tous les composants sgcHTMLParcourez la matrice complète des fonctionnalités de plus de 80 composants. | Ouvrir | |
| Télécharger la version d'essai gratuiteLa version d'essai de 30 jours fournit les projets de démonstration 60.HTML. | Ouvrir | |
| TarifsLicences Single, Team et Site avec code source complet. | Ouvrir |