1. 项目概述
作为一名长期从事AI应用开发的工程师,我在2025年初使用Spring AI 1.0.0-M6构建智能体项目时遇到了一个典型的技术挑战:如何高效地实现模块化工具调用。这个版本虽然功能强大但文档匮乏,特别是在MCP(模块化控制协议)扩展加载方面,官方资料几乎是一片空白。
经过反复试验和大量资料查阅,我最终成功实现了MCP Server的构建、Client的调用以及多Server动态接入的完整解决方案。本文将详细记录这一过程中的关键步骤、踩过的坑以及最终验证可行的实施方案。
2. MCP Server构建详解
2.1 依赖配置的坑与解决方案
在Spring AI 1.0.0-M6版本中,MCP模块的依赖关系相当复杂。经过多次尝试,我发现必须同时引入以下四个核心依赖才能确保功能完整:
xml复制<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-mcp-server-webmvc-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-mcp</artifactId>
</dependency>
<dependency>
<groupId>com.openai</groupId>
<artifactId>openai-java</artifactId>
<version>0.32.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
这里需要特别注意:
spring-ai-mcp-server-webmvc-spring-boot-starter是核心启动器spring-ai-mcp提供了基础协议支持openai-java虽然是可选依赖,但在实际工具调用中经常需要- Actuator用于健康检查,这在生产环境中至关重要
提示:M6版本中如果缺少任何一个依赖,都可能导致难以诊断的ClassNotFound异常,建议严格按照上述组合配置。
2.2 关键配置参数解析
MCP Server的配置需要特别注意以下几个核心参数:
properties复制spring.ai.mcp.server.enabled=true
spring.ai.mcp.server.name=webmvc-mcp-server
spring.ai.mcp.server.version=1.0.0
spring.ai.mcp.server.type=SYNC
spring.ai.mcp.server.sse-message-endpoint=/mcp/messages
# 暴露健康检查端点
management.endpoints.web.exposure.include=health
参数说明:
server.type:可选SYNC(同步)或ASYNC(异步),同步模式更简单但性能较低sse-message-endpoint:SSE(Server-Sent Events)的消息端点,这是实现实时通信的关键- Actuator的健康检查端点必须暴露,这是监控Server状态的基础
2.3 核心工具类实现
工具类是MCP Server的核心功能载体。以下是一个天气查询服务的完整实现示例:
java复制@Service
public class WeatherService {
@Tool(description = "Get weather information by city name")
public String getWeather(String cityName) {
return cityName+":"+simulateTempAtTime(2.0,25.0,10.0);
}
public static BigDecimal simulateTempAtTime(double time, double baseTemp, double tempRange) {
double amplitude = tempRange / 2.0;
double trend = -Math.cos((time - 2) * Math.PI / 12);
double noise = ThreadLocalRandom.current().nextDouble(-0.5, 0.5);
return BigDecimal.valueOf(baseTemp + (trend * amplitude) + noise)
.setScale(1, RoundingMode.HALF_UP);
}
}
关键点解析:
@Tool注解标记了这是一个可被远程调用的工具方法- description属性必须清晰描述功能,这会被AI模型用于决策
- 方法参数和返回值应该尽量简单,复杂对象可能导致序列化问题
- 温度模拟算法考虑了基础温度、日变化趋势和随机波动
3. MCP Client实现细节
3.1 客户端依赖配置
客户端需要引入专门的starter:
xml复制<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-mcp-client-spring-boot-starter</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
注意这个starter在M6版本中仍然是SNAPSHOT状态,可能需要配置特殊的仓库才能获取。
3.2 多Server连接配置
当需要连接多个MCP Server时,配置方式如下:
properties复制spring.ai.mcp.client.enabled=true
spring.ai.mcp.client.name=my-mcp-client
spring.ai.mcp.client.request-timeout=60s
spring.ai.mcp.client.type=SYNC
spring.ai.mcp.client.sse.connections.server1.url=http://127.0.0.1:8888
spring.ai.mcp.client.sse.connections.server2.url=http://127.0.0.1:7777
spring.ai.mcp.client.sse.connections.server2.sse-endpoint=/server2-sse
特殊配置项说明:
- 每个Server需要指定唯一的连接名称(server1, server2等)
- 不同Server可以使用不同的SSE端点路径
- 超时设置对稳定性至关重要,建议根据网络状况调整
3.3 客户端调用示例
典型的工具调用代码如下:
java复制@Autowired
private List<McpSyncClient> mcpSyncClients;
@Autowired
private SyncMcpToolCallbackProvider toolCallbackProvider;
@PostMapping("/a/chat")
public String chat() {
ToolCallback[] toolCallbacks = toolCallbackProvider.getToolCallbacks();
return chatClient
.prompt("查詢重慶天氣信息")
.tools(toolCallbacks)
.call()
.content();
}
调用过程解析:
- 所有可用的MCP Client会自动注入到List中
- ToolCallbackProvider负责管理可用的工具集
- chatClient是Spring AI提供的对话客户端
- tools()方法将工具集绑定到本次调用
4. 动态接入多MCP Server方案
4.1 动态接入的核心挑战
在实际生产环境中,我们经常需要动态添加或移除MCP Server,而传统的配置方式无法满足这一需求。主要面临以下问题:
- 配置写在application.properties中,无法运行时修改
- 新Server的注册需要重启应用
- 无法实现Server的健康状态动态管理
4.2 基于数据库的动态配置方案
通过扩展Spring AI的底层实现,我们可以实现基于数据库的MCP Client动态管理:
java复制@Configuration
public class DbMcpClientConfiguration {
@Bean
public List<NamedClientMcpTransport> dbMcpClientTransports(
ObjectMapper objectMapper) {
List<McpClientConfig> cfgs = repository.findEnabled();
List<NamedClientMcpTransport> transports = new ArrayList<>();
for (McpClientConfig cfg : cfgs) {
var transport = new HttpClientSseClientTransport(
HttpClient.newBuilder(),
cfg.getBaseUrl(),
objectMapper
);
transports.add(
new NamedClientMcpTransport(cfg.getCode(), transport)
);
}
return transports;
}
}
实现要点:
- 从数据库读取启用的MCP Server配置
- 为每个配置创建独立的Transport
- 使用唯一的code作为标识符
- 整个过程无需重启应用
4.3 配置实体类设计
建议的数据库实体结构:
java复制@Entity
public class McpClientConfig {
@Id
private String code;
private String name;
private String baseUrl;
private String sseEndpoint;
private boolean enabled;
private Duration timeout;
private String version;
// getters and setters
}
字段说明:
- code: 唯一业务标识
- baseUrl: Server基础地址
- sseEndpoint: SSE路径,可空(使用默认值)
- enabled: 是否启用
- timeout: 超时设置
5. 实战经验与问题排查
5.1 常见问题速查表
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 工具调用超时 | 1. Server未启动 2. 网络不通 3. 配置错误 |
1. 检查Server状态 2. 测试网络连接 3. 验证配置项 |
| 序列化异常 | 1. 参数类型复杂 2. 缺少序列化器 |
1. 简化参数类型 2. 添加Jackson注解 |
| SSE连接中断 | 1. 防火墙拦截 2. 超时设置过短 |
1. 检查防火墙规则 2. 调整超时时间 |
| 工具方法未识别 | 1. 缺少@Tool注解 2. 包扫描问题 |
1. 检查注解 2. 确认组件扫描范围 |
5.2 性能优化建议
-
连接池配置:对于高频调用的工具,建议配置专用连接池
java复制HttpClient.create() .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000) .doOnConnected(conn -> conn.addHandlerLast(new ReadTimeoutHandler(10))); -
缓存策略:对数据查询类工具实现本地缓存
java复制@Cacheable("weather") @Tool(description = "Get weather info") public String getWeather(String city) { // ... } -
批量调用:合并多个工具请求减少网络开销
java复制chatClient.batch() .add("查天气", tools) .add("查新闻", tools) .execute();
5.3 监控与日志
建议添加以下监控指标:
- 工具调用成功率
- 平均响应时间
- 并发调用数
- 错误类型统计
日志配置示例:
properties复制logging.level.org.springframework.ai.mcp=DEBUG
logging.level.reactor.netty.http.client=WARN
在开发过程中,DEBUG级别的日志可以帮助快速定位问题,但在生产环境建议调整为WARN或ERROR级别以避免性能影响。