一句话先概括

这一节我做的是 MCP 协议里的 tools/list 能力,把原来写死返回的工具定义,改造成基于数据库配置动态生成工具列表和参数 Schema。

如果面试官问:你具体解决了什么问题?

在 MCP 场景里,客户端连接服务端后,通常会先调用 tools/list 获取当前服务端支持哪些工具、每个工具叫什么、用途是什么、入参长什么样。

前一个版本的问题是,tools/list 返回的是硬编码的 demo 数据。这样虽然能跑通流程,但不具备可扩展性。只要换一个网关、换一个工具、换一套参数结构,就得改代码。

所以这节的目标就是把“工具定义”从代码里抽出来,放到数据库配置里,由系统运行时动态生成返回结果。这样新增工具时主要改配置,不需要改 ToolsListHandler 的业务代码。

如果面试官问:你的整体设计思路是什么?

我的设计思路是“配置驱动 + 递归组装”。

系统里已经有两类数据:

  • 网关配置:这个网关对应哪个工具、工具名称、工具描述是什么
  • 协议映射配置:工具的每个字段如何定义,字段类型、父子层级、是否必填、排序等

我做的事就是把这两类配置读取出来,然后动态组装成 MCP 标准要求的 tools 结构,重点是生成 inputSchema

整个链路是:

  1. ToolsListHandler 收到 tools/list 请求
  2. 根据 gatewayId 查询网关配置
  3. 查询该网关下的工具映射配置列表
  4. toolId 把多条字段配置分组成一个个工具
  5. 再按 parentPath 组装字段层级关系
  6. 递归生成 JSON Schema
  7. 最终返回标准的 tools 数组

如果面试官问:为什么需要按 parentPath 组织?

因为工具参数不一定是平铺结构,很多场景是嵌套对象。

比如一个请求可能是:

  • xxxRequest01
  • xxxRequest01.city
  • xxxRequest01.company
  • xxxRequest01.company.name
  • xxxRequest01.company.type

如果只存平铺字段,是没法直接还原成 JSON Schema 的树结构的。所以我在配置表里用了:

  • fieldName 表示当前字段名
  • mcpPath 表示完整路径
  • parentPath 表示父节点路径

这样就能把所有字段先读成平铺记录,再通过 parentPath 还原成树,最后递归生成:

  • type
  • properties
  • required
  • description

这套结构对于嵌套对象很适合,也方便以后扩展数组或更复杂的 schema。

如果面试官问:核心代码在哪,怎么实现的?

核心就在 ToolsListHandler.java

它做了两件关键事:

第一件是 buildTools
它负责把数据库返回的配置列表按 toolId 分组,每一组代表一个工具。然后把每个工具的根节点和子节点分开处理,构建出最外层 inputSchema

第二件是 buildProperty
这个方法是递归的。它拿到当前字段后,会先写当前字段的 typedescription,再去看它有没有子字段。如果有,就继续递归构建 properties,同时收集哪些字段是必填,生成 required

也就是说,整个 schema 的构建本质上是一棵树的递归遍历。

如果面试官问:仓储层做了什么?

为了让领域层不直接依赖 DAO 细节,我在仓储接口 ISessionRepository.java 里新增了按网关查询工具配置的方法。

在实现类 SessionRepository.java 里:

  • 调用协议映射 DAO 查询配置
  • 把 PO 转成领域值对象 McpGatewayToolConfigVO

这样领域服务只关心“工具配置长什么样”,不关心底层表结构怎么查。

如果面试官问:协议模型为什么也要改?

因为之前 McpSchemaVOtools/list 的建模还不完整,所以我在 McpSchemaVO.java 里补了三个结构:

  • Tool
  • JsonSchema
  • ListToolsResult

这样返回值不是临时拼 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。