news 2026/7/19 2:58:19

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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 解决了什么问题

MCP(Model 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、文件系统等数据源。

工作流程

  1. 用户向 AI 应用提出问题
  2. AI 应用通过 MCP 客户端发现可用工具
  3. LLM 判断需要调用哪个工具
  4. MCP 客户端通过协议调用 MCP 服务器
  5. MCP 服务器执行具体操作并返回结果
  6. AI 应用整合结果生成最终响应

1.3 MCP 的两种通信模式

MCP 支持两种主要的通信模式,适应不同部署场景:

stdio 模式:基于标准输入输出的进程间通信,适合本地部署的轻量级工具。MCP 服务器作为子进程被客户端启动和管理。

SSE 模式:基于 HTTP Server-Sent Events 的远程通信,适合独立部署的重量级工具服务,支持多客户端并发访问。

2. 环境准备与项目配置

2.1 环境要求

在开始编码前,需要确保开发环境满足以下要求:

组件版本要求说明
Java17+Spring 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 dependencies=webflux,ai \ -d packageName=com.example.mcp \ -d name=mcp-demo \ -d type=maven-project \ -o mcp-demo.zip

解压后,在pom.xml中添加 Spring AI Alibaba 依赖:

<properties> <spring-ai.version>2.0.0</spring-ai.version> </properties> <dependencies> <!-- Spring AI Alibaba 核心依赖 --> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-alibaba-spring-boot-starter</artifactId> <version>${spring-ai.version}</version> </dependency> <!-- MCP 客户端支持 --> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-mcp-client-spring-boot-starter</artifactId> <version>${spring-ai.version}</version> </dependency> <!-- WebFlux 用于 SSE 通信 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> </dependencies>

2.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.stdio=true", "-Dspring.main.web-application-type=none", "-jar", "/path/to/your-weather-server.jar" ], "env": { "API_TIMEOUT": "30000" } } } }

3. 构建 MCP 服务器:天气查询服务实战

3.1 创建 MCP 服务器项目

首先创建一个独立的 MCP 服务器项目,提供天气查询能力:

<!-- MCP 服务器项目的 pom.xml --> <dependencies> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-mcp-server-spring-boot-starter</artifactId> <version>${spring-ai.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> </dependencies>

3.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) { // 城市坐标映射(实际项目应使用地理编码服务) Map<String, 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 构建可执行 JAR:

mvn clean package -DskipTests

测试服务器功能:

# 启动服务器进行测试 java -jar target/weather-mcp-server.jar

4. 开发 MCP 客户端应用

4.1 配置客户端连接

在客户端项目的mcp-servers-config.json中配置服务器连接:

{ "mcpServers": { "weather": { "command": "java", "args": [ "-Dspring.ai.mcp.server.stdio=true", "-Dspring.main.web-application-type=none", "-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 List<AvailableTool> 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 ResponseEntity<ChatResponse> 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 ResponseEntity<List<AvailableTool>> 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:run

5.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.2s

6. 常见问题排查与解决方案

6.1 MCP 连接问题

问题现象:客户端启动时报错,无法连接 MCP 服务器。

可能原因

  1. MCP 服务器 JAR 路径配置错误
  2. 服务器启动失败或端口冲突
  3. 环境变量配置不正确

排查步骤

# 检查服务器进程 ps aux | grep weather-mcp-server # 验证 JAR 文件路径 ls -la /path/to/weather-mcp-server.jar # 检查服务器日志 java -jar weather-mcp-server.jar --logging.level.io.modelcontextprotocol=DEBUG

解决方案

  • 使用绝对路径配置 JAR 文件位置
  • 确保服务器先于客户端启动
  • 检查文件权限和 Java 版本兼容性

6.2 工具调用失败

问题现象:AI 可以识别需要调用工具,但工具执行报错。

常见错误

ERROR - Tool execution failed: java.net.ConnectException: Connection timed out

排查步骤

  1. 检查外部 API 的可访问性
  2. 验证网络连接和防火墙设置
  3. 检查 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 性能优化建议

问题现象:工具调用响应慢,影响用户体验。

优化方案

  1. 连接池配置
spring: ai: mcp: client: connection-pool: max-size: 10 idle-timeout: 30000
  1. 缓存常用结果
@Service public class CachedWeatherService { private final Map<String, 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, Supplier<T> 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 { List<AvailableTool> 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 应用奠定了坚实基础。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/19 2:58:05

雪茄沙发常见问题解答(2026最新专家版)

美式复古雪茄沙发工厂常见问题解析 Q1&#xff1a;靠谱的美式复古雪茄沙发工厂怎么选&#xff1f; 看场景专注度&#xff1a;优先选择聚焦雪茄吧、威士忌吧细分场景的工厂&#xff0c;这类工厂更懂场景的真实使用需求&#xff0c;产品设计更贴合使用习惯。 看产业集群优势&…

作者头像 李华
网站建设 2026/7/19 2:57:03

WTM框架快速搭建博客后台系统实战

1. WTM框架与博客系统后台搭建WTM&#xff08;WalkingTec MVVM&#xff09;是一个基于.NET Core的全栈快速开发框架&#xff0c;它特别适合快速构建管理后台类应用。我在多个企业级项目中采用WTM框架开发后台系统&#xff0c;其最大优势在于通过代码生成器可以快速完成80%的基础…

作者头像 李华
网站建设 2026/7/19 2:56:53

ROS2 Humble下MoveIt2实操指南:从URDF配置到轨迹执行

1. 这不是一本“教材”&#xff0c;而是一张机器人运动规划的实操地图如果你刚在ROS工作空间里敲下rosrun moveit_setup_assistant moveit_setup_assistant&#xff0c;看着那个带3D模型预览的GUI界面发呆&#xff1b;或者你已经把UR5机械臂的URDF拖进MoveIt Setup Assistant&a…

作者头像 李华
网站建设 2026/7/19 2:56:32

数据科学家的四重身份:业务翻译、技术权衡、组织协同与伦理守门

1. 这不是岗位说明书&#xff0c;而是一份数据科学家的生存手记“数据科学家”这五个字&#xff0c;从2012年《哈佛商业评论》那篇《The Sexiest Job of the 21st Century》开始&#xff0c;就裹着金粉上了神坛。但十年过去&#xff0c;我带过27个数据项目、面试过156位候选人、…

作者头像 李华
网站建设 2026/7/19 2:56:26

FastAPI安全架构实战:JWT与Redis优化方案

1. FastAPI安全体系架构解析这个完整示例展示了FastAPI在现代Web开发中的核心安全能力整合方案。作为Python生态中增长最快的异步框架&#xff0c;FastAPI通过类型提示和依赖注入系统&#xff0c;为开发者提供了构建安全后端的优雅方式。整套方案包含六个关键安全层&#xff1a…

作者头像 李华
网站建设 2026/7/19 2:56:11

【干货】企业AI工具落地测试全流程:告别瞎点测试,建立标准化AI测试体系

前言 适合人群与收益本文为企业内部AI项目提供可直接落地的测试体系与判定准则。适合企业测试工程师、QA 负责人、产品/研发经理&#xff1a;可复用的“AI缺陷判定模板”与标准化测试用例覆盖准确性、一致性、上下文、注入攻击、性能与体验的 6 大测试维度实操示例与复用模板目…

作者头像 李华