Skip to content

AI 服务 MCP 扩展与启动自动注册教程 ​

本文以当前 muotai-ai 实现为准,说明 docreview 的 MCP 工具为什么能够在服务启动时进入 ai_tool,以及后续如何增加本地工具或独立的 Nacos MCP 服务。

1. 先区分两种 MCP ​

当前 AI 服务同时支持两条注册链路。两者都可能被模型看到,但代码入口、数据库字段和执行方式不同。

类型典型代码发现方式ai_tool 标识调用方式
本地 MCP/本地工具DocReviewMcpService、VerifiedDocReviewMcpService扫描 AI 服务进程内的 Spring Beanservice_type=LOCAL、discover_mode=ANNOTATION_SCAN当前 JVM 内通过反射调用方法
Nacos 远程 MCP独立 MCP 服务Nacos Naming + Nacos MCP Registryservice_type=MCP_REMOTE、discover_mode=NACOS通过 MCP client 调用远程服务的 tools/call

这里的“本地 MCP”是项目内部对 LangChain4j @Tool 方法的称呼,不代表它会注册为一个独立的 Nacos MCP Server。要让其他服务复用能力,应采用远程 MCP 方式。

2. docreview 本地工具如何启动并入库 ​

2.1 真实调用链 ​

启动后的调用顺序如下:

text
Spring 根上下文刷新
        |
        v
LocalToolExecutor.onApplicationEvent(ContextRefreshedEvent)
        |
        v
LocalToolExecutor.init()
        |
        +-- 扫描所有 Spring Bean 的 declared methods
        +-- 查找 dev.langchain4j.agent.tool.Tool
        +-- 生成工具注册信息、参数 JSON Schema、返回值 Schema
        +-- 写入内存 toolRegistry
        +-- saveToolToDatabase() upsert ai_tool

关键实现是:

  • com.pig4cloud.pigx.ai.agent.chatbot.tool.impl.LocalToolExecutor
  • com.pig4cloud.pigx.ai.docreview.mcp.DocReviewMcpService
  • com.pig4cloud.pigx.ai.docreview.mcp.VerifiedDocReviewMcpService

LocalToolExecutor 只处理根应用上下文,避免 Feign 子上下文重复扫描;同时用 initialized 标志避免同一个上下文重复初始化。

2.2 docreview 当前注册的工具 ​

DocReviewMcpService:

  • report_doc_review_progress
  • save_doc_review_risks
  • save_doc_review_redline
  • save_doc_review_report
  • finish_doc_review

VerifiedDocReviewMcpService:

  • save_verified_doc_review_risks
  • finish_verified_doc_review

这些方法使用的是:

java
import dev.langchain4j.agent.tool.P;
import dev.langchain4j.agent.tool.Tool;

不要替换成 org.springframework.ai.tool.annotation.Tool,否则当前 LocalToolExecutor 不会发现它们。

2.3 本地工具写入 ai_tool 的字段 ​

扫描器第一次发现工具时,以工具名作为业务唯一 ID:

text
tool_id       = @Tool(name=...) 的值
name          = tool_id
service_type  = LOCAL
discover_mode = ANNOTATION_SCAN
status        = ACTIVE
version       = 1.0.0
tags          = local,langchain4j,auto-discovered

同时会根据 Java 方法参数生成 parameter_schema,根据返回类型生成 response_schema,并在 metadata 中保存 Bean 类名、方法名和返回类型。

如果数据库中已经存在同名工具:

  • 只有原记录同时满足 service_type=LOCAL 和 discover_mode=ANNOTATION_SCAN 时,扫描器才会更新代码生成的描述、Schema 和元数据。
  • 如果原记录来自 MCP_REMOTE、MCP_NACOS 或人工配置,扫描器会跳过,不覆盖其他来源。

因此,新增本地工具不需要手工插入 ai_tool;只要 Bean 被 Spring 扫描到,重启 AI 服务即可完成内存注册和数据库 upsert。

3. 如何新增本地 MCP 工具 ​

3.1 最小示例 ​

在 muotai-ai-biz 的 Spring 扫描范围内新增一个 @Component:

java
package com.pig4cloud.pigx.ai.example.mcp;

import dev.langchain4j.agent.tool.P;
import dev.langchain4j.agent.tool.Tool;
import org.springframework.stereotype.Component;

@Component
public class InvoiceLocalTool {

    @Tool(
        name = "query_invoice_summary",
        value = "根据发票号码查询发票摘要。成功返回摘要,查询不到时返回明确的错误信息。"
    )
    public String queryInvoiceSummary(
            @P(value = "发票号码", required = true) String invoiceNo) {
        // 调用本地 Service 或 Repository,避免在 Tool 方法中堆积业务逻辑。
        return "invoice=" + invoiceNo;
    }
}

启动后可在日志中看到类似信息:

text
注册本地工具: query_invoice_summary -> InvoiceLocalTool.queryInvoiceSummary

并在数据库验证:

sql
SELECT tool_id, name, service_type, discover_mode, status,
       parameter_schema, response_schema
FROM ai_tool
WHERE tool_id = 'query_invoice_summary';

3.2 编写约束 ​

  1. 工具类必须是 Spring Bean,例如使用 @Component。
  2. 方法必须使用 dev.langchain4j.agent.tool.Tool。
  3. @Tool(name=...) 必须明确、稳定、全局唯一。
  4. 工具名只能使用字母、数字、下划线和连字符;不要使用中文、空格、点号或斜杠。
  5. 参数使用 @P 写清名称、说明和 required,这样生成的 JSON Schema 才能被模型正确使用。
  6. 方法返回值应是容易序列化的值,例如 String、数字、布尔值或结构化对象。
  7. 业务逻辑放在 Service 中,Tool 方法只负责参数接收、调用和结果整理。
  8. 工具名不能与远程 MCP 或人工配置中的 tool_id 冲突。

3.3 本地工具的边界 ​

本地工具只存在于当前 AI 服务 JVM 内:

  • 不需要 Nacos 服务名、端口和 MCP endpoint。
  • 不会被其他微服务直接发现。
  • 服务重启后由扫描器重新建立内存注册表。
  • 数据库记录主要用于统一管理、Agent 绑定和生成工具规格。

如果工具需要独立部署、独立扩容、被多个服务复用,应该新增远程 Nacos MCP,而不是继续在 muotai-ai 中添加本地 Bean。

4. Nacos 远程 MCP 如何启动并自动入库 ​

4.1 启动流程 ​

远程 MCP 的自动注册入口是:

text
NacosMcpServiceAutoRegistrationConfig implements ApplicationRunner
        |
        +-- syncMcpServersFromRegistry()
        |       `-- 扫描 Nacos MCP Registry 的服务和 toolSpec
        |
        +-- discoverMcpServices(group)
        |       `-- 扫描 Nacos Naming 中以 mcp- 开头的服务
        |
        +-- syncService(service, group)
        |       `-- 获取健康实例
        |       `-- 获取 tools/list 或 Registry toolSpec
        |       `-- McpServiceSyncService.syncMcpTool()
        |
        `-- registerServiceListener(service, group)
                `-- 服务实例变化时增量同步

关键类:

  • NacosMcpServiceAutoRegistrationConfig
  • NacosMcpServiceListenerConfig
  • McpServiceSyncService
  • NacosNamingServiceConfig

启动同步和定时同步都使用同一个 McpServiceSyncService,因此新增 MCP 服务不需要在 AI 服务中额外写一个注册类。

4.2 发现和取工具的优先级 ​

当前同步器按以下顺序获取工具定义:

  1. Nacos MCP Registry 的 McpServerDetailInfo.toolSpec。
  2. MCP 服务实例 Nacos 元数据中的 tools JSON。
  3. 如果仍然拿不到有效工具列表,则跳过本次同步,不创建空工具记录。

因此,远程服务至少要保证 Registry 中有完整的工具定义,或者实例元数据中存在可解析的 tools 数组。工具定义应包含工具名、描述和 inputSchema。

4.3 关键配置 ​

配置前缀为 ai.mcp.nacos-discovery,属性类是 NacosMcpServiceProperties。典型配置如下:

yaml
ai:
  mcp:
    nacos-discovery:
      enabled: true
      sync-on-startup: true
      service-name-prefix: mcp-
      nacos-groups:
        - DEFAULT_GROUP
      health-check-interval: 60000
      listener-delay-ms: 1000
      default-timeout-ms: 30000
      auto-publish: true
      exclude-services: []

Nacos 连接配置来自 Spring Cloud Nacos 和 Alibaba MCP Nacos 配置:

yaml
spring:
  cloud:
    nacos:
      username: ${NACOS_USERNAME}
      password: ${NACOS_PASSWORD}
      discovery:
        server-addr: ${NACOS_HOST:wds-register}:${NACOS_PORT:8848}

ai:
  alibaba:
    mcp:
      nacos:
        server-addr: ${NACOS_HOST:wds-register}:${NACOS_PORT:8848}
        username: ${NACOS_USERNAME}
        password: ${NACOS_PASSWORD}

实际项目使用 Nacos 配置中心时,以上配置也可能来自 application-<profile>.yml 或 muotai-ai-<profile>.yml,应以运行环境中最终合并后的配置为准。

默认行为:

配置默认值作用
enabledtrue是否启用 MCP 自动发现和同步
sync-on-startuptrue应用启动时是否立即全量同步
service-name-prefixmcp-只有此前缀的 Naming 服务才会被识别
nacos-groupsDEFAULT_GROUP扫描的 Nacos 分组
health-check-interval60000定期健康检查间隔,单位毫秒
listener-delay-ms1000监听事件防抖时间,单位毫秒
default-timeout-ms30000工具默认超时时间
auto-publishtrue新同步工具默认写入 ACTIVE,否则写入 DISABLED

4.4 远程同步写入的字段 ​

McpServiceSyncService 创建工具时主要写入:

text
tool_id       = 当前 MCP 工具名
name          = 当前 MCP 工具名
description   = MCP 工具描述
version       = 实例元数据中的 mcp-version,默认 1.0.0
service_type  = MCP_REMOTE
discover_mode = NACOS
nacos_service_name = Nacos 服务名
nacos_group        = Nacos 分组
endpoint      = 网关地址/服务路径,并可能带系统 MCP API Key
parameter_schema = MCP inputSchema,缺失时使用空 object Schema
response_schema  = 默认 object Schema
status        = auto-publish ? ACTIVE : DISABLED
metadata      = 服务名、分组、实例数、工具名和最后同步时间

同步按 serviceName@groupName 加锁,单个工具遇到数据库锁冲突最多重试三次。已存在的工具会更新描述、Schema、Nacos 归属、endpoint、实例数和同步时间。

4.5 服务下线和工具删除 ​

当前实现的处理方式是:

  • 没有实例或没有健康实例时,优先尝试从 Nacos MCP Registry 兜底同步;如果也失败,则跳过本次更新。
  • 服务实例变化会触发监听器同步。
  • 已注册服务由定时健康检查重新同步。
  • removeDeletedTools() 会把服务当前不存在的工具标记为 DISABLED,不会物理删除。
  • 当前启动流程没有启用删除服务记录的逻辑,removeDeletedServices() 调用被注释,因此数据库中可能保留历史服务工具记录。

5. 新增独立 Nacos MCP 服务的步骤 ​

5.1 服务端必须提供的能力 ​

独立 MCP 服务需要实现标准 MCP 协议的至少两个操作:

  • tools/list:返回工具名、描述和 inputSchema。
  • tools/call:根据工具名和参数执行实际业务并返回结果。

伪代码:

text
tools/list -> {
  "tools": [
    {
      "name": "query_invoice_summary",
      "description": "根据发票号码查询摘要",
      "inputSchema": {
        "type": "object",
        "properties": {
          "invoiceNo": {"type": "string", "description": "发票号码"}
        },
        "required": ["invoiceNo"]
      }
    }
  ]
}

tools/call(name="query_invoice_summary", arguments={"invoiceNo":"INV-001"})
    -> 返回 MCP 标准结果

5.2 注册到 Nacos ​

  1. 服务名使用 mcp- 前缀,例如 mcp-invoice-server。
  2. 使用与 AI 服务一致的 Nacos 地址、命名空间和分组。
  3. 服务启动后注册健康实例。
  4. 在 Nacos MCP Registry 中注册服务详情,包括名称、版本、协议、远程服务引用、endpoint 和 toolSpec。
  5. toolSpec.tools 中每个工具必须有稳定的 name、description 和 inputSchema。
  6. 如果服务依赖实例元数据兜底同步,可额外设置 tools JSON、mcp-version、timeout、rate-limit、permissions、tags 等元数据。
  7. 确认实例健康后,重启 AI 服务,或等待健康检查周期触发同步。

注册完成后可在 AI 数据库确认:

sql
SELECT tool_id, name, service_type, discover_mode,
       nacos_service_name, nacos_group, endpoint, status
FROM ai_tool
WHERE nacos_service_name = 'mcp-invoice-server'
ORDER BY id;

5.3 远程工具命名注意事项 ​

当前 generateToolIdForTool() 直接返回 MCP 工具名,不包含服务名和分组。例如两个服务都提供 query_invoice_summary,会竞争同一个 ai_tool.tool_id 唯一键。

新增远程 MCP 时必须暂时保证工具名全局不重复,例如:

text
invoice_query_summary
vehicle_query_summary

后续如果要支持不同服务提供同名工具,应先改造 ID 策略,例如使用 服务标识_工具名,并同步调整 Agent 配置、历史数据迁移和执行器查找逻辑。不要只修改数据库唯一索引。

6. 智能体如何使用已注册工具 ​

工具进入 ai_tool 后,还需要被 Agent 配置引用,才会进入模型可用工具列表。

调用链如下:

text
Agent.toolIds
    |
    v
加载 ai_tool 记录
    |
    v
AiToolServiceImpl.buildToolSpecifications()
    |
    v
向模型发送工具名称、描述和 parameter_schema
    |
    v
ToolExecutorFactory 按 service_type 选择执行器

注意事项:

  • 仅写入 ai_tool 不等于所有 Agent 自动可用;需要把工具 ID 绑定到目标 Agent。
  • MCP_REMOTE 记录由 McpRemoteToolExecutor 处理。
  • MCP_NACOS 类型由 McpNacosToolExecutor 处理。当前数据库同步逻辑写入的是 MCP_REMOTE,不要只根据类名判断实际路径。
  • 远程调用会使用数据库中的 endpoint,并结合 nacos_service_name/nacos_group 做服务发现;客户端随后执行 MCP tools/call。
  • parameter_schema 会转换为 LangChain4j 的工具规格。Schema 不完整时,模型可能无法正确生成参数。

7. 数据库检查 SQL ​

7.1 查询所有 MCP 工具 ​

sql
SELECT id, tool_id, name, service_type, discover_mode,
       nacos_service_name, nacos_group, status, endpoint,
       created_at, updated_at
FROM ai_tool
WHERE deleted_at IS NULL
  AND service_type IN ('LOCAL', 'MCP_REMOTE', 'MCP_NACOS')
ORDER BY service_type, nacos_service_name, id;

7.2 查询指定 Nacos 服务 ​

sql
SELECT tool_id, description, parameter_schema, response_schema,
       nacos_service_name, nacos_group, endpoint, status, metadata
FROM ai_tool
WHERE deleted_at IS NULL
  AND nacos_service_name = 'mcp-invoice-server';

7.3 查询禁用或不可用工具 ​

sql
SELECT tool_id, service_type, nacos_service_name, nacos_group,
       status, metadata, updated_at
FROM ai_tool
WHERE deleted_at IS NULL
  AND status <> 'ACTIVE'
ORDER BY updated_at DESC;

7.4 检查跨服务同名工具 ​

sql
SELECT tool_id, COUNT(*) AS total,
       GROUP_CONCAT(DISTINCT nacos_service_name ORDER BY nacos_service_name) AS services
FROM ai_tool
WHERE deleted_at IS NULL
  AND service_type IN ('MCP_REMOTE', 'MCP_NACOS')
GROUP BY tool_id
HAVING COUNT(DISTINCT nacos_service_name) > 1;

7.5 检查工具参数 Schema ​

sql
SELECT tool_id, JSON_PRETTY(parameter_schema) AS parameter_schema
FROM ai_tool
WHERE tool_id = 'query_invoice_summary';

8. 日志和故障排查 ​

8.1 建议关注的日志关键字 ​

  • 开始 Nacos MCP 服务自动发现
  • 发现 MCP 服务
  • Nacos MCP Registry 工具同步完成
  • 创建新的 MCP 工具
  • 更新 MCP 工具
  • 注册 MCP 服务监听器
  • MCP 服务自动发现失败
  • 无法从 MCP 服务获取工具列表

8.2 服务名没有 mcp- 前缀 ​

现象:Nacos 中实例健康,但 AI 服务完全没有发现日志。

处理:确认 service-name-prefix 的值,默认必须是 mcp-;服务名例如 mcp-invoice-server。如果确实需要其他前缀,应修改 AI 服务配置,而不是手工插入 ai_tool。

8.3 Nacos 分组、命名空间或账号不一致 ​

现象:Naming 能看到服务,但 Registry 查不到详情,或 AI 服务查询不到实例。

处理:核对以下值是否一致:

  • Nacos server address
  • namespace
  • username/password
  • DEFAULT_GROUP 或自定义 group
  • AI 服务 nacos-groups 配置

8.4 实例不健康 ​

现象:服务被发现,但工具没有入库或状态没有更新。

处理:先确认实例健康;同步器只优先使用健康实例。没有健康实例时会尝试 Registry 兜底,兜底失败会保留原记录,不会用空工具覆盖数据库。

8.5 Registry 没有 toolSpec ​

现象:服务和实例都存在,但日志显示工具列表为空。

处理:在 Nacos MCP Registry 补齐服务详情和 toolSpec.tools;或者在实例元数据提供合法的 tools JSON。每个工具至少应包含 name 和可用的 inputSchema。

8.6 tools 元数据 JSON 错误 ​

现象:日志出现元数据解析失败。

处理:确认 tools 是 JSON 数组,而不是 Java 对象字符串或单个 JSON 对象。例如:

json
[
  {
    "name": "query_invoice_summary",
    "description": "查询发票摘要",
    "inputSchema": {
      "type": "object",
      "properties": {
        "invoiceNo": {"type": "string"}
      },
      "required": ["invoiceNo"]
    }
  }
]

8.7 工具名冲突 ​

现象:数据库出现唯一键冲突、同名工具归属异常,或一个工具覆盖另一个服务的描述和 endpoint。

处理:使用全局唯一工具名;检查第 7.4 节 SQL。当前实现以工具名作为 tool_id,不能依赖服务名自动隔离。

8.8 Agent 未绑定工具 ID ​

现象:ai_tool 中有记录,但模型没有看到工具。

处理:将对应 ai_tool.id/工具 ID 绑定到目标 Agent,确认 Agent 加载配置时包含该工具,并检查工具状态为 ACTIVE。

8.9 endpoint、API Key 或网关地址错误 ​

现象:工具已经入库,但调用时连接失败或返回 401/404。

处理:检查 endpoint 是否指向正确的 MCP 路径,网关地址是否与服务命名约定一致;系统会尝试从 UPMS 获取系统 MCP API Key,若未获取到,endpoint 可能不带 api_key 参数。必要时直接检查数据库中的 endpoint 和 AI 服务日志。

8.10 工具名包含中文或非法字符 ​

现象:模型工具规格构建失败,或调用阶段提示工具名不合法。

处理:改成只包含 [A-Za-z0-9_-] 的名称,例如 query_invoice_summary;中文应放在 description 中。

9. 新增 MCP 的推荐检查清单 ​

本地工具 ​

  • [ ] 类是 Spring @Component。
  • [ ] 方法使用 dev.langchain4j.agent.tool.Tool。
  • [ ] 工具名全局唯一且只含合法字符。
  • [ ] 参数使用 @P,说明和必填性完整。
  • [ ] 重启 AI 服务后日志出现本地工具注册信息。
  • [ ] ai_tool 中为 LOCAL + ANNOTATION_SCAN + ACTIVE。
  • [ ] 目标 Agent 已绑定工具。

Nacos 远程 MCP ​

  • [ ] 服务名以 mcp- 开头,或已调整 service-name-prefix。
  • [ ] Nacos 地址、namespace、账号和 group 与 AI 服务一致。
  • [ ] 实现 tools/list 和 tools/call。
  • [ ] Registry 中有有效 toolSpec,或实例元数据有合法 tools JSON。
  • [ ] 实例健康。
  • [ ] 工具名全局唯一,避免当前 tool_id 冲突。
  • [ ] 启动同步或定时同步日志显示工具创建/更新。
  • [ ] ai_tool 中为 MCP_REMOTE + NACOS + ACTIVE。
  • [ ] 目标 Agent 已绑定工具。
  • [ ] 实际调用 endpoint、API Key 和 MCP 路径可达。

10. 代码参考位置 ​

  • 本地扫描和入库:muotai-ai/muotai-ai-biz/src/main/java/com/pig4cloud/pigx/ai/agent/chatbot/tool/impl/LocalToolExecutor.java
  • 文档审查本地工具:muotai-ai/muotai-ai-biz/src/main/java/com/pig4cloud/pigx/ai/docreview/mcp/DocReviewMcpService.java
  • 已验证审查工具:muotai-ai/muotai-ai-biz/src/main/java/com/pig4cloud/pigx/ai/docreview/mcp/VerifiedDocReviewMcpService.java
  • Nacos 启动同步:muotai-ai/muotai-ai-biz/src/main/java/com/pig4cloud/pigx/ai/config/NacosMcpServiceAutoRegistrationConfig.java
  • Nacos 监听和定时发现:muotai-ai/muotai-ai-biz/src/main/java/com/pig4cloud/pigx/ai/config/NacosMcpServiceListenerConfig.java
  • MCP 同步和 upsert:muotai-ai/muotai-ai-biz/src/main/java/com/pig4cloud/pigx/ai/service/McpServiceSyncService.java
  • AI 工具实体:muotai-ai/muotai-ai-biz/src/main/java/com/pig4cloud/pigx/ai/domain/entity/AiToolEntity.java
  • 工具表初始化:muotai-ai/muotai-ai-biz/src/main/resources/db.migration/V0.0.1__create_ai_tool.sql
  • 发现模式扩展:muotai-ai/muotai-ai-biz/src/main/resources/db.migration/V0.0.67__make_ai_tool_discovery_extensible.sql