環(huán)境準(zhǔn)備與基線項(xiàng)目(.NET 9 + SK + MCP)
目標(biāo):搭建最小可運(yùn)行的 .NET 控制臺(tái),引用 SK 與 MCP,完成一次 MCP Ping 健康檢查(Stdio 與 SSE/HTTP 各跑通一次),并為后續(xù)端到端示例打下基線。
1.1 創(chuàng)建解決方案與基線項(xiàng)目
# 安裝 .NET 9 SDK(略)
mkdir SkMcp && cd SkMcp
dotnet new sln -n SkMcp
# 創(chuàng)建三個(gè)項(xiàng)目:
# 1) 最小可運(yùn)行基線(含 Ping 冒煙測(cè)試)
# 2) MCP Server(Stdio/HTTP 兩種宿主)
# 3) SK 客戶端(演示“把 MCP 工具當(dāng)作 SK 函數(shù)”)
mkdir -p src/SkMcp.Baseline src/SkMcp.Server src/SkMcp.SkClient tests/SkMcp.End2End
dotnet new console -n SkMcp.Baseline -o src/SkMcp.Baseline
dotnet new console -n SkMcp.Server -o src/SkMcp.Server
dotnet new console -n SkMcp.SkClient -o src/SkMcp.SkClient
dotnet new xunit -n SkMcp.End2End -o tests/SkMcp.End2End
# 解決方案引用
dotnet sln add src/*/**.csproj tests/*/**.csproj
結(jié)構(gòu)建議
src/*與tests/*分離,后者用于 E2E 冒煙(Ping、ListTools、CallTool 回顯)。- 為每個(gè) Server 建
docs/示例資源目錄,便于 resources 流程(第 6 章)。
版本固定策略:以上
dotnet add僅添加包名,實(shí)際生效版本由根目錄Directory.Packages.props鎖定(Microsoft.SemanticKernel=1.65.0,McpDotNet.Extensions.SemanticKernel=0.0.1-preview-04)。構(gòu)建管線中啟用-p:ContinuousIntegrationBuild=true與-warnaserror,確保出現(xiàn)不兼容 API 時(shí)能第一時(shí)間在 CI 上失敗而非運(yùn)行期暴露問題。
版本與兼容性建議
- MCP SDK 仍在快速迭代,推薦在根目錄使用
Directory.Packages.props鎖定主次版本,避免傳遞依賴“偷偷升級(jí)”。 McpDotNet.Extensions.SemanticKernel與 SDK 存在配套關(guān)系:如遇 API 不匹配,優(yōu)先以 MCP SDK 版本為錨回退/前進(jìn)。
建議的
Directory.Packages.props片段(版本固定)<Project> <ItemGroup> <PackageVersion Include="ModelContextProtocol" Version="0.3.0-preview.*" /> <PackageVersion Include="McpDotNet.Extensions.SemanticKernel" Version="0.0.1-preview-04" /> <PackageVersion Include="Microsoft.SemanticKernel" Version="1.65.0" /> </ItemGroup> </Project>說明:構(gòu)建時(shí)將嚴(yán)格解析到以上版本;如需升級(jí),請(qǐng)同步驗(yàn)證擴(kuò)展包與 SDK 的 API 兼容性,再做漸進(jìn)發(fā)布(先預(yù)發(fā)環(huán)境,再生產(chǎn)灰度)。
<Project>
<ItemGroup>
<PackageVersion Include="ModelContextProtocol" Version="0.3.0-preview.*" />
<PackageVersion Include="McpDotNet.Extensions.SemanticKernel" Version="0.3.*" />
<PackageVersion Include="Microsoft.SemanticKernel" Version="1.*" />
</ItemGroup>
</Project>
1.3 .env 與本地機(jī)密管理(User Secrets)
開發(fā)期:
- 使用 User Secrets +
IConfiguration,避免把密鑰寫入源碼; appsettings.Development.json僅放非敏感默認(rèn)值;- 機(jī)密鍵命名約定:
OpenAI:ApiKey、GitHub:Token:RepoRead等。
生產(chǎn)期:
- 云端使用 Key Vault/Secrets Manager + 托管標(biāo)識(shí);
- 容器僅掛載只讀 Secret,不要把
.envbake 進(jìn)鏡像; - 最小權(quán)限:OpenAI 僅
apiKey;GitHub PAT 起步repo:read、issues:read。
1.4 MCP “Ping” 最小驗(yàn)證(Stdio 與 SSE/HTTP)
新增一個(gè)最小冒煙控制臺(tái)(或在 Baseline 中內(nèi)置):
- Stdio:通過
npx/Node 啟動(dòng)本地示例 server(如 server-everything),客戶端PingAsync+ListTools+CallTool("echo"); - SSE/HTTP:對(duì)等流程;
- 跨平臺(tái)提示(Windows 常見問題):若出現(xiàn) ENOENT/找不到
npx,請(qǐng)使用 Node 的絕對(duì)路徑或where npx結(jié)果; - 長連接?;?/strong>:定期
PingAsync; - E2E 測(cè)試點(diǎn):
Ping成功;ListTools至少包含echo;CallTool("echo", { message: "hello" })返回包含hello的文本內(nèi)容。
關(guān)于傳輸:SSE 仍可用,但更推薦 Streamable HTTP(第 2.4、4.2 詳述)。
MCP 概念速通與協(xié)議選型(強(qiáng)化到 Streamable HTTP)
2.1 核心概念
- Tools:由 MCP Server 暴露的可調(diào)用能力(讀/寫/搜索等)。
- Resources:可檢索/讀取的外部資料(文檔、代碼、數(shù)據(jù)),支持分頁與哈希標(biāo)識(shí)。
- Prompts:可復(fù)用提示模板,由 Server 聲明,客戶端可發(fā)現(xiàn)與調(diào)用。
2.4 傳輸協(xié)議選型
- Stdio:本地開發(fā)/快速試驗(yàn),零網(wǎng)絡(luò)依賴。
- SSE:歷史方案,適合流式文本下行。
- Streamable HTTP(推薦):統(tǒng)一 HTTP 請(qǐng)求/響應(yīng) + 可選事件流,覆蓋一次性結(jié)果與長任務(wù)輸出;同一服務(wù)可既支持傳統(tǒng) HTTP,也支持事件流端點(diǎn),便于漸進(jìn)遷移。
遷移建議:HTTP 宿主默認(rèn)啟用 Streamable HTTP,保留 SSE 兼容端點(diǎn)若干版本。
作為 MCP Client——在 SK 中“接工具”
3.1 方式 A:用擴(kuò)展包“一行接入”(Stdio & SSE/HTTP)
// Program.cs 片段
var builder = Kernel.CreateBuilder();
// ... 配置 OpenAI/AzureOpenAI 等模型
// 以 Stdio 形式連接某 MCP Server(示例:server-everything)
await builder.AddMcpFunctionsFromStdioServerAsync(
kernel:
null, // 可省略,使用 builder 內(nèi)核
serverExecutablePath:
"/absolute/path/to/node", // Windows 下建議絕對(duì)路徑
serverArgs: new [] { "npx", "-y", "@modelcontextprotocol/server-everything" },
namePrefix: "everything_", // 避免與其他 Server 同名工具沖突
includedPlugins: null,
includedFunctions: null,
cancellationToken: ct);
var kernel = builder.Build();
// 打印工具清單(調(diào)試)
foreach (var f in kernel.GetFunctions())
{
Console.WriteLine($"{f.PluginName}.{f.Name} -> {f.Description}");
}
// 白名單方式減少“誤觸發(fā)”
var behavior = FunctionChoiceBehavior.Auto(allowance: 3,
functions: kernel.GetFunctions()
.Where(f => f.Name.StartsWith("http_") || f.Name == "echo"));
實(shí)踐加強(qiáng)
- 限流/超時(shí):為高成本工具(如
http/get)套一層包裝,設(shè)置超時(shí)、最大返回大?。↘B/MB)與調(diào)用間隔(冷卻時(shí)間)。 - 系統(tǒng)提示:在
system中明確“僅在需要時(shí)才調(diào)用工具,并優(yōu)先使用指定白名單”。
3.2 方式 B:純 SDK(“裸接入”)把 MCP 工具轉(zhuǎn)為 SK 函數(shù)
要點(diǎn):
- JSON schema → SK 參數(shù):對(duì)
object/array入?yún)?,?yōu)先整體 JSON 字符串透?jìng)?,減少 LLM 構(gòu)造復(fù)雜結(jié)構(gòu)的出錯(cuò)率; - Content 聚合:
call_tool返回可能包含text與resource鏈接,注意把ResourceLink也回傳給終端應(yīng)用(用于可追溯)。
作為 MCP Server——用 C# 暴露你自己的工具
4.1 Stdio 版本(控制臺(tái)宿主)
// Program.cs(簡(jiǎn)化)
var server = McpServerBuilder.Create("demo-server")
.WithToolsFromAssembly(typeof(Program).Assembly)
.WithResources(opts => opts.RootDirectory = "./docs")
.Build();
await server.RunStdioAsync();
建議
- 每個(gè)工具方法都接受
CancellationToken,并在服務(wù)器級(jí)配置軟超時(shí); - 輸入校驗(yàn):例如
CsvFilter(string column, string op, string value)增加列名白名單、最大行數(shù)/文件大小限制,避免 OOM; - 日志分流:業(yè)務(wù)日志走
stdout、診斷/調(diào)試走stderr,方便客戶端區(qū)分。
4.3 容器化(Dockerfile)
# 多階段構(gòu)建
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
WORKDIR /src
COPY . .
RUN dotnet publish src/SkMcp.Server/SkMcp.Server.csproj -c Release -o /out
FROM mcr.microsoft.com/dotnet/aspnet:9.0
WORKDIR /app
COPY --from=build /out .
# 只讀掛載機(jī)密;不要復(fù)制 .env 到鏡像
ENTRYPOINT ["dotnet", "SkMcp.Server.dll"]
4.4 部署與運(yùn)維(systemd / Nginx / 健康檢查)
- 健康檢查:除 MCP 通道
Ping外,暴露獨(dú)立/healthz區(qū)分進(jìn)程活性與業(yè)務(wù)健康; - 日志與指標(biāo):結(jié)構(gòu)化日志(JSON)+ Prometheus 指標(biāo)(QPS、失敗率、P95 時(shí)延、事件流存活數(shù));
- 滾動(dòng)升級(jí):灰度/金絲雀,回滾策略與版本錨定。
端到端 A:開發(fā)者 GitHub 助手
5.1 選用官方 GitHub MCP Server
- 優(yōu)先使用官方鏡像/源碼運(yùn)行(Stdio 或遠(yuǎn)端 HTTP);
- 從只讀權(quán)限起步(
repo:read、issues:read),確認(rèn)調(diào)用路徑穩(wěn)定再逐步放開寫權(quán)限; - 為
search issues之類高頻工具加冷卻時(shí)間與分頁上限。
5.2 在 SK 側(cè)注冊(cè) GitHub 工具(擴(kuò)展包寫法)
await builder.AddMcpFunctionsFromSseServerAsync(
serverUrl: new Uri("https://your-github-mcp.example/mcp"),
namePrefix: "gh_",
includedFunctions: new [] { "search_issues", "get_repo" },
cancellationToken: ct);
端到端 B:企業(yè)知識(shí)檢索助手(Resources)
6.1 Server 側(cè)資源暴露
- 大文件/大目錄務(wù)必強(qiáng)制分頁(按字節(jié)或行數(shù)),返回
cursor/next; - 明確
contentType與編碼,例如text/markdown; charset=utf-8; - 返回
resourceUri與hash/etag,便于可追溯與緩存控制。
6.2 客戶端(SK)檢索與回答(ReAct 思路)
- 將“檢索→調(diào)用→回答”的中間觀察寫入隱藏槽位或日志;
- 對(duì)用戶輸出統(tǒng)一以“三句話摘要 + 引用 URI”收尾;
- 對(duì)模型的工具選擇策略使用白名單 + 置信閾值,避免“為了調(diào)用而調(diào)用”。
端到端 C:數(shù)據(jù)處理流水線(Prompts + Tools)
7.1 在 MCP Server 注冊(cè)可復(fù)用 Prompt
- 將規(guī)范化的數(shù)據(jù)清洗/匯總模板以 Prompt 形式暴露,參數(shù)化日期、項(xiàng)目名等;
- 對(duì) Prompt 做版本號(hào)管理(如
v2025.09)以便回溯。
文檔化、學(xué)習(xí)路徑與 FAQ(擴(kuò)展)
8.1 學(xué)習(xí)路徑
- 跑通第 1 章最小冒煙(Stdio + SSE/HTTP)→ 2) 第 3 章把工具接入 SK → 3) 任一端到端示例(5/6/7 章)→ 4) 生產(chǎn)化與安全(4.3/4.4)。
8.2 踩坑手冊(cè)
- 代理與事件流:公司網(wǎng)關(guān)可能“截流”事件流,需在反代層
proxy_buffering off并放寬空閑超時(shí); - 同名工具沖突:不同 Server 暴露同名工具時(shí),在注冊(cè)時(shí)加
namePrefix或放入不同插件命名空間; - CSV 換行混用:
\r\n/\n混用會(huì)導(dǎo)致“按行分頁錯(cuò)位”,統(tǒng)一轉(zhuǎn) LF 后再分頁; - Windows
npx不可見:使用 Node 絕對(duì)路徑或where npx結(jié)果; - 模型“過度調(diào)用工具”:白名單 + 明確系統(tǒng)提示 + 冷卻時(shí)間控制。
浙公網(wǎng)安備 33010602011771號(hào)