什么是 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.md、db://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 信封。您专注于业务逻辑。
设计良好工具的几个原则,从艰难的方式学到:
- 每个工具一个动词。"get_weather"比带子操作参数的"weather_operations"更好。当每个名称恰好描述一个结果时,模型更快地选择正确的工具。
- 对描述要无情。模式描述是您教给模型该工具做什么和不做什么的唯一机会。详细说明边缘情况、单位、格式和先决条件。
- 尽可能默认一切。带有合理默认值的可选参数让模型即使只知道一半上下文也能成功调用您的工具。
- 返回小的、聚焦的负载。50 行的文本响应没问题;5,000 行的转储会膨胀上下文窗口和账单。在服务器端总结。
- 显式失败。返回结构化错误消息("无法识别城市。您是想说 Madrid 还是 Madras?"),而不是静默地返回空结果。
公开资源
工具用于操作;资源用于模型可以读取的数据。资源处理程序返回给定 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 服务器发布到生产之前需要预算的几件事:
- 身份验证:stdio 继承操作系统级信任,但 HTTP 需要 bearer 令牌或 mTLS。使用
OnHTTPAuthenticate进行验证。 - 速率限制:啰嗦的模型可以在紧密循环中触发工具调用。将 MCP 服务器与
TsgcWebSocketHTTPServer_RateLimiter或您自己的配额逻辑配对。 - 日志记录:连接
OnLog以捕获每个工具调用。当用户报告"Claude 做了一些奇怪的事情"时,您将需要它。 - 模式纪律:您的 JSON 模式越准确(描述、枚举、示例),模型选择正确工具和正确参数的能力就越好。
- 幂等性:如果模型重试,可能会调用工具两次。将写操作标记为幂等或添加事务 ID 参数。
调试技巧
MCP 在 AI 客户端内不可见地运行,这使得第一次出现问题时调试很棘手。三个习惯将为您节省数小时:
- Stderr 是您的控制台。在 stdio 模式下,stdout 属于 JSON-RPC。您写入它的任何内容都会破坏协议。日志记录到 stderr(Claude Desktop 将其捕获到其日志文件中)或带进程 ID 后缀的旁路日志文件。
- 使用 MCP 检查器。Anthropic 提供一个免费工具 (
npx @modelcontextprotocol/inspector),让您将 UI 指向您的服务器、单击每个工具、查看原始请求/响应,并验证模式而无需涉及任何 AI 客户端。 - 添加"诊断"工具。返回构建日期、主机和版本的简单
server_info工具使得在 AI 客户端中确认您正在与正确的服务器通信变得微不足道。我们将其添加到每个生产 MCP 服务器。
总结
MCP 将内部 Delphi API 转变为每个现代 AI 客户端都可以使用的东西,无需每客户端连接。TsgcAI_MCP_Server 组件处理 JSON-RPC 帧、传输和功能广告,让您编写实际的工具主体。从一个只读工具开始,看看模型如何使用它,然后扩展到写入、资源和提示。
我们在内部推出 MCP 的最大收获是:价值不是"Claude 现在可以使用我们的 API"。价值是"公司中的任何人现在都可以通过自然语言使用我们的 API,无需学习 API"。这改变了谁可以做什么,这就是实际生产力收益的来源。
想要匹配的客户端吗?请参阅 TsgcAI_MCP_Client 以构建消费任何 MCP 服务器的 Delphi 应用。如果您是 AI 组件的新手,开始使用中心会在五分钟内引导您完成安装。