Bitzsoft.Integrations.McpServer
1.0.0-alpha.10
dotnet add package Bitzsoft.Integrations.McpServer --version 1.0.0-alpha.10
NuGet\Install-Package Bitzsoft.Integrations.McpServer -Version 1.0.0-alpha.10
<PackageReference Include="Bitzsoft.Integrations.McpServer" Version="1.0.0-alpha.10" />
<PackageVersion Include="Bitzsoft.Integrations.McpServer" Version="1.0.0-alpha.10" />
<PackageReference Include="Bitzsoft.Integrations.McpServer" />
paket add Bitzsoft.Integrations.McpServer --version 1.0.0-alpha.10
#r "nuget: Bitzsoft.Integrations.McpServer, 1.0.0-alpha.10"
#:package Bitzsoft.Integrations.McpServer@1.0.0-alpha.10
#addin nuget:?package=Bitzsoft.Integrations.McpServer&version=1.0.0-alpha.10&prerelease
#tool nuget:?package=Bitzsoft.Integrations.McpServer&version=1.0.0-alpha.10&prerelease
Bitzsoft.Integrations.McpServer
基于官方 MCP C# SDK 的安全 Client/Server 集成。包名为兼容既有命名保留
McpServer,实际同时提供 Streamable HTTP、stdio 客户端以及 HTTP、stdio 服务端。
适用范围
- 将受信 MCP 工具接入 Microsoft Agent Framework 或
Microsoft.Extensions.AI; - 把现有 ASP.NET Core 业务能力显式发布为 MCP 工具;
- 启动经过审批的本地 stdio MCP 服务;
- 按租户、地域、环境和凭据名称解析 MCP 访问凭据。
目标框架为 net8.0;net10.0。MCP 官方 SDK 不支持 .NET 5,因此老系统应把 MCP/Agent
能力部署为独立 .NET 8/10 服务,再通过受控 HTTP 边界调用。
安装
dotnet add package Bitzsoft.Integrations.McpServer
安全默认值
本封装采用默认拒绝策略:
- 客户端必须配置非空
AllowedTools,连接成功后只返回白名单工具; - HTTP 必须配置
AllowedHosts;远程地址必须使用 HTTPS; - URL 禁止携带用户名、密码、query 或 fragment;
- 默认阻止远程地址解析到私网、链路本地、共享地址、文档/基准测试地址和特殊网段, 并固定首次校验得到的 IP,降低 SSRF 与 DNS 重绑定风险;
- loopback 主机必须只解析到 loopback IP,避免被污染的主机解析把本地配置导向远端;
- HTTP Bearer Token 通过
IIntegrationCredentialResolver按当前IntegrationContext解析,不进入 Options、配置文件或日志; - stdio 命令必须是白名单中的绝对路径,不通过 shell 拼接参数;
- stdio 子进程不继承完整父进程环境,只传官方安全基础环境和显式凭据字段;
- 服务端不扫描程序集、不附带示例工具,只有显式调用
WithTools<T>()的类型会被公开; - HTTP 服务端自动加入 MCP 授权过滤器,但宿主仍必须为
MapMcp()端点配置认证授权。
AllowPrivateNetwork = true 只应对经过审批的企业内网 MCP 服务开启;它不会关闭
主机白名单、HTTPS、禁重定向或 DNS 固定。客户端当前提供
Bearer Token;OAuth 动态授权、mTLS 或企业代理由宿主使用官方 MCP SDK 定制传输。
HTTP 客户端
注册
using Bitzsoft.Integrations.McpServer;
using Bitzsoft.Integrations.McpServer.Models;
// 命名客户端配置只保存非敏感连接策略。
services.AddBitzMcpHttpClient("knowledge", options =>
{
options.Endpoint = new Uri("https://mcp.example.com/mcp");
options.ProviderId = "knowledge";
options.CredentialName = "production";
options.AllowedHosts.Add("mcp.example.com");
options.AllowedTools.Add("search_knowledge");
options.AllowedTools.Add("read_document");
options.RequireAuthentication = true;
});
生产宿主应实现 IIntegrationCredentialResolver 并连接 Vault、KMS 或云密钥服务。
开发测试可以显式使用进程内 Store:
// 仅开发/测试:生产环境替换为 Vault、KMS 或云密钥服务。
services.AddInMemoryIntegrationCredentials();
// 凭据键必须与实际调用时的 IntegrationContext 完全匹配。
var context = new IntegrationContext(
tenantId: "tenant-a",
region: "cn-east",
environment: "production");
var key = IntegrationCredentialKey.FromContext(
context,
"MCP:knowledge",
"production");
using var credential = new IntegrationCredential(
new Dictionary<string, string>
{
[McpCredentialFieldNames.BearerToken] = developmentToken
});
credentialStore.Set(key, credential);
不要把真实 Token 放入 appsettings.json、环境映射 Options 或日志。进程内 Store 不是
生产密钥服务。
连接并交给 Agent
每次操作必须先设置租户上下文。McpClientConnection 持有底层会话,工具仍依赖该会话,
因此必须在 Agent 完成调用后再释放连接。
// 当前租户上下文是凭据解析的强制输入。
using var contextScope = contextAccessor.Push(
new IntegrationContext("tenant-a", correlationId: traceId));
await using var connection = await mcpClientFactory.ConnectHttpAsync(
"knowledge",
cancellationToken);
// AgentTools 已转换为 Agent 构造函数需要的 IList<AITool>。
IList<AITool> tools = connection.AgentTools;
推荐按请求或一次 Agent Run 创建连接,不要跨租户缓存连接或工具。白名单中有工具未被 远端公开时连接会失败,避免服务端能力漂移被静默忽略。
完整 HTTP Client + Agent 示例
以下示例使用开发 Token,并确保 MCP 连接覆盖整个 Agent Run:
using Bitzsoft.Integrations.AI;
using Bitzsoft.Integrations.Core;
using Bitzsoft.Integrations.McpServer;
using Bitzsoft.Integrations.McpServer.Models;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.DependencyInjection;
var services = new ServiceCollection();
// 仅开发/测试;生产环境注册安全凭据解析器。
services.AddInMemoryIntegrationCredentials();
// 注册租户感知模型;模型密钥同样不进入 Options。
services.AddBitzChatClient(options =>
{
options.ProviderId = "deepseek-dev";
options.ProfileId = "deepseek";
options.ModelId = "deepseek-chat";
});
services.AddBitzMcpHttpClient("knowledge", options =>
{
options.Endpoint = new Uri("https://mcp.example.com/mcp");
options.ProviderId = "knowledge";
options.CredentialName = "development";
options.AllowedHosts.Add("mcp.example.com");
options.AllowedTools.Add("search_knowledge");
options.RequireAuthentication = true;
});
await using var provider = services.BuildServiceProvider(
new ServiceProviderOptions { ValidateScopes = true });
// 为 tenant-a 写入 MCP:knowledge 的开发 Bearer Token。
var credentialStore = provider.GetRequiredService<
InMemoryIntegrationCredentialStore>();
// 写入模型凭据 AI:deepseek-dev。
using (var credential = new IntegrationCredential(
[
new(AICredentialFieldNames.ApiKey, "<开发环境模型 API Key>")
]))
{
credentialStore.Set(
new IntegrationCredentialKey(
tenantId: "tenant-a",
providerKey: "AI:deepseek-dev",
environment: IntegrationEnvironments.Development),
credential);
}
// 写入 MCP 凭据 MCP:knowledge。
using (var credential = new IntegrationCredential(
[
new(McpCredentialFieldNames.BearerToken, "<开发环境 Bearer Token>")
]))
{
credentialStore.Set(
new IntegrationCredentialKey(
tenantId: "tenant-a",
providerKey: "MCP:knowledge",
environment: IntegrationEnvironments.Development,
credentialName: "development"),
credential);
}
using var scope = provider.CreateScope();
var contextAccessor = scope.ServiceProvider.GetRequiredService<
IIntegrationContextAccessor>();
using (contextAccessor.Push(new IntegrationContext(
tenantId: "tenant-a",
environment: IntegrationEnvironments.Development)))
{
var factory = scope.ServiceProvider.GetRequiredService<
IMcpClientFactory>();
// connection 必须比 Agent 和它持有的 MCP tools 活得更久。
await using var connection = await factory.ConnectHttpAsync("knowledge");
var chatClient = scope.ServiceProvider.GetRequiredService<IChatClient>();
var innerAgent = new ChatClientAgent(
chatClient,
instructions: "只能使用已批准的只读知识工具。",
name: "knowledge-agent",
description: "查询租户知识库",
tools: connection.AgentTools);
// 即使当前白名单只有只读工具,也保留官方审批中间件。
AIAgent agent = new ToolApprovalAgent(innerAgent);
var response = await agent.RunAsync("查询差旅报销标准。");
Console.WriteLine(response.Text);
}
stdio 客户端
stdio 适合本机受控工具。不要允许用户输入决定 Command 或拼接 Arguments。
// Command 和白名单都必须是管理员部署的绝对路径。
services.AddBitzMcpStdioClient("local-files", options =>
{
options.Command = "/opt/company/mcp/files-server";
options.AllowedExecutablePaths.Add("/opt/company/mcp/files-server");
options.Arguments.Add("--read-only");
// 工作目录必须是部署时已存在的绝对路径。
options.WorkingDirectory = "/srv/approved-documents";
options.AllowedTools.Add("read_approved_file");
options.ProviderId = "local-files";
options.RequireCredential = true;
// 这里只保存“环境变量名 -> 凭据字段名”,不保存 Token 值。
options.CredentialEnvironmentVariables["FILES_ACCESS_TOKEN"] =
"AccessToken";
});
CredentialEnvironmentVariables 的 key 是子进程环境变量名,value 是凭据 Store 中的
字段名,字典本身不保存凭据值。若服务不需要凭据,保持 RequireCredential = false
并让映射为空。
// stdio 进程也按当前租户解析凭据;不要跨请求缓存 connection。
using var contextScope = contextAccessor.Push(
new IntegrationContext("tenant-a"));
await using var connection = await mcpClientFactory.ConnectStdioAsync(
"local-files",
cancellationToken);
Streamable HTTP 服务端
工具必须显式注册。工具方法应再次执行租户、资源和业务权限检查,不能把“模型决定调用” 视为授权。
using System.ComponentModel;
using Microsoft.AspNetCore.Authorization;
using ModelContextProtocol.Server;
[McpServerToolType]
public sealed class KnowledgeTools(IKnowledgeService knowledge)
{
[McpServerTool(Name = "search_knowledge")]
[Description("Search approved tenant knowledge.")]
[Authorize(Policy = "McpKnowledgeRead")]
public Task<SearchResult> SearchAsync(
string query,
CancellationToken cancellationToken)
// 业务服务内部仍需校验当前租户、用户和资源权限。
=> knowledge.SearchAsync(query, cancellationToken);
}
// 只显式公开 KnowledgeTools,不扫描程序集。
builder.Services
.AddBitzMcpHttpServer(options =>
{
options.ServerName = "company-knowledge";
options.ServerVersion = "1.0.0";
options.Instructions = "Read-only access to approved knowledge.";
})
.WithTools<KnowledgeTools>();
var app = builder.Build();
// MCP 端点必须同时配置认证和授权策略。
app.MapMcp("/mcp").RequireAuthorization("McpClient");
生产端点还应配置 TLS、受众和 issuer 校验、速率限制、审计、超时、请求体限制以及网关 级防护。对有副作用的工具采用细粒度授权、幂等键和人工审批,不要只依赖自然语言指令。
stdio 服务端
// stdio 服务端只公开显式工具,标准输出保留给 MCP 协议。
services
.AddBitzMcpStdioServer(options =>
{
options.ServerName = "company-local-tools";
options.ServerVersion = "1.0.0";
})
.WithTools<ApprovedLocalTools>();
stdio 服务端是父进程控制的单会话服务。标准输出只能写 MCP 协议消息,应用日志应写入 标准错误或其他 Sink。
配置参考
McpHttpClientOptions
| 属性 | 默认值 | 说明 |
|---|---|---|
Endpoint |
https://localhost/mcp |
MCP 绝对地址 |
ProviderId |
default |
凭据 Provider 标识,最终键为 MCP:{ProviderId} |
CredentialName |
default |
凭据名称 |
RequireAuthentication |
true |
是否解析 Bearer Token |
AllowedHosts |
空 | 允许的 DNS 主机精确白名单 |
AllowPrivateNetwork |
false |
是否允许非回环私网 |
AllowedTools |
空 | 可交给模型使用的工具精确白名单 |
TransportMode |
AutoDetect |
Streamable HTTP/SSE 协商方式 |
ConnectionTimeout |
30 秒 | 初始连接超时 |
ClientName / ClientVersion |
Bitzsoft / 1.0.0 | MCP 握手客户端身份 |
McpStdioClientOptions
| 属性 | 默认值 | 说明 |
|---|---|---|
Command |
空 | 可执行文件绝对路径 |
AllowedExecutablePaths |
空 | 可执行文件绝对路径白名单 |
Arguments |
空 | 独立进程参数 |
WorkingDirectory |
空 | 可选绝对工作目录 |
AllowedTools |
空 | 工具精确白名单 |
CredentialEnvironmentVariables |
空 | 环境变量名到凭据字段名映射 |
RequireCredential |
false |
是否要求凭据快照 |
ShutdownTimeout |
5 秒 | 子进程退出等待时间 |
与 Agent Framework 的边界
本包负责 MCP 传输、凭据、端点和工具最小权限;Bitzsoft.Integrations.AgentFramework
负责 Agent 组装与会话。工具调用审批、业务授权、幂等和审计属于宿主业务边界。MCP
连接不能比其所属 AgentSession 更早释放。
常见错误
- 把远端返回的全部工具直接交给模型;
- 在配置文件中保存
Authorization或 API Key; - 对 HTTP 服务调用
WithToolsFromAssembly(); - 允许任意
node、npx、bash或用户输入作为 stdio 命令; - stdio 子进程继承父进程全部环境变量;
- 跨租户复用
McpClientConnection; - 仅靠 Agent Instructions 限制删除、付款、发信等副作用。
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Bitzsoft.Integrations.Core (>= 1.0.0-alpha.10)
- ModelContextProtocol (>= 1.4.1)
- ModelContextProtocol.AspNetCore (>= 1.4.1)
-
net8.0
- Bitzsoft.Integrations.Core (>= 1.0.0-alpha.10)
- ModelContextProtocol (>= 1.4.1)
- ModelContextProtocol.AspNetCore (>= 1.4.1)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Bitzsoft.Integrations.McpServer:
| Package | Downloads |
|---|---|
|
Bitzsoft.Integrations.All
Bitzsoft 第三方集成聚合包 — 包含全部 Integration 模块 |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0-alpha.10 | 0 | 7/26/2026 |
| 1.0.0-alpha.9 | 56 | 7/12/2026 |
| 1.0.0-alpha.8 | 62 | 7/1/2026 |
| 1.0.0-alpha.7 | 81 | 6/16/2026 |
| 1.0.0-alpha.6 | 71 | 6/16/2026 |
| 1.0.0-alpha.5 | 69 | 6/14/2026 |
| 1.0.0-alpha.3 | 66 | 6/7/2026 |
| 1.0.0-alpha.2 | 69 | 5/29/2026 |
| 1.0.0-alpha.1 | 64 | 5/28/2026 |