MCPApp

TsgcHTMLComponent_MCPApp:一个以页面而不是文本作答的 MCP 工具,因此渲染任意网页的同一套 Delphi 组件,也可以在 ChatGPT、Claude 和 VS Code 中渲染(MCP Apps)。

TsgcHTMLComponent_MCPApp

MCP App 是一种工具,其结果由宿主渲染,而不只是被读取。该组件会在您已有的 MCP 服务器上发布这个工具、承载页面的资源,以及页面自身调用的配套工具,页面则由与其他页面相同的 sgcHTML 组件构建。需要 sgcWebSockets 的 Enterprise 或 All-Access 版本,或者 sgcAI 包,因为它运行在 MCP 服务器上。

组件类

TsgcHTMLComponent_MCPApp,带有 TsgcHTMLMCPApps 集合,其条目为 TsgcHTMLMCPApp

渲染为

宿主在沙盒框架中显示的单个 HTML 文档,其资源全部内联

家族

聊天与 AI

语言

Delphi, C++ Builder, .NET

声明一个应用,响应三个事件

MCPServer 设为某个 TsgcWSAPIServer_MCP,添加一个带有工具名称和描述的应用,响应 OnRenderOnToolCallOnFragment,然后调用 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();

关键属性与方法

您最常使用的成员。

Apps

Apps 是一个 TsgcHTMLMCPApps 集合,由 TsgcHTMLMCPApp 条目组成。ToolName 是必需的:没有它的应用不会注册任何内容。Description 是模型用来决定是否调用该工具的内容,InputSchema 是其参数的 JSON schema。TitleResourceURIPageBuilderEnabledPreferredWidthPreferredHeight 构成一个应用的其余部分,最后两个是请求宿主打开框架时使用的尺寸。

RegisterApps 声明了什么

服务器上每个应用有三样东西:工具,以 ToolName 命名,其 _meta.ui.resourceUri 告诉宿主由哪个资源承载页面;配套工具 <ToolName>.fragment,由页面调用,模型从不调用;以及资源 ui://sgchtml/<toolname>,以 text/html;profile=mcp-app 提供。在 Delphi 中,RegisterAppsLoaded 运行,添加应用的代码会再次调用它;在 .NET 中,需要您自己调用。UnregisterApps 会移除它们。

结果的两个部分

工具结果有两类读者。在 OnToolCall 中,aText 是模型读取的句子,aStructured 是页面读取的 JSON。两者都是可选的,文本为空时会使用应用的 Title。两者不同时(通常都是如此)请分别提供:模型需要一句话,页面需要数字。

页面

OnRender 以普通 sgcHTML 标记的形式返回页面。aHTML 到达时已包含应用的 PageBuilder 所构建的页面(如果已指定的话),因此处理程序可以替换它,也可以保持原样。GetAppHTML 返回一个应用的完整文档,也就是资源所返回的内容,以及测试所读取的内容。

片段与桥接

沙盒框架没有 HTTP 服务器来响应 hx-get。每个文档都带有 window.sgcMCPApp:页面就绪时它发送 ui/initialize,套用宿主回复的主题,报告页面高度以便宿主调整框架大小,并注册一个 htmx 扩展,把每个 hx-gethx-post 转换为对片段工具的 tools/callOnFragment 负责响应:aTarget 是元素请求的目标,aArguments 是表单或查询的 JSON,aHTML 是换入的标记。同样的标记通过 HTTP 提供时,就是普通的 htmx 请求。

单个文档

框架处于沙盒中,通常没有自己的网络,因此无法链接任何外部内容。InlineAssets 默认开启,使样式表、脚本和页面合并在一个文件中传输。仅当宿主在同一台机器上、能够提供这些资源时,才将它关闭。

MCP 服务器

MCPServer 是声明这些应用所在的 TsgcAI_MCP_ServerTsgcWSAPIServer_MCP 将它公开为公共属性,因此 FApps.MCPServer := FMCP.MCPServer 会把这些应用放到 Web 服务器的端点上。组件会订阅工具和资源事件,并把原有的处理程序串联起来,因此您已发布的工具仍然可以继续工作。自行处理这些事件的宿主可以调用 ProcessToolsCallProcessResourcesRead,当请求不属于某个应用时,它们返回 False。在 .NET 中,请传入您已有的 TsgcAI_MCP_Server,例如 TsgcAI_MCP_Server_Stdio 所拥有的那一个。

无状态

组件自身不保存任何状态。工具调用意味着什么、谁可以调用它、片段请求可以返回什么,都由您的应用决定,与 Web 应用中的路由完全一样。模型能够触发的,是您注册的某个工具,它返回的是您构建的标记。

无需助手即可查看

Demos\60.HTML\01.RunTime\19.MCPApp 是一个监听 5725 端口的控制台服务器,在同一个端点上发布两个应用 orders.boardsales.summary,并附带一个参考宿主。打开 http://localhost:5725/,左侧面板会执行真实宿主所做的事情:调用工具,读取资源,在沙盒框架中渲染它,并转发页面的调用。要体验真实效果,请将 Claude、ChatGPT 或 VS Code 指向 http://localhost:5725/mcp

可用性

该单元在同时定义了 SGC_HTMLSGC_AI_MCP 时编译。SGC_AI_MCP 随 Enterprise 和 All-Access 版本以及 sgcAI 包提供。SGC_HTML 随 sgcHTML 提供,后者独立于 sgcWebSockets 单独销售,适用于除 Android 和 iOS 之外的所有平台。

继续探索

在线帮助MCP Apps 使用指南:工具、资源、片段工具和桥接。
所有 sgcHTML 组件浏览 80 多个组件的完整功能矩阵。
下载免费试用版30 天试用版附带 60.HTML 演示项目。
价格Single、Team 和 Site 授权,均含完整源代码。
超值之选:All-AccesseSeGeCe 全部产品,含高级支持,每年 €1,059 起。
查看 All-Access 价格

准备好开始了吗?

下载免费试用版,开始在 Delphi、C++ Builder 和 .NET 中构建 Web 界面。