AI MCP Gateway-tools/list 能力
一句话先概括
这一节我做的是 MCP 协议里的 tools/list 能力,把原来写死返回的工具定义,改造成基于数据库配置动态生成工具列表和参数 Schema。
如果面试官问:你具体解决了什么问题?
在 MCP 场景里,客户端连接服务端后,通常会先调用 tools/list 获取当前服务端支持哪些工具、每个工具叫什么、用途是什么、入参长什么样。
前一个版本的问题是,tools/list 返回的是硬编码的 demo 数据。这样虽然能跑通流程,但不具备可扩展性。只要换一个网关、换一个工具、换一套参数结构,就得改代码。
所以这节的目标就是把“工具定义”从代码里抽出来,放到数据库配置里,由系统运行时动态生成返回结果。这样新增工具时主要改配置,不需要改 ToolsListHandler 的业务代码。
如果面试官问:你的整体设计思路是什么?
我的设计思路是“配置驱动 + 递归组装”。
系统里已经有两类数据:
- 网关配置:这个网关对应哪个工具、工具名称、工具描述是什么
- 协议映射配置:工具的每个字段如何定义,字段类型、父子层级、是否必填、排序等
我做的事就是把这两类配置读取出来,然后动态组装成 MCP 标准要求的 tools 结构,重点是生成 inputSchema。
整个链路是:
ToolsListHandler收到tools/list请求- 根据
gatewayId查询网关配置 - 查询该网关下的工具映射配置列表
- 按
toolId把多条字段配置分组成一个个工具 - 再按
parentPath组装字段层级关系 - 递归生成 JSON Schema
- 最终返回标准的
tools数组
如果面试官问:为什么需要按 parentPath 组织?
因为工具参数不一定是平铺结构,很多场景是嵌套对象。
比如一个请求可能是:
xxxRequest01xxxRequest01.cityxxxRequest01.companyxxxRequest01.company.namexxxRequest01.company.type
如果只存平铺字段,是没法直接还原成 JSON Schema 的树结构的。所以我在配置表里用了:
fieldName表示当前字段名mcpPath表示完整路径parentPath表示父节点路径
这样就能把所有字段先读成平铺记录,再通过 parentPath 还原成树,最后递归生成:
typepropertiesrequireddescription
这套结构对于嵌套对象很适合,也方便以后扩展数组或更复杂的 schema。
如果面试官问:核心代码在哪,怎么实现的?
核心就在 ToolsListHandler.java。
它做了两件关键事:
第一件是 buildTools。
它负责把数据库返回的配置列表按 toolId 分组,每一组代表一个工具。然后把每个工具的根节点和子节点分开处理,构建出最外层 inputSchema。
第二件是 buildProperty。
这个方法是递归的。它拿到当前字段后,会先写当前字段的 type、description,再去看它有没有子字段。如果有,就继续递归构建 properties,同时收集哪些字段是必填,生成 required。
也就是说,整个 schema 的构建本质上是一棵树的递归遍历。
如果面试官问:仓储层做了什么?
为了让领域层不直接依赖 DAO 细节,我在仓储接口 ISessionRepository.java 里新增了按网关查询工具配置的方法。
在实现类 SessionRepository.java 里:
- 调用协议映射 DAO 查询配置
- 把 PO 转成领域值对象
McpGatewayToolConfigVO
这样领域服务只关心“工具配置长什么样”,不关心底层表结构怎么查。
如果面试官问:协议模型为什么也要改?
因为之前 McpSchemaVO 对 tools/list 的建模还不完整,所以我在 McpSchemaVO.java 里补了三个结构:
ToolJsonSchemaListToolsResult
这样返回值不是临时拼 Map,而是有明确协议语义的对象结构,后续维护和扩展更清晰。
如果面试官问:数据库层做了哪些支撑?
我在 DAO 接口 IMcpProtocolMappingDao.java 和 MyBatis 映射 mcp_protocol_mapping_mapper.xml 里新增了一个查询:
queryMcpGatewayToolConfigList
这个查询会按 gateway_id 取出协议映射配置,并按 sort_order 排序。排序很重要,因为同级字段展示顺序、生成 schema 的顺序都依赖这个字段。
另外 SQL 示例文件 ai_mcp_gateway.sql 也补了 gateway_001 的示例映射数据,方便本地验证。
如果面试官问:这一版实现的价值是什么?
主要有三个价值。
第一,去硬编码。
新增工具不需要再改 ToolsListHandler 的返回内容,改配置即可。
第二,支持复杂参数结构。
不只支持简单字符串参数,也支持对象嵌套对象。
第三,为下一步 tool call 做准备。
因为 tools/list 本质上是在告诉客户端“你可以怎么调用我”。只有先把工具定义和参数 schema 动态化,后面的工具调用才能真正通用化。
如果面试官追问:这版实现还有哪些局限?
我会主动指出几个点,这样会更像真实工程回答。
- 当前工具名称和描述的获取逻辑还有收敛空间。代码里是通过网关配置和
toolId去匹配,若一个网关下多个工具并存,后续更适合直接从工具注册表按toolId查完整信息。 - 当前主要覆盖的是对象型嵌套结构,对数组、联合类型等更复杂 schema 的支持还可以继续扩展。
- 目前测试更多是集成验证,后续可以补更细粒度的单元测试,专门验证递归组装 schema 的边界情况。
最后用一句收尾
如果让我用一句比较像面试总结的话来说:
这节我完成的是把 MCP tools/list 从“静态示例返回”升级成“基于数据库配置的动态工具发现机制”,核心实现手段是通过协议映射表描述参数层级,再在服务端递归生成 JSON Schema。
