環境準備與基線項目(.NET 9 + SK + MCP)
目標:搭建最小可運行的 .NET 控制臺,引用 SK 與 MCP,完成一次 MCP Ping 健(jian)康檢查(Stdio 與 SSE/HTTP 各跑通一(yi)次(ci)),并為后續端(duan)到端(duan)示例(li)打下基線。
1.1 創建解決方案與基線項目
# 安裝 .NET 9 SDK(略)
mkdir SkMcp && cd SkMcp
dotnet new sln -n SkMcp
# 創建三個項目:
# 1) 最小可運行基線(含 Ping 冒煙測試)
# 2) MCP Server(Stdio/HTTP 兩種宿主)
# 3) SK 客戶端(演示“把 MCP 工具當作 SK 函數”)
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
結構建議
src/*與tests/*分離,后者用于 E2E 冒煙(Ping、ListTools、CallTool 回顯)。- 為每個 Server 建
docs/示例資源目錄,便于 resources 流程(第 6 章)。
版本固定策略:以上
dotnet add僅添加包名,實際生效版本由根目錄Directory.Packages.props鎖定(Microsoft.SemanticKernel=1.65.0,McpDotNet.Extensions.SemanticKernel=0.0.1-preview-04)。構建管線中啟用-p:ContinuousIntegrationBuild=true與-warnaserror,確保出現不(bu)兼容 API 時能第一時間在 CI 上失(shi)敗而(er)非運行期暴(bao)露問題。
版本與兼容性建議
- MCP SDK 仍在快速迭代,推薦在根目錄使用
Directory.Packages.props鎖定主次版本,避免傳遞依賴“偷偷升級”。 McpDotNet.Extensions.SemanticKernel與 SDK 存在配套關系:如遇 API 不匹配,優先以 MCP SDK 版本為錨回退/前進。
建議的
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>說明:構建時將(jiang)嚴格解析到以(yi)上版本(ben);如需升級,請同步驗(yan)證擴展包與 SDK 的 API 兼容性,再做漸進發布(先預發環境,再生產灰度)。
<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 與本地機密管理(User Secrets)
開發期:
- 使用 User Secrets +
IConfiguration,避免把密鑰寫入源碼; appsettings.Development.json僅放非敏感默認值;- 機密鍵命名約定:
OpenAI:ApiKey、GitHub:Token:RepoRead等。
生產期:
- 云端使用 Key Vault/Secrets Manager + 托管標識;
- 容器僅掛載只讀 Secret,不要把
.envbake 進鏡像; - 最小權限:OpenAI 僅
apiKey;GitHub PAT 起步repo:read、issues:read。
1.4 MCP “Ping” 最小驗證(Stdio 與 SSE/HTTP)
新增一個最小冒煙控制臺(或(huo)在 Baseline 中內置):
- Stdio:通過
npx/Node 啟動本地示例 server(如 server-everything),客戶端PingAsync+ListTools+CallTool("echo"); - SSE/HTTP:對等流程;
- 跨平臺提示(Windows 常見問題):若出現 ENOENT/找不到
npx,請使用 Node 的絕對路徑或where npx結果; - 長連接保活:定期
PingAsync; - E2E 測試點:
Ping成功;ListTools至少包含echo;CallTool("echo", { message: "hello" })返回包含hello的文本內容。
關于傳輸:SSE 仍可用,但更推薦 Streamable HTTP(第(di) 2.4、4.2 詳(xiang)述)。
MCP 概念速通與協議選型(強化到 Streamable HTTP)
2.1 核心概念
- Tools:由 MCP Server 暴露的可調用能力(讀/寫/搜索等)。
- Resources:可檢索/讀取的外部資料(文檔、代碼、數據),支持分頁與哈希標識。
- Prompts:可復用提示模板,由 Server 聲明,客戶端可發現與調用。
flowchart LR
subgraph Host[LLM Host / Orchestrator]
SK[Semantic Kernel]
Behavior[FunctionChoiceBehavior Policies]
end
subgraph MCPClient[MCP Client]
Discovery[List Tools/Resources/Prompts]
Invoke[call_tool]
end
subgraph MCPServers[MCP Servers]
S1[Server A
e.g., GitHub]
S2[Server B
Doc/FS/DB]
end
subgraph Backends[External Systems]
GH[GitHub API]
FS[File Store]
DB[Database]
end
SK-->Behavior-->Discovery-->Invoke
Invoke-->S1-->GH
Invoke-->S2-->FS
S2-->DB
sequenceDiagram
autonumber
participant User as User/Agent
participant SK as SK (Kernel)
participant MCPc as MCP Client
participant MCPs as MCP Server
participant Ext as External System
User->>SK: 觸發任務(含系統/用戶提示)
SK->>MCPc: 列舉可用工具(ListTools/Prompts/Resources)
MCPc-->>SK: 工具與參數 Schema
SK->>SK: 策略評估(白名單/置信度/成本)
SK->>MCPc: call_tool(tool, args)
MCPc->>MCPs: 執行(Streamable HTTP/SSE/Stdio)
MCPs->>Ext: 調用外部系統(冪等/超時/重試)
Ext-->>MCPs: 結果/分頁數據
MCPs-->>MCPc: 流式增量內容(可包含 resource link)
MCPc-->>SK: 聚合結果
SK-->>User: 最終回答(含引用與可追溯信息)
2.4 傳輸協議選型
- Stdio:本地開發/快速試驗,零網絡依賴。
- SSE:歷史方案,適合流式文本下行。
- Streamable HTTP(推薦):統一 HTTP 請求/響應 + 可選事件流,覆蓋一次性結果與長任務輸出;同一服務可既支持傳統 HTTP,也支持事件流端點,便于漸進遷移。
遷移建議:HTTP 宿主默認啟用 Streamable HTTP,保留 SSE 兼(jian)容端點若干版(ban)本(ben)。
作為 MCP Client——在 SK 中“接工具”
3.1 方式 A:用擴展包“一行接入”(Stdio & SSE/HTTP)
// Program.cs 片段
var builder = Kernel.CreateBuilder();
// ... 配置 OpenAI/AzureOpenAI 等模型
// 以 Stdio 形式連接某 MCP Server(示例:server-everything)
await builder.AddMcpFunctionsFromStdioServerAsync(
kernel:
null, // 可省略,使用 builder 內核
serverExecutablePath:
"/absolute/path/to/node", // Windows 下建議絕對路徑
serverArgs: new [] { "npx", "-y", "@modelcontextprotocol/server-everything" },
namePrefix: "everything_", // 避免與其他 Server 同名工具沖突
includedPlugins: null,
includedFunctions: null,
cancellationToken: ct);
var kernel = builder.Build();
// 打印工具清單(調試)
foreach (var f in kernel.GetFunctions())
{
Console.WriteLine($"{f.PluginName}.{f.Name} -> {f.Description}");
}
// 白名單方式減少“誤觸發”
var behavior = FunctionChoiceBehavior.Auto(allowance: 3,
functions: kernel.GetFunctions()
.Where(f => f.Name.StartsWith("http_") || f.Name == "echo"));
實踐加強
- 限流/超時:為高成本工具(如
http/get)套一層包裝,設置超時、最大返回大小(KB/MB)與調用間隔(冷卻時間)。 - 系統提示:在
system中明確“僅在需要時才調用工具,并優先使用指定白名單”。
3.2 方式 B:純 SDK(“裸接入”)把 MCP 工具轉為 SK 函數
要點:
- JSON schema → SK 參數:對
object/array入參,優先整體 JSON 字符串透傳,減少 LLM 構造復雜結構的出錯率; - Content 聚合:
call_tool返回可能包含text與resource鏈接,注意把ResourceLink也回傳給終端應用(用于可追溯)。
作為 MCP Server——用 C# 暴露你自己的工具
4.1 Stdio 版本(控制臺宿主)
// Program.cs(簡化)
var server = McpServerBuilder.Create("demo-server")
.WithToolsFromAssembly(typeof(Program).Assembly)
.WithResources(opts => opts.RootDirectory = "./docs")
.Build();
await server.RunStdioAsync();
建議
- 每個工具方法都接受
CancellationToken,并在服務器級配置軟超時; - 輸入校驗:例如
CsvFilter(string column, string op, string value)增加列名白名單、最大行數/文件大小限制,避免 OOM; - 日志分流:業務日志走
stdout、診斷/調試走stderr,方便客戶端區分。
graph TB
Client\[Client/Browser/Agent] --> Proxy\[Reverse Proxy/Nginx]
Proxy --> Kestrel\[ASP.NET Core (Kestrel)]
Kestrel -->|/mcp (HTTP)| EndpointHTTP\[HTTP Endpoint]
Kestrel -->|/mcp/events (SSE)| EndpointSSE\[SSE/Events]
EndpointHTTP --> Tools\[Tools]
EndpointHTTP --> Resources\[Resources]
EndpointSSE --> Tools
EndpointSSE --> Resources
4.3 容器化(Dockerfile)
# 多階段構建
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 .
# 只讀掛載機密;不要復制 .env 到鏡像
ENTRYPOINT ["dotnet", "SkMcp.Server.dll"]
4.4 部署與運維(systemd / Nginx / 健康檢查)
- 健康檢查:除 MCP 通道
Ping外,暴露獨立/healthz區分進程活性與業務健康; - 日志與指標:結構化日志(JSON)+ Prometheus 指標(QPS、失敗率、P95 時延、事件流存活數);
- 滾動升級:灰度/金絲雀,回滾策略與版本錨定。
端到端 A:開發者 GitHub 助手
5.1 選用官方 GitHub MCP Server
- 優先使用官方鏡像/源碼運行(Stdio 或遠端 HTTP);
- 從只讀權限起步(
repo:read、issues:read),確認調用路徑穩定再逐步放開寫權限; - 為
search issues之類高頻工具加冷卻時間與分頁上限。
5.2 在 SK 側注冊 GitHub 工具(擴展包寫法)
await builder.AddMcpFunctionsFromSseServerAsync(
serverUrl: new Uri("//your-github-mcp.example/mcp"),
namePrefix: "gh_",
includedFunctions: new [] { "search_issues", "get_repo" },
cancellationToken: ct);
端到端 B:企業知識檢索助手(Resources)
6.1 Server 側資源暴露
- 大文件/大目錄務必強制分頁(按字節或行數),返回
cursor/next; - 明確
contentType與編碼,例如text/markdown; charset=utf-8; - 返回
resourceUri與hash/etag,便于可追溯與緩存控制。
6.2 客戶端(SK)檢索與回答(ReAct 思路)
- 將“檢索→調用→回答”的中間觀察寫入隱藏槽位或日志;
- 對用戶輸出統一以“三句話摘要 + 引用 URI”收尾;
- 對模型的工具選擇策略使用白名單 + 置信閾值,避免“為了調用而調用”。
端到端 C:數據處理流水線(Prompts + Tools)
7.1 在 MCP Server 注冊可復用 Prompt
- 將規范化的數據清洗/匯總模板以 Prompt 形式暴露,參數化日期、項目名等;
- 對 Prompt 做版本號管理(如
v2025.09)以便回溯。
flowchart TD
T0[觸發: {date}:{project}] --> T1[檢索與預清洗
(MCP: CsvClean/CsvFilter/HttpGet)]
T1 -->|失敗: 重試N次→死信| DLQ[Dead Letter Queue]
T1 --> T2[聚合與特征化
(MCP: Prompt v2025.09)]
T2 --> T3[生成報告草稿
(SK: LLM with FunctionChoiceBehavior)]
T3 -->|資源引用與哈希| T4[落盤→命名含內容哈希]
T4 --> T5[發布/通知/審計]
文檔化、學習路徑與 FAQ(擴展)
8.1 學習路徑
- 跑通第 1 章最小冒煙(Stdio + SSE/HTTP)→ 2) 第 3 章把工具接入 SK → 3) 任一端到端示例(5/6/7 章)→ 4) 生產化與安全(4.3/4.4)。
8.2 踩坑手冊
- 代理與事件流:公司網關可能“截流”事件流,需在反代層
proxy_buffering off并放寬空閑超時; - 同名工具沖突:不同 Server 暴露同名工具時,在注冊時加
namePrefix或放入不同插件命名空間; - CSV 換行混用:
\r\n/\n混用會導致“按行分頁錯位”,統一轉 LF 后再分頁; - Windows
npx不可見:使用 Node 絕對路徑或where npx結果; - 模型“過度調用工具”:白名單 + 明確系統提示 + 冷卻時間控制。