MCPApp

TsgcHTMLComponent_MCPApp: uma ferramenta MCP que responde com uma página em vez de texto, de modo que os mesmos componentes Delphi que renderizam qualquer página web renderizam dentro do ChatGPT, do Claude e do VS Code (MCP Apps).

TsgcHTMLComponent_MCPApp

Um MCP App é uma ferramenta cujo resultado o host renderiza em vez de apenas ler. O componente publica a ferramenta, o recurso que carrega a página e uma ferramenta complementar que a própria página chama, no servidor MCP que você já tem, e a página é construída com os mesmos componentes sgcHTML de qualquer outra. Exige a edição Enterprise ou All-Access do sgcWebSockets, ou o pacote sgcAI, porque roda no servidor MCP.

Classe do componente

TsgcHTMLComponent_MCPApp, com a coleção TsgcHTMLMCPApps de itens TsgcHTMLMCPApp

Renderiza

Um documento HTML que o host exibe em um frame em sandbox, com seus assets embutidos

Família

Chat & IA

Linguagens

Delphi, C++ Builder, .NET

Declare um app, responda a três eventos

Passe ao componente o MCPServer de um TsgcWSAPIServer_MCP, adicione um app com um nome de ferramenta e uma descrição, responda a OnRender, OnToolCall e OnFragment e então chame 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();

Principais propriedades & métodos

Os membros que você usa com mais frequência.

Apps

Apps é uma coleção TsgcHTMLMCPApps de itens TsgcHTMLMCPApp. ToolName é obrigatório: um app sem ele não registra nada. Description é o que o modelo lê para decidir se chama a ferramenta, e InputSchema é o esquema JSON dos argumentos dela. Title, ResourceURI, PageBuilder, Enabled, PreferredWidth e PreferredHeight completam um app, sendo os dois últimos o tamanho com que se pede ao host que abra o frame.

O que RegisterApps declara

Três coisas por app no servidor: a ferramenta, nomeada por ToolName, cujo _meta.ui.resourceUri diz ao host qual recurso carrega a página; a ferramenta complementar <ToolName>.fragment, que a página chama e o modelo nunca chama; e o recurso ui://sgchtml/<toolname>, servido como text/html;profile=mcp-app. No Delphi, RegisterApps executa a partir de Loaded e o código que adiciona um app o chama de novo; no .NET, chame-o você mesmo. UnregisterApps os remove.

Duas metades de um resultado

Um resultado de ferramenta tem dois públicos. Em OnToolCall, aText é a frase que o modelo lê e aStructured é o JSON que a página lê. Ambos são opcionais e, quando o texto está vazio, usa-se o Title do app. Responda a ambos quando forem diferentes, o que geralmente acontece: o modelo quer uma frase e a página quer números.

A página

OnRender devolve a página como markup sgcHTML comum. aHTML chega contendo a página que o PageBuilder do app constrói, quando há um atribuído, de modo que um handler pode substituí-la ou deixá-la como está. GetAppHTML devolve o documento inteiro de um app, que é o que o recurso responde e o que um teste lê.

Fragmentos e a ponte

Um frame em sandbox não tem servidor HTTP para responder a um hx-get. Todo documento traz window.sgcMCPApp: ele envia ui/initialize quando a página está pronta, aplica o tema com que o host responde, informa a altura da página para que o host dimensione o frame e registra uma extensão do htmx que transforma cada hx-get e hx-post em um tools/call da ferramenta de fragmento. OnFragment responde a essa chamada: aTarget é o que o elemento pediu, aArguments o JSON do formulário ou da consulta, e aHTML o markup inserido. O mesmo markup servido por HTTP é uma requisição htmx comum.

Um documento

O frame está em sandbox e geralmente não tem rede própria, então nada pode ser referenciado por link. InlineAssets vem ligado por padrão e faz a folha de estilo, os scripts e a página viajarem em um único arquivo. Desligue-o somente para um host na mesma máquina que possa servir os assets.

O servidor MCP

MCPServer é o TsgcAI_MCP_Server no qual os apps são declarados. TsgcWSAPIServer_MCP o expõe como uma propriedade pública, então FApps.MCPServer := FMCP.MCPServer coloca os apps no endpoint de um servidor web. O componente se inscreve nos eventos de ferramenta e de recurso e encadeia os handlers que já estavam lá, de modo que as ferramentas que você já publica continuam funcionando. Um host que trata esses eventos por conta própria chama ProcessToolsCall e ProcessResourcesRead, que respondem False quando a requisição não era de um app. No .NET, entregue a ele o TsgcAI_MCP_Server que você tem, por exemplo o que pertence a TsgcAI_MCP_Server_Stdio.

Sem estado

O componente não guarda nenhum estado próprio. O que uma chamada de ferramenta significa, quem pode chamá-la e o que uma requisição de fragmento pode devolver são decisões da sua aplicação, exatamente como são para uma rota em uma aplicação web. O que o modelo pode disparar é uma ferramenta que você registrou, respondendo com um markup que você construiu.

Veja sem um assistente

Demos\60.HTML\01.RunTime\19.MCPApp é um servidor de console na porta 5725 que publica dois apps, orders.board e sales.summary, em um único endpoint, e traz um host de referência. Abra http://localhost:5725/ e o painel da esquerda faz o que um host de verdade faz: chama a ferramenta, lê o recurso, renderiza-o em um frame em sandbox e repassa as chamadas da página. Aponte o Claude, o ChatGPT ou o VS Code para http://localhost:5725/mcp para ver o cenário real.

Disponibilidade

A unit compila onde SGC_HTML e SGC_AI_MCP estão ambos definidos. SGC_AI_MCP vem com as edições Enterprise e All-Access e com o pacote sgcAI. SGC_HTML vem com o sgcHTML, que é vendido de forma independente do sgcWebSockets, em todas as plataformas, exceto Android e iOS.

Continue explorando

Ajuda onlineGuia de uso dos MCP Apps: a ferramenta, o recurso, a ferramenta de fragmento e a ponte.
Todos os Componentes sgcHTMLExplore a matriz completa de recursos com mais de 80 componentes.
Baixar Versão de Avaliação GratuitaA avaliação de 30 dias inclui os projetos de demonstração 60.HTML.
PreçosLicenças Single, Team e Site com código-fonte completo.
Melhor custo-benefício: All-AccessTodos os produtos da eSeGeCe, com Suporte Premium incluído, a partir de €1,059/ano.
Ver preços do All-Access

Pronto para Começar?

Baixe a versão de avaliação gratuita e comece a construir UIs web em Delphi, C++ Builder e .NET.