Lzh on GitHub

MCP 客户端启动器

Spring AI MCP(模型上下文协议)客户端启动器为 Spring Boot 应用提供了 MCP 客户端功能的自动配置。它支持同步和异步客户端实现,并提供多种传输选项。

Spring AI MCP(模型上下文协议)客户端启动器为 Spring Boot 应用提供了 MCP 客户端功能的自动配置。它支持同步和异步客户端实现,并提供多种传输选项。

MCP 客户端启动器提供以下功能:

  • 管理多个客户端实例
  • 自动初始化客户端(如果启用)
  • 支持多种命名传输(STDIO、HTTP/SSE 和 Streamable HTTP)
  • 与 Spring AI 的工具执行框架集成
  • 工具过滤功能,可选择性地包含或排除工具
  • 可自定义工具名称前缀,避免命名冲突
  • 合理的生命周期管理,在应用上下文关闭时自动清理资源
  • 可通过自定义器定制客户端创建过程

启动器

标准 MCP 客户端

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-mcp-client</artifactId>
</dependency>

标准启动器可同时通过 STDIO(进程内)、SSEStreamable-HTTP 以及 Stateless Streamable-HTTP 传输连接到一个或多个 MCP 服务器。SSE 和 Streamable-HTTP 传输使用基于 JDK HttpClient 的实现。

每次连接 MCP 服务器都会创建一个新的 MCP 客户端实例。你可以选择使用 SYNC(同步)或 ASYNC(异步) MCP 客户端(注意:同步和异步客户端不能混用)。

在生产环境部署中,我们建议使用基于 WebFlux 的 SSE 和 Streamable-HTTP 连接,并使用 spring-ai-starter-mcp-client-webflux 启动器。

WebFlux 客户端

WebFlux 启动器提供与标准启动器类似的功能,但使用基于 WebFlux 的 Streamable-HTTP、Stateless Streamable-HTTP 以及 SSE 传输实现。

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-mcp-client-webflux</artifactId>
</dependency>

配置属性

通用属性

通用属性的前缀为 spring.ai.mcp.client

属性描述默认值
enabled启用或禁用 MCP 客户端true
nameMCP 客户端实例名称spring-ai-mcp-client
versionMCP 客户端实例版本1.0.0
initialized是否在创建时初始化客户端true
request-timeoutMCP 客户端请求的超时时间20s
type客户端类型(SYNC 或 ASYNC),所有客户端必须统一类型,不支持混用SYNC
root-change-notification启用或禁用所有客户端的根变更通知true
toolcallback.enabled启用或禁用 MCP 工具回调与 Spring AI 工具执行框架的集成true

MCP 注解属性

MCP 客户端注解(MCP Client Annotations)提供了一种使用 Java 注解以声明式方式实现 MCP 客户端处理器的方法。客户端 mcp-annotations 属性的前缀为 spring.ai.mcp.client.annotation-scanner

属性描述默认值
enabled启用或禁用 MCP 客户端注解的自动扫描true

STDIO 传输属性

标准输入/输出(Standard I/O)传输的属性前缀为 spring.ai.mcp.client.stdio

属性描述默认值
servers-configuration包含 MCP 服务器配置的资源,JSON 格式-
connections命名的 stdio 连接配置映射-
connections.[name].command启动 MCP 服务器的命令-
connections.[name].args命令参数列表-
connections.[name].env服务器进程的环境变量映射-

示例配置:

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"

另外,也可以使用 Claude Desktop 格式 的外部 JSON 文件配置 stdio 连接:

spring:
  ai:
    mcp:
      client:
        stdio:
          servers-configuration: classpath:mcp-servers.json

Claude Desktop 格式示例:

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

Windows STDIO 配置

在 Windows 上,像 npxnpmnode 这样的命令实际上是批处理文件(.cmd),而不是原生可执行文件。Java 的 ProcessBuilder 无法直接执行批处理文件,需要通过 cmd.exe /c 来包装执行。

为什么 Windows 需要特殊处理

当 Java 的 ProcessBuilder(在 StdioClientTransport 内部使用)尝试在 Windows 上启动进程时,它只能执行:

  • 原生可执行文件(.exe
  • 系统命令(cmd.exe 可识别的命令)

而 Windows 批处理文件,如 npx.cmdnpm.cmd,甚至来自 Microsoft Store 的 python.cmd,都必须通过 cmd.exe 来执行。

解决方案:使用 cmd.exe 包装

使用 cmd.exe /c 包裹批处理文件命令:

Windows 配置示例:

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

Linux/macOS 配置示例:

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

跨平台的程序化配置

如果希望应用程序在不同平台上使用同一套配置,可以在 Spring Boot 中通过操作系统检测来实现:

@Bean(destroyMethod = "close")
@ConditionalOnMissingBean(McpSyncClient.class)
public McpSyncClient mcpClient() {
    ServerParameters stdioParams;

    if (isWindows()) {
        // Windows: 使用 cmd.exe /c + npx
        var winArgs = new ArrayList<>(Arrays.asList(
            "/c", "npx", "-y", "@modelcontextprotocol/server-filesystem", "target"));
        stdioParams = ServerParameters.builder("cmd.exe")
                .args(winArgs)
                .build();
    } else {
        // Linux/Mac: 直接执行 npx
        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();
}

private static boolean isWindows() {
    return System.getProperty("os.name").toLowerCase().contains("win");
}
在使用基于 @Bean 的编程式配置时,添加 @ConditionalOnMissingBean(McpSyncClient.class),以避免与来自 JSON 文件的自动配置发生冲突。

路径注意事项

相对路径(推荐,便于移植):

{
  "command": "cmd.exe",
  "args": ["/c", "npx", "-y", "@modelcontextprotocol/server-filesystem", "target"]
}

MCP 服务器会基于应用程序的工作目录解析相对路径。

绝对路径(Windows 需要使用反斜杠或转义斜杠):

{
  "command": "cmd.exe",
  "args": ["/c", "npx", "-y", "@modelcontextprotocol/server-filesystem", "C:\\Users\\username\\project\\target"]
}

常见需要通过 cmd.exe 执行的 Windows 批处理文件

  • npx.cmdnpm.cmd —— Node 包管理器
  • python.cmd —— Python(Microsoft Store 版本)
  • pip.cmd —— Python 包管理器
  • mvn.cmd —— Maven wrapper
  • gradle.cmd —— Gradle wrapper
  • 自定义 .cmd.bat 脚本

参考实现

可以参考 Spring AI Examples - Filesystem,该示例提供了完整的跨平台 MCP 客户端实现,会自动检测操作系统并配置客户端,使 Windows 和 Linux/macOS 都能顺利运行。

Streamable-HTTP 传输属性

用于连接 Streamable-HTTPStateless Streamable-HTTP MCP 服务器。

Streamable-HTTP 传输的属性前缀为 spring.ai.mcp.client.streamable-http:

属性说明默认值
connections命名的 Streamable-HTTP 连接配置映射-
connections.[name].url与 MCP 服务器进行 Streamable-HTTP 通信的基础 URL 端点-
connections.[name].endpoint用于连接的 Streamable-HTTP 端点(URL 后缀)/mcp

配置示例:

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

SSE 传输属性

用于 Server-Sent Events (SSE) 传输的属性前缀为 spring.ai.mcp.client.sse

属性说明默认值
connections命名的 SSE 连接配置映射-
connections.[name].url与 MCP 服务器进行 SSE 通信的基础 URL 端点-
connections.[name].sse-endpoint用于连接的 SSE 端点(URL 后缀)/sse

配置示例

spring:
  ai:
    mcp:
      client:
        sse:
          connections:
            # 使用默认 /sse 端点的简单配置
            server1:
              url: http://localhost:8080
            # 自定义 SSE 端点
            server2:
              url: http://otherserver:8081
              sse-endpoint: /custom-sse
            # 带路径和 token 的复杂 URL(如 MCP Hub)
            mcp-hub:
              url: http://localhost:3000
              sse-endpoint: /mcp-hub/sse/cf9ec4527e3c4a2cbb149a85ea45ab01
            # 带查询参数的 SSE 端点
            api-server:
              url: https://api.example.com
              sse-endpoint: /v1/mcp/events?token=abc123&format=json

URL 拆分指南

当你有完整的 SSE URL 时,应拆分为基础 URL 和端点路径:

完整 URL配置方式
http://localhost:3000/mcp-hub/sse/token123url: localhost:3000
sse-endpoint: /mcp-hub/sse/token123
https://api.service.com/v2/events?key=secreturl: api.service.com
sse-endpoint: /v2/events?key=secret
http://localhost:8080/sseurl: localhost:8080
sse-endpoint: /sse(或省略使用默认值)

SSE 连接排查指南

404 Not Found 错误:

  • 检查 URL 拆分是否正确:基础 URL 只包含协议、主机和端口。
  • 确认 sse-endpoint/ 开头,并包含完整路径和查询参数。
  • 可直接在浏览器或使用 curl 测试完整 URL,确认可访问。

Streamable HTTP 传输属性

用于 Streamable-HTTP 传输的属性前缀为 spring.ai.mcp.client.streamable-http

属性说明默认值
connections命名的 Streamable-HTTP 连接配置映射-
connections.[name].url与 MCP 服务器进行 Streamable-HTTP 通信的基础 URL 端点-
connections.[name].endpoint用于连接的 Streamable-HTTP 端点(URL 后缀)/mcp

配置示例

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

功能

同步/异步客户端类型

该 Starter 支持两种类型的客户端:

1. 同步客户端(Synchronous)

  • 同步(Synchronous)—— 默认的客户端类型(spring.ai.mcp.client.type=SYNC),适用于具有阻塞操作的传统请求-响应模式。
SYNC 客户端只会注册同步的 MCP 注解方法,异步方法将被忽略。

2. 异步客户端(Asynchronous)

  • 异步(Asynchronous)—— 适用于具有非阻塞操作的响应式应用,通过 spring.ai.mcp.client.type=ASYNC 进行配置。
ASYNC 客户端只会注册异步的 MCP 注解方法,同步方法将被忽略。

客户端自定义

自动配置通过回调接口提供了丰富的客户端自定义能力。这些自定义器允许你配置 MCP 客户端行为的各个方面,从请求超时到事件处理和消息处理。

自定义类型

可用的自定义选项包括:

  • 请求配置(Request Configuration):设置自定义请求超时。
  • 自定义采样处理器(Custom Sampling Handlers):
    • 为服务器通过客户端请求 LLM 采样(完成或生成)提供标准化方式。
    • 此流程允许客户端控制模型访问、选择和权限,同时让服务器利用 AI 功能,无需服务器 API Key。
  • 文件系统(根目录)访问(File system (Roots) Access):
    • 提供客户端向服务器暴露文件系统根目录的标准化方式。
    • 根目录定义了服务器在文件系统中可操作的边界,使其知道可访问的目录和文件。
    • 服务器可以请求支持客户端的根目录列表,并在列表变更时接收通知。
  • 信息获取处理器(Elicitation Handlers):
    • 标准化方式,让服务器在交互过程中通过客户端向用户请求额外信息。
  • 事件处理器(Event Handlers):当特定服务器事件发生时,客户端可收到通知:
    • 工具变更通知 —— 当服务器可用工具列表发生变化
    • 资源变更通知 —— 当服务器可用资源列表发生变化
    • 提示变更通知 —— 当服务器可用提示列表发生变化
    • 日志处理器(Logging Handlers):标准化方式,让服务器向客户端发送结构化日志消息。
    • 进度处理器(Progress Handlers):标准化方式,让服务器向客户端发送结构化进度消息。

客户端可以通过设置最低日志级别来控制日志详细程度。

客户端自定义示例

根据应用需求,你可以实现 McpSyncClientCustomizer(同步客户端)或 McpAsyncClientCustomizer(异步客户端)。

@Component
public class CustomMcpSyncClientCustomizer implements McpSyncClientCustomizer {
    @Override
    public void customize(String serverConfigurationName, McpClient.SyncSpec spec) {

        // 自定义请求超时配置
        spec.requestTimeout(Duration.ofSeconds(30));

        // 设置客户端可访问的根 URI
        spec.roots(roots);

        // 设置自定义采样处理器,用于处理消息创建请求
        spec.sampling((CreateMessageRequest messageRequest) -> {
            // 处理采样
            CreateMessageResult result = ...
            return result;
        });

        // 设置自定义信息获取处理器,用于处理信息请求
        spec.elicitation((ElicitRequest request) -> {
            // 处理信息请求
            return new ElicitResult(ElicitResult.Action.ACCEPT, Map.of("message", request.message()));
        });

        // 添加进度通知消费者
        spec.progressConsumer((ProgressNotification progress) -> {
            // 处理进度通知
        });

        // 添加工具变更通知消费者(如工具被添加或移除)
        spec.toolsChangeConsumer((List<McpSchema.Tool> tools) -> {
            // 处理工具变更
        });

        // 添加资源变更通知消费者(如资源被添加或移除)
        spec.resourcesChangeConsumer((List<McpSchema.Resource> resources) -> {
            // 处理资源变更
        });

        // 添加提示变更通知消费者(如提示被添加或移除)
        spec.promptsChangeConsumer((List<McpSchema.Prompt> prompts) -> {
            // 处理提示变更
        });

        // 添加日志消息消费者,用于接收服务器发送的日志
        spec.loggingConsumer((McpSchema.LoggingMessageNotification log) -> {
            // 处理日志消息
        });
    }
}

serverConfigurationName 参数表示该自定义器所应用的服务器配置名称,即 MCP 客户端创建时对应的服务器配置。

MCP 客户端的自动配置会自动检测应用上下文中的所有自定义器,并将其应用到客户端实例中,无需手动注册。

传输支持

自动配置支持多种传输类型:

  • 标准 I/O(Stdio):由 spring-ai-starter-mcp-clientspring-ai-starter-mcp-client-webflux 激活
  • HTTP/SSE 和 Streamable-HTTP(HttpClient):由 spring-ai-starter-mcp-client 激活
  • HTTP/SSE 和 Streamable-HTTP(WebFlux):由 spring-ai-starter-mcp-client-webflux 激活

工具过滤

MCP Client Boot Starter 支持通过 McpToolFilter 接口对发现的工具进行过滤。这允许你根据自定义条件(如 MCP 连接信息或工具属性)选择性地包含或排除工具。

要实现工具过滤,可以创建一个实现 McpToolFilter 接口的 Bean:

@Component
public class CustomMcpToolFilter implements McpToolFilter {

    @Override
    public boolean test(McpConnectionInfo connectionInfo, McpSchema.Tool tool) {
        // 根据连接信息和工具属性进行过滤
        // 返回 true 表示包含该工具,返回 false 表示排除

        // 示例:排除来自特定客户端的工具
        if (connectionInfo.clientInfo().name().equals("restricted-client")) {
            return false;
        }

        // 示例:只包含特定名称开头的工具
        if (tool.name().startsWith("allowed_")) {
            return true;
        }

        // 示例:根据工具描述或其他属性进行过滤
        if (tool.description() != null &&
            tool.description().contains("experimental")) {
            return false;
        }

        return true; // 默认包含其他工具
    }
}

McpConnectionInfo 记录提供对以下信息的访问:

  • clientCapabilities —— MCP 客户端的能力
  • clientInfo —— MCP 客户端信息(名称和版本)
  • initializeResult —— 来自 MCP 服务器的初始化结果

该过滤器会被自动检测,并应用于同步和异步的 MCP 工具回调提供者。如果没有提供自定义过滤器,则默认包含所有发现的工具。

在应用上下文中只应定义一个 McpToolFilter Bean。如果需要多个过滤逻辑,请将它们合并为单个复合过滤器实现。

工具名称前缀生成

MCP Client Boot Starter 支持通过 McpToolNamePrefixGenerator 接口自定义工具名称前缀生成。这一功能可以在集成来自多个 MCP 服务器的工具时,避免工具名称冲突,通过为工具名称添加唯一前缀来实现。

如果没有提供自定义的 McpToolNamePrefixGenerator Bean,Starter 会使用 DefaultMcpToolNamePrefixGenerator,保证所有 MCP 客户端连接中的工具名称唯一性。默认生成器的行为如下:

  • 跟踪所有已有的连接和工具名称,确保名称唯一
  • 将工具名称中的非字母数字字符替换为下划线(例如:my-toolmy_tool
  • 当在不同连接中检测到重复工具名称时,添加计数前缀(例如:alt_1_toolNamealt_2_toolName
  • 线程安全且幂等 —— 相同的(客户端、服务器、工具)组合总是生成相同的唯一名称
  • 确保最终名称不超过 64 个字符(必要时从开头截断)

示例

  • 第一次出现的工具 searchsearch
  • 来自不同连接的重复工具 searchalt_1_search
  • 含特殊字符的工具 my-special-toolmy_special_tool

可以通过提供自定义实现来自定义工具名称前缀生成逻辑:

@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();
        return serverName + "_v" + serverVersion.replace(".", "_") + "_" + tool.name();
    }
}

McpConnectionInfo 记录提供关于 MCP 连接的完整信息:

  • clientCapabilities —— MCP 客户端的能力
  • clientInfo —— MCP 客户端信息(名称、标题和版本)
  • initializeResult —— 来自 MCP 服务器的初始化结果,包括服务器信息

内置前缀生成器

框架提供了几个内置前缀生成器:

  • DefaultMcpToolNamePrefixGenerator:通过跟踪重复工具并在必要时添加计数前缀,确保工具名称唯一(默认使用,如果没有自定义 Bean)
  • McpToolNamePrefixGenerator.noPrefix():返回不带任何前缀的工具名称(如果多个服务器提供同名工具,可能会导致冲突)

如果希望完全禁用前缀并使用原始工具名称(在使用多个 MCP 服务器时不推荐),可以将无前缀生成器注册为一个 Bean:

@Configuration
public class McpConfiguration {

    @Bean
    public McpToolNamePrefixGenerator mcpToolNamePrefixGenerator() {
        return McpToolNamePrefixGenerator.noPrefix();
    }
}

前缀生成器会通过 Spring 的 ObjectProvider 机制,自动检测并应用于同步和异步的 MCP 工具回调提供者。如果没有提供自定义生成器 Bean,默认使用 DefaultMcpToolNamePrefixGenerator

在使用 McpToolNamePrefixGenerator.noPrefix() 并同时连接多个 MCP 服务器时,重复的工具名称会导致 IllegalStateException。默认的 DefaultMcpToolNamePrefixGenerator 会通过自动为重复的工具名称添加唯一前缀来避免这一问题。

工具上下文到 MCP 元数据转换器

MCP Client Boot Starter 支持通过 ToolContextToMcpMetaConverter 接口自定义将 Spring AI 的 ToolContext 转换为 MCP 工具调用元数据的行为。此功能允许你在 LLM 生成的调用参数之外,附加额外的上下文信息(例如用户 ID、密钥 token)作为元数据传递。

例如,你可以在工具上下文中传递 MCP 的 progressToken,以便在长时间操作中 跟踪进度

ChatModel chatModel = ...;

String response = ChatClient.create(chatModel)
        .prompt("Tell me more about the customer with ID 42")
        .toolContext(Map.of("progressToken", "my-progress-token"))
        .call()
        .content();

默认情况下,如果没有提供自定义的 Converter Bean,Starter 会使用 ToolContextToMcpMetaConverter.defaultConverter(),其行为如下:

  • 过滤掉 MCP 交换键 (McpToolUtils.TOOL_CONTEXT_MCP_EXCHANGE_KEY)
  • 过滤掉值为 null 的条目
  • 其余上下文条目会原样作为元数据传递

可以通过实现自己的 Converter 来自定义转换逻辑:

@Component
public class CustomToolContextToMcpMetaConverter implements ToolContextToMcpMetaConverter {

    @Override
    public Map<String, Object> convert(ToolContext toolContext) {
        if (toolContext == null || toolContext.getContext() == null) {
            return Map.of();
        }

        // 自定义逻辑:将工具上下文转换为 MCP 元数据
        Map<String, Object> metadata = new HashMap<>();

        // 示例:给所有键添加自定义前缀
        for (Map.Entry<String, Object> entry : toolContext.getContext().entrySet()) {
            if (entry.getValue() != null) {
                metadata.put("app_" + entry.getKey(), entry.getValue());
            }
        }

        // 示例:添加额外元数据
        metadata.put("timestamp", System.currentTimeMillis());
        metadata.put("source", "spring-ai");

        return metadata;
    }
}

内置 Converter

框架提供了几个内置转换器:

  • ToolContextToMcpMetaConverter.defaultConverter():过滤掉 MCP 交换键以及空值(在未提供自定义 Bean 时默认使用)。
  • ToolContextToMcpMetaConverter.noOp():返回空映射,相当于禁用上下文到元数据的转换

如果希望完全禁用上下文到元数据的转换:

@Configuration
public class McpConfiguration {

    @Bean
    public ToolContextToMcpMetaConverter toolContextToMcpMetaConverter() {
        return ToolContextToMcpMetaConverter.noOp();
    }
}

该转换器会通过 Spring 的 ObjectProvider 机制自动检测,并应用于同步和异步的 MCP 工具回调。如果没有提供自定义转换器 Bean,则会自动使用默认转换器。

禁用 MCP ToolCallback 自动配置

MCP 的 ToolCallback 自动配置默认是启用的,但可以通过设置属性 spring.ai.mcp.client.toolcallback.enabled=false 来禁用。

禁用后,将不会从可用的 MCP 工具创建任何 ToolCallbackProvider Bean。

MCP 客户端注解

MCP Client Boot Starter 会自动检测并注册带注解的方法,用于处理各种 MCP 客户端操作:

  • @McpLogging —— 处理来自 MCP 服务器的日志消息通知
  • @McpSampling —— 处理来自 MCP 服务器的 LLM 采样请求
  • @McpElicitation —— 处理信息获取请求,从用户收集额外信息
  • @McpProgress —— 处理长时间操作的进度通知
  • @McpToolListChanged —— 处理服务器工具列表变更通知
  • @McpResourceListChanged —— 处理服务器资源列表变更通知
  • @McpPromptListChanged —— 处理服务器提示列表变更通知

示例用法:

@Component
public class McpClientHandlers {

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

    @McpSampling(clients = "server1")
    public CreateMessageResult handleSamplingRequest(CreateMessageRequest request) {
        // 处理请求并生成响应
        String response = generateLLMResponse(request);

        return CreateMessageResult.builder()
            .role(Role.ASSISTANT)
            .content(new TextContent(response))
            .model("gpt-4")
            .build();
    }

    @McpProgress(clients = "server1")
    public void handleProgressNotification(ProgressNotification notification) {
        double percentage = notification.progress() * 100;
        System.out.println(String.format("Progress: %.2f%% - %s",
            percentage, notification.message()));
    }

    @McpToolListChanged(clients = "server1")
    public void handleToolListChanged(List<McpSchema.Tool> updatedTools) {
        System.out.println("Tool list updated: " + updatedTools.size() + " tools available");
        // 更新本地工具注册表
        toolRegistry.updateTools(updatedTools);
    }
}

这些注解既支持同步实现,也支持异步实现,并且可以通过 clients 参数为特定客户端进行配置:

@McpLogging(clients = "server1")
public void handleServer1Logs(LoggingMessageNotification notification) {
    // 处理特定服务器的日志
    logToFile("server1.log", notification);
}

@McpSampling(clients = "server1")
public Mono<CreateMessageResult> handleAsyncSampling(CreateMessageRequest request) {
    return Mono.fromCallable(() -> {
        String response = generateLLMResponse(request);
        return CreateMessageResult.builder()
            .role(Role.ASSISTANT)
            .content(new TextContent(response))
            .model("gpt-4")
            .build();
    }).subscribeOn(Schedulers.boundedElastic());
}

有关所有可用注解及其使用方式的详细信息,请参见 MCP Client Annotations 文档。

使用示例

向项目中添加相应的 Starter 依赖,并在 application.propertiesapplication.yml 中配置客户端:

spring:
  ai:
    mcp:
      client:
        enabled: true
        name: my-mcp-client
        version: 1.0.0
        request-timeout: 30s
        type: SYNC  # 对于响应式应用可使用 ASYNC
        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"

MCP 客户端 Bean 会自动配置并可注入使用:

@Autowired
private List<McpSyncClient> mcpSyncClients;  // 同步客户端

// 或者

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

当工具回调启用(默认行为)时,注册的 MCP 工具会通过 ToolCallbackProvider 提供:

@Autowired
private SyncMcpToolCallbackProvider toolCallbackProvider;

ToolCallback[] toolCallbacks = toolCallbackProvider.getToolCallbacks();

示例应用

  • Brave Web Search Chatbot — 一个使用模型上下文协议(MCP)与 Web 搜索服务器交互的聊天机器人示例。
  • Default MCP Client Starter — 使用默认的 spring‑ai‑starter‑mcp‑client MCP Client Boot Starter 的简单示例项目。
  • WebFlux MCP Client Starter — 使用 spring‑ai‑starter‑mcp‑client‑webflux MCP Client Boot Starter(基于 WebFlux 的实现)的示例项目。

附加资源