MCP(Model Context Protocol)是一种用于AI应用与后端服务通信的标准化协议。它定义了主机(Host)、客户端(Client)和服务端(Server)三种核心角色:
协议支持多种传输方式,包括标准输入输出(stdio)和服务器发送事件(SSE)。其中stdio适用于本地进程间通信,SSE则用于基于HTTP的远程实时通信。
关键设计原则:MCP采用松耦合架构,服务端功能与传输机制分离,使得同一套业务逻辑可以适配不同通信场景。
Stdio传输通过子进程方式实现本地通信,典型实现流程:
csharp复制builder.Services.AddMcpServer()
.WithStdioServerTransport()
.WithTools();
csharp复制var clientTransport = new StdioClientTransport(new() {
Command = "path/to/server.exe"
});
await using var mcpClient = await McpClientFactory.CreateAsync(clientTransport);
底层原理是客户端通过Process.Start()启动服务端进程,双方通过标准输入输出流交换JSON-RPC消息,每条消息以换行符分隔。
SSE传输基于HTTP长连接,服务端需要提供两个端点:
/sse (GET):建立事件流连接/messages (POST):接收客户端请求服务端配置示例:
csharp复制builder.Services.AddMcpServer()
.WithHttpTransport()
.WithTools();
客户端连接方式:
csharp复制var transport = new SseClientTransport(new() {
Endpoint = new Uri("http://localhost:5000/sse")
});
性能对比:Stdio延迟约3-5ms,SSE延迟约50-100ms。高并发场景建议使用SSE,本地工具推荐Stdio。
工具方法需使用特定注解:
csharp复制[McpServerToolType]
public class EchoTool {
[McpServerTool, Description("返回输入字符串")]
public static string Echo(string message) => $"Hello {message}";
}
注解说明:
[McpServerToolType]:标识工具类[McpServerTool]:标识工具方法[Description]:提供工具说明工具方法支持参数注入:
csharp复制[McpServerTool]
public static string Echo(ILogger logger, string message) {
logger.LogInformation("Processing: {msg}", message);
return message;
}
服务注册:
csharp复制builder.Services.AddScoped<ILogger>(sp =>
sp.GetRequiredService<ILoggerFactory>().CreateLogger("Tools"));
json复制{
"McpServers": {
"amap": {
"url": "https://mcp.amap.com/sse?key=您的KEY"
}
}
}
csharp复制var transport = new SseClientTransport(new() {
Endpoint = new Uri(config["McpServers:amap:url"])
});
var client = await McpClientFactory.CreateAsync(transport);
var tools = await client.ListToolsAsync();
// 转换为Semantic Kernel函数
var kernel = Kernel.CreateBuilder()
.AddAzureOpenAIChatCompletion(...)
.Build();
kernel.Plugins.AddFromFunctions("amap",
tools.Select(t => t.AsKernelFunction()));
高德提供的主要工具:
maps_direction_driving:驾车路线规划maps_geo:地理编码maps_weather:天气查询调用示例:
csharp复制var result = await kernel.InvokeAsync("amap.maps_direction_driving", new() {
["origin"] = "北京西站",
["destination"] = "颐和园"
});
资源URI格式:
code复制[协议]://[主机]/[路径]
例如:
db://production/customers
file://docs/report.pdf
静态资源注册:
csharp复制.WithListResourcesHandler(async (ctx, ct) => {
return new ListResourcesResult {
Resources = new[] {
new Resource {
Uri = "file://docs/README.md",
MimeType = "text/markdown"
}
}
};
})
动态资源模板:
csharp复制.WithListResourceTemplatesHandler(async (ctx, ct) => {
return new ListResourceTemplatesResult {
ResourceTemplates = new[] {
new ResourceTemplate {
UriTemplate = "db://{database}/{table}"
}
}
};
})
读取资源内容:
csharp复制var resource = await client.ReadResourceAsync("file://docs/README.md");
if (resource.Contents[0] is TextResourceContents text) {
Console.WriteLine(text.Text);
}
对于SSE连接,建议实现连接池:
csharp复制public class SseConnectionPool {
private readonly ConcurrentDictionary<string, Lazy<SseClientTransport>> _connections;
public async Task<McpClient> GetClientAsync(string endpoint) {
var transport = _connections.GetOrAdd(endpoint,
e => new Lazy<SseClientTransport>(() => new SseClientTransport(...)));
return await McpClientFactory.CreateAsync(transport.Value);
}
}
工具元数据缓存:
csharp复制private static readonly ConcurrentDictionary<string, McpClientTool> _toolCache;
async Task<IList<McpClientTool>> GetToolsAsync(McpClient client) {
return await _toolCache.GetOrAddAsync(client.Id,
async _ => await client.ListToolsAsync());
}
| 错误类型 | 状态码 | 处理建议 |
|---|---|---|
| 传输错误 | 5xx | 重试或切换传输方式 |
| 协议错误 | 400 | 检查消息格式 |
| 工具错误 | 422 | 验证输入参数 |
指数退避重试示例:
csharp复制var policy = Policy<HttpResponseMessage>
.HandleResult(r => !r.IsSuccessStatusCode)
.WaitAndRetryAsync(3, attempt =>
TimeSpan.FromSeconds(Math.Pow(2, attempt)));
JWT认证示例:
csharp复制builder.Services.AddMcpServer()
.WithHttpTransport(o => {
o.AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme;
});
工具参数验证:
csharp复制[McpServerTool]
public static string Search(string keyword) {
if (string.IsNullOrWhiteSpace(keyword) || keyword.Length > 100)
throw new ArgumentException("无效关键词");
// ...
}
关键监控指标:
Serilog配置示例:
csharp复制builder.Logging.AddSerilog(new LoggerConfiguration()
.Enrich.WithProperty("Protocol", "MCP")
.WriteTo.Console(outputTemplate: "{Timestamp} [{Level}] {Protocol} {Message}")
.CreateLogger());
启用调试日志:
csharp复制builder.Logging.AddConsole(options => {
options.LogToStandardErrorThreshold = LogLevel.Trace;
});
工具测试示例:
csharp复制[Fact]
public async Task EchoTool_ReturnsFormattedMessage() {
var tool = new EchoTool();
var result = tool.Echo("test");
Assert.Equal("Hello test", result);
}
在实际项目中使用MCP协议时,建议从简单工具开始逐步扩展,特别注意不同传输方式的适用场景。对于需要高频调用的工具,推荐使用stdio传输;需要远程访问的场景则选择SSE。高德地图集成案例展示了如何将现有API快速转换为MCP工具,这种模式可以复用到其他第三方服务集成中。