MCP C# 在 2026-07-28 协议更新后,服务器必须从当前请求读取客户端支持的能力。现代无状态请求会在_meta里携带自己的clientCapabilities,不再有初始化握手可供连接保留值。
这带来一个 C# 里小而关键的陷阱。在无状态 HTTP 处理器内部,request.Server.ClientCapabilities被有意设为 null。权威值只存在于当前 JSON-RPC 请求上。如果缓存了上一次的值,或者把服务器属性当作现代来源,就可能为下一次调用做出错误决定。
协议变化:会话与初始化被移除
MCP 2026-07-28 版本从现代路径中移除了协议级会话和 initialize 交换。每个请求都是自包含的,这让不同的服务器实例可以处理调用,而不需要粘性路由或共享的 MCP 会话存储。
请求现在携带保留元数据,例如:
{"_meta": {"io.modelcontextprotocol/protocolVersion": "2026-07-28","io.modelcontextprotocol/clientInfo": {"name": "report-client","version": "1.0.0"},"io.modelcontextprotocol/clientCapabilities": {"extensions": {"com.example/report-export": {}}关键词是“请求”。服务器不能从更早的消息推断能力。到达同一进程的两次调用,可以合法地声明不同的扩展支持,并发处理器必须保持隔离。
旧握手模型不再适用
旧的握手模型鼓励连接级思维:协商一次,之后查询协商好的服务器对象。这种假设在无会话请求下不成立,因为请求可能落到任意实例。没有持久的“当前客户端”,其特征集可以安全地放进单例、静态字段或进程级缓存。即使一个客户端通常每次都发送相同声明,协议边界仍要把收到的信封当作该次调用的来源。
这在分阶段迁移期间也很重要。负载测试、代理或兼容客户端可以在一个服务器进程里混合不同扩展映射的现代请求。只用一个客户端看起来正确的代码,可能只在重叠时失败,因此更推荐并发回归测试,而不是单个串行示例。
示例使用了稳定的ModelContextProtocol.AspNetCore 2.2.0包。这是已发布功能,不是预览 API。
C# SDK 的读取方式
C# SDK 通过JsonRpcMessageContext.ClientCapabilities暴露解析后的值。工具可以从注入的RequestContext访问它:
public async Task InspectClientCapabilityAsync(string requestName,RequestContext request,CapabilityBarrier barrier,CancellationToken cancellationToken)await barrier.WaitForBothAsync(cancellationToken);var capabilities = request.JsonRpcRequest.Context?.ClientCap核心结论是:在无状态 MCP 请求路径里,能力判断必须绑定到当前请求上下文,而不是服务器对象或任何跨请求缓存。这样才能避免多客户端、多扩展映射并存时出现错误决策。
热门跟贴