MCP插件。网
  
   
概述
MCP插件。网 是一个全面的集成解决方案。NET应用程序 模型上下文协议(MCP)。它允许您轻松地从中公开方法和数据。NET应用程序 工具, 提示,以及 资源 AI助手(如Claude)和其他MCP客户端。
问题:独立生命周期
标准MCP服务器通常设计为由客户端作为子进程启动(例如,Claude Desktop生成Python脚本)。这对于轻量级脚本来说效果很好,但对于复杂的脚本来说却带来了挑战。NET应用程序,如 Unity引擎, WPF桌面应用程序,或 游戏服务器:
- 重型启动:这些应用程序通常太重,无法由MCP客户端重复生成。
- 独立生命周期:它们通常需要独立运行(例如,您已经在Unity编辑器中工作)。
- 现场环境:您想与 *当前正在运行* 实例(例如,“将多维数据集添加到当前场景”),而不是启动一个新的空实例。
解决方案:桥梁模式
该项目通过使用 桥梁建筑:
- McpPlugin(应用内):您添加到您的库中的轻量级库。NET应用程序(例如Unity、WPF、Console)。它通过以下方式连接到桥上 信号员.
- McpPlugin。服务器(网桥):MCP客户端(Claude)启动的高性能网关。它充当了一个持久的调解人。
为什么使用ViewModel?
- 韧性:内置自动重新连接逻辑。如果网桥重新启动,您的应用程序将立即重新连接。
- 简洁:通过单个HTTP端口操作(默认
8080).没有复杂的防火墙规则。 - 双向:网桥可以调用应用程序中的工具,应用程序可以将更新(日志、进度)推回到网桥。
建筑
该系统采用中心辐射式架构,其中 McpPlugin.Server 充当中央网关。
graph LR
subgraph "Your .NET Apps (SignalR Clients)"
A[Unity Editor] -- SignalR --> S
B[WPF Desktop] -- SignalR --> S
E[Game Server] -- SignalR --> S
end
subgraph "MCP Infrastructure (Bridge)"
S[McpPlugin.Server]
end
subgraph "AI / MCP Clients"
C[Claude Desktop] -- StdIO/HTTP --> S
D[MCP Inspector] -- StdIO/HTTP --> S
end特性
- 基于属性的注册:使用以下工具轻松公开工具、提示和资源
[McpPluginTool],[McpPluginPrompt],以及[McpPluginResource]属性。 - 由ReflectorNet提供动力:
- 复杂类型支持:无缝处理工具参数中的嵌套对象、集合和自定义类型。 - 模糊匹配:AI可以查找和调用方法,即使是部分名称或稍微不匹配的签名。 - 自动模式生成:为您的C#类型生成精确的JSON模式,以帮助LLM完美理解您的代码。
- 实时双向通信:使用ViewModel在您的应用程序和网桥之间建立持久、低延迟的链接。
- 柔性运输:这座桥支撑着两者
stdio(适用于Claude Desktop等本地AI代理)和http(用于远程连接)。 - 依赖注入:一流的支持
Microsoft.Extensions.DependencyInjection. - 装配扫描:自动发现并注册整个项目中的组件。
通信协议(ViewModel)
这种架构的一个关键特征是使用 信号员 用于您的应用程序之间的连接(McpPlugin)还有那座桥(McpPlugin.Server).
- 单端口:所有通信都通过一个明确配置的HTTP端口进行(默认值:
8080).不需要复杂的防火墙规则或多个套接字连接。 - 双向ViewModel提供了一个持久、实时、双向的通道。服务器可以调用客户端上的工具,客户端可以向服务器发送更新(如日志消息或进度)。
- 坚韧的:该插件包括内置的自动重新连接逻辑。如果服务器重新启动,您的应用程序将自动重新建立链接。
默认连接:
- 服务器:收听
http://localhost:8080 - 集线器端点:
/hub/mcp-server - 客户:连接到
http://localhost:8080/hub/mcp-server
入门指南
1.服务器(McpPlugin.Server)
服务器充当集线器。您可以运行提供的 DemoWebApp 或者将其托管在您自己的ASP中。NET核心应用程序。
运行演示服务器:
cd DemoWebApp
dotnet run port=11111 client-transport=stdio*注:使用 client-transport=stdio 如果从克劳德桌面连接,或 client-transport=http 对于基于HTTP的客户端。*
在您自己的Web应用程序中托管:
// Program.cs
using com.IvanMurzak.McpPlugin.Common;
using com.IvanMurzak.McpPlugin.Common.Utils;
using com.IvanMurzak.McpPlugin.Server;
var builder = WebApplication.CreateBuilder(args);
// 1. Prepare arguments (or load from config)
var dataArguments = new DataArguments(args);
// 2. Register MCP Server services
builder.Services
.WithMcpServer(dataArguments) // Configures transport based on dataArguments.ClientTransport
.WithMcpPluginServer(dataArguments);
// 3. Configure Kestrel with separate IPv4/IPv6 bindings (avoids dual-stack issues on macOS)
builder.WebHost.UseKestrelForMcpPlugin(dataArguments.Port);
var app = builder.Build();
// 4. Use MCP Server middleware
app.UseMcpPluginServer(dataArguments);
app.Run();2.客户端应用程序(McpPlugin)
添加 com.IvanMurzak.McpPlugin 包裹到你的。NET应用程序。
定义工具、提示和资源:
using com.IvanMurzak.McpPlugin;
using System.ComponentModel;
[McpPluginToolType]
public static class MyMcpComponents
{
// --- Tools ---
[McpPluginTool("calculate-sum", "Adds two numbers")]
[Description("Adds two numbers")]
public static int Add(int a, int b) => a + b;
// --- Prompts ---
[McpPluginPrompt("explain-code", "Explains a piece of code")]
public static string ExplainCode(string code) => $"The following code: {code} does X, Y, and Z.";
// --- Resources ---
[McpPluginResource("system-logs", "Returns the latest system logs", "logs://system")]
public static string GetLogs() => "Log entry 1: System started...";
}连接到服务器:
using com.IvanMurzak.McpPlugin;
using com.IvanMurzak.ReflectorNet;
// 1. Initialize Reflector (The core engine)
var reflector = new Reflector();
// 2. Configure and build the plugin
var version = new com.IvanMurzak.McpPlugin.Common.Version
{
Api = "1.0.0",
Plugin = "1.0.0"
};
var plugin = new McpPluginBuilder(version)
.WithConfig(config => {
config.Host = "http://localhost:11111"; // Match your server port
})
// Option A: Scan assemblies for [McpPluginTool], [McpPluginPrompt], [McpPluginResource]
.WithToolsFromAssembly(typeof(MyMcpComponents).Assembly)
.WithPromptsFromAssembly(typeof(MyMcpComponents).Assembly)
.WithResourcesFromAssembly(typeof(MyMcpComponents).Assembly)
.Build(reflector);
// 3. Connect to the MCP server
await plugin.Connect();高级功能
🧩 复杂型支架(通过ReflectorNet)
与难以处理复杂问题的标准MCP实现不同。NET类型,此插件本机处理它们。您可以将嵌套对象或集合作为工具参数传递:
public class UserProfile {
public string Name { get; set; }
public List Roles { get; set; }
}
[McpPluginTool("update-user")]
public static void UpdateUser(UserProfile profile) {
// ReflectorNet automatically deserializes the JSON from the AI into this object
}🔍 模糊匹配
您可以配置AI必须与您的方法名称匹配的严格程度。当LLM使用略有不同的术语时,这很有用:
var plugin = new McpPluginBuilder(version)
// ...
.Build(reflector);
// Configure fuzzy matching level (1-6)
// 6: Exact, 3: StartsWith (Case-Insensitive), 1: Contains (Case-Insensitive)
plugin.MethodNameMatchLevel = 3;配置参考
服务器(McpPlugin.Server)
命令行参数优先于环境变量。
| 参数 | 环境变量 | 描述 | 默认值 |
|---|---|---|---|
port | MCP_PLUGIN_PORT | SqlCohub监听的端口 | 8080 |
client-transport | MCP_PLUGIN_CLIENT_TRANSPORT | 运输方式: stdio 或 streamableHttp. | streamableHttp |
plugin-timeout | MCP_PLUGIN_CLIENT_TIMEOUT | 插件操作超时(ms)。 | 10000 |
token | MCP_PLUGIN_TOKEN | 连接插件需要承载令牌。 | *(无)* |
authorization | MCP_AUTHORIZATION | 授权方式: none 或 required. | none |
分析Webhooks
McpPlugin.Server 可以向外部端点发出即发即弃HTTP POST通知,以进行可观察性和分析。每个事件类别都有一个独立的URL,因此您可以将工具、提示、资源和连接事件路由到不同的系统。
| 参数 | 环境变量 | 描述 | 默认值 |
|---|---|---|---|
webhook-tool-url | MCP_PLUGIN_WEBHOOK_TOOL_URL | 用于接收工具调用事件的端点。 | *(无)* |
webhook-prompt-url | MCP_PLUGIN_WEBHOOK_PROMPT_URL | 接收提示检索事件的端点。 | *(无)* |
webhook-resource-url | MCP_PLUGIN_WEBHOOK_RESOURCE_URL | 用于接收资源访问事件的终结点。 | *(无)* |
webhook-connection-url | MCP_PLUGIN_WEBHOOK_CONNECTION_URL | 用于接收客户端连接/断开连接事件的终结点。 | *(无)* |
webhook-token | MCP_PLUGIN_WEBHOOK_TOKEN | 在每个webhook请求标头中发送的安全令牌。 | *(无)* |
webhook-header | MCP_PLUGIN_WEBHOOK_HEADER | 安全令牌的标头名称。 | X-Webhook-Token |
webhook-timeout | MCP_PLUGIN_WEBHOOK_TIMEOUT | HTTP传递超时(毫秒)。 | 10000 |
示例——启用工具和连接分析:
dotnet run \
client-transport=stdio \
webhook-tool-url=https://analytics.example.com/hooks/tools \
webhook-connection-url=https://analytics.example.com/hooks/connections \
webhook-token=my-secret-token事件有效载荷结构 (所有事件均遵循此信封):
{
"schemaVersion": "1.0",
"eventType": "tool.call.completed",
"timestamp": "2026-03-01T12:34:56.789Z",
"data": {
"toolName": "add",
"requestSizeBytes": 42,
"responseSizeBytes": 18,
"status": "success",
"durationMs": 150
}
}支持的事件类型:
| 事件类型 | 触发器 |
|---|---|
tool.call.completed | 每次MCP工具调用(成功或失败) |
prompt.retrieved | 每次MCP提示检索 |
resource.accessed | 每个MCP资源访问 |
connection.ai-agent.connected | AI代理(MCP客户端)连接 |
connection.ai-agent.disconnected | AI代理(MCP客户端)断开连接 |
connection.plugin.connected | McpPlugin(.NET客户端)通过args连接 |
connection.plugin.disconnected | McpPlugin(.NET客户端)断开连接 |
笔记:
- Webhooks是 发射后不管 --记录交付失败,但从不阻止MCP响应。
- 如果没有配置webhook URL,则整个子系统处于非活动状态,开销为零。
- 在生产环境中使用HTTPS端点来保护传输中的安全令牌。
授权Webhooks
McpPlugin.Server 可以配置为 同步授权webhook 它控制来自MCP客户端(通过HTTP的AI代理)和McpPlugin客户端(通过ViewModel的.NET应用程序)的连接。与上面的即发即弃分析webhook不同,授权webhook会阻塞连接,直到端点响应。
| 参数 | 环境变量 | 描述 | 默认值 |
|---|---|---|---|
webhook-authorization-url | MCP_PLUGIN_WEBHOOK_AUTHORIZATION_URL | 授权/拒绝连接的端点。 | *(无)* |
webhook-authorization-fail-open | MCP_PLUGIN_WEBHOOK_AUTHORIZATION_FAIL_OPEN | 何时 true,如果webhook超时或出现错误,则允许连接。当 false否认失败。 | false |
示例--启用具有失败关闭行为的连接授权:
dotnet run \
client-transport=stdio \
webhook-authorization-url=https://auth.example.com/authorize \
webhook-token=my-secret-token \
webhook-authorization-fail-open=false请求格式 (从服务器到webhook的POST):
用于AI代理连接(authorization.ai-agent):
{
"schemaVersion": "1.0",
"eventType": "authorization.ai-agent",
"timestamp": "2025-03-04T22:45:30.1234567Z",
"connectionId": "trace-id-or-connection-id",
"clientType": "ai-agent",
"bearerToken": "",
"remoteIpAddress": "192.168.1.100",
"userAgent": "claude-ai/1.0",
"requestPath": "/mcp",
"clientName": null,
"clientVersion": null,
"hmacSignature": "sha256=abc123..."
}用于插件连接(authorization.plugin):
{
"schemaVersion": "1.0",
"eventType": "authorization.plugin",
"timestamp": "2025-03-04T22:45:30.1234567Z",
"connectionId": "trace-id-or-connection-id",
"clientType": "plugin",
"bearerToken": "",
"remoteIpAddress": null,
"userAgent": null,
"requestPath": null,
"clientName": "my-unity-plugin",
"clientVersion": "1.2.0",
"hmacSignature": "sha256=def456..."
}注: 这hmacSignature字段仅在以下情况下存在webhook-token已配置。它包含请求体的HMAC-SHA256签名(在添加签名字段之前),使用webhook令牌作为密钥计算。
预期响应格式 (从您的webhook到服务器):
{ "allowed": true }或
{ "allowed": false, "reason": "IP not in allowlist" }行为:
- 2xx响应
allowed: true→ 连接继续 - 2xx响应
allowed: false→ 连接被拒绝(原因记录为警告) - 非-2xx响应、超时或解析错误 → 连接被拒绝(除非
fail-open=true)
安全:
- 服务器在名为的标头中发送配置的安全令牌
webhook-header(默认值:X-Webhook-Token) - 您的webhook必须验证此令牌,以防止未经授权的授权请求
- 在生产环境中使用HTTPS端点
笔记:
- 授权webhooks是 同步 --服务器等待您的响应(默认超时10秒)
- 将webhook响应时间控制在1秒以内,以获得最佳性能
- 授权webhooks独立于上述分析webhooks运行
- 如果
webhook-authorization-url未配置,授权已禁用(允许所有连接)
插件(McpPlugin)
命令行参数和环境变量通过以下方式自动解析 ConnectionConfig.BuildFromArgsOrEnv()它们也可以通过编程方式被覆盖 McpPluginBuilder.WithConfig(...).
| 参数 | 环境变量 | 属性 | 描述 | 默认值 |
|---|---|---|---|---|
mcp-server-endpoint | MCP_SERVER_ENDPOINT | Host | 网桥服务器的URL。 | http://localhost:8080 |
mcp-server-timeout | MCP_SERVER_TIMEOUT | TimeoutMs | 操作超时(ms)。 | 10000 |
mcp-plugin-token | MCP_PLUGIN_TOKEN | Token | 已发送到服务器进行身份验证的承载令牌。 | *(无)* |
mcp-skills-folder | MCP_SKILLS_FOLDER | SkillsPath | 生成的技能标记文件的路径。 | SKILLS |
仅程序化属性 (通过设置 WithConfig(...)):
| 属性 | 描述 | 默认值 |
|---|---|---|
KeepConnected | 如果连接丢失,则自动重新连接。 | true |
GenerateSkillFiles | 为每个注册的工具自动生成技能标记文件。 | true |
Docker支持
您可以在Docker容器中运行网桥服务器:
docker build -t mcp-bridge -f McpPlugin.Server/Dockerfile .
docker run -p 8080:8080 mcp-bridge port=8080 client-transport=http项目结构
McpPlugin:的客户端库。NET应用程序。包含管理工具、提示和资源的核心逻辑。McpPlugin.Server:将ViewModel客户端桥接到MCP协议的服务器实现。McpPlugin.Common:共享数据结构、接口和协议定义。DemoConsoleApp:演示如何公开工具的示例客户端应用程序。DemoWebApp:演示如何托管MCP网桥的示例服务器应用程序。
许可证
此项目根据Apache-2.0许可证获得许可。版权所有-伊万·穆扎克。
