【Spring AI MCP】三、MCP 客户端
Spring AI MCP客户端启动器为Spring Boot应用提供模型上下文协议(MCP)的自动配置,支持多种传输协议和客户端类型。核心功能包括多实例管理、工具集成、自动序列化/反序列化、降级兼容等。提供标准(基于JDK HttpClient)和WebFlux两种启动器,支持STDIO/HTTP/SSE等多种传输方式。配置灵活,可通过YML文件定义服务器连接参数,包括命令执行、环境变量、URL
Spring AI MCP学习目录
一、MCP 原理详解
二、MCP 客户端
三、MCP 客户端
四、MCP 服务端
五、SpringAI MCP 服务端
六、SpringAI MCP 服务端 STDIO & SSE
七、SpringAI MCP 服务端 Streamable-HTTP
八、SpringAI MCP 服务端 Stateless Streamable-HTTP
九、 MCP 安全(Security)
十、SpringAI MCP 安全(Security)
十一、SpringAI MCP 客户端注解
十二、SpringAI MCP 服务端注解
十三、SpringAI MCP 特殊参数(Special Parameters)
一、简介
Spring AI MCP(模型上下文协议)客户端启动器为Spring Boot应用提供MCP客户端的自动配置,支持同步/异步客户端实现及多种传输协议。
核心功能包括:
- 多客户端实例管理
- 自动初始化(可选)
- 支持STDIO/Http/SSE/Streamable HTTP传输
- 与Spring AI工具执行框架集成
- 工具过滤(按需启用/禁用)
- 可自定义工具名前缀(避免冲突)
- 应用关闭时自动清理资源
- 通过自定义器配置客户端创建
MCP 客户端应支持:
| 功能 | 说明 |
|---|---|
| ✅ 构造 MCP 请求 | 在标准 LLM 请求基础上,添加 context 字段(含 tools/retrieval/images 等) |
| ✅ 自动序列化/反序列化 | 将工具函数、检索配置转为 MCP JSON Schema |
| ✅ 处理 Tool Call 响应 | 解析模型返回的 tool_calls,执行本地函数并回传结果 |
| ✅ 降级兼容 | 当后端不支持 MCP 时,自动转为传统 prompt 拼接模式 |
| ✅ 与主流框架集成 | 如 LangChain、LlamaIndex、Spring AI |
MCP 客户端工作流程
二、启动器
Standard MCP Client
标准启动器通过STDIO(进程内)、SSE、Streamable-HTTP和Stateless Streamable-HTTP传输协议,同时连接一个或多个MCP服务器。SSE和Streamable-HTTP传输使用基于JDK HttpClient的实现。每个MCP服务器连接会创建一个新的MCP客户端实例,您可以选择同步(SYNC)或异步(ASYNC)客户端(注意:不能混用同步和异步客户端)。对于生产环境部署,建议使用基于WebFlux的SSE和StreamableHttp连接,并搭配spring-ai-starter-mcp-client-webflux启动器。
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-client</artifactId>
</dependency>
WebFlux Client
WebFlux启动器与标准启动器功能类似,但采用基于WebFlux的Streamable-HTTP、无状态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 |
| name | MCP客户端实例名称 | spring-ai-mcp-client |
| version | MCP客户端实例版本 | 1.0.0 |
| initialized | 是否在创建时初始化客户端 | true |
| request-timeout | MCP客户端请求超时时长 | 20s |
| type | 客户端类型(SYNC或ASYNC)。所有客户端必须同为同步或异步,不支持混合 | SYNC |
| root-change-notification | 为所有客户端启用/禁用根变更通知 | true |
| toolcallback.enabled | 启用/禁用MCP工具回调与Spring AI工具执行框架的集成 | true |
注解
MCP客户端注解提供了一种使用Java注解实现MCP客户端处理器的声明式方式。客户端mcp-annotations属性以spring.ai.mcp.client.annotation-scanner为前缀:
| 属性 | 描述 | 默认值 |
|---|---|---|
| enabled | 启用/禁用MCP客户端注解自动扫描 | true |
Stdio
标准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"
Streamable-HTTP
用于连接 Streamable-HTTP 和无状态 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
服务器发送事件(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:
# Simple configuration using default /sse endpoint
server1:
url: http://localhost:8080
# Custom SSE endpoint
server2:
url: http://otherserver:8081
sse-endpoint: /custom-sse
# Complex URL with path and token (like MCP Hub)
mcp-hub:
url: http://localhost:3000
sse-endpoint: /mcp-hub/sse/cf9ec4527e3c4a2cbb149a85ea45ab01
# SSE endpoint with query parameters
api-server:
url: https://api.example.com
sse-endpoint: /v1/mcp/events?token=abc123&format=json
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
四、特性
1、同步/异步
- 同步客户端 - 默认客户端类型(spring.ai.mcp.client.type=SYNC),适用于传统的请求-响应模式,采用阻塞操作
注意:同步客户端仅会注册同步的MCP注解方法,异步方法将被忽略
- 异步客户端 - 适用于采用非阻塞操作的响应式应用,通过配置 spring.ai.mcp.client.type=ASYNC 启用
注意:异步客户端仅会注册异步的MCP注解方法,同步方法将被忽略
2、客户自定义
自动配置通过回调接口提供广泛的客户端规范定制能力。这些自定义器允许您配置MCP客户端行为的各个方面,从请求超时到事件处理和消息处理。
提供以下定制选项:
- 请求配置:设置自定义请求超时
- 自定义采样处理器:服务器通过客户端向LLM请求采样(补全或生成)的标准化方式。该流程允许客户端保持对模型访问、选择和权限的控制,同时使服务器能够利用AI能力——无需服务器API密钥
- 文件系统(根目录)访问:客户端向服务器暴露文件系统根目录的标准化方式。根目录定义了服务器在文件系统中可操作的范围边界,使其了解可以访问哪些目录和文件。服务器可以从支持客户端请求根目录列表,并在该列表发生变化时接收通知
- 信息征询处理器:服务器在交互过程中通过客户端向用户请求额外信息的标准化方式
- 事件处理器:当特定服务器事件发生时通知客户端的处理器:
- 工具变更通知 - 当可用服务器工具列表发生变化时
- 资源变更通知 - 当可用服务器资源列表发生变化时
- 提示词变更通知 - 当可用服务器提示词列表发生变化时
- 日志处理器:服务器向客户端发送结构化日志消息的标准化方式
- 进度处理器:服务器向客户端发送结构化进度消息的标准化方式
客户端可通过设置最低日志级别来控制日志详细程度
样例:
@Component
public class CustomMcpAsyncClientCustomizer implements McpAsyncClientCustomizer {
@Override
public void customize(String serverConfigurationName, McpClient.AsyncSpec spec) {
// Customize the async client configuration
spec.requestTimeout(Duration.ofSeconds(30));
}
}
3、传输协议
自动配置支持以下传输类型:
- 标准I/O(Stdio):通过spring-ai-starter-mcp-client和spring-ai-starter-mcp-client-webflux激活,适用于进程内通信。
- HTTP/SSE与Streamable-HTTP:
- 基于HttpClient的实现(spring-ai-starter-mcp-client)。
- 基于WebFlux的实现(spring-ai-starter-mcp-client-webflux),适用于响应式高并发场景。
注:SSE(服务器发送事件)和Streamable-HTTP均为流式传输协议,后者为前者的演进版本。
4、Tool Filtering
MCP客户端启动器通过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。如需多个过滤器,请将其组合到单个复合过滤器实现中。
5、Tool Name Prefix Generation
MCP客户端启动器通过McpToolNamePrefixGenerator接口支持可自定义的工具名称前缀生成功能。该特性通过为工具名称添加唯一前缀,有助于在集成来自多个MCP服务器的工具时避免命名冲突。
默认情况下,如果未提供自定义的McpToolNamePrefixGenerator bean,启动器将使用DefaultMcpToolNamePrefixGenerator,确保所有MCP客户端连接中的工具名称唯一。默认生成器具有以下特性:
- 追踪所有现有连接和工具名称以确保唯一性
- 通过将非字母数字字符替换为下划线来格式化工具名称(例如,my-tool变为my_tool)
- 在不同连接中检测到重复工具名称时,添加计数器前缀(例如,alt_1_toolName、alt_2_toolName)
- 线程安全且保持幂等性——相同的(客户端、服务器、工具)组合始终获得相同的唯一名称
- 确保最终名称不超过64个字符(必要时从开头截断)
例如:
- 首次出现的search工具 → search
- 来自不同连接的第二个search工具 → alt_1_search
- 包含特殊字符的工具my-special-tool → my_special_tool
样例:
@Component
public class CustomToolNamePrefixGenerator implements McpToolNamePrefixGenerator {
@Override
public String prefixedToolName(McpConnectionInfo connectionInfo, Tool tool) {
// Custom logic to generate prefixed tool names
// Example: Use server name and version as prefix
String serverName = connectionInfo.initializeResult().serverInfo().name();
String serverVersion = connectionInfo.initializeResult().serverInfo().version();
return serverName + "_v" + serverVersion.replace(".", "_") + "_" + tool.name();
}
}
6、MCP Meta 转换
MCP客户端启动器通过ToolContextToMcpMetaConverter接口,支持将Spring AI的ToolContext自定义转换为MCP工具调用元数据。该功能允许您将额外的上下文信息(如用户ID、密钥令牌)作为元数据,与LLM生成的调用参数一同传递。
例如,可以在工具上下文中传递MCP progressToken至MCP进度流,以追踪长时间运行操作的进度:
Copy Code
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();
默认情况下,如果未提供自定义转换器bean,启动器将使用ToolContextToMcpMetaConverter.defaultConverter(),其功能包括:
- 过滤掉MCP交换键(McpToolUtils.TOOL_CONTEXT_MCP_EXCHANGE_KEY)
- 过滤掉值为null的条目
- 将所有其他上下文条目作为元数据透传
五、MCP客户端注解
MCP客户端启动器能够自动检测并注册使用注解的方法,用于处理各类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) {
// Process the request and generate a response
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");
// Update local tool registry
toolRegistry.updateTools(updatedTools);
}
}
六、样例
样例代码:https://gitee.com/mqiqe/zixiai
调用天气mcp的样例:
Maven配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.zixi.ai</groupId>
<artifactId>zixi-ai-eaxmple</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<groupId>com.zixi.ai.eaxmple.mcp.weather.client</groupId>
<artifactId>mcp-weather-client</artifactId>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-client-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-ollama</artifactId>
</dependency>
</dependencies>
</project>
相关代码:
package com.zixi.ai.eaxmple.mcp.weather.client;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
@SpringBootApplication(exclude = {
org.springframework.ai.mcp.client.autoconfigure.SseHttpClientTransportAutoConfiguration.class
})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
// 直接硬编码中文问题,避免配置文件编码问题
// @Value("${ai.user.input}")
// private String userInput;
private String userInput1 = "青岛天气如何?";
private String userInput2 = "将字符串:Wfg heloa,wfgxif 转为大写";
@Bean
public CommandLineRunner predefinedQuestions(ChatClient.Builder chatClientBuilder, ToolCallbackProvider tools,
ConfigurableApplicationContext context) {
return args -> {
var chatClient = chatClientBuilder
.defaultToolCallbacks(tools)
.build();
System.out.println("\n>>> QUESTION: " + userInput1);
System.out.println("\n>>> ASSISTANT: " + chatClient.prompt(userInput1).call().content());
System.out.println("\n>>> QUESTION: " + userInput2);
System.out.println("\n>>> ASSISTANT: " + chatClient.prompt(userInput2).call().content());
context.close();
};
}
}
配置文件
server:
port: 8888
servlet:
encoding:
charset: UTF-8
enabled: true
force: true
spring:
application:
name: mcp
main:
web-application-type: none
ai:
ollama:
base-url: http://localhost:11434
chat:
# model: qwen3:0.6b
enabled: true
options:
model: qwen3:0.6b
temperature: 0.7
mcp:
client:
sse:
connections:
server1:
# 实际的连接地址为:http://localhost:8080/sse/mcp
url: http://localhost:8080/
mandatory-file-encoding: UTF-8
# 调试日志
logging:
level:
io:
modelcontextprotocol:
client: DEBUG
spec: DEBUG
ai:
user:
input: 北京的天气如何?
更多推荐



所有评论(0)