> 技术文档 > 【Spring AI快速上手 (六)】MCP工具调用新范式_java spring-ai mcp-server服务启动了 mcp-server有提供接口供服务调

【Spring AI快速上手 (六)】MCP工具调用新范式_java spring-ai mcp-server服务启动了 mcp-server有提供接口供服务调

一、前言

二、MCP简介 

        2.1 协议定位与核心价值

        2.2 核心组件与交互模型

        2.3 通信模式演进

        2.4 Spring AI与MCP深度集成范式

        2.5 MCP协议演进与行业实践

        2.6 未来方向:构建智能体互联网的基石

三、MCP工具调用新范式

        Spring AI与MCP的Stdio模式集成实战

        基于Stdio协议的MCP服务声明与调用指南

        SSE协议下的MCP实时通信实现


一、前言

 Spring AI详解:【Spring AI详解】开启Java生态的智能应用开发新时代(附不同功能的Spring AI实战项目)-CSDN博客

二、MCP简介 

2.1 协议定位与核心价值

模型上下文协议(Model Context Protocol, MCP) 是由Anthropic推动的开放标准,旨在为大型语言模型(LLM)应用提供标准化接口,实现与外部数据源、工具的安全高效交互。

其核心价值体现在:

解耦AI与数据源:通过统一协议屏蔽底层数据源差异,开发者无需为每个工具编写定制化接口。

构建工具生态系统:持续扩展的预集成工具库(如数据库、API、地图服务),支持动态发现和调用。

安全上下文传递:在企业基础设施内安全处理敏感数据,通过OAuth等机制保障数据隐私。

2.2 核心组件与交互模型

MCP采用客户端-服务器架构,包含五大角色:

角色 功能描述 实例 MCP Host 终端应用载体,发起AI任务 Claude Desktop、IDE插件 MCP Client 协议客户端,维护与服务器的1:1连接 Spring AI MCP Client MCP Server 轻量级服务,通过标准化接口暴露特定能力 文件系统服务、天气查询服务 本地数据源 服务器可安全访问的本地资源(文件/数据库) 用户本地文件系统 远程服务 通过API连接的云端系统 百度地图API、GitHub API

数据流向:Host → Client → Server → 数据源/API → 返回结果至LLM

2.3 通信模式演进

MCP支持三类通信模式,适应不同场景需求:

Stdio模式

通过标准输入输出流通信,适用于本地轻量化工具(如文件操作)。

优势:无网络依赖、低延迟;缺陷:仅限单机部署 

SSE模式

基于HTTP的Server-Sent Events,支持远程服务实时流式响应。

典型场景:企业级天气预报服务、金融数据流 

Streamable HTTP模式(2025年新增):

融合普通HTTP与SSE流式传输优势,支持断线恢复与无状态服务。

解决传统SSE长连接压力问题,成为当前推荐方案 

2.4 Spring AI与MCP深度集成范式

分层架构设计

Spring AI通过抽象层屏蔽协议细节,实现业务代码与协议解耦:

声明层:@Tool注解定义工具能力,@ToolParam描述参数语义(如航班退票接口) 

协议层:ToolCallbackProvider处理MCP协议通信,支持同步/异步调用 

执行层:ChatClient构建对话流程,注入系统参数(如当前日期)增强上下文感知 

两种调用范式详解

模式 适用场景 配置要点 优势与局限 Stdio 本地轻量化工具 JSON定义进程命令/环境变量,如启动百度地图服务需配置BAIDU_MAP_API_KEY  无网络延迟,但无法跨主机扩展 SSE 企业级远程服务 YAML配置服务URL(如url: http://service:8088),支持OAuth令牌认证  支持分布式部署,需处理网络波动

典型场景选择建议

高频工具(如实时导航)选SSE模式保障响应速度

敏感操作(如数据库更新)用Stdio本地化部署

复杂业务链组合多个MCP微服务实现能力复用

协议无关性设计

Spring AI通过统一编程模型屏蔽底层协议差异:

同一套@Tool接口可同时发布为Stdio服务与SSE端点 

McpFunctionCallback将MCP工具转换为Spring AI函数,供ChatClient直接调用。

动态工具注册:SSE模式下@Tool方法自动转换为API端点,无需额外配置 

2.5 MCP协议演进与行业实践

关键技术里程碑

Streamable HTTP替代SSE成为默认传输协议,解决长连接不可恢复问题 。

安全四原则强化:用户同意控制、数据最小化、工具沙箱隔离、LLM采样授权 

Claude Messages API原生支持MCP,通过mcp_servers参数直连远程服务 

动态客户端注册(DCR)机制简化OAuth集成,降低认证开发成本 

安全架构升级

MCP面临的七类核心风险与应对策略:

风险类型 案例 防护方案 工具描述投毒 伪造天气查询工具劫持LLM 工具签名校验 + 来源白名单  间接提示词注入 文件内容嵌入恶意指令 输入内容沙箱检测  越权数据访问 利用路径遍历获取敏感文件 资源访问RBAC模型  OAuth令牌泄露 中间人攻击拦截授权令牌 MTLS双向认证 + 令牌短期有效 

行业应用场景

领域 典型场景 MCP价值 智能开发 Cursor IDE通过MCP调用GitHub管理代码 自动化提交/合并/回滚  企业服务 Cloudflare远程托管MCP服务多租户隔离 安全审计 + 资源配额  金融分析 实时聚合多源经济数据生成投研报告 动态数据上下文注入  医疗辅助 检索患者历史病历提供诊断建议 HIPAA兼容的数据访问

2.6 未来方向:构建智能体互联网的基石

技术演进趋势

去中心化身份认证:集成W3C DID标准,实现跨域身份互通 

分层智能体系统:MCP Gateway协调多智能体协作,解决复杂工作流 

协议标准化:推进ISO/IEC国际标准认证,增强跨平台兼容性 

开发者行动建议

新项目架构:优先采用Streamable HTTP协议,平衡性能与可靠性 

存量系统改造:通过McpFunctionCallback渐进式替换传统插件 

生产部署规范:启用logging.level.io.modelcontextprotocol=DEBUG监控协议交互、敏感操作强制OAuth Scope授权

终极愿景: MCP不仅是技术协议,更是智能体生态的“连接器哲学”——当每个数据源都拥有标准化接口,AI应用将真正实现“即插即用”的革命性体验,推动从封闭模型开放智能体网络的范式迁移 

三、MCP工具调用新范式

pom.xml

spring-boot-starter-web提供Web基础支持。

spring-ai-alibaba-starter-dashscope集成阿里云Dashscope服务。

spring-ai-starter-mcp-client-webflux支持SSE和Stdio两种通信协议

     com.alibaba.cloud.ai spring-ai-alibaba-bom 1.0.0.2 pom import     org.springframework.ai spring-ai-bom 1.0.0 pom import     org.springframework.boot spring-boot-dependencies 3.2.5 pom import     org.springframework.boot spring-boot-starter-web   com.alibaba.cloud.ai spring-ai-alibaba-starter-dashscope     org.springframework.ai spring-ai-starter-mcp-client-webflux 

application.propertise

# 阿里大模型spring.ai.dashscope.api-key=${TONGYI_AI_KEY}spring.ai.dashscope.chat.options.model= qwen-pluslogging.level.org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor= debug

Spring AI与MCP的Stdio模式集成实战

application.yml

MCP客户端配置

超时时间设为60秒(生产环境需按业务调整)。

支持两种Stdio模式配置:

外部JSON文件:通过classpath:/mcp/mcp-servers-config.json加载服务器配置(推荐模块化管理)。

内联配置:直接定义命令、参数和环境变量(适合简单场景,但复杂命令建议用JSON) 

日志配置:启用MCP客户端和协议规范的DEBUG日志,便于调试

server: port: 8080 # 应用服务端口 servlet: encoding: charset: UTF-8 # 强制使用UTF-8编码 enabled: true # 启用编码过滤器 force: true # 强制响应使用配置的字符集spring: ai: mcp: client: request-timeout: 60000 # MCP请求超时时间(毫秒),建议生产环境根据业务调整 stdio: # 1. 推荐将服务器配置分离到独立JSON文件(模块化管理) # 文件格式需符合Claude Desktop规范,支持多服务器配置 servers-configuration: classpath:/mcp/mcp-servers-config.json # 2. 替代方案:直接内联配置(适合简单场景) # 注意:复杂命令建议使用外部JSON文件 # connections: # baidu-map: # command: cmd # args: # - /c # - npx # - -y # - @baidumap/mcp-server-baidu-map # env: # BAIDU_MAP_API_KEY: xxx# 调试日志配置logging: level: io: modelcontextprotocol: client: DEBUG # 启用MCP客户端详细日志 spec: DEBUG # 启用协议规范级日志(含请求/响应详情)

resources.map.mcp-servers-config.json(百度地图MCP)

定义百度地图MCP服务的启动配置:

使用cmdcmd执行命令,参数包括npx和@baidumap/mcp-server-baidu-map。

环境变量BAIDU_MAP_API_KEY需替换为实际值 

{ \"mcpServers\": { \"baidu-map\": { \"command\": \"cmd\", \"args\": [ \"-c\", \"npx\", \"-y\", \"@baidumap/mcp-server-baidu-map\" ], \"env\": { \"BAIDU_MAP_API_KEY\": \"xxx\" } } }}

Tool 工具类

声明工具接口供AI模型调用

import org.springframework.ai.tool.annotation.Tool;import org.springframework.ai.tool.annotation.ToolParam;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;@Servicepublic class ToolsService { /** * 退票 * @param ticketNumber 预定号 * @param name 真实人名 * @return 退票成功 */ @Tool(description = \"退票/取消预定,调用之前先查询航班\") public String cancel( // @ToolParam告诉大模型参数的描述 @ToolParam(description = \"预定号,可以是纯数字\") String ticketNumber, @ToolParam(description = \"真实人名(必填,必须为人的真实姓名,严禁用其他信息代替;如缺失请传null)\") String name  ) { return \"退票成功\"; } @Tool(description = \"获取航班信息\") public FlightBookingService.BookingDetails getBookingDetails( @ToolParam(description = \"预定号,可以是纯数字\") String bookingNumber, @ToolParam(description = \"真实人名(必填,必须为人的真实姓名,严禁用其他信息代替;如缺失请传null)\") String name) { return \"查询成功\"; }}

对话调用控制器类

通过ChatClient.Builder构建客户端,注册工具服务和回调处理器

import org.springframework.ai.chat.client.ChatClient;import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;import org.springframework.ai.tool.ToolCallbackProvider;import org.springframework.http.MediaType;import org.springframework.web.bind.annotation.CrossOrigin;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;import reactor.core.publisher.Flux;import java.time.LocalDate;/** * 航空公司智能客服控制器 * 核心功能: * 1. 集成百度地图服务(通过MCP协议调用) * 2. 航班查询与预订服务 * 3. 流式对话接口(SSE) * * 关于百度地图MCP调用: * - 实际调用通过ToolCallbackProvider实现 * - ToolsService仅声明工具接口,不直接处理调用 * - MCP服务配置在application.yaml中定义 */@RestController@CrossOriginpublic class OpenAiController { private final ChatClient chatClient; /** * 构造方法(关键依赖注入) * @param chatClientBuilder ChatClient构建器 * @param toolsService 工具服务(声明百度地图/航班等工具) * @param toolCallbackProvider MCP调用处理器(实际触发百度地图服务调用) */ public OpenAiController(ChatClient.Builder chatClientBuilder, ToolsService toolsService, ToolCallbackProvider toolCallbackProvider) { this.chatClient = chatClientBuilder // 系统角色定义(包含百度地图服务说明) .defaultSystem(\"\"\"  ## 角色  您是一个航空公司的智能客服助手,集成了百度地图服务功能。  ## 服务能力  1. 可以查询地点、路线规划、周边搜索等地图相关服务  2. 提供航班信息查询、预订等航空服务  3. 支持多轮对话,保持上下文理解  ## 交互要求  1. 使用简体中文进行交流  2. 涉及地图服务时,明确说明使用的是百度地图数据  3. 对于需要确认的操作(如预订、修改等),必须获得用户明确确认后再执行  4. 提供准确的地理位置信息时,需注明数据来源和更新时间  ## 当前信息  今天的日期是 {current_date}  地图数据来源: 百度地图API  \"\"\") // 注册工具声明(工具实际调用通过toolCallbackProvider) .defaultTools(toolsService) // 关键配置:设置MCP回调处理器(实际触发百度地图调用) .defaultToolCallbacks(toolCallbackProvider) .build(); } /** * 流式对话接口 * 当用户请求涉及百度地图服务时: * 1. AI生成工具调用请求 * 2. ToolCallbackProvider通过MCP协议调用百度地图 * 3. 返回结果后生成响应 * * @param message 用户消息(示例:\"查询北京西站到首都机场的驾车路线\") * @return 流式响应(包含地图数据时会标注来源) */ @CrossOrigin @GetMapping(value = \"/ai/generateStreamAsString\", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux generateStreamAsString( @RequestParam(value = \"message\", defaultValue = \"查询北京到上海的自驾路线\") String message) { return chatClient.prompt() .user(message) .system(p -> p.param(\"current_date\", LocalDate.now())) .stream() .content(); }}

 这样就成功用Spring AI调用了百度地图的MCP

基于Stdio协议的MCP服务声明与调用指南

pom.xml

依赖spring-ai-starter-mcp-server提供Stdio模式支持(含协议核心、工具管理)。

可选spring-web依赖(HTTP调用外部工具时需添加)

     org.springframework.ai spring-ai-bom 1.0.0 pom import     org.springframework.boot spring-boot-dependencies 3.2.5 pom import       org.springframework.ai spring-ai-starter-mcp-server       org.springframework spring-web      org.springframework.boot spring-boot-maven-plugin     repackage      

application.yml

强制非Web环境(web-application-type: none),禁用Banner避免干扰Stdio通信

spring: main: # 禁用Web应用类型(强制非Web环境) # 重要:MCP STDIO模式需要非Web环境 web-application-type: none # 禁用Spring Boot横幅(必需配置) # 原因:控制台输出会干扰STDIO协议通信 banner-mode: off ai: mcp: server: # 服务器标识(需全局唯一) name: my-weather-server # 服务版本号(遵循语义化版本规范) version: 0.0.1 

调用外部OpenMeteo接口获取指定经纬度的天气预报

调用OpenMeteo免费API获取天气预报,支持:

当前天气(温度、湿度、降水等)。

7天预报(最高/低温、风速、天气代码转换)。

import java.util.List;import java.time.LocalDate;import java.time.format.DateTimeFormatter;import com.fasterxml.jackson.annotation.JsonIgnoreProperties;import com.fasterxml.jackson.annotation.JsonProperty;import org.springframework.ai.tool.annotation.Tool;import org.springframework.ai.tool.annotation.ToolParam;import org.springframework.stereotype.Service;import org.springframework.web.client.RestClient;import org.springframework.web.client.RestClientException;/** * 利用OpenMeteo的免费天气API提供天气服务 * 该API无需API密钥,可以直接使用 */@Servicepublic class OpenMeteoService { // OpenMeteo免费天气API基础URL private static final String BASE_URL = \"https://api.open-meteo.com/v1\"; private final RestClient restClient; public OpenMeteoService() { this.restClient = RestClient.builder() .baseUrl(BASE_URL) .defaultHeader(\"Accept\", \"application/json\") .defaultHeader(\"User-Agent\", \"OpenMeteoClient/1.0\") .build(); } // OpenMeteo天气数据模型 @JsonIgnoreProperties(ignoreUnknown = true) public record WeatherData( @JsonProperty(\"latitude\") Double latitude, @JsonProperty(\"longitude\") Double longitude, @JsonProperty(\"timezone\") String timezone, @JsonProperty(\"current\") CurrentWeather current, @JsonProperty(\"daily\") DailyForecast daily, @JsonProperty(\"current_units\") CurrentUnits currentUnits) { @JsonIgnoreProperties(ignoreUnknown = true) public record CurrentWeather( @JsonProperty(\"time\") String time, @JsonProperty(\"temperature_2m\") Double temperature, @JsonProperty(\"apparent_temperature\") Double feelsLike, @JsonProperty(\"relative_humidity_2m\") Integer humidity, @JsonProperty(\"precipitation\") Double precipitation, @JsonProperty(\"weather_code\") Integer weatherCode, @JsonProperty(\"wind_speed_10m\") Double windSpeed, @JsonProperty(\"wind_direction_10m\") Integer windDirection) { } @JsonIgnoreProperties(ignoreUnknown = true) public record CurrentUnits( @JsonProperty(\"time\") String timeUnit, @JsonProperty(\"temperature_2m\") String temperatureUnit, @JsonProperty(\"relative_humidity_2m\") String humidityUnit, @JsonProperty(\"wind_speed_10m\") String windSpeedUnit) { } @JsonIgnoreProperties(ignoreUnknown = true) public record DailyForecast( @JsonProperty(\"time\") List time, @JsonProperty(\"temperature_2m_max\") List tempMax, @JsonProperty(\"temperature_2m_min\") List tempMin, @JsonProperty(\"precipitation_sum\") List precipitationSum, @JsonProperty(\"weather_code\") List weatherCode, @JsonProperty(\"wind_speed_10m_max\") List windSpeedMax, @JsonProperty(\"wind_direction_10m_dominant\") List windDirection) { } } /** * 获取天气代码对应的描述 */ private String getWeatherDescription(int code) { return switch (code) { case 0 -> \"晴朗\"; case 1, 2, 3 -> \"多云\"; case 45, 48 -> \"雾\"; case 51, 53, 55 -> \"毛毛雨\"; case 56, 57 -> \"冻雨\"; case 61, 63, 65 -> \"雨\"; case 66, 67 -> \"冻雨\"; case 71, 73, 75 -> \"雪\"; case 77 -> \"雪粒\"; case 80, 81, 82 -> \"阵雨\"; case 85, 86 -> \"阵雪\"; case 95 -> \"雷暴\"; case 96, 99 -> \"雷暴伴有冰雹\"; default -> \"未知天气\"; }; } /** * 获取风向描述 */ private String getWindDirection(int degrees) { if (degrees >= 337.5 || degrees = 22.5 && degrees = 67.5 && degrees = 112.5 && degrees = 157.5 && degrees = 202.5 && degrees = 247.5 && degrees < 292.5) return \"西风\"; return \"西北风\"; } /** * 获取指定经纬度的天气预报 * * @param latitude 纬度 * @param longitude 经度 * @return 指定位置的天气预报 * @throws RestClientException 如果请求失败 */ @Tool(description = \"获取指定经纬度的天气预报,根据位置自动推算经纬度\") public String getWeatherForecastByLocation(  double latitude,  double longitude) { // 获取天气数据(当前和未来7天) var weatherData = restClient.get() .uri(\"/forecast?latitude={latitude}&longitude={longitude}&current=temperature_2m,apparent_temperature,relative_humidity_2m,precipitation,weather_code,wind_speed_10m,wind_direction_10m&daily=temperature_2m_max,temperature_2m_min,precipitation_sum,weather_code,wind_speed_10m_max,wind_direction_10m_dominant&timezone=auto&forecast_days=7\", latitude, longitude) .retrieve() .body(WeatherData.class); // 拼接天气信息 StringBuilder weatherInfo = new StringBuilder(); // 添加当前天气信息 WeatherData.CurrentWeather current = weatherData.current(); String temperatureUnit = weatherData.currentUnits() != null ? weatherData.currentUnits().temperatureUnit() : \"°C\"; String windSpeedUnit = weatherData.currentUnits() != null ? weatherData.currentUnits().windSpeedUnit() : \"km/h\"; String humidityUnit = weatherData.currentUnits() != null ? weatherData.currentUnits().humidityUnit() : \"%\"; weatherInfo.append(String.format(\"\"\" 当前天气: 温度: %.1f%s (体感温度: %.1f%s) 天气: %s 风向: %s (%.1f %s) 湿度: %d%s 降水量: %.1f 毫米 \"\"\", current.temperature(), temperatureUnit, current.feelsLike(), temperatureUnit, getWeatherDescription(current.weatherCode()), getWindDirection(current.windDirection()), current.windSpeed(), windSpeedUnit, current.humidity(), humidityUnit, current.precipitation())); // 添加未来天气预报 weatherInfo.append(\"未来天气预报:\\n\"); WeatherData.DailyForecast daily = weatherData.daily(); for (int i = 0; i < daily.time().size(); i++) { String date = daily.time().get(i); double tempMin = daily.tempMin().get(i); double tempMax = daily.tempMax().get(i); int weatherCode = daily.weatherCode().get(i); double windSpeed = daily.windSpeedMax().get(i); int windDir = daily.windDirection().get(i); double precip = daily.precipitationSum().get(i); // 格式化日期 LocalDate localDate = LocalDate.parse(date); String formattedDate = localDate.format(DateTimeFormatter.ofPattern(\"yyyy-MM-dd (EEE)\")); weatherInfo.append(String.format(\"\"\"  %s:  温度: %.1f%s ~ %.1f%s  天气: %s  风向: %s (%.1f %s)  降水量: %.1f 毫米  \"\"\",  formattedDate,  tempMin, temperatureUnit,  tempMax, temperatureUnit,  getWeatherDescription(weatherCode),  getWindDirection(windDir),  windSpeed, windSpeedUnit,  precip)); } return weatherInfo.toString(); }}

McpServerApplication启动类

注册OpenMeteoService为MCP工具,通过MethodToolCallbackProvider暴露@Tool方法。

import org.springframework.ai.tool.ToolCallbackProvider;import org.springframework.ai.tool.method.MethodToolCallbackProvider;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.context.annotation.Bean;@SpringBootApplicationpublic class McpServerApplication { /** * MCP服务器主启动类 * 核心功能: * 1. 注册MCP工具服务(如天气查询) * 2. 初始化MCP协议通信端点 * 3. 提供工具回调处理器 * * 工作流程: * 1. AI模型生成工具调用请求 → * 2. ToolCallbackProvider通过MCP协议转发 → * 3. OpenMeteoService执行实际业务逻辑 → * 4. 结果返回AI模型生成响应 */@SpringBootApplicationpublic class McpServerApplication { /** * 应用主入口 * @param args 命令行参数 * * 注意:根据传输协议不同需要特殊配置: * - STDIO模式:需设置spring.main.web-application-type=none * - SSE模式:需配置server.port和MCP端点 */ public static void main(String[] args) { SpringApplication.run(McpServerApplication.class, args); } /** * 注册MCP工具回调处理器 * @param openMeteoService 天气查询服务(需包含@Tool注解方法) * @return ToolCallbackProvider实例 * * 关键作用: * 1. 将OpenMeteoService中的@Tool方法暴露为MCP可调用接口 * 2. 处理AI模型发起的工具调用请求 * 3. 管理工具执行结果返回流程 */ @Bean public ToolCallbackProvider weatherTools(OpenMeteoService openMeteoService) { return MethodToolCallbackProvider.builder() .toolObjects(openMeteoService) // 注册工具类实例 .build(); }}

生成JAR后,客户端通过JSON配置JAR路径和启动命令

接下来就是把这个项目进行 package 打成一个 jar 包:mcp-stdio-server-0.0.1.jar

然后就是配置到客户端(这里直接用前面 Spring AI接入MCP 的项目中配置):

resources.map.mcp-servers-config.json

去除百度MCP防止天气查询冲突,配置jar包路径及启动命令

{ \"mcpServers\": { \"mcp-server-weather\": { \"command\": \"java\", \"args\": [ \"-Dspring.ai.mcp.server.stdio=true\", \"-Dlogging.pattern.console=\", \"-jar\", \"D:\\\\ideaworkspace\\\\git_pull\\\\spring-ai-parent\\\\mcp-stdio-server\\\\target\\\\mcp-stdio-server-0.0.1.jar\" ] } }}

修改之前的路线查询消息,改为天气查询消息

import org.springframework.ai.chat.client.ChatClient;import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;import org.springframework.ai.tool.ToolCallbackProvider;import org.springframework.http.MediaType;import org.springframework.web.bind.annotation.CrossOrigin;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;import reactor.core.publisher.Flux;import java.time.LocalDate;/** * 航空公司智能客服控制器 * 核心功能: * 1. 集成百度地图、OpenMeteo服务(通过MCP协议调用) * 2. 航班查询与预订服务 * 3. 流式对话接口(SSE) * * 关于OpenMeteo MCP调用: * - 实际调用通过ToolCallbackProvider实现 * - ToolsService仅声明工具接口,不直接处理调用 * - MCP服务配置在application.yaml中定义 */@RestController@CrossOriginpublic class OpenAiController { private final ChatClient chatClient; /** * 构造方法(关键依赖注入) * @param chatClientBuilder ChatClient构建器 * @param toolsService 工具服务(声明百度地图/OpenMeteo/航班等工具) * @param toolCallbackProvider MCP调用处理器 */ public OpenAiController(ChatClient.Builder chatClientBuilder, ToolsService toolsService, ToolCallbackProvider toolCallbackProvider) { this.chatClient = chatClientBuilder // 系统角色定义(包含百度地图服务说明) .defaultSystem(\"\"\"  ## 角色  您是一个航空公司的智能客服助手,集成了百度地图、OpenMeteo查询天气服务功能。  ## 服务能力  1. 可以查询地点、路线规划、周边搜索等地图相关服务  2. 可查询指定地点的天气预报、天气情况  3. 提供航班信息查询、预订等航空服务  4. 支持多轮对话,保持上下文理解  ## 交互要求  1. 使用简体中文进行交流  2. 涉及地图服务时,明确说明使用的是百度地图数据  3. 对于需要确认的操作(如预订、修改等),必须获得用户明确确认后再执行  4. 提供准确的地理位置信息时,需注明数据来源和更新时间  5. 指定地点时自动生成对应的经纬度再调用根据进行查询 ## 当前信息  今天的日期是 {current_date}  天气数据来源: OpenMeteo  \"\"\") // 注册工具声明(工具实际调用通过toolCallbackProvider) .defaultTools(toolsService) // 关键配置:设置MCP回调处理器 .defaultToolCallbacks(toolCallbackProvider) .build(); } /** * 流式对话接口 */ @CrossOrigin @GetMapping(value = \"/ai/generateStreamAsString\", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux generateStreamAsString( @RequestParam(value = \"message\", defaultValue = \"查询北京的天气\") String message) { return chatClient.prompt() .user(message) .system(p -> p.param(\"current_date\", LocalDate.now())) .stream() .content(); }}

这样就可以实现自定义MCP打成jar包让外部访问

SSE协议下的MCP实时通信实现

目前MCP协议已弃用,但是streamable模式Spring AI没有适配,不过这两种都是通过部署成Web服务进行传输数据的,因此可以暂时用SSE进行实现

pom.xml

依赖spring-ai-starter-mcp-server-webflux提供WebFlux实现的SSE支持

     org.springframework.ai spring-ai-bom 1.0.0 pom import     org.springframework.boot spring-boot-dependencies 3.2.5 pom import      org.springframework.ai spring-ai-starter-mcp-server-webflux    org.springframework spring-web      org.springframework.boot spring-boot-maven-plugin     repackage      

application.yml

配置服务端口(8088)和MCP服务标识。

spring: ai: mcp: server: name: my-weather-server # 服务标识符 version: 0.0.1 # 服务版本号server: port: 8088  # 服务监听端口

Tool 示例工具类

import java.util.List;import java.time.LocalDate;import java.time.format.DateTimeFormatter;import java.util.Map;import com.fasterxml.jackson.annotation.JsonIgnoreProperties;import com.fasterxml.jackson.annotation.JsonProperty;import org.springframework.ai.tool.annotation.Tool;import org.springframework.ai.tool.annotation.ToolParam;import org.springframework.stereotype.Service;import org.springframework.web.client.RestClient;import org.springframework.web.client.RestClientException;@Servicepublic class UserToolService { Map userScore = Map.of( \"wangwu\",99.0, \"zhangsan\",2.0, \"lisi\",3.0); @Tool(description = \"获取用户分数\") public String getScore(String username) { if(userScore.containsKey(username)){ return userScore.get(username).toString(); } return \"未检索到当前用户\"; }}

McpSSEApplication 启动类

import org.springframework.ai.tool.ToolCallbackProvider;import org.springframework.ai.tool.method.MethodToolCallbackProvider;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.context.annotation.Bean;@SpringBootApplicationpublic class McpSSEApplication { public static void main(String[] args) { SpringApplication.run(McpSSEApplication.class, args); } @Bean public ToolCallbackProvider userTools(UserToolService userToolService) { return MethodToolCallbackProvider.builder().toolObjects(userToolService).build(); }}

然后启动该服务,本机IP地址为: localhost:8088

这时候就修改客户端的yml文件(这里直接用前面 Spring AI接入MCP 的项目中配置)

配置 url 为sse服务端启动的IP地址

application.yml

修改application.yml,替换Stdio配置为SSE:

指定服务端URL(如http://localhost:8088)

server: port: 8080 # 应用服务端口 servlet: encoding: charset: UTF-8 # 强制使用UTF-8编码 enabled: true # 启用编码过滤器 force: true # 强制响应使用配置的字符集spring: ai: mcp: client: request-timeout: 60000 # MCP请求超时时间(毫秒),建议生产环境根据业务调整 sse: connections: UserSSEInfo: # 连接名称(可自定义)  url: http://localhost:8088 # SSE服务基础URL  # sse-endpoint: /custom-sse # 可选的SSE端点路径(默认/sse)# 调试日志配置logging: level: io: modelcontextprotocol: client: DEBUG # 启用MCP客户端详细日志 spec: DEBUG # 启用协议规范级日志(含请求/响应详情)

这时候进行会话接口调用即可,信息为:查询zhangsan的分数

import org.springframework.ai.chat.client.ChatClient;import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;import org.springframework.ai.tool.ToolCallbackProvider;import org.springframework.http.MediaType;import org.springframework.web.bind.annotation.CrossOrigin;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;import reactor.core.publisher.Flux;import java.time.LocalDate;/** * 航空公司智能客服控制器 * 核心功能: * 1. 集成百度地图、OpenMeteo服务(通过MCP协议调用) * 2. 航班查询与预订服务 * 3. 流式对话接口(SSE) * * 关于OpenMeteo MCP调用: * - 实际调用通过ToolCallbackProvider实现 * - ToolsService仅声明工具接口,不直接处理调用 * - MCP服务配置在application.yaml中定义 */@RestController@CrossOriginpublic class OpenAiController { private final ChatClient chatClient; /** * 构造方法(关键依赖注入) * @param chatClientBuilder ChatClient构建器 * @param toolsService 工具服务(声明百度地图/OpenMeteo/航班等工具) * @param toolCallbackProvider MCP调用处理器 */ public OpenAiController(ChatClient.Builder chatClientBuilder, ToolsService toolsService, ToolCallbackProvider toolCallbackProvider) { this.chatClient = chatClientBuilder // 系统角色定义(包含百度地图服务说明) .defaultSystem(\"\"\"  ## 角色  您是一个航空公司的智能客服助手,集成了百度地图、OpenMeteo查询天气服务功能。  ## 服务能力  1. 可以查询地点、路线规划、周边搜索等地图相关服务  2. 可查询指定地点的天气预报、天气情况  3. 提供航班信息查询、预订等航空服务  4. 支持多轮对话,保持上下文理解  5. 根据用户名查询用户分数  ## 交互要求  1. 使用简体中文进行交流  2. 涉及地图服务时,明确说明使用的是百度地图数据  3. 对于需要确认的操作(如预订、修改等),必须获得用户明确确认后再执行  4. 提供准确的地理位置信息时,需注明数据来源和更新时间  5. 指定地点时自动生成对应的经纬度再调用根据进行查询 ## 当前信息  今天的日期是 {current_date}  天气数据来源: OpenMeteo  \"\"\") // 注册工具声明(工具实际调用通过toolCallbackProvider) .defaultTools(toolsService) // 关键配置:设置MCP回调处理器 .defaultToolCallbacks(toolCallbackProvider) .build(); } /** * 流式对话接口 */ @CrossOrigin @GetMapping(value = \"/ai/generateStreamAsString\", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux generateStreamAsString( @RequestParam(value = \"message\", defaultValue = \"查询zhangsan的分数\") String message) { return chatClient.prompt() .user(message) .system(p -> p.param(\"current_date\", LocalDate.now())) .stream() .content(); }}

 MCP(Model Context Protocol)是一种标准化的工具调用协议,旨在简化AI模型与外部服务(如API、数据库、地图服务等)的交互。它通过统一的接口定义和通信机制,使开发者能够轻松集成多种工具,而无需关心底层实现细节。MCP的核心优势在于协议无关性,支持多种通信模式(如Stdio、SSE、HTTP Streamable),并提供了安全、高效的上下文传递机制。

在Spring AI生态中,MCP扮演着关键角色,使开发者可以通过简单的注解(如@Tool、@ToolParam)声明工具功能,并由框架自动处理协议通信、数据转换和结果返回。无论是本地轻量级工具(如文件操作)还是远程企业级服务(如百度地图API),MCP都能提供一致的调用体验,极大提升了AI应用的开发效率。

未来,随着MCP协议的持续演进(如Streamable HTTP的普及、安全机制的强化),它将成为构建智能体互联网(Agent Internet)的核心基石,推动AI应用向更开放、更互联的方向发展。

GitHub:3323223659/Java-AI: SpringAI与LangChain4j学习dome

上一篇:【Spring AI快速上手 (五)】Agent复杂任务智能体初探-CSDN博客

有任何问题或建议欢迎评论区留言讨论!