MCP 客户端启动Starters
Spring AI MCP(模型上下文协议)客户端启动Starters为 Spring Boot 应用中的 MCP 客户端功能提供自动配置。 它支持同步和异步客户端实现,并支持多种传输选项。
MCP 客户端启动Starters提供:
-
多客户端实例管理
-
自动客户端初始化(如果启用的话)
-
支持多命名传输(STDIO、Http/SSE 和可流式 HTTP)
-
与 Spring AI 工具执行框架的集成
-
工具过滤功能,用于选择性工具的包含/排除
-
可自定义工具名称前缀生成,以避免命名冲突
-
当应用上下文关闭时,实现正确的生命周期管理,自动清理资源
-
通过自定义工具实现可定制的客户端创建
首先
标准MCP客户端
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-client</artifactId>
</dependency>
标准Starters通过STDIO(进行中),SSE,可流式HTTP和无状态流式HTTP运输。
SSE和可流式Http传输采用了基于JDK的Http客户端传输实现。
每次连接到MCP服务器都会创建一个新的MCP客户端实例。
你可以选择其中任何一个同步或异步MCP客户端(注意:不能混合使用同步和异步客户端)。
对于生产部署,我们建议使用基于WebFlux的SSE和StreamableHttp连接,配合Spring-AI-starter-mcp-client-webflux.
配置属性
常见性质
常见的性质前缀为spring.ai.mcp.client:
| 属性 | 描述 | 默认值 |
|---|---|---|
|
启用/禁用MCP客户端 |
|
|
MCP客户端实例名称 |
|
|
MCP 客户端实例的版本 |
|
|
是否在创建时初始化客户端 |
|
|
MCP客户端请求的超时持续时间 |
|
|
客户端类型(同步或非同步)。所有客户端必须是同步或异步;不支持混合 |
|
|
启用/禁用所有客户端的根更改通知 |
|
|
启用/禁用MCP工具回调与Spring AI工具执行框架的集成 |
|
MCP 注释属性
MCP 客户端注释提供了一种声明式方式,利用 Java 注释实现 MCP 客户端处理器。
客户端mcp注释属性前缀为Spring.ai.mcp.client.annotation-scanner:
| 属性 | 描述 | 默认值 |
|---|---|---|
|
启用/禁用MCP客户端注释自动扫描 |
|
Stdio交通资产
标准I/O传输的属性前缀为spring.ai.mcp.client.stdio:
| 属性 | 描述 | 默认值 |
|---|---|---|
|
包含 MCP 服务器配置的 JSON 格式资源 |
- |
|
命名stdio连接配置的映射 |
- |
|
MCP服务器执行命令 |
- |
|
命令参数列表 |
- |
|
服务器进程环境变量映射 |
- |
示例配置:
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 桌面格式如下:
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"/Users/username/Desktop",
"/Users/username/Downloads"
]
}
}
}
Windows STDIO 配置
在Windows上,像这样的命令NPX,NPM和节点以批处理文件的形式实现 (.cmd),而不是本地可执行文件。爪哇的ProcessBuilder无法直接执行批处理文件,并且需要cmd.exe /c包装纸。 |
为什么Windows需要特殊处理
当Java的ProcessBuilder(内部使用StdioClientTransport)尝试在Windows上生成进程,只能执行:
-
本地可执行文件(
.exe文件) -
系统命令可使用
cmd.exe
Windows批处理文件,比如npx.cmd,npm.cmd,甚至python.cmd(来自 Microsoft Store 的)要求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 approach
var winArgs = new ArrayList<>(Arrays.asList(
"/c", "npx", "-y", "@modelcontextprotocol/server-filesystem", "target"));
stdioParams = ServerParameters.builder("cmd.exe")
.args(winArgs)
.build();
} else {
// Linux/Mac: direct npx approach
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.cmd,npm.cmd- 节点包管理器 -
python.cmd- Python(Microsoft Store 安装) -
pip.cmd- Python 包管理器 -
mvn.cmd- Maven 包装 -
gradle.cmd- Gradle 包装器 -
习惯
.cmd或。蝙蝠脚本
参考实现
请参阅 Spring AI 示例——文件系统,了解一个完整的跨平台 MCP 客户端实现,能够自动检测作系统并适当配置客户端。
可流式HTTP传输属性
用于连接可流式HTTP和无状态流式HTTP的MCP服务器。
可流式HTTP传输的属性前缀为spring.ai.mcp.client.streamable-http:
| 属性 | 描述 | 默认值 |
|---|---|---|
|
命名的 Streamable-HTTP 连接配置映射 |
- |
|
用于与MCP服务器进行Streamable-Http通信的基础URL端点 |
- |
|
用于连接的Streamable-HTTP端点(作为URL后缀) |
|
示例配置:
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:
| 属性 | 描述 | 默认值 |
|---|---|---|
|
命名SSE连接配置映射 |
- |
|
用于与MCP服务器的SSE通信的基础URL端点 |
- |
|
连接时使用的SSE端点(作为URL后缀) |
|
示例配置:
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
URL拆分指南
当你拥有完整的SSE URL后,将其拆分为基础URL和端点路径:
| 完整网址 | 配置 |
|---|---|
|
|
|
|
|
|
可流式 HTTP 传输属性
可流式Http传输的属性前缀为spring.ai.mcp.client.streamable-http:
| 属性 | 描述 | 默认值 |
|---|---|---|
|
命名的可流式 Http 连接配置映射 |
- |
|
用于与MCP服务器进行Streamable-Http通信的基础URL端点 |
- |
|
用于连接的Streamable-HTTP端点(作为URL后缀) |
|
示例配置:
spring:
ai:
mcp:
client:
streamable-http:
connections:
server1:
url: http://localhost:8080
server2:
url: http://otherserver:8081
endpoint: /custom-sse
特征
同步/异步客户端类型
起始程序支持两种客户端:
-
同步 - 默认客户端类型(
spring.ai.mcp.client.type=SYNC),适用于传统的请求-响应模式,并带有阻塞作
注意:SYNC客户端只注册同步MCP注释方法。异步方法将被忽略。
-
异步——适合具有非阻塞作的响应式应用,配置为
spring.ai.mcp.client.type=ASYNC
注意:ASYNC客户端只会注册异步MCP注释方法。同步方法将被忽略。
客户端定制
自动配置通过回调接口提供广泛的客户规格定制功能。这些自定义工具允许你配置MCP客户端行为的各个方面,从请求超时到事件处理和消息处理。
客户端定制示例
你可以实现其中任何一种McpSyncClientCustomizer对于同步客户端或McpAsyncClientCustomizer对于异步客户端,取决于你的应用需求。
-
Sync
-
Async
@Component
public class CustomMcpSyncClientCustomizer implements McpSyncClientCustomizer {
@Override
public void customize(String serverConfigurationName, McpClient.SyncSpec spec) {
// Customize the request timeout configuration
spec.requestTimeout(Duration.ofSeconds(30));
// Sets the root URIs that this client can access.
spec.roots(roots);
// Sets a custom sampling handler for processing message creation requests.
spec.sampling((CreateMessageRequest messageRequest) -> {
// Handle sampling
CreateMessageResult result = ...
return result;
});
// Sets a custom elicitation handler for processing elicitation requests.
spec.elicitation((ElicitRequest request) -> {
// handle elicitation
return new ElicitResult(ElicitResult.Action.ACCEPT, Map.of("message", request.message()));
});
// Adds a consumer to be notified when progress notifications are received.
spec.progressConsumer((ProgressNotification progress) -> {
// Handle progress notifications
});
// Adds a consumer to be notified when the available tools change, such as tools
// being added or removed.
spec.toolsChangeConsumer((List<McpSchema.Tool> tools) -> {
// Handle tools change
});
// Adds a consumer to be notified when the available resources change, such as resources
// being added or removed.
spec.resourcesChangeConsumer((List<McpSchema.Resource> resources) -> {
// Handle resources change
});
// Adds a consumer to be notified when the available prompts change, such as prompts
// being added or removed.
spec.promptsChangeConsumer((List<McpSchema.Prompt> prompts) -> {
// Handle prompts change
});
// Adds a consumer to be notified when logging messages are received from the server.
spec.loggingConsumer((McpSchema.LoggingMessageNotification log) -> {
// Handle log messages
});
}
}
@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));
}
}
这serverConfigurationName参数是应用定制器的服务器配置名称,也是创建MCP客户端的对象。
MCP客户端自动配置会自动检测并应用应用上下文中的任何自定义器。
交通支持
自动配置支持多种传输类型:
-
标准I/O(标准)(由
Spring-AI-starter-mcp-client和Spring-AI-starter-mcp-client-webflux) -
(Http客户端)HTTP/SSE 和 Streamable-HTTP(由
Spring-AI-starter-mcp-client) -
(网络流)HTTP/SSE 和 Streamable-HTTP(由
Spring-AI-starter-mcp-client-webflux)
工具过滤
MCP 客户端启动Starters支持通过McpToolFilter接口。这允许你根据自定义条件(如MCP连接信息或工具属性)选择性地包含或排除工具。
要实现工具过滤,可以创建一个实现McpToolFilter接口:
@Component
public class CustomMcpToolFilter implements McpToolFilter {
@Override
public boolean test(McpConnectionInfo connectionInfo, McpSchema.Tool tool) {
// Filter logic based on connection information and tool properties
// Return true to include the tool, false to exclude it
// Example: Exclude tools from a specific client
if (connectionInfo.clientInfo().name().equals("restricted-client")) {
return false;
}
// Example: Only include tools with specific names
if (tool.name().startsWith("allowed_")) {
return true;
}
// Example: Filter based on tool description or other properties
if (tool.description() != null &&
tool.description().contains("experimental")) {
return false;
}
return true; // Include all other tools by default
}
}
这McpConnectionInfoRecord提供以下访问权限:
-
客户端功能- MCP客户端的能力 -
客户信息- 关于MCP客户端的信息(名称和版本) -
初始化结果- MCP服务器的初始化结果
该过滤器会自动检测并应用于同步和异步MCP工具回调提供者。 如果没有提供自定义过滤器,所有发现的工具默认都会包含在内。
注:只有一个McpToolFilterBEAN 应在应用上下文中定义。
如果需要多个Filter,则将它们合并为单一的复合Filter实现。
工具名称前缀生成
MCP 客户端启动Starters支持通过McpToolNamePrefixGenerator接口。此功能通过在工具名称中添加独特的前缀,有助于避免在整合多个 MCP 服务器工具时出现命名冲突。
默认情况下,如果没有自定义McpToolNamePrefixGenerator豆子是提供的,发酵者使用DefaultMcpToolNamePrefixGenerator这确保了所有MCP客户端连接中工具名称的唯一。默认生成器:
-
跟踪所有现有连接和工具名称,确保唯一性
-
格式化工具名称通过用下划线替换非字母数字字符(例如,
我的工具成为my_tool) -
当不同连接中检测到重复工具名称时,会添加计数器前缀(例如,
alt_1_toolName,alt_2_toolName) -
它是线程安全的,并且保持幂等性——客户端、服务器、工具的相同组合总是获得相同的唯一名称
-
确保最终名字不超过64个字符(如有需要,从开头截断)
例如:
* 工具的首次出现搜索→搜索* 工具的第二次出现搜索来自不同的连接→alt_1_search* 带有特殊字符的工具我的——特制工具→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();
}
}
这McpConnectionInfo记录提供了关于MCP连接的全面信息:
-
客户端功能- MCP客户端的能力 -
客户信息- 关于MCP客户端的信息(名称、标题和版本) -
初始化结果- MCP服务器的初始化结果,包括服务器信息
内置前缀生成器
该框架提供了若干内置前缀生成器:
-
DefaultMcpToolNamePrefixGenerator- 通过追踪重复工具并在需要时添加计数器前缀(如果没有自定义豆子,默认使用)确保唯一工具名称 -
McpToolNamePrefixGenerator.noPrefix()- 返回工具名称,不带任何前缀(如果多个服务器提供相同名称的工具,可能会引起冲突)
要完全禁用前缀并使用原始工具名(如果使用多个MCP服务器,不建议这样做),请将无前缀生成器注册为豆子:
@Configuration
public class McpConfiguration {
@Bean
public McpToolNamePrefixGenerator mcpToolNamePrefixGenerator() {
return McpToolNamePrefixGenerator.noPrefix();
}
}
前缀生成器通过 Spring 的对象提供者机制。
如果没有提供自定义生成器豆,则DefaultMcpToolNamePrefixGenerator自动使用。
使用McpToolNamePrefixGenerator.noPrefix()在多个MCP服务器中,重复的工具名称会导致非法州例外.默认DefaultMcpToolNamePrefixGenerator通过自动为重复工具名称添加唯一前缀来防止这种情况。 |
工具上下文到 MCP 元转换器
比如你可以通过MCPprogressToken(进步Tokens)在工具上下文中,将 MCP 进度流用于跟踪长期运行作的进展:
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();
默认情况下,如果没有提供自定义转换器豆,起始器会使用工具上下文ToMcpMetaConverter.defaultConverter()哪:
-
过滤掉MCP交换密钥(
McpToolUtils.TOOL_CONTEXT_MCP_EXCHANGE_KEY) -
过滤出空值的条目
-
通过所有其他上下文条目作为元数据传递
您可以通过提供自己的实现来自定义这种行为:
@Component
public class CustomToolContextToMcpMetaConverter implements ToolContextToMcpMetaConverter {
@Override
public Map<String, Object> convert(ToolContext toolContext) {
if (toolContext == null || toolContext.getContext() == null) {
return Map.of();
}
// Custom logic to convert tool context to MCP metadata
Map<String, Object> metadata = new HashMap<>();
// Example: Add custom prefix to all keys
for (Map.Entry<String, Object> entry : toolContext.getContext().entrySet()) {
if (entry.getValue() != null) {
metadata.put("app_" + entry.getKey(), entry.getValue());
}
}
// Example: Add additional metadata
metadata.put("timestamp", System.currentTimeMillis());
metadata.put("source", "spring-ai");
return metadata;
}
}
内置转换器
该框架内置了转换器:
-
工具上下文ToMcpMetaConverter.defaultConverter()- 过滤MCP交换密钥和空值(如果没有提供自定义豆,默认使用) -
ToolContextToMcpMetaConverter.noOp()- 返回空映射,有效禁用上下文到元数据的转换
要完全禁用上下文转元数据转换:
@Configuration
public class McpConfiguration {
@Bean
public ToolContextToMcpMetaConverter toolContextToMcpMetaConverter() {
return ToolContextToMcpMetaConverter.noOp();
}
}
转换器通过Spring的同步和异步MCP工具回调自动检测并应用对象提供者机制。
如果没有提供自定义转换器,默认转换器会自动使用。
MCP 客户端注释
MCP 客户端启动Starters自动检测并注册处理各种 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);
}
}
注释支持同步和异步实现,并可针对特定客户端配置客户端参数:
@McpLogging(clients = "server1")
public void handleServer1Logs(LoggingMessageNotification notification) {
// Handle logs from specific server
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客户端注释文档。
使用示例
给你的项目添加合适的起始依赖,并配置客户端application.properties或application.yml:
spring:
ai:
mcp:
client:
enabled: true
name: my-mcp-client
version: 1.0.0
request-timeout: 30s
type: SYNC # or ASYNC for reactive applications
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客户端豆子将自动配置并可供注入:
@Autowired
private List<McpSyncClient> mcpSyncClients; // For sync client
// OR
@Autowired
private List<McpAsyncClient> mcpAsyncClients; // For async client
当工具回调启用(默认行为)时,所有MCP客户端注册的MCP工具将作为ToolCallbackProvider实例:
@Autowired
private SyncMcpToolCallbackProvider toolCallbackProvider;
ToolCallback[] toolCallbacks = toolCallbackProvider.getToolCallbacks();
应用示例
-
Brave Web Search 聊天机器人——一款使用 Model Context 协议与网络搜索服务器交互的聊天机器人。
-
默认MCP客户端Starters——使用默认的简单示例
Spring-AI-starter-mcp-clientMCP 客户端启动Starters。 -
WebFlux MCP 客户端Starters——使用
Spring-AI-starter-mcp-client-webfluxMCP 客户端启动Starters。