在实际 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、文件系统等数据源。
工作流程:
- 用户向 AI 应用提出问题
- AI 应用通过 MCP 客户端发现可用工具
- LLM 判断需要调用哪个工具
- MCP 客户端通过协议调用 MCP 服务器
- MCP 服务器执行具体操作并返回结果
- AI 应用整合结果生成最终响应
1.3 MCP 的两种通信模式
MCP 支持两种主要的通信模式,适应不同部署场景:
stdio 模式:基于标准输入输出的进程间通信,适合本地部署的轻量级工具。MCP 服务器作为子进程被客户端启动和管理。
SSE 模式:基于 HTTP Server-Sent Events 的远程通信,适合独立部署的重量级工具服务,支持多客户端并发访问。
2. 环境准备与项目配置
2.1 环境要求
在开始编码前,需要确保开发环境满足以下要求:
| 组件 | 版本要求 | 说明 |
|---|---|---|
| Java | 17+ | Spring AI Alibaba 2.0 需要 Java 17 或更高版本 |
| Maven | 3.6+ | 项目构建和依赖管理 |
| Spring Boot | 3.2+ | 基础框架版本 |
| Spring AI Alibaba | 2.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.jar4. 开发 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: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.modelcontextprotocol=DEBUG解决方案:
- 使用绝对路径配置 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 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 应用奠定了坚实基础。