
摘要
Aliware
发布 stdio 模式的 MCP Server 发布 SSE 模式的 MCP Server 开发另一个 Spring 应用作为 MCP Client 调用 MCP Server 服务 使用 Claude 桌面应用接入我们的 Java MCP Server
配置并调用 stdio 模式的 MCP Server 配置并调用 SSE 模式的 MCP Server
模型上下文协议
(Model Context Protocol)入门
Aliware
2024 年 11 月,Anthropic 公司搞了个挺有意思的新玩意 - Model Context Protocol(模型上下文协议)简称为 MCP 协议。简单来说,它就是给 AI 和各类工具数据之间搭了个标准化的”桥梁”,让开发者不用再为对接问题头疼了。
awesome-mcp-servers mcp.so
使用百度/高德地图分析旅线计算时间 接 Puppeteer 自动操作网页 使用 Github/Gitlab 让大模型接管代码仓库 使用数据库组件完成对 Mysql、ES、Redis 等数据库的操作 使用搜索组件扩展大模型的数据搜索能力
~/Library/Application Support/Claude/claude_desktop_config.json
%APPDATA%\Claude\claude_desktop_config.json
{ "mcpServers": { "github": { "command": "npx", "args": [ "-y", "@modelcontextprotocol/server-github" ], "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "`" } } }
客户端:一般指的是大模型应用,比如 Claude、通过 Spring AI Alibaba、Langchain 等框架开发的 AI 应用 服务端:连接各种数据源的服务和工具
在 Spring AI 中
使用 MCP Server
Aliware
Spring AI 应用程序:使用 Spring AI 框架构建想要通过 MCP 访问数据的生成式 AI 应用程序 Spring MCP 客户端:MCP 协议的 Spring AI 实现,与服务器保持 1:1 连接
基于 stdio 的进程间通信传输,以独立的进程运行在 AI 应用本地,适用于比较轻量级的工具。 基于 SSE(Server-Sent Events) 进行远程服务访问,需要将服务单独部署,客户端通过服务端的 URL 进行远程访问,适用于比较重量级的工具。
添加依赖
<dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-mcp-server-spring-boot-starter</artifactId></dependency>
配置 MCP 服务端
spring:main:web-application-type: none # 必须禁用web应用类型banner-mode: off # 禁用bannerai:mcp:server:stdio: true # 启用stdio模式name: my-weather-server # 服务器名称version: 0.0.1 # 服务器版本
实现 MCP 工具
@Servicepublic class OpenMeteoService {private final WebClient webClient;public OpenMeteoService(WebClient.Builder webClientBuilder) {this.webClient = webClientBuilder.baseUrl("https://api.open-meteo.com/v1").build();}@Tool(description = "根据经纬度获取天气预报")public String getWeatherForecastByLocation(@ToolParameter(description = "纬度,例如:39.9042") String latitude,@ToolParameter(description = "经度,例如:116.4074") String longitude) {try {String response = webClient.get().uri(uriBuilder -> uriBuilder.path("/forecast").queryParam("latitude", latitude).queryParam("longitude", longitude).queryParam("current", "temperature_2m,wind_speed_10m").queryParam("timezone", "auto").build()).retrieve().bodyToMono(String.class).block();// 解析响应并返回格式化的天气信息// 这里简化处理,实际应用中应该解析JSONreturn "当前位置(纬度:" + latitude + ",经度:" + longitude + ")的天气信息:\n" + response;} catch (Exception e) {return "获取天气信息失败:" + e.getMessage();}}@Tool(description = "根据经纬度获取空气质量信息")public String getAirQuality(@ToolParameter(description = "纬度,例如:39.9042") String latitude,@ToolParameter(description = "经度,例如:116.4074") String longitude) {// 模拟数据,实际应用中应调用真实APIreturn "当前位置(纬度:" + latitude + ",经度:" + longitude + ")的空气质量:\n" +"- PM2.5: 15 μg/m³ (优)\n" +"- PM10: 28 μg/m³ (良)\n" +"- 空气质量指数(AQI): 42 (优)\n" +"- 主要污染物: 无";}}
注册 MCP 工具
@SpringBootApplicationpublic class McpServerApplication {public static void main(String[] args) {SpringApplication.run(McpServerApplication.class, args);}@Beanpublic ToolCallbackProvider weatherTools(OpenMeteoService openMeteoService) {return MethodToolCallbackProvider.builder().toolObjects(openMeteoService).build();}}
运行服务端
mvn clean package -DskipTests
添加依赖
<dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-mcp-server-webflux-spring-boot-starter</artifactId></dependency>
配置 MCP 服务端
server:port: 8080 # 服务器端口配置spring:ai:mcp:server:name: my-weather-server # MCP服务器名称version: 0.0.1 # 服务器版本号
实现 MCP 工具
@Servicepublic class OpenMeteoService {private final WebClient webClient;public OpenMeteoService(WebClient.Builder webClientBuilder) {this.webClient = webClientBuilder.baseUrl("https://api.open-meteo.com/v1").build();}@Tool(description = "根据经纬度获取天气预报")public String getWeatherForecastByLocation(@ToolParameter(description = "纬度,例如:39.9042") String latitude,@ToolParameter(description = "经度,例如:116.4074") String longitude) {try {String response = webClient.get().uri(uriBuilder -> uriBuilder.path("/forecast").queryParam("latitude", latitude).queryParam("longitude", longitude).queryParam("current", "temperature_2m,wind_speed_10m").queryParam("timezone", "auto").build()).retrieve().bodyToMono(String.class).block();// 解析响应并返回格式化的天气信息return "当前位置(纬度:" + latitude + ",经度:" + longitude + ")的天气信息:\n" + response;} catch (Exception e) {return "获取天气信息失败:" + e.getMessage();}}@Tool(description = "根据经纬度获取空气质量信息")public String getAirQuality(@ToolParameter(description = "纬度,例如:39.9042") String latitude,@ToolParameter(description = "经度,例如:116.4074") String longitude) {// 模拟数据,实际应用中应调用真实APIreturn "当前位置(纬度:" + latitude + ",经度:" + longitude + ")的空气质量:\n" +"- PM2.5: 15 μg/m³ (优)\n" +"- PM10: 28 μg/m³ (良)\n" +"- 空气质量指数(AQI): 42 (优)\n" +"- 主要污染物: 无";}}
注册 MCP 工具
@SpringBootApplicationpublic class McpServerApplication {public static void main(String[] args) {SpringApplication.run(McpServerApplication.class, args);}@Beanpublic ToolCallbackProvider weatherTools(OpenMeteoService openMeteoService) {return MethodToolCallbackProvider.builder().toolObjects(openMeteoService).build();}@Beanpublic WebClient.Builder webClientBuilder() {return WebClient.builder();}}
运行服务端
mvn spring-boot:run
{"mcpServers": {"github": {"command": "npx","args": ["-y","@modelcontextprotocol/server-github"],"env": {"GITHUB_PERSONAL_ACCESS_TOKEN": your token}},"weather": {"command": "java","args": ["-Dspring.ai.mcp.server.stdio=true","-Dspring.main.web-application-type=none","-Dlogging.pattern.console=","-jar","<修改为stdio编译之后的jar包全路径>"],"env": {}}}}
在 Spring AI Alibaba 中
集成 Mcp Client
Aliware
添加依赖
<dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-mcp-client-spring-boot-starter</artifactId></dependency><!-- 添加Spring AI MCP starter依赖 --><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-mcp-client-spring-boot-starter</artifactId></dependency>
配置 MCP 服务器
spring:ai:dashscope:# 配置通义千问API密钥api-key: ${DASH_SCOPE_API_KEY}mcp:client:stdio:# 指定MCP服务器配置文件路径(推荐)servers-configuration: classpath:/mcp-servers-config.json# 直接配置示例,和上边的配制二选一# connections:# server1:# command: java# args:# - -jar# - path/to/your/mcp-server.jar
{"mcpServers": {// 定义名为"weather"的MCP服务器"weather": {// 指定启动命令为java"command": "java",// 定义启动参数"args": ["-Dspring.ai.mcp.server.stdio=true","-Dspring.main.web-application-type=none","-jar","<修改为stdio编译之后的jar包全路径>"],// 环境变量配置(可选)"env": {}}}}
编写一个启动类进行测试:
```java@SpringBootApplicationpublic class Application {public static void main(String[] args) {// 启动Spring Boot应用SpringApplication.run(Application.class, args);}@Beanpublic CommandLineRunner predefinedQuestions(ChatClient.Builder chatClientBuilder,ToolCallbackProvider tools,ConfigurableApplicationContext context) {return args -> {// 构建ChatClient并注入MCP工具var chatClient = chatClientBuilder.defaultTools(tools).build();// 定义用户输入String userInput = "北京的天气如何?";// 打印问题System.out.println("\n>>> QUESTION: " + userInput);// 调用LLM并打印响应System.out.println("\n>>> ASSISTANT: " +chatClient.prompt(userInput).call().content());// 关闭应用上下文context.close();};}}```
mvn spring-boot:run
>>> QUESTION: 北京的天气如何?2025-03-31T17:56:17.931+08:00 DEBUG 23455 --- [mcp] [pool-1-thread-1] io.modelcontextprotocol.spec.McpSchema : Received JSON message: {"jsonrpc":"2.0","id":"60209de5-3","result":{"content":[{"type":"text","text":"\"当前天气:\\n温度: 18.6°C (体感温度: 15.1°C)\\n天气: 多云\\n风向: 南风 (4.7 km/h)\\n湿度: 18%\\n降水量: 0.0 毫米\\n\\n未来天气预报:\\n2025-03-31 (周一):\\n温度: 2.4°C ~ 19.5°C\\n天气: 多云\\n风向: 南风 (8.4 km/h)\\n降水量: 0.0 毫米\\n\\n2025-04-01 (周二):\\n温度: 7.6°C ~ 20.6°C\\n天气: 多云\\n风向: 西北风 (19.1 km/h)\\n降水量: 0.0 毫米\\n\\n2025-04-02 (周三):\\n温度: 6.9°C ~ 18.4°C\\n天气: 晴朗\\n风向: 西北风 (12.8 km/h)\\n降水量: 0.0 毫米\\n\\n2025-04-03 (周四):\\n温度: 7.0°C ~ 19.8°C\\n天气: 多云\\n风向: 南风 (16.3 km/h)\\n降水量: 0.0 毫米\\n\\n2025-04-04 (周五):\\n温度: 7.5°C ~ 21.6°C\\n天气: 多云\\n风向: 西北风 (19.6 km/h)\\n降水量: 0.0 毫米\\n\\n2025-04-05 (周六):\\n温度: 5.6°C ~ 20.7°C\\n天气: 多云\\n风向: 西风 (16.5 km/h)\\n降水量: 0.0 毫米\\n\\n2025-04-06 (周日):\\n温度: 8.4°C ~ 22.3°C\\n天气: 晴朗\\n风向: 南风 (9.4 km/h)\\n降水量: 0.0 毫米\\n\\n\""}],"isError":false}}2025-03-31T17:56:17.932+08:00 DEBUG 23455 --- [mcp] [pool-1-thread-1] i.m.spec.McpClientSession : Received Response: JSONRPCResponse[jsonrpc=2.0, id=60209de5-3, result={content=[{type=text, text="当前天气:\n温度: 18.6°C (体感温度: 15.1°C)\n天气: 多云\n风向: 南风 (4.7 km/h)\n湿度: 18%\n降水量: 0.0 毫米\n\n未来天气预报:\n2025-03-31 (周一):\n温度: 2.4°C ~ 19.5°C\n天气: 多云\n风向: 南风 (8.4 km/h)\n降水量: 0.0 毫米\n\n2025-04-01 (周二):\n温度: 7.6°C ~ 20.6°C\n天气: 多云\n风向: 西北风 (19.1 km/h)\n降水量: 0.0 毫米\n\n2025-04-02 (周三):\n温度: 6.9°C ~ 18.4°C\n天气: 晴朗\n风向: 西北风 (12.8 km/h)\n降水量: 0.0 毫米\n\n2025-04-03 (周四):\n温度: 7.0°C ~ 19.8°C\n天气: 多云\n风向: 南风 (16.3 km/h)\n降水量: 0.0 毫米\n\n2025-04-04 (周五):\n温度: 7.5°C ~ 21.6°C\n天气: 多云\n风向: 西北风 (19.6 km/h)\n降水量: 0.0 毫米\n\n2025-04-05 (周六):\n温度: 5.6°C ~ 20.7°C\n天气: 多云\n风向: 西风 (16.5 km/h)\n降水量: 0.0 毫米\n\n2025-04-06 (周日):\n温度: 8.4°C ~ 22.3°C\n天气: 晴朗\n风向: 南风 (9.4 km/h)\n降水量: 0.0 毫米\n\n"}], isError=false}, error=null]
添加依赖
<dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-mcp-client-webflux-spring-boot-starter</artifactId></dependency>
配置 MCP 服务器
spring:ai:dashscope:api-key: ${DASH_SCOPE_API_KEY}mcp:client:sse:connections:server1:url: http://localhost:8080 #服务地址
使用 MCP 客户端
@SpringBootApplicationpublic class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}@Beanpublic CommandLineRunner predefinedQuestions(ChatClient.Builder chatClientBuilder,ToolCallbackProvider tools,ConfigurableApplicationContext context) {return args -> {// 构建ChatClient并注入MCP工具var chatClient = chatClientBuilder.defaultTools(tools).build();// 使用ChatClient与LLM交互String userInput = "北京的天气如何?";System.out.println("\n>>> QUESTION: " + userInput);System.out.println("\n>>> ASSISTANT: " + chatClient.prompt(userInput).call().content());context.close();};}}
mvn spring-boot:run
Caused by: java.lang.IllegalStateException: Multiple tools with the same name (spring-ai-mcp-client-getWeatherForecastByLocation, spring-ai-mcp-client-getAirQuality)at org.springframework.ai.mcp.SyncMcpToolCallbackProvider.validateToolCallbacks(SyncMcpToolCallbackProvider.java:126) ~[spring-ai-mcp-1.0.0-20250325.064812-147.jar:1.0.0-SNAPSHOT]at org.springframework.ai.mcp.SyncMcpToolCallbackProvider.getToolCallbacks(SyncMcpToolCallbackProvider.java:110) ~[spring-ai-mcp-1.0.0-20250325.064812-147.jar:1.0.0-SNAPSHOT]at org.springframework.ai.autoconfigure.mcp.client.McpClientAutoConfiguration.toolCallbacksDeprecated(McpClientAutoConfiguration.java:196) ~[spring-ai-mcp-client-spring-boot-autoconfigure-1.0.0-M6.jar:1.0.0-M6]at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[na:na]at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[na:na]at org.springframework.beans.factory.support.SimpleInstantiationStrategy.lambda$instantiate$0(SimpleInstantiationStrategy.java:171) ~[spring-beans-6.2.0.jar:6.2.0]... 23 common frames omitted
SseHttpClientTransportAutoConfiguration 和
SseWebFluxTransportAutoConfiguration。这两个自动配置类提供了同步和异步两种方式,本身应该是互斥的,但是 Spring AI 对于互斥的处理上出了问题,导致两个自动配置类都会加载。
@SpringBootApplication(exclude = {org.springframework.ai.autoconfigure.mcp.client.SseHttpClientTransportAutoConfiguration.class})public class Application {...
mvn spring-boot:run

在 Spring AI Alibaba 的
Open Manus 中体验 MCP
Aliware
添加依赖
<dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-mcp-client-spring-boot-starter</artifactId><version>${spring-ai.version}</version></dependency>
配置 MCP 服务器
{"mcpServers": {"baidu-map": {"command": "npx","args": ["-y","@baidumap/mcp-server-baidu-map"],"env": {"BAIDU_MAP_API_KEY": "your_baidu_AK"}}}}
使用 MCP 工具
public LlmService(ChatModel chatModel, ToolCallbackProvider toolCallbackProvider) {this.chatModel = chatModel;this.planningChatClient = ChatClient.builder(chatModel).defaultSystem(PLANNING_SYSTEM_PROMPT).defaultAdvisors(new MessageChatMemoryAdvisor(planningMemory)).defaultAdvisors(new SimpleLoggerAdvisor()).defaultTools(ToolBuilder.getPlanningAgentToolCallbacks()).defaultTools(toolCallbackProvider).build();this.chatClient = ChatClient.builder(chatModel).defaultSystem(MANUS_SYSTEM_PROMPT).defaultAdvisors(new MessageChatMemoryAdvisor(memory)).defaultAdvisors(new SimpleLoggerAdvisor()).defaultTools(ToolBuilder.getManusAgentToolCalls()).defaultTools(toolCallbackProvider).defaultOptions(OpenAiChatOptions.builder().internalToolExecutionEnabled(false).build()).build();this.finalizeChatClient = ChatClient.builder(chatModel).defaultSystem(FINALIZE_SYSTEM_PROMPT).defaultAdvisors(new MessageChatMemoryAdvisor(finalizeMemory)).defaultAdvisors(new SimpleLoggerAdvisor()).build();}
测试效果
Steps:0. [ ] [MANUS] 使用百度地图的地理编码服务获取北京市和上海市的经纬度坐标1. [ ] [MANUS] 使用百度地图的路线规划服务计算从北京市到上海市的驾车路线2. [ ] [MANUS] 分析并提供最终的路线信息,包括距离、预计耗时等
Here is a summary of what we accomplished in this step:- For Beijing, we received the coordinates: Longitude (lng): 116.4133836971231, Latitude (lat): 39.910924547299565.- For Shanghai, we received the coordinates: Longitude (lng): 121.48053886017651, Latitude (lat): 31.235929042252014.
Distance: The total distance of the route is 1,223,200 meters (approximately 1,223 kilometers).Duration: The estimated travel time is 50,592 seconds (approximately 14 hours and 3 minutes).
总结
Aliware
文章转载自阿里巴巴中间件,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




