SpringAI 通过 SpringBoot 集成扩展了 MCP Java SDK ,提供了客户端和服务端 starter,让 AI 应用程序快速支持 MCP。
接下来直接演示。
1_调用公用MCP
在使用其他开发者提供好的 MCP 服务时,仅仅引入 MCP 的客户端依赖即可。
SpringAI 提供了如下两种 MCP Client 依赖,根据需求选择一种引入即可(常见第二种)。
仅支持 Stdio 的依赖
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-client</artifactId>
</dependency>
既支持 SSE(远程)也支持 Stdio(进程内)
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-client-webflux</artifactId>
</dependency>
以使用百度地图提供的 MCP 服务为例,演示如何使用 Stdio 的方式调用公用的 MCP Server。
首先,对 MCP 进行配置,Stdio 的配置方式有两种。
一种是直接配置全局配置文件中:
spring:
ai:
mcp:
client:
enabled: true
name: spring-ai-mcp-client
version: 1.0.0
type: sync # 或 async 调用响应式流应用
request-timeout: 60000
stdio:
connections:
baidu-map:
command: cmd
args:
- "/c"
- "npx"
- "-y"
- "@baidumap/mcp-server-baidu-map" # 第一次启动会安装此包
env:
BAIDU_MAP_API_KEY: xxx # 百度地图开放平台查看
另外一种是使用 Claude Desktop 格式的 JSON 文件单独存放(推荐)
{
"mcpServers": {
"baidu-map": {
"command": "cmd",
"args": [
"/c",
"npx",
"-y",
"@baidumap/mcp-server-baidu-map"
],
"env": {
"BAIDU_MAP_API_KEY": "xxxx"
}
}
}
}
然后在 application.yaml
中指定文件的位置
spring:
ai:
mcp:
client:
request-timeout: 60000
stdio:
servers-configuration: classpath:/mcp/stdio-server-config.json
通过 ToolCallbackProvider 绑定 ChatClient,ToolCallbackProvider 可以看做外部工具提供商。
@Bean
public ChatClient serviceChatClient(OpenAiChatModel model, ChatMemory chatMemory, ToolCallbackProvider toolCallbackProvider) {
return ChatClient.builder(model)
.defaultSystem(new ClassPathResource("call.txt"))
.defaultAdvisors(
MessageChatMemoryAdvisor.builder(chatMemory).build(), // CHAT MEMORY
new SimpleLoggerAdvisor())
.defaultToolCallbacks(toolCallbackProvider)//关键
.build();
}
如果想观察到调用 mcp 工具的日志信息进行调试,可以添加如下配置:
logging:
level:
io:
modelcontextprotocol:
client: debug
spec: debug
测试结果:
2_Stdio方式
以查询天气为目标,定义 MCP-Server 工程,引入 Stdio 服务端依赖
<!-- mcp-server依赖-->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server</artifactId>
</dependency>
配置 application.yaml
属性:
spring:
application:
name: mcp-server
main:
web-application-type: none # stdio 并不需要启动为web应用
banner-mode: off # 关掉banner 下方有说明
ai:
mcp:
server:
name: mcp-server # 应用名称
version: 1.0.0 # 版本
# 注意: 您必须禁用 banner 和控制台日志记录,以允许 Stdio 传输工作!!!
定义查询城市天气信息的工具类
@Service
public class WeatherService {
@Tool(description = "根据城市名称获得天气信息")
public String getWeather(@ToolParam(description = "城市名称") String cityName) {
return cityName + "今日天气 晴";
}
}
将工具绑定到 ToolCallbackProvider:
@SpringBootApplication
public class McpServerApplication {
public static void main(String[] args) {
SpringApplication.run(McpServerApplication.class, args);
}
@Bean
public ToolCallbackProvider weatherTools(WeatherService weatherService){
return MethodToolCallbackProvider.builder().toolObjects(weatherService).build();
}
// 客户端使用 .defaultToolCallbacks(toolCallbackProvider) 添加
}
将项目打成 jar 包后,在客户端 mcp/stdio-server-config.json
文件的 mcpServers 属性中新增如下配置:
"mcp-server-weather": {
"command": "java",
"args": [
"-Dspring.ai.mcp.server.stdio=true",
"-Dlogging.pattern.console=",
"-jar",
"D:\\WorkSpace\\IdeaProject\\jar\\mcp-server-weather.jar"
]
}
说明:
- command:内部 java 程序直接调用,所以无需 cmd 命令。
- args:以 stdio 的方式启动,清空控制台,否则 mcp-server 启动也会打印干扰信息。
3_Stdio实现原理
当客户端启动之后,会由 McpClientAutoConfiguration 初始化并启动传输连接获取 tool 列表。
传输连接的关键流程及类图如下:
在 StdioClientTransport 的 connect 方法中,会利用 ProcessBuilder 对象将配置文件中的命令、参数、环境等信息封装起来创建子进程并启动。
之后建立输入、输出流连接循环地读取或写入信息,源码中的大致实现如下:
// 准备指定的命令、参数、环境
List<String> fullCommand = new ArrayList<>();
fullCommand.add(params.getCommand());
fullCommand.addAll(params.getArgs());
ProcessBuilder processBuilder = this.getProcessBuilder();
processBuilder.command(fullCommand);
processBuilder.environment().putAll(params.getEnv());
// 启动子进程
this.process = processBuilder.start();
// 循环地从连接中读取数据
try (BufferedReader processReader = new BufferedReader(new InputStreamReader(proces
String line;
while (!isClosing && (line = processReader.readLine()) != null) {
try {
JSONRPCMessage message = McpSchema.deserializeJsonRpcMessage(this.objec
if (!this.inboundSink.tryEmitNext(message).isSuccess()) {
if (!isClosing) {
logger.error("Failed to enqueue inbound message: {}", message);
}
break;
}
}
}
}
// 如果有消息向连接中写入数据
try {
String jsonMessage = objectMapper.writeValueAsString(message);
// Escape any embedded newlines in the JSON message as per spec:
// https://spec.modelcontextprotocol.io/specification/basic/transports/#stdio
// - Messages are delimited by newlines, and MUST NOT contain
// embedded newlines.
jsonMessage = jsonMessage.replace("\r\n", "\\n").replace("\n", "\\n").replace("\r", "\\n");
var os = this.process.getOutputStream();
synchronized (os) {
os.write(jsonMessage.getBytes(StandardCharsets.UTF_8));
os.write("\n".getBytes(StandardCharsets.UTF_8));
os.flush();
}
s.next(message);
}
注意:这种方式传输信息时,服务端运行时打印的各种信息,如:banner、日志等都会成为干扰项。
4_SSE方式
最新版 MCP 协议已经弃用了这种方式,转为感知的 Streamable HTTP,但 SpringAI 目前还不支持 Streamable HTTP。
SSE 需要将 MCP Server 部署为 Web 服务。
以加法计算为例,定义 MCP-Server 工程,引入 SSE 服务端依赖
<!--支持webflux以及stdio的方式,还支持webflux的方式暴露服务,-->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server-webflux</artifactId>
</dependency>
<!---webmvc是基于mvc的依赖,同样支持stdio,还支持-webmvc的方式暴露服务-->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
</dependency>
配置 application.yaml
属性:
spring:
application:
name: mcp-server
main:
# (mvc,webflux依赖都引入则只能使用webflux,不然客户端会404)
web-application-type: reactive # webflux的方式暴露服务,除此外还有mvc-servlet
ai:
mcp:
server:
name: mcp-server # 应用名称
version: 1.0.0 # 版本
type: async # 异步响应通信方式
base-url: # base-url
sse-endpoint: /sse # 切入点,client默认会向url/sse下发送请求
server:
port: 8888
定义加法计算的工具类,这里让它返回错误的结果方便验证
@Service
public class CalculationService {
@Tool(description = "两数加法计算")
public Long getWeather(@ToolParam(description = "加数") Long addend,
@ToolParam(description = "被加数") Long summand) {
return addend * summand;
}
}
将工具绑定到 ToolCallbackProvider:
@SpringBootApplication
public class McpServerApplication {
public static void main(String[] args) {
SpringApplication.run(McpServerApplication.class, args);
}
@Bean
public ToolCallbackProvider weatherTools(CalculationService calculationService) {
return MethodToolCallbackProvider.builder().toolObjects(calculationService).build();
}
// 客户端使用 .defaultToolCallbacks(toolCallbackProvider) 添加
}
直接启动 MCP Server 后,客户端新增如下配置即调用服务端提供的工具
spring:
ai:
mcp:
client:
sse: # 新增此处配置,与stdio同级,其他配置无需更改
connections: # 配置多个连接
addition-calculation: # 服务名称
url: http://localhost:8888 # mcp-server 地址
验证:
5_自定义MCP客户端
在 Spring AI 中,MCP 客户端分为两类:
- 同步客户端(Sync Client):默认类型,适用于传统的请求-响应模式,使用阻塞操作。
- 异步客户端(Async Client):适用于响应式应用,使用非阻塞操作。可以通过配置项
spring.ai.mcp.client.type=async
启用。
Spring 提供了自动装配机制,开发者只需实现 McpSyncClientCustomizer 或 McpAsyncClientCustomizer 接口,就能对 MCP 客户端的各个方面进行个性化配置。
例如,控制请求超时、事件监听以及消息处理逻辑。
import io.modelcontextprotocol.client.McpClient;
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpSchema.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.mcp.customizer.McpSyncClientCustomizer;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.util.List;
/**
* 自定义同步 MCP 客户端配置器
* (兼容所有的传输方式: stdio、mvc、webflux 传输方式)
*/
@Slf4j
@Component
public class CustomMcpSyncClientCustomizer implements McpSyncClientCustomizer {
@Override
public void customize(String serverConfigurationName, McpClient.SyncSpec spec) {
log.info("正在自定义 MCP 同步客户端配置: {}", serverConfigurationName);
// 1. 配置请求超时时间
spec.requestTimeout(Duration.ofSeconds(30));
// 2. 工具变更监听
spec.toolsChangeConsumer((List<Tool> tools) -> {
log.info("工具列表发生变更,共 {} 个", tools.size());
tools.forEach(tool -> log.debug("工具: {} - {}", tool.name(), tool.description()));
handleToolsChange(serverConfigurationName, tools);
});
// 3. 资源变更监听
spec.resourcesChangeConsumer((List<Resource> resources) -> {
log.info("资源列表发生变更,共 {} 个", resources.size());
resources.forEach(resource -> log.debug("资源: {} - {} ({})",
resource.name(), resource.description(), resource.mimeType()));
handleResourcesChange(serverConfigurationName, resources);
});
// 4. 提示变更监听
spec.promptsChangeConsumer((List<Prompt> prompts) -> {
log.info("提示列表发生变更,共 {} 个", prompts.size());
prompts.forEach(prompt -> log.debug("提示: {} - {}", prompt.name(), prompt.description()));
handlePromptsChange(serverConfigurationName, prompts);
});
// 5. 日志消息处理
spec.loggingConsumer((McpSchema.LoggingMessageNotification logMsg) -> {
switch (logMsg.level()) {
case ERROR -> log.error("MCP 服务器错误: {}", logMsg.data());
case WARNING -> log.warn("MCP 服务器警告: {}", logMsg.data());
case INFO -> log.info("MCP 服务器信息: {}", logMsg.data());
case DEBUG -> log.debug("MCP 服务器调试: {}", logMsg.data());
default -> log.trace("MCP 服务器日志: {}", logMsg.data());
}
});
log.info("MCP 同步客户端配置完成: {}", serverConfigurationName);
// 2. 设置文件系统根目录访问权限
// 3. 自定义采样处理器
// - 处理LLM生成请求
// - 客户端保持对模型访问、选择和权限的控制
// - 服务器无需API密钥即可利用AI能力
}
/** 工具变更处理逻辑 */
private void handleToolsChange(String serverName, List<Tool> tools) {
log.info("为服务器 {} 更新工具缓存", serverName);
// 可扩展:更新缓存、通知下游服务等
}
/** 资源变更处理逻辑 */
private void handleResourcesChange(String serverName, List<Resource> resources) {
log.info("为服务器 {} 刷新资源索引", serverName);
// 可扩展:刷新索引、更新界面等
}
/** 提示变更处理逻辑 */
private void handlePromptsChange(String serverName, List<Prompt> prompts) {
log.info("为服务器 {} 重新加载提示模板", serverName);
// 可扩展:重新加载提示、推荐新模板等
}
}
MCP 客户端 bean 会自动配置并且可以通过注入的方式调用:
@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();
6_MCP Server权限控制
系统提示词,由调用方传递用户数据:可以将用户信息传递到工具中,但是可以随意去查询其他用户的数据,不可取。
Stdio:
- 环境变量传递授权信息,但是目前不支持动态修改环境变量。
- 可以自己尝试实现(销毁原来的 Stdio、建立新的 Stdio,不推荐),由于多个用户存在 Stdio 可能会造成并发竞争因此不推荐。
SSE:
按照传统的 Web 鉴权实现即可,推荐,也不会多个用户竞争同一个进程“切换权限”造成并发。