在 Delphi 中构建 Model Context Protocol (MCP) 服务器

· 组件

什么是 Model Context Protocol?

Model Context Protocol (MCP) 是 Anthropic 用于向 AI 模型公开工具、数据和提示模板的开放标准。把它想象成 AI 的 USB:任何 MCP 服务器都可以插入任何 MCP 客户端(Claude Desktop、Cursor、Continue、您自己的 Delphi 应用、OpenAI 的客户端),模型立即发现哪些工具可用以及如何调用它们。

在 MCP 之前,每个 AI 集成都意味着将工具定义、JSON 模式和调用调度器连接到每个单独的应用中。MCP 将其归结为单个协议:编写服务器一次,每个模型都可以使用它。对于已经拥有一堆业务 API(ERP 查询、CRM 查找、文件生成器、计费函数)的 Delphi 公司来说,MCP 是使这些 API 可访问 AI 的最便宜路径。

本教程通过使用 TsgcAI_MCP_Server 组件在 Delphi 中构建 MCP 服务器,公开一个可工作的 get_weather 工具,并通过 stdio 和 HTTP 传输将其连接到 Claude Desktop。

一点历史将 MCP 置于背景中。Anthropic 在 2024 年底发布了规范,作为对工具调用格式激增的不兼容性的回答——OpenAI 的函数调用 JSON、Google 的 Gemini 接地工具、每个供应商的定制模式。在您阅读本文时,MCP 支持正在 Claude Desktop、Cursor、Continue、Zed 和大多数现代 AI IDE 中发布;OpenAI 在 2026 年初宣布兼容性。换句话说:今天构建 MCP 服务器是一次性投资,它跨您的客户今天和明天可能使用的每个客户端付款。

MCP 构建块

MCP 服务器可以公开三种功能:

工具

带类型化输入的可调用函数。模型决定何时调用它们。示例:get_weather(city)create_invoice(customer, lines)
资源

模型可以获取的只读数据。每个资源都有 URI。示例:file:///docs/handbook.mddb://customers/12345
提示

用户(不是模型)选择的可重用提示模板。示例:"翻译为正式西班牙语"或"总结会议记录"模板。

该协议在两种传输之一上使用 JSON-RPC 2.0:stdio(服务器是子进程;消息通过 stdin/stdout 流动)或带 Server-Sent Events 的 HTTP 用于服务器到客户端流。Stdio 是 Claude Desktop 等桌面集成的默认值;当服务器在不同的机器上运行或为多个客户端提供服务时,HTTP 是正确的选择。

该组件将两种传输隐藏在单个 API 后面。您编写一次工具处理程序,然后在启动时选择传输。相同的代码作为 Claude Desktop 助手、网络可访问的 HTTP 服务运行,或在一个进程内同时运行两者。我们有公司在单个机器上运行 30 多个 MCP 服务器,通过不同的端口和一两个 stdio 二进制文件进行多路复用,所有这些都为同一个 Delphi 代码库提供服务。

项目设置

sgcAI_MCP_Server 添加到您的 uses 子句,将 TsgcAI_MCP_Server 放在窗体上(或在控制台应用的代码中创建它),连接事件,然后启动它。该组件为您处理 JSON-RPC 帧、消息路由和功能广告。

uses
  sgcAI_MCP_Server, sgcAI_MCP_Classes;

procedure TForm1.FormCreate(Sender: TObject);
begin
  oMCP := TsgcAI_MCP_Server.Create(Self);
  oMCP.ServerInfo.Name    := 'weather-mcp';
  oMCP.ServerInfo.Version := '1.0.0';

  // Wire handlers
  oMCP.OnListTools := DoListTools;
  oMCP.OnCallTool  := DoCallTool;

  // Stdio transport for Claude Desktop integration
  oMCP.Transport := mtStdio;
  oMCP.Active    := True;
end;

实现工具:get_weather

工具需要两个处理程序。OnListTools 告诉客户端存在哪些工具以及它们接受什么参数。OnCallTool 实际运行调用。这两个处理程序都是普通的 Delphi 事件——无需手动 JSON 构造。

procedure TForm1.DoListTools(Sender: TObject;
  const aTools: TsgcAI_MCP_Tools);
var
  oTool: TsgcAI_MCP_Tool;
begin
  oTool := aTools.Add;
  oTool.Name        := 'get_weather';
  oTool.Description := 'Return the current weather for a city.';
  oTool.InputSchema :=
    '{"type":"object",' +
     '"properties":{' +
       '"city":{"type":"string","description":"City name, e.g. Madrid"},' +
       '"units":{"type":"string","enum":["metric","imperial"],"default":"metric"}' +
     '},' +
     '"required":["city"]}';
end;

procedure TForm1.DoCallTool(Sender: TObject;
  const aRequest : TsgcAI_MCP_ToolCall;
  const aResult  : TsgcAI_MCP_ToolResult);
var
  vCity, vUnits, vReport: string;
begin
  if aRequest.Name = 'get_weather' then
  begin
    vCity  := aRequest.Arguments.S['city'];
    vUnits := aRequest.Arguments.S['units'];
    if vUnits = '' then vUnits := 'metric';

    // Call your own weather backend
    vReport := MyWeatherService.Fetch(vCity, vUnits);
    aResult.AddText(vReport);
  end
  else
    aResult.Error('Unknown tool: ' + aRequest.Name);
end;

这就是完整的实现。该组件负责针对模式的输入验证、错误格式化和底层 JSON-RPC 信封。您专注于业务逻辑。

设计良好工具的几个原则,从艰难的方式学到:

公开资源

工具用于操作;资源用于模型可以读取的数据。资源处理程序返回给定 URI 的内容。文档服务器可能将 docs/ 下的每个 Markdown 文件公开为资源。

oMCP.OnListResources := DoListResources;
oMCP.OnReadResource  := DoReadResource;

procedure TForm1.DoListResources(Sender: TObject;
  const aResources: TsgcAI_MCP_Resources);
var
  vFiles: TStringDynArray;
  i     : Integer;
  oRes  : TsgcAI_MCP_Resource;
begin
  vFiles := TDirectory.GetFiles('C:\docs', '*.md');
  for i := 0 to High(vFiles) do
  begin
    oRes := aResources.Add;
    oRes.URI         := 'file:///' + StringReplace(vFiles[i], '\', '/', [rfReplaceAll]);
    oRes.Name        := ExtractFileName(vFiles[i]);
    oRes.MimeType    := 'text/markdown';
    oRes.Description := 'Documentation page';
  end;
end;

procedure TForm1.DoReadResource(Sender: TObject;
  const aURI: string; const aResult: TsgcAI_MCP_ResourceResult);
var
  vPath: string;
begin
  vPath := StringReplace(Copy(aURI, 9, MaxInt), '/', '\', [rfReplaceAll]);
  if FileExists(vPath) then
    aResult.AddText(TFile.ReadAllText(vPath))
  else
    aResult.Error('Resource not found');
end;

Stdio vs HTTP 传输

根据服务器运行的位置和使用者选择传输。

传输 用例 优点 缺点
Stdio 本地桌面工具(Claude Desktop、Cursor) 零配置、无端口、OS 处理进程生命周期 每进程一个客户端、无远程访问
HTTP / SSE 共享企业服务器、Web 客户端、多租户 许多客户端、网络可访问、TLS、auth 需要托管、端口管理、auth 设计

切换传输是一行代码:

// Stdio (default for desktop)
oMCP.Transport := mtStdio;

// HTTP listener on port 8080
oMCP.Transport       := mtHTTP;
oMCP.HTTPOptions.Host := '0.0.0.0';
oMCP.HTTPOptions.Port := 8080;
oMCP.HTTPOptions.TLS.Enabled  := True;
oMCP.HTTPOptions.TLS.CertFile := 'cert.pem';
oMCP.HTTPOptions.TLS.KeyFile  := 'key.pem';
oMCP.Active := True;

从 Claude Desktop 连接

对于 stdio,编辑 claude_desktop_config.json 并添加指向您编译的 .exe 的条目。Claude Desktop 将按需生成进程。

{
  "mcpServers": {
    "weather": {
      "command": "C:\\Tools\\weather-mcp.exe",
      "args": []
    }
  }
}

重新启动 Claude Desktop,您将在提示区域看到一个小工具图标。问"东京天气怎么样?",Claude 将调用您的 Delphi 工具。

对于 HTTP,将任何支持 MCP 的客户端指向 https://your-host:8080/mcp。同一个 Delphi 服务器愉快地同时处理 Claude Desktop 实例、Cursor 会话和自定义 Delphi MCP 客户端。

提示:未充分利用的第三个能力

工具和资源得到了所有关注;提示是安静的工作马。MCP 提示是一个可重用的模板——"总结此次会议"、"提取行动项"、"翻译为正式西班牙语"——用户从其 MCP 客户端的 UI 菜单中调用。然后模型针对用户提供的任何上下文运行该提示。

对于内部工具,这是金。您的支持团队在 Claude Desktop 中获得了一组经过公司批准的精选提示。您的销售团队获得了一个"起草跟进电子邮件"提示,该提示已经知道您的语气。他们都不必学习提示工程——他们只是选择模板。

oMCP.OnListPrompts := DoListPrompts;
oMCP.OnGetPrompt   := DoGetPrompt;

procedure TForm1.DoListPrompts(Sender: TObject;
  const aPrompts: TsgcAI_MCP_Prompts);
var
  oPrompt: TsgcAI_MCP_Prompt;
begin
  oPrompt := aPrompts.Add;
  oPrompt.Name        := 'summarise_meeting';
  oPrompt.Description := 'Turn a meeting transcript into bullet decisions and actions.';
  oPrompt.AddArgument('transcript', 'Plain-text transcript', True);
end;

procedure TForm1.DoGetPrompt(Sender: TObject;
  const aRequest: TsgcAI_MCP_PromptRequest;
  const aResult : TsgcAI_MCP_PromptResult);
begin
  if aRequest.Name = 'summarise_meeting' then
    aResult.AddMessage('user',
      'You are a meeting note taker. Read the transcript and produce: ' +
      '(1) a 3-sentence summary, (2) decisions taken, (3) action items ' +
      'with owners and due dates.' + sLineBreak + sLineBreak +
      aRequest.Arguments.S['transcript']);
end;

生产注意事项

在将 MCP 服务器发布到生产之前需要预算的几件事:

调试技巧

MCP 在 AI 客户端内不可见地运行,这使得第一次出现问题时调试很棘手。三个习惯将为您节省数小时:

总结

MCP 将内部 Delphi API 转变为每个现代 AI 客户端都可以使用的东西,无需每客户端连接。TsgcAI_MCP_Server 组件处理 JSON-RPC 帧、传输和功能广告,让您编写实际的工具主体。从一个只读工具开始,看看模型如何使用它,然后扩展到写入、资源和提示。

我们在内部推出 MCP 的最大收获是:价值不是"Claude 现在可以使用我们的 API"。价值是"公司中的任何人现在都可以通过自然语言使用我们的 API,无需学习 API"。这改变了谁可以做什么,这就是实际生产力收益的来源。

想要匹配的客户端吗?请参阅 TsgcAI_MCP_Client 以构建消费任何 MCP 服务器的 Delphi 应用。如果您是 AI 组件的新手,开始使用中心会在五分钟内引导您完成安装。