Spring AI 1.x 系列【40】MCP 客户端 Spring Boot 启动器

1. 概述

Spring AI 提供的模型上下文协议(MCP)客户端 Spring Boot 启动器 ,可为 Spring Boot 应用自动配置 MCP 客户端能力。该组件同时支持同步、异步客户端实现,并兼容多种通信传输方式。

本启动器具备以下能力:

  • 支持管理多个客户端实例
  • 可配置客户端自动初始化
  • 兼容多种命名传输方式:标准输入输出(STDIO)、HTTP/SSE、流式 HTTP
  • 无缝集成 Spring AI 工具调用框架
  • 提供工具过滤能力,可按需启用/屏蔽指定工具
  • 支持自定义工具名称前缀生成,避免命名冲突
  • 完善的生命周期管理:应用上下文关闭时自动释放资源
  • 支持通过自定义扩展器定制客户端创建逻辑

2. 启动器依赖

2.1 标准 MCP 客户端

标准启动器可通过进程内 STDIOSSE、流式 HTTP、无状态流式 HTTP 多种方式,同时连接一个或多个 MCP 服务端。其中 SSE 与流式 HTTP 基于 JDK 原生 HttpClient 实现。

每一条 MCP 服务端连接都会创建独立客户端实例,可选择同步 (SYNC)或异步 (ASYNC)模式(两种模式不可混用)。

xml 复制代码
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-mcp-client</artifactId>
</dependency>

生产环境推荐搭配 spring-ai-starter-mcp-client-webflux,使用基于 WebFluxSSE/流式 HTTP 连接。

2.2 WebFlux 客户端

功能与标准启动器一致,但底层基于 WebFlux 实现流式 HTTP、无状态流式 HTTPSSE传输。

xml 复制代码
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-mcp-client-webflux</artifactId>
</dependency>

3. 配置项说明

3.1 通用配置

配置前缀:spring.ai.mcp.client

配置项 说明 默认值
enabled 开启/关闭 MCP 客户端 true
name MCP 客户端实例名称 spring-ai-mcp-client
version MCP 客户端版本 1.0.0
initialized 创建客户端时是否自动初始化 true
request-timeout 客户端请求超时时间 20s
type 客户端类型:SYNC(同步) / ASYNC(异步),全局只能选一种 SYNC
root-change-notification 开启/关闭所有客户端的根路径变更通知 true
toolcallback.enabled 开启/关闭 MCP 工具回调与 Spring AI 工具框架的集成 true

3.2 MCP 注解配置

配置前缀:spring.ai.mcp.client.annotation-scanner

配置项 说明 默认值
enabled 开启/关闭 MCP 客户端注解自动扫描 true

3.3 标准输入输出(STDIO)传输配置

配置前缀:spring.ai.mcp.client.stdio

配置项 说明 默认值
servers-configuration JSON 格式的 MCP 服务端配置文件地址
connections 命名式 STDIO 连接配置集合
connections.name.command 启动 MCP 服务端的执行命令
connections.name.args 命令行参数列表
connections.name.env 服务端进程环境变量

配置示例(YAML):

yaml 复制代码
spring:
  ai:
    mcp:
      client:
        stdio:
          root-change-notification: true
          connections:
            server1:
              command: /path/to/server
              args:
                - --port=8080
                - --mode=production
              env:
                API_KEY: your-api-key
                DEBUG: "true"

引用外部 JSON 配置文件(兼容 Claude Desktop 格式):

yaml 复制代码
spring:
  ai:
    mcp:
      client:
        stdio:
          servers-configuration: classpath:mcp-servers.json

mcp-servers.json 示例:

json 复制代码
{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": [
        "-y",
        "@modelcontextprotocol/server-filesystem",
        "/Users/username/Desktop",
        "/Users/username/Downloads"
      ]
    }
  }
}

3.3.1 Windows 平台特殊适配

Windows 系统中,npxnpmnodepython 等均为批处理文件(.cmd),JavaProcessBuilder 无法直接执行批处理 ,必须通过 cmd.exe /c 包裹命令。

Windows 配置示例(JSON):

json 复制代码
{
  "mcpServers": {
    "filesystem": {
      "command": "cmd.exe",
      "args": [
        "/c",
        "npx",
        "-y",
        "@modelcontextprotocol/server-filesystem",
        "C:\\Users\\username\\Desktop"
      ]
    }
  }
}

Linux / macOS 配置示例(JSON):

json 复制代码
{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": [
        "-y",
        "@modelcontextprotocol/server-filesystem",
        "/Users/username/Desktop"
      ]
    }
  }
}

跨平台代码式配置:

通过代码判断操作系统,实现一套配置兼容全平台,同时添加 @ConditionalOnMissingBean 避免与配置文件自动配置冲突:

java 复制代码
@Bean(destroyMethod = "close")
@ConditionalOnMissingBean(McpSyncClient.class)
public McpSyncClient mcpClient() {
    ServerParameters stdioParams;

    if (isWindows()) {
        // Windows 系统:使用 cmd.exe 包装命令
        var winArgs = new ArrayList<>(Arrays.asList(
            "/c", "npx", "-y", "@modelcontextprotocol/server-filesystem", "target"));
        stdioParams = ServerParameters.builder("cmd.exe")
                .args(winArgs)
                .build();
    } else {
        // Linux / Mac 系统:直接执行命令
        stdioParams = ServerParameters.builder("npx")
                .args("-y", "@modelcontextprotocol/server-filesystem", "target")
                .build();
    }

    return McpClient.sync(new StdioClientTransport(stdioParams, McpJsonMapper.createDefault()))
            .requestTimeout(Duration.ofSeconds(10))
            .build()
            .initialize();
}

// 判断当前系统是否为 Windows
private static boolean isWindows() {
    return System.getProperty("os.name").toLowerCase().contains("win");
}

路径使用说明:

  • 相对路径(推荐,可移植性强):基于应用运行目录解析
  • 绝对路径 (Windows):使用反斜杠 \ 或转义正斜杠 /

Windows 下需要 cmd.exe 包裹的常用批处理:

npx.cmdnpm.cmdpython.cmdpip.cmdmvn.cmdgradle.cmd 及自定义 .cmd/.bat 脚本。

3.4 流式 HTTP(Streamable-HTTP)传输配置

配置前缀:spring.ai.mcp.client.streamable-http

配置项 说明 默认值
connections 命名式流式 HTTP 连接配置集合
connections.name.url MCP 服务端基础地址
connections.name.endpoint 接口后缀路径 /mcp

配置示例:

yaml 复制代码
spring:
  ai:
    mcp:
      client:
        streamable-http:
          connections:
            server1:
              url: http://localhost:8080
            server2:
              url: http://otherserver:8081
              endpoint: /custom-sse

3.5 SSE(服务端推送事件)传输配置

配置前缀:spring.ai.mcp.client.sse

配置项 说明 默认值
connections 命名式 SSE 连接配置集合
connections.name.url MCP 服务端基础地址
connections.name.sse-endpoint SSE 接口后缀路径 /sse

配置示例:

yaml 复制代码
spring:
  ai:
    mcp:
      client:
        sse:
          connections:
            server1:
              url: http://localhost:8080
            server2:
              url: http://otherserver:8081
              sse-endpoint: /custom-sse

URL 拆分规则:

完整 SSE 地址需拆分为 基础URL + 接口后缀

完整地址 基础 url sse-endpoint 后缀
http://localhost:3000/mcp-hub/sse/token123 localhost:3000 /mcp-hub/sse/token123
https://api.service.com/v2/events?key=secret api.service.com /v2/events?key=secret
http://localhost:8080/sse localhost:8080 /sse(可省略)

SSE 连接排错(404 错误):

  1. 拆分地址时,基础 URL 仅保留协议、域名、端口
  2. 接口后缀必须以 / 开头,包含完整路径与请求参数
  3. 先通过浏览器或 curl 直接访问完整地址,验证接口可用性

4. 核心功能详解

4.1 同步/异步客户端

  • 同步客户端(默认 SYNC):适用于传统阻塞式请求响应场景,仅注册同步类型 MCP 注解方法,异步方法会被忽略。
  • 异步客户端(ASYNC):适用于响应式非阻塞应用,仅注册异步类型 MCP 注解方法,同步方法会被忽略。

4.2 客户端自定义扩展

通过扩展接口可深度定制客户端行为,支持超时配置、事件监听、消息处理等。

支持两类扩展接口:

  • McpSyncClientCustomizer:同步客户端扩展
  • McpAsyncClientCustomizer:异步客户端扩展

可自定义能力:

  1. 请求超时配置
  2. LLM 采样处理器
  3. 文件根路径权限管理
  4. 交互式信息收集处理器
  5. 各类事件监听器(工具/资源/提示词变更、进度通知、日志通知等)

同步客户端扩展示例:

java 复制代码
@Component
public class CustomMcpSyncClientCustomizer implements McpSyncClientCustomizer {
    @Override
    public void customize(String serverConfigurationName, McpClient.SyncSpec spec) {
        // 自定义超时时间
        spec.requestTimeout(Duration.ofSeconds(30));
        // 根路径配置
        spec.roots(roots);
        // 采样请求处理
        spec.sampling(request -> { /* 业务逻辑 */ });
        // 信息收集请求处理
        spec.elicitation(request -> { /* 业务逻辑 */ });
        // 进度通知监听
        spec.progressConsumer(progress -> { /* 业务逻辑 */ });
        // 工具列表变更监听
        spec.toolsChangeConsumer(tools -> { /* 业务逻辑 */ });
        // 资源列表变更监听
        spec.resourcesChangeConsumer(resources -> { /* 业务逻辑 */ });
        // 提示词列表变更监听
        spec.promptsChangeConsumer(prompts -> { /* 业务逻辑 */ });
        // 日志消息监听
        spec.loggingConsumer(log -> { /* 业务逻辑 */ });
    }
}

4.3 传输方式支持

传输方式 标准启动器 WebFlux 启动器
STDIO 标准输入输出
HttpClient 版 HTTP/SSE、流式 HTTP
WebFlux 版 HTTP/SSE、流式 HTTP

4.4 工具过滤

实现 McpToolFilter 接口,可根据连接信息、工具属性动态过滤工具(启用/禁用),全局仅允许一个过滤 Bean。

java 复制代码
@Component
public class CustomMcpToolFilter implements McpToolFilter {
    @Override
    public boolean test(McpConnectionInfo connectionInfo, McpSchema.Tool tool) {
        // 过滤指定客户端的所有工具
        if ("restricted-client".equals(connectionInfo.clientInfo().name())) {
            return false;
        }
        // 仅保留名称前缀为 allowed_ 的工具
        if (tool.name().startsWith("allowed_")) {
            return true;
        }
        // 过滤标注为实验性的工具
        if (tool.getDescription() != null && tool.getDescription().contains("experimental")) {
            return false;
        }
        return true;
    }
}

4.5 工具名称前缀生成

通过 McpToolNamePrefixGenerator 为工具名添加前缀,解决多服务端工具重名冲突

框架内置实现:

  • DefaultMcpToolNamePrefixGenerator(默认):自动去重、特殊字符转下划线、超长截断,保证全局唯一
  • noPrefix():不添加前缀(多服务端场景不推荐,易引发冲突)

自定义前缀规则示例:

java 复制代码
@Component
public class CustomToolNamePrefixGenerator implements McpToolNamePrefixGenerator {
    @Override
    public String prefixedToolName(McpConnectionInfo connectionInfo, Tool tool) {
        String serverName = connectionInfo.initializeResult().serverInfo().name();
        String serverVersion = connectionInfo.initializeResult().serverInfo().version().replace(".", "_");
        // 格式:服务名_版本_工具名
        return serverName + "_v" + serverVersion + "_" + tool.name();
    }
}

关闭前缀功能:

java 复制代码
@Configuration
public class McpConfiguration {
    @Bean
    public McpToolNamePrefixGenerator mcpToolNamePrefixGenerator() {
        return McpToolNamePrefixGenerator.noPrefix();
    }
}

4.6 工具上下文与元数据转换

通过 ToolContextToMcpMetaConverter,将 Spring AI 工具上下文转为 MCP 调用元数据,可传递用户ID、令牌、追踪标识等上下文。

框架内置:

  • 默认转换器:过滤空值与内置上下文 Key
  • noOp():禁用上下文转换

4.7 关闭工具回调自动配置

配置 spring.ai.mcp.client.toolcallback.enabled=false,将不再自动创建工具回调实例。


5. MCP 客户端注解

使用注解以声明式方式处理 MCP 各类事件与请求:

注解 用途
@McpLogging 接收服务端日志通知
@McpSampling 处理 LLM 采样/补全请求
@McpElicitation 向用户收集补充信息
@McpProgress 接收长任务进度通知
@McpToolListChanged 工具列表变更通知
@McpResourceListChanged 资源列表变更通知
@McpPromptListChanged 提示词列表变更通知

使用示例:

java 复制代码
@Component
public class McpClientHandlers {

    @McpLogging(clients = "server1")
    public void handleLoggingMessage(LoggingMessageNotification notification) {
        System.out.println("日志:" + notification.level() + " - " + notification.data());
    }

    @McpSampling(clients = "server1")
    public CreateMessageResult handleSamplingRequest(CreateMessageRequest request) {
        // 处理LLM请求并返回结果
        return CreateMessageResult.builder()
                .role(Role.ASSISTANT)
                .content(new TextContent("响应内容"))
                .model("gpt-4")
                .build();
    }

    @McpProgress(clients = "server1")
    public void handleProgress(ProgressNotification notification) {
        double percent = notification.progress() * 100;
        System.out.printf("执行进度:%.2f%% %s%n", percent, notification.message());
    }
}

注解同步/异步方法均支持 ,可通过 clients 属性指定绑定的客户端实例。


6. 项目使用示例

6.1 完整 YAML 配置

yaml 复制代码
spring:
  ai:
    mcp:
      client:
        enabled: true
        name: my-mcp-client
        version: 1.0.0
        request-timeout: 30s
        type: SYNC
        sse:
          connections:
            server1:
              url: http://localhost:8080
            server2:
              url: http://otherserver:8081
        streamable-http:
          connections:
            server3:
              url: http://localhost:8083
              endpoint: /mcp
        stdio:
          root-change-notification: false
          connections:
            server1:
              command: /path/to/server
              args:
                - --port=8080
                - --mode=production
              env:
                API_KEY: your-api-key
                DEBUG: "true"

6.2 代码注入使用

java 复制代码
// 同步客户端注入
@Autowired
private List<McpSyncClient> mcpSyncClients;

// 异步客户端注入
@Autowired
private List<McpAsyncClient> mcpAsyncClients;

// 工具回调注入
@Autowired
private SyncMcpToolCallbackProvider toolCallbackProvider;

相关推荐
糖果店的幽灵1 小时前
Spring AI 从入门到精通-Spring AI 是什么
java·人工智能·spring
不知名的老吴1 小时前
机器学习评价之基础指标
人工智能·算法·机器学习
申通之声1 小时前
3年稳定率90%+:申通五星管理经
大数据·人工智能·交通物流
陕西企来客1 小时前
西安豆包获客技巧深度解析:核心问题与原因分析
人工智能
超人也会哭️呀1 小时前
视觉模型中的坐标漂移
人工智能·ai·llm·ocr·vlm·视觉模型·dots.ocr
tedcloud1231 小时前
FluentFlyout部署教程:打造更美观的Windows桌面环境
数据库·人工智能·sql·学习·自动化
LoserChaser1 小时前
大语言模型基础-语言模型与 Transformer 架构
人工智能·语言模型·transformer
Token炼金师1 小时前
RoPE 解构:从复数平面到 Transformer 的位置魔法
人工智能