这篇适合谁、读完能做到什么
适合要把多个模型、多个 MCP 服务或多个地区的工具接在一起的开发者。读完你能分清三类路由(按内容、按负载、按工具上下文)各自解决什么问题,看懂示例里的分类和选择逻辑,并知道哪些地方只是占位,不能直接上线。最后,你应当能为自己的系统画出一张简单的路由图,标出每个判断点依据什么做决定。
路由要解决什么问题
路由是把请求送到最合适的模型或服务上的过程。判断依据可以是内容类型、用户上下文和系统负载[1]。落到 MCP 里,大致分三个层面:按内容选专用服务,按负载在多个节点之间分配,按上下文决定工具调用去哪个端点[1]。
三种路由经常同时存在。一个请求可能先被归为代码类,再由负载均衡选出代码节点,它调用的工具又可能因为地区不同走向不同端点。每一层各自依据什么判断,排查问题时才知道该看哪里。
基于内容的路由:先分类,再转发
内容路由的思路很直接:代码生成类的请求送到代码模型,创意写作类的请求送到创作模型[1]。示例用 C# 实现,核心是一个字典,键是类别,即 code、creative、scientific 和 general,值是对应的 MCP 客户端[1]。
分类器是关键词匹配。示例先把提示词转成小写:包含 code、function、program 或 algorithm,归为 code;包含 story、creative、imagine 或 design,归为 creative;包含 science、research、analyze 或 study,归为 scientific。都不匹配时归为 general[1]。路由方法本身很短:
public async Task<McpResponse> RouteAndProcessAsync(string prompt, IDictionary<string, object> parameters = null)
{
// Classify the prompt to determine the best specialized service
string category = await _classifier.ClassifyPromptAsync(prompt);
// Get the appropriate client or fall back to general
var client = _specializedClients.ContainsKey(category)
? _specializedClients[category]
: _specializedClients["general"];
Console.WriteLine($"Routing request to {category} specialized service");
// Send request to the selected service
return await client.SendPromptAsync(prompt, parameters);
}关键词分类很粗。提示词里只要出现 design 这个词,就会被送进创作模型,哪怕它讨论的是工程设计。上线前,关键词只适合做兜底,主分类最好换成可靠的模型或规则。另外,判断顺序是固定的,同时含有 program 和 story 的提示词会先命中 code 类。关键词写法的好处是一眼能看懂、容易调试,适合在原型阶段先验证分类思路,再换成更稳的方法。
负载均衡:三种策略和示例里的细节
Java 示例实现了三种策略:轮询(round robin)、加权响应时间(weighted response time)和内容感知(content-aware)[1]。
轮询最简单。示例先筛出健康节点,再用原子计数器对健康节点数取模,依次分配请求[1]:
public static class RoundRobinStrategy implements LoadBalancingStrategy {
private AtomicInteger counter = new AtomicInteger(0);
@Override
public McpServerNode selectNode(List<McpServerNode> nodes, McpRequest request) {
List<McpServerNode> healthyNodes = nodes.stream()
.filter(McpServerNode::isHealthy)
.collect(Collectors.toList());
if (healthyNodes.isEmpty()) {
throw new RuntimeException("No healthy nodes available");
}
int index = counter.getAndIncrement() % healthyNodes.size();
return healthyNodes.get(index);
}
}响应时间策略挑选健康节点中平均响应时间最短的那个[1]。名字里有「加权」,代码里却没有计算任何权重,直接取最小值。如果你需要真正的加权分配,要自己实现。
内容感知策略先判断请求的性质:提示词含有 code,或者允许的工具里有 codeInterpreter,视为代码请求;提示词含有 creative 或 story,视为创作请求。命中后选择专长(specialization)匹配的健康节点;找不到,就选当前负载最低的健康节点[1]。
节点出错时的处理也值得看。负载均衡器会对出错的节点调用 recordFailure() 做标记,然后从剩下的节点里选一个再试一次。没有剩余节点时,抛出 "All MCP server nodes failed to process the request"[1]。注意这里只做一次兜底,不是循环重试直到成功。
健康检查由 startHealthChecks 完成。它在 ScheduledExecutorService 上按固定间隔检查每个节点,检查抛出异常的节点会被标记为不健康[1]。
健康检查和负载均衡要配合看。检查间隔太长,节点已经挂掉却还被当作健康,请求就会先失败一轮才被兜底;间隔太短,又会给节点增加无用的探测负载。间隔没有标准答案,要按节点的恢复速度自己定。
工具路由:按地区、按版本
工具调用同样需要路由。天气工具可能要按用户所在地区切换到区域端点,计算器工具可能要固定在某个 API 版本上[1]。Python 示例的 McpToolRouter 类用三张表保存这些信息:基础工具端点、区域端点和版本端点[1]。
入口方法很简单,先选端点,再发请求:
async def route_tool_request(self, tool_name, parameters, user_context=None):
"""Route a tool request to the appropriate endpoint based on context"""
endpoint = self._select_endpoint(tool_name, parameters, user_context)
if not endpoint:
raise ValueError(f"No endpoint available for tool: {tool_name}")
# Perform the actual request to the selected endpoint
return await self._execute_tool_request(endpoint, tool_name, parameters)选择端点的逻辑分几步。第一步取基础端点。第二步读取参数里的 _version,没有则使用该工具的默认版本,天气工具的默认版本是 v2[1]。第三步看用户地区,如果该地区的区域表里有这个工具,就使用区域端点[1]:
# Check for regional routing if user region is known
if user_context and "region" in user_context:
user_region = user_context["region"]
if user_region in self.regional_endpoints:
regional_tools = self.regional_endpoints[user_region]
if tool_name in regional_tools:
# Use region-specific endpoint
return regional_tools[tool_name]这一步有一个容易漏掉的问题。只要地区命中,函数就直接返回区域端点,前面选定的版本被忽略了[1]。也就是说,同时带了地区和版本的请求,拿到的可能不是它要的版本。上线前应当补一条测试,明确这种组合的预期结果。
数据驻留(data residency)和延迟敏感(latency-sensitive)两个分支只有注释和 pass,什么都不做[1]。它们是占位,不是已经实现的功能,不要因为代码里有这两个名字就以为它们生效了。
调试这一段时,建议把每一步选出的端点都打印出来,而不是只看最终结果。这样才能看出是哪一步改写了端点。
执行部分用 aiohttp 向所选端点发送 JSON,内容包含 toolName 和 parameters。状态码不是 200 时抛出异常[1]。错误路径只是打印并重新抛出,注释提醒要自己加上重试或兜底逻辑[1]。
读代码时要注意的几点
- 三个示例都是草图。端点是占位域名,分类靠关键词,健康检查只是打印结果。它们说明结构,不适合直接部署。
- 架构图把三类路由串在一条链路上:请求先经过路由器和分析器,再由负载均衡器从服务池中选节点;模型发出的工具调用经过工具路由器,访问主工具注册表或区域工具注册表[1]。
- 这里的「采样」指按请求内容分类,它和 MCP 规范里的 sampling 功能不是一回事。读规范时要把两者分开看。
常见问题
- 请求总是落到 general:先检查关键词表,再看提示词里有没有表外的说法。关键词覆盖不全时,general 会吃掉大部分请求。
- 所有节点都失败:Java 示例只在剩余节点中兜底一次,节点全部出错时会抛出上面那条异常。这时要看的是节点本身,而不是路由逻辑。
- 健康检查误判:节点其实正常,却被标记为不健康。示例只在检查抛出异常时才标记,超时和慢响应要由检查方法自己处理,否则节点会一直被当作健康。
- 指定的工具版本没生效:多半是请求同时带了地区,被前面说的区域分支覆盖了[1]。
下一步
采样(sampling)是下一个值得读的主题。协议层面的规则以 MCP 2026-07-28 规范为准,见脚注[2]。
本文改编自 Microsoft 开源课程《MCP for Beginners》中关于请求路由的高级主题[1],原文以 MIT 许可证发布。
来源
- 1Routing in Model Context ProtocolMicrosoft mcp-for-beginners (MIT)
- 2MCP Specification 2026-07-28Model Context Protocol
引用本页: MCP 请求路由. TellWorld, https://www.tellworld.net/zh/practice/practice-mcp-routing. 更新于 2026-11-14