Spring AI Alibaba 2.0集成MCP协议:构建智能工具调用系统实战

Spring AI Alibaba 2.0集成MCP协议:构建智能工具调用系统实战 在实际 Java AI 应用开发中最麻烦的不是调用大模型 API而是如何让 AI 系统安全、可控地访问外部工具和数据源。传统方案需要为每个工具编写特定的适配器维护复杂的认证和错误处理逻辑而 Model Context Protocol (MCP) 的出现彻底改变了这一局面。本文将以 Spring AI Alibaba 2.0 为核心完整演示如何基于 MCP 协议构建具备外部工具调用能力的智能体应用。1. 理解 MCP 协议的核心价值与工作机制1.1 MCP 解决了什么问题MCPModel Context Protocol是 Anthropic 公司推出的标准化协议旨在为 AI 应用与外部工具之间建立统一的通信桥梁。在传统 AI 应用开发中每个工具集成都需要编写特定的 API 调用代码处理复杂的认证和授权逻辑设计专用的错误处理机制维护工具版本兼容性MCP 通过标准化协议解决了这些问题让开发者可以像使用插件一样集成各种工具能力。1.2 MCP 的整体架构MCP 采用客户端-服务器架构AI 应用 (MCP Client) ← MCP 协议 → MCP Server ← 工具/数据源MCP 客户端通常是 AI 应用框架如 Spring AI Alibaba、LangChain 等负责与 LLM 交互并决定何时调用工具。MCP 服务器封装具体工具能力的服务提供标准化的工具接口可以连接数据库、API、文件系统等数据源。工作流程用户向 AI 应用提出问题AI 应用通过 MCP 客户端发现可用工具LLM 判断需要调用哪个工具MCP 客户端通过协议调用 MCP 服务器MCP 服务器执行具体操作并返回结果AI 应用整合结果生成最终响应1.3 MCP 的两种通信模式MCP 支持两种主要的通信模式适应不同部署场景stdio 模式基于标准输入输出的进程间通信适合本地部署的轻量级工具。MCP 服务器作为子进程被客户端启动和管理。SSE 模式基于 HTTP Server-Sent Events 的远程通信适合独立部署的重量级工具服务支持多客户端并发访问。2. 环境准备与项目配置2.1 环境要求在开始编码前需要确保开发环境满足以下要求组件版本要求说明Java17Spring AI Alibaba 2.0 需要 Java 17 或更高版本Maven3.6项目构建和依赖管理Spring Boot3.2基础框架版本Spring AI Alibaba2.0.0核心 AI 框架2.2 创建 Spring Boot 项目使用 Spring Initializr 创建基础项目结构curl https://start.spring.io/starter.zip \ -d dependencieswebflux,ai \ -d packageNamecom.example.mcp \ -d namemcp-demo \ -d typemaven-project \ -o mcp-demo.zip解压后在pom.xml中添加 Spring AI Alibaba 依赖properties spring-ai.version2.0.0/spring-ai.version /properties dependencies !-- Spring AI Alibaba 核心依赖 -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-alibaba-spring-boot-starter/artifactId version${spring-ai.version}/version /dependency !-- MCP 客户端支持 -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-mcp-client-spring-boot-starter/artifactId version${spring-ai.version}/version /dependency !-- WebFlux 用于 SSE 通信 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-webflux/artifactId /dependency /dependencies2.3 配置应用参数在application.yml中配置基础参数spring: application: name: mcp-demo ai: dashscope: api-key: ${DASH_SCOPE_API_KEY:sk-your-key-here} mcp: client: stdio: enabled: true servers-configuration: classpath:/mcp-servers-config.json server: port: 8080 logging: level: org.springframework.ai: DEBUG io.modelcontextprotocol: DEBUG创建src/main/resources/mcp-servers-config.json配置文件{ mcpServers: { weather-service: { command: java, args: [ -Dspring.ai.mcp.server.stdiotrue, -Dspring.main.web-application-typenone, -jar, /path/to/your-weather-server.jar ], env: { API_TIMEOUT: 30000 } } } }3. 构建 MCP 服务器天气查询服务实战3.1 创建 MCP 服务器项目首先创建一个独立的 MCP 服务器项目提供天气查询能力!-- MCP 服务器项目的 pom.xml -- dependencies dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-mcp-server-spring-boot-starter/artifactId version${spring-ai.version}/version /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-webflux/artifactId /dependency /dependencies3.2 实现天气查询工具创建核心服务类使用Tool注解暴露工具能力Service public class WeatherService { private final WebClient webClient; private static final String WEATHER_API_URL https://api.open-meteo.com/v1; public WeatherService(WebClient.Builder webClientBuilder) { this.webClient webClientBuilder .baseUrl(WEATHER_API_URL) .build(); } Tool(description 根据经纬度获取天气预报信息) public WeatherResponse getWeatherForecast( ToolParameter(description 纬度例如39.9042) double latitude, ToolParameter(description 经度例如116.4074) double longitude) { try { String response webClient.get() .uri(uriBuilder - uriBuilder .path(/forecast) .queryParam(latitude, latitude) .queryParam(longitude, longitude) .queryParam(current, temperature_2m,wind_speed_10m,relative_humidity_2m) .queryParam(timezone, auto) .queryParam(forecast_days, 3) .build()) .retrieve() .bodyToMono(String.class) .block(Duration.ofSeconds(10)); return parseWeatherResponse(response, latitude, longitude); } catch (Exception e) { return WeatherResponse.error(获取天气信息失败: e.getMessage()); } } Tool(description 根据城市名称查询天气支持中文城市名) public WeatherResponse getWeatherByCity( ToolParameter(description 城市名称例如北京、上海) String cityName) { // 城市坐标映射实际项目应使用地理编码服务 MapString, double[] cityCoordinates Map.of( 北京, new double[]{39.9042, 116.4074}, 上海, new double[]{31.2304, 121.4737}, 广州, new double[]{23.1291, 113.2644}, 深圳, new double[]{22.5431, 114.0579} ); double[] coords cityCoordinates.get(cityName); if (coords null) { return WeatherResponse.error(暂不支持该城市查询); } return getWeatherForecast(coords[0], coords[1]); } private WeatherResponse parseWeatherResponse(String jsonResponse, double latitude, double longitude) { try { ObjectMapper mapper new ObjectMapper(); JsonNode root mapper.readTree(jsonResponse); JsonNode current root.path(current); double temperature current.path(temperature_2m).asDouble(); double windSpeed current.path(wind_speed_10m).asDouble(); double humidity current.path(relative_humidity_2m).asDouble(); return WeatherResponse.success(latitude, longitude, temperature, windSpeed, humidity, 查询成功); } catch (Exception e) { return WeatherResponse.error(解析天气数据失败: e.getMessage()); } } }定义响应数据结构public class WeatherResponse { private final boolean success; private final String message; private final Double latitude; private final Double longitude; private final Double temperature; private final Double windSpeed; private final Double humidity; private final Instant timestamp; // 构造方法和静态工厂方法 public static WeatherResponse success(double lat, double lng, double temp, double wind, double humidity, String msg) { return new WeatherResponse(true, msg, lat, lng, temp, wind, humidity, Instant.now()); } public static WeatherResponse error(String errorMsg) { return new WeatherResponse(false, errorMsg, null, null, null, null, null, Instant.now()); } // getter 方法... }3.3 配置 MCP 服务器在application.yml中配置服务器参数spring: main: web-application-type: none # 禁用 Web 应用类型 banner-mode: off ai: mcp: server: stdio: true name: weather-mcp-server version: 1.0.0 description: 天气查询 MCP 服务注册工具提供者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(); } Bean public WebClient.Builder webClientBuilder() { return WebClient.builder() .defaultHeader(User-Agent, Weather-MCP-Server/1.0) .codecs(configurer - configurer.defaultCodecs().maxInMemorySize(256 * 1024)); } }3.4 构建和测试服务器使用 Maven 构建可执行 JARmvn clean package -DskipTests测试服务器功能# 启动服务器进行测试 java -jar target/weather-mcp-server.jar4. 开发 MCP 客户端应用4.1 配置客户端连接在客户端项目的mcp-servers-config.json中配置服务器连接{ mcpServers: { weather: { command: java, args: [ -Dspring.ai.mcp.server.stdiotrue, -Dspring.main.web-application-typenone, -jar, /absolute/path/to/weather-mcp-server.jar ], env: { LOG_LEVEL: INFO } } } }4.2 实现智能对话服务创建主要的业务服务类Service public class SmartAssistantService { private final ChatClient chatClient; private final ToolCallbackProvider toolCallbackProvider; public SmartAssistantService(ChatClient.Builder chatClientBuilder, ToolCallbackProvider toolCallbackProvider) { this.toolCallbackProvider toolCallbackProvider; this.chatClient chatClientBuilder .defaultTools(toolCallbackProvider) .defaultSystem( 你是一个智能助手可以帮用户查询天气信息。 当用户询问天气时你应该使用可用的天气查询工具来获取准确信息。 回答要友好、详细包含温度、风速、湿度等关键信息。 ) .defaultOptions(ChatOptions.builder() .temperature(0.3) .maxTokens(1000) .build()) .build(); } public String processQuery(String userQuery) { try { ChatResponse response chatClient.prompt() .user(userQuery) .call() .chatResponse(); return response.getResult().getOutput().getContent(); } catch (Exception e) { return 处理请求时出现错误: e.getMessage(); } } public ListAvailableTool getAvailableTools() { return toolCallbackProvider.getToolCallbacks().stream() .map(tool - new AvailableTool( tool.getName(), tool.getDescription(), tool.getInputSchema() )) .collect(Collectors.toList()); } }4.3 创建 REST 控制器提供 Web 接口供前端调用RestController RequestMapping(/api/assistant) public class AssistantController { private final SmartAssistantService assistantService; public AssistantController(SmartAssistantService assistantService) { this.assistantService assistantService; } PostMapping(/chat) public ResponseEntityChatResponse chat(RequestBody ChatRequest request) { try { String response assistantService.processQuery(request.getMessage()); return ResponseEntity.ok(new ChatResponse(response, success)); } catch (Exception e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body(new ChatResponse(null, 处理失败: e.getMessage())); } } GetMapping(/tools) public ResponseEntityListAvailableTool getAvailableTools() { return ResponseEntity.ok(assistantService.getAvailableTools()); } // DTO 类定义 public static class ChatRequest { private String message; // getter/setter } public static class ChatResponse { private String response; private String status; // 构造方法和 getter/setter } public static class AvailableTool { private String name; private String description; private Object inputSchema; // 构造方法和 getter/setter } }4.4 应用启动配置配置主应用类SpringBootApplication public class McpClientApplication { public static void main(String[] args) { SpringApplication.run(McpClientApplication.class, args); } Bean public CommandLineRunner demo(SmartAssistantService assistantService) { return args - { System.out.println( MCP 客户端应用启动完成 ); System.out.println(可用工具列表:); assistantService.getAvailableTools().forEach(tool - { System.out.println(- tool.getName() : tool.getDescription()); }); System.out.println(\n测试查询: 今天北京天气怎么样); String response assistantService.processQuery(今天北京天气怎么样); System.out.println(AI 响应: response); }; } }5. 运行验证与结果分析5.1 启动完整应用栈按照以下顺序启动服务# 1. 启动 MCP 服务器新终端 cd weather-mcp-server java -jar target/weather-mcp-server.jar # 2. 启动客户端应用新终端 cd mcp-client-app mvn spring-boot:run5.2 功能验证测试使用 curl 测试 API 接口# 测试聊天接口 curl -X POST http://localhost:8080/api/assistant/chat \ -H Content-Type: application/json \ -d {message: 北京和上海的天气对比怎么样} # 查看可用工具 curl http://localhost:8080/api/assistant/tools预期响应示例{ response: 根据查询结果北京当前温度18.6°C风速4.7km/h湿度18%上海当前温度22.1°C风速3.2km/h湿度45%。上海比北京温暖湿润一些。, status: success }5.3 监控和日志分析检查应用日志确认 MCP 协议通信正常DEBUG - 接收到 MCP 工具调用请求: getWeatherForecast DEBUG - 天气API调用成功纬度: 39.9042, 经度: 116.4074 DEBUG - MCP 响应处理完成返回数据长度: 245 INFO - 用户查询处理完成耗时: 1.2s6. 常见问题排查与解决方案6.1 MCP 连接问题问题现象客户端启动时报错无法连接 MCP 服务器。可能原因MCP 服务器 JAR 路径配置错误服务器启动失败或端口冲突环境变量配置不正确排查步骤# 检查服务器进程 ps aux | grep weather-mcp-server # 验证 JAR 文件路径 ls -la /path/to/weather-mcp-server.jar # 检查服务器日志 java -jar weather-mcp-server.jar --logging.level.io.modelcontextprotocolDEBUG解决方案使用绝对路径配置 JAR 文件位置确保服务器先于客户端启动检查文件权限和 Java 版本兼容性6.2 工具调用失败问题现象AI 可以识别需要调用工具但工具执行报错。常见错误ERROR - Tool execution failed: java.net.ConnectException: Connection timed out排查步骤检查外部 API 的可访问性验证网络连接和防火墙设置检查 API 密钥和认证配置解决方案// 在工具实现中添加超时控制和重试机制 Tool(description 天气查询) public WeatherResponse getWeatherWithRetry(double lat, double lng) { int maxRetries 3; for (int i 0; i maxRetries; i) { try { return getWeatherForecast(lat, lng); } catch (Exception e) { if (i maxRetries - 1) throw e; Thread.sleep(1000 * (i 1)); // 指数退避 } } throw new RuntimeException(查询失败); }6.3 性能优化建议问题现象工具调用响应慢影响用户体验。优化方案连接池配置spring: ai: mcp: client: connection-pool: max-size: 10 idle-timeout: 30000缓存常用结果Service public class CachedWeatherService { private final MapString, WeatherResponse cache new ConcurrentHashMap(); private final Duration cacheTtl Duration.ofMinutes(10); Tool(description 带缓存的天气查询) public WeatherResponse getWeatherCached(String city) { String cacheKey city - Instant.now().getEpochSecond() / 600; // 10分钟窗口 return cache.computeIfAbsent(cacheKey, k - { WeatherResponse freshData getWeatherForecastByCity(city); // 异步清理过期缓存 CompletableFuture.delayedExecutor(cacheTtl.toMinutes(), TimeUnit.MINUTES) .execute(() - cache.remove(cacheKey)); return freshData; }); } }7. 生产环境部署最佳实践7.1 安全配置API 密钥管理spring: ai: dashscope: api-key: ${DASHSCOPE_API_KEY} # 从环境变量读取使用 Kubernetes Secret 或配置中心管理敏感信息。访问控制Configuration public class SecurityConfig { Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { return http .authorizeHttpRequests(auth - auth .requestMatchers(/api/assistant/**).authenticated() .anyRequest().permitAll() ) .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt) .build(); } }7.2 监控和告警集成 Micrometer 监控指标Component public class McpMetrics { private final MeterRegistry meterRegistry; private final Counter toolCallCounter; private final Timer toolExecutionTimer; public McpMetrics(MeterRegistry meterRegistry) { this.meterRegistry meterRegistry; this.toolCallCounter Counter.builder(mcp.tool.calls) .description(MCP 工具调用次数) .register(meterRegistry); this.toolExecutionTimer Timer.builder(mcp.tool.duration) .description(MCP 工具执行时间) .register(meterRegistry); } public T T recordToolCall(String toolName, SupplierT operation) { toolCallCounter.increment(); return toolExecutionTimer.record(() - { try { return operation.get(); } catch (Exception e) { meterRegistry.counter(mcp.tool.errors, tool, toolName).increment(); throw e; } }); } }7.3 高可用部署多实例部署# Kubernetes Deployment 配置 apiVersion: apps/v1 kind: Deployment metadata: name: mcp-client spec: replicas: 3 strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 maxUnavailable: 0健康检查Component public class McpHealthIndicator implements HealthIndicator { private final ToolCallbackProvider toolProvider; Override public Health health() { try { ListAvailableTool tools toolProvider.getToolCallbacks(); return Health.up() .withDetail(availableTools, tools.size()) .withDetail(status, MCP 连接正常) .build(); } catch (Exception e) { return Health.down(e).build(); } } }通过本文的完整实践可以看到 Spring AI Alibaba 2.0 结合 MCP 协议为 Java 开发者提供了强大的 AI 应用开发能力。关键是要理解 MCP 的协议机制合理设计工具接口并在生产环境中做好安全、监控和性能优化。这种架构模式为构建复杂的企业级 AI 应用奠定了坚实基础。