新闻详情

新闻详情

首页 / 资讯中心 / 详情

MCP实战手记系列(六):自建 MCP 网关(文末附github源码链接)

发布时间:2026/10/1 10:30:23来源:尧图网络
MCP实战手记系列(六):自建 MCP 网关(文末附github源码链接)
MCP 实战手记系列六· 架构实操系列五把工具结果做成了一块能点的界面。可真到要接的时候问题变了我手上有两个机房跑的是同一套服务工具名一字不差。客户端连上去拿到两个get_room_dashboard模型分不清该开哪一个。这篇做个网关把它们收成一个入口代码在 tagv06。引子两个同名工具把客户端逼到没法配两个机房各跑一份系列五那套服务端口 8085 和 8087。分别tools/list一下返回长得一模一样get_room_dashboard、control_device连描述都一样。客户端要么配两个 endpoint配置散在每台机器、每个人手里要么自己想办法区分。我选第三条路前面挡一层网关8086客户端只认这一个地址。网关给出去的表长这样site-a__control_device site-a__get_room_dashboard site-b__control_device site-b__get_room_dashboard两个get_room_dashboard还在只是各自带上了机房前缀。模型看得懂调用也能落回正确的机房。一、先说什么时候不该上网关不是「有了多个 server 就该上网关」。我的判断很具体后端不超过两个、工具名全局唯一、也不需要统一鉴权和调用审计——这种情况直接配多个 endpoint别上网关。理由有两条都是这次踩出来的。网关自己会变成一个新的单点所有后端都挂在它后面而且它的工具表是启动时拉的快照后面会讲后端变了它不一定知道。为了省两行配置引入这两个麻烦不划算。真正把人逼到上网关的是命名空间撞车。几个团队各写各的 MCP Server工具名叫get_status、query、search的概率高得离谱谁也没错放一起就废了。二、网关干的其实是三件不同的事聚合排第一。启动时拿 MCP client 挨个连后端initialize之后listTools各家的工具拼成一张表。这一步决定了它做不了纯转发——tools/list的响应得合并不是一个请求转给一个人。命名空间是第二件也是这三件里最容易做漏的。给每个后端一个前缀工具名改成前缀__原名。我用双下划线看中的就是它不容易误伤工具名里几乎不会自己出现连续两个下划线。路由听着最平常。调用进来按前缀剥出后端和原始工具名转发出去后端挂了就返回isError的工具结果别拖死整个网关。顺带说一句协议版本后端握手时回的是2025-11-25。网关用的 MCP Java SDK 2.0.0 走 Streamable HTTP跟系列三的无状态配置是同一套。三、动手依赖先做个反直觉的选择前几篇一直用spring-ai-starter-mcp-server-webmvc靠McpTool声明工具。网关用不了这条路——工具是运行时从后端拉来的注解里没法写「等我连上 8085 再看有什么」。所以这一篇直接用底层 SDK网关两头都是它对上游是 server对下游是 client。dependencygroupIdio.modelcontextprotocol.sdk/groupIdartifactIdmcp-core/artifactIdversion2.0.0/version/dependency!-- JSON 映射实现SDK 通过 ServiceLoader 发现代码里不直接引用 --dependencygroupIdio.modelcontextprotocol.sdk/groupIdartifactIdmcp-json-jackson3/artifactIdversion2.0.0/version/dependency后端清单写在配置里prefix 就是命名空间mcp:gateway:backends:-name:site-aurl:http://localhost:8085prefix:site-a-name:site-burl:http://localhost:8087prefix:site-b① 建连与聚合BackendRegistry启动时跑一次vartransportHttpClientStreamableHttpTransport.builder(cfg.getUrl()).build();McpSyncClientclientMcpClient.sync(transport).requestTimeout(Duration.ofSeconds(8)).build();client.initialize();ListMcpSchema.Tooltoolsclient.listTools().tools();拼表的时候有个地方容易漏all.add(McpSchema.Tool.builder().name(backend.prefix()SEPARATORtool.name()).title(tool.title())// 漏了宿主列表页只剩裸名.description([backend.name()] tool.description()).inputSchema(tool.inputSchema()).outputSchema(tool.outputSchema())// 漏了结构化输出和界面都拿不到数据.annotations(tool.annotations()).meta(tool.meta()).build());② 路由。前缀剥掉剩下的原样转发BackendRegistry.Routerouteregistry.resolve(request.name());if(routenull){returnerror(网关不认识这个工具request.name());}try{returnroute.backend().client().callTool(newMcpSchema.CallToolRequest(route.originalName(),request.arguments()));}catch(Exceptione){// 一个后端故障不该变成网关级失败returnerror(后端 route.backend().name() 调用失败e.getMessage());}③ 对上游那一面。HttpServletStatelessServerTransport本身就是个HttpServlet注册到/mcp就行vartransportHttpServletStatelessServerTransport.builder().messageEndpoint(/mcp).build();McpServer.sync(transport).serverInfo(mcp-gateway,1.0.0).tools(specs).build();returnnewServletRegistrationBean(transport,/mcp);四、怎么证明它真的分流了只看tools/list有 4 个工具说明不了任何事——拼字符串谁都会。要证明调用真的落到了不同后端得让两边数据不一样。我通过网关只启动 site-b 的风扇然后各查一次# 只动 site-bcurl-slocalhost:8086/mcp-HContent-Type: application/json\-HAccept: application/json, text/event-stream--data-binary call-b-ctrl.json结果是 site-a 的fan-02仍是falsesite-b 的变成true。同一份代码、同一份内存 mock只有路由对了才会出现这个差异。再把 site-b 那个进程杀掉网关的行为也符合预期{content:[{type:text,text:后端 site-b 调用失败java.net.ConnectException}],isError:true}同一时刻调 site-a温湿度照常返回23.5 / 48.2。一个后端倒下另一个不受牵连。五、踩坑记录①title和outputSchema是我自己漏的代价不小。第一版拼表只复制了 name / description / inputSchema / annotations / meta。跑出来一看工具没有titleget_room_dashboard的outputSchema也没了。宿主列表页只剩一串下划线名字界面拿不到结构化数据。补两行解决但这类字段丢失不会报错只能靠对返回逐字段看。② 工具表是启动时的快照。site-b 被我杀掉之后tools/list照样列出site-b__*四个工具一个没少。网关不会去探活也不会刷新。这意味着后端的工具变更对网关基本不可见除非重启或者自己做热更新——我这篇没做。③ 后端没起来网关照样能起。启动时连不上不算致命BackendRegistry记一条 WARN 就往下走WARN BackendRegistry : 后端 site-bhttp://localhost:8087挂载失败 网关继续启动该后端工具不暴露java.lang.RuntimeException: Client failed to initialize by explicit API call好处是网关不会因一个后端挂掉而整体起不来代价是后端必须早于网关启动晚起的后端这次就进不来了。④ 资源没代理界面直接断链。这条是系列五的延续也是我觉得最值得写的一条。工具的_meta.ui.resourceUri被我原样透传指向ui://room-dashboard。宿主拿到工具就会回头找网关要这个资源而网关压根没注册 resources handlerFailed to handle request: Missing handler for request type: resources/list数据回得来界面出不来。想在网关后面保住 MCP Apps必须把resources/list和resources/read一起代理掉而ui://这个 URI 又在各后端之间重名——还得再做一层资源命名空间。这篇没做留给后面。⑤ 打包被自己锁住。网关正在跑的时候mvn packagespring-boot 插件要重写 jarWindows 上文件被占直接PluginExecutionException。先停进程再打包这个坑跟 MCP 没关系但每次都会撞上。六、还没解决什么resources 和 prompts 没代理界面和资源类工具在网关后面是残的。鉴权也没处理——系列四那套 CIMD token 怎么往下带、要不要换成网关自己的凭据我没动这是生产上绕不开的一节。工具表热更新没做后端调用耗时也没埋点网关自己还成了单点。网关把麻烦收拢到一处风险也跟着集中到一处。这笔账得自己算。小结网关真正解决的是撞名性能那点事轮不到它管。工具名撞车才是逼你上网关的原因拼表要连title/outputSchema一起复制漏字段不报错只会让界面和结构化输出悄悄失效工具表是启动时快照后端挂了它照样列、后端晚起它收不到故障隔离只做在调用这一层完整可运行代码Gitee 仓库 GitHub 镜像tag:v06目录06-mcp-gateway。启动顺序先起 8085、8087 两个后端再起网关 8086。v06目录06-mcp-gateway。启动顺序先起 8085、8087 两个后端再起网关 8086。MCP 实战手记系列路线图#篇目状态1总纲篇MCP 到哪一步了✅2跑通第一个 MCP Server✅3把 MCP Server 改成无状态✅4CIMD 授权实战✅5让工具返回一个能点的界面✅6自建 MCP 网关本篇✅7MCP 安全接入检查清单规划你们那边是几个 MCP Server 在跑工具名撞过车没有评论区说说撞得厉害的话我下一篇就写资源代理和鉴权透传。参考MCP Java SDKMCP 官方规范 · 2025-11-25MCP Apps 官方规范SEP-1865
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

数据结构全梳理:从数组链表到哈希表与复杂度分析 2026/10/1 11:10:56

数据结构全梳理:从数组链表到哈希表与复杂度分析

读过多年书,带过新人,也在线上给别人看过代码,发现一个非常有意思的现象:很多人写程序,真正卡住的不是语法,不是框架,而是“数据该怎么摆”。一次简单的查询优化,有人能把数组复制出…

阅读更多 →
数据结构入门指南:从线性表到哈希表,掌握核心原理与应用 2026/10/1 11:10:47

数据结构入门指南:从线性表到哈希表,掌握核心原理与应用

1. 数据结构是什么,为什么每个程序员都绕不开它我第一次接触数据结构这个词是在大二的数据结构课上,当时完全不明白这门课到底在讲什么。链表、栈、队列、二叉树,每一个概念都抽象得要命,考试前背了一堆定义,考完就忘。…

阅读更多 →
【共创稿事节】喵屿 Pura X Max 折叠屏适配:HarmonyOS Dev Assistant 实战全记录 2026/10/1 11:10:39

【共创稿事节】喵屿 Pura X Max 折叠屏适配:HarmonyOS Dev Assistant 实战全记录

喵屿 Pura X Max 折叠屏适配:HarmonyOS Dev Assistant 实战全记录 本文基于「喵屿」应用在 HUAWEI Pura X Max 折叠屏上的一多适配实战,结合 HarmonyOS Dev Assistant 的官方能力与全流程编排,系统梳理插件介绍、安装配置、一多适配能力、一次…

阅读更多 →
Matlab中用CNN做单输入单输出时间序列预测的完整实践指南 2026/10/1 11:10:33

Matlab中用CNN做单输入单输出时间序列预测的完整实践指南

上个月我在做一个设备振动信号的预测任务:输入过去20个采样点的振动幅值,预测下一个采样点的数值。这就是典型的单输入单输出时间序列预测——只有一个特征序列作为输入,输出也只是一个未来值。我一开始用的ARIMA,后来同时试了LST…

阅读更多 →
基于Spring Boot的自习室预订座位管理系统:从规则设计到并发实践 2026/10/1 11:10:24

基于Spring Boot的自习室预订座位管理系统:从规则设计到并发实践

1. 为什么这类系统总在“预选座”和“实际履约”之间翻车先说个我亲眼见过的场景:学校考研自习室,两百多个座位,每天早上六点半开门,五点半就有人在门口排队。有人为了占座,把复习资料往桌上一堆,一整天人都…

阅读更多 →
Node-RED低代码可视化:零Node.js基础构建工业数据看板 2026/10/1 11:10:10

Node-RED低代码可视化:零Node.js基础构建工业数据看板

1. 这不是写代码,是搭积木:为什么“拖拽可视化”能绕过Node.js门槛 “即使不会node.js,拖拽就可完成数据的可视化展示”——这句话乍看像营销话术,但背后是一套真实存在的、已被工业现场和中小团队验证数年的低代码可视化路径。它…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉