当前位置: 首页 > news >正文

Spring AI 系列之八 - MCP Server

之前做个几个大模型的应用,都是使用Python语言,后来有一个项目使用了Java,并使用了Spring AI框架。随着Spring AI不断地完善,最近它发布了1.0正式版,意味着它已经能很好的作为企业级生产环境的使用。对于Java开发者来说真是一个福音,其功能已经能满足基于大模型开发企业级应用。借着这次机会,给大家分享一下Spring AI框架。

注意由于框架不同版本改造会有些使用的不同,因此本次系列中使用基本框架是 Spring AI-1.0.0,JDK版本使用的是19
代码参考: https://github.com/forever1986/springai-study

目录

  • 1 MCP-Server
  • 2 示例演示
    • 2.1 MCP-Server(Stdio模式)
      • 2.1.1 stdio-server子模块
      • 2.1.2 stdio-client子模块
      • 2.1.3 演示效果
    • 2.2 MCP-Server(SSE模式)
      • 2.2.1 sse-server子模块
      • 2.2.2 sse-client子模块
      • 2.2.3 演示效果

上一章通过Spring AI的MCP-Client实现访问MCP服务,如果用户需要自定义自己的MCP服务,也就是MCP-Server,在Spring AI也提供非常方便的集成,这一章将通过Stido和SSE两种模式的示例来讲解。

1 MCP-Server

在Java MCP SDK中建立MCP-Server提供两种通讯方式,分别是Stdio和SSE。而Spring AI 也集成了这两种方式,并且基于Spring Boot,提供了三种方式的建立MCP-Server:

插件支持通讯方式同步或异步
spring-ai-starter-mcp-serverStdio同步或异步
spring-ai-starter-mcp-server-webmvcStdio或SSE同步或异步
spring-ai-starter-mcp-server-webfluxStdio或SSE同步或异步

下面通过spring-ai-starter-mcp-server和spring-ai-starter-mcp-server-webmvc分别演示Stdio模式和SSE模式。

2 示例演示

下面两个示例参考lesson10子模块

示例说明:建立自己的MCP-Server服务,该服务提供一个访问天气情况的工具,这里使用高德的公共API访问

2.1 MCP-Server(Stdio模式)

代码参考lesson10子模块下面的,stdio-server和stdio-client两个子模块

2.1.1 stdio-server子模块

1)在lesson10子模块下,新建stdio-server子模块,其pom引入如下:

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- 引入的是mcp-server --><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-mcp-server</artifactId></dependency>
</dependencies><!-- 使用springboot的打包插件-->
<build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>${spring-boot.version}</version><configuration><!--增加此行,可以解决打包时,找不到jar包的问题--><addResources>true</addResources><includeSystemScope>true</includeSystemScope><mainClass>com.demo.lesson10.stdio.server.Lesson10MCPServerStdioApplication</mainClass></configuration><executions><execution><id>repackage</id><goals><goal>repackage</goal></goals></execution></executions></plugin></plugins>
</build>

注意:由于采用本地运行,因此使用spring-boot-maven-plugin打包工具,指定启动类Lesson10MCPServerStdioApplication

2)配置application.properties文件

# 非web模式运行(必须设置)
spring.main.web-application-type=none
# 关闭打印banner(必须设置)
spring.main.banner-mode=off
# 不在控制台输出日志(如果设置在log文件输出也可以,但是不能在控制台输出,因为这个会传递到client的解析里面)(必须设置)
logging.pattern.console=
# 服务名称
spring.ai.mcp.server.name=stdio-weather-server
# 服务版本
spring.ai.mcp.server.version=0.0.1
# 定义同步模式(必须设置)
spring.ai.mcp.server.type=SYNC
# 定义通讯方式stdio
spring.ai.mcp.server.stdio=true

说明:这里有几个重点配置需要说明
1)使用非web运行模式,因为stdio采用控制台内容输出来通讯
2)关闭banner和日志打印,因为stdio采用控制台内容输出来通讯,开启会导致客户端解析错误

3)新建天气预报服务WeatherService:

import org.springframework.ai.tool.annotation.Tool;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;@Service
public class WeatherService {@Autowiredprivate RestTemplate restTemplate;private static final String adcode = "adcode";@Tool(description = "获取中国城市的天气情况")public String getWeatherForecastByCity(String city) {// 获取城市的adcodeString result = restTemplate.getForObject("https://restapi.amap.com/v3/geocode/geo?address=西安&key=你的高德APIKEY", String.class);// 这里为了方便简单处理一下字符串获取adcode,正式的话需要解析json格式int startIndex = result.indexOf(adcode);String code = result.substring(startIndex+adcode.length()+3,result.indexOf(",",startIndex)-1);// 通过城市的adcode,进行获取天气预报result = restTemplate.getForObject("https://restapi.amap.com/v3/weather/weatherInfo?extensions=all&key=你的高德APIKEY&city="+code, String.class);return result;}
}

4)由于使用RestTemplate访问API,因此配置RestTemplateConfig:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;@Configuration
public class RestTemplateConfig {@Beanpublic RestTemplate restTemplate() {return new RestTemplate();}
}

5)配置启动类Lesson10MCPServerStdioApplication :

import com.demo.lesson10.stdio.server.service.WeatherService;
import org.springframework.ai.support.ToolCallbacks;
import org.springframework.ai.tool.ToolCallback;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;import java.util.Arrays;
import java.util.List;@SpringBootApplication
public class Lesson10MCPServerStdioApplication {@Beanpublic List<ToolCallback> weatherTools(WeatherService weatherService) {return Arrays.stream(ToolCallbacks.from(weatherService)).toList();}public static void main(String[] args) {SpringApplication.run(Lesson10MCPServerStdioApplication.class, args);}
}

2.1.2 stdio-client子模块

1)在lesson10子模块下,新建stdio-client子模块,其pom引入如下:

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-model-zhipuai</artifactId></dependency><!-- 引入的是mcp-client --><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-mcp-client</artifactId></dependency>
</dependencies>

2)配置application.properties文件

# 聊天模型
spring.ai.zhipuai.api-key=你的智谱模型的API KEY
spring.ai.zhipuai.chat.options.model=GLM-4-Flash-250414
spring.ai.zhipuai.chat.options.temperature=0.7# 指定mcp-servers的文件
spring.ai.mcp.client.stdio.servers-configuration=classpath:/mcp-servers-config.json

3)配置MCP-Server文件mcp-servers-config.json

{"mcpServers": {"spring-ai-mcp-weather": {"command": "java","args": ["-Dspring.ai.mcp.server.stdio=true","-jar","E:/workspace/iip-factory/springai-study/lesson10/stdio-server/target/stdio-server-1.0-SNAPSHOT.jar"]}}
}

注意:其中jar包的路径由自己实际路径为主,这里其实就是使用java命令启动MCP-Server服务

4)新建MCPClientStdioController类进行访问

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;@RestController
public class MCPClientStdioController {private ChatClient chatClient;public MCPClientStdioController(ChatClient.Builder chatClientBuilder, ToolCallbackProvider tools) {this.chatClient = chatClientBuilder.defaultToolCallbacks(tools).build();}/*** @param message 问题*/@GetMapping("/ai/stdio/client")public String stdioClient(@RequestParam(value = "message", required = true) String message) {return this.chatClient.prompt().user(message).call().content();}
}

5)新建Lesson10MCPClientStdioApplication启动类

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class Lesson10MCPClientStdioApplication {public static void main(String[] args) {SpringApplication.run(Lesson10MCPClientStdioApplication.class, args);}}

2.1.3 演示效果

1)对stdio-server子模块进行打包,会在target目录下得到jar包

在这里插入图片描述

2)启动stdio-client子模块

在这里插入图片描述

上图中可以看到有一个子线程访问工具连接

3)访问地址

http://localhost:8080/ai/stdio/client?message=广州市的天气预报

在这里插入图片描述

说明:可以看到,其访问结果是正确的。

2.2 MCP-Server(SSE模式)

代码参考lesson10子模块下面的,sse-server和sse-client两个子模块

2.2.1 sse-server子模块

1)在lesson10子模块下,新建sse-server子模块,其pom引入如下:

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- 引入的是mcp-server --><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-mcp-server-webmvc</artifactId></dependency>
</dependencies>

2)配置application.properties文件

# 为了防止与客户端端口冲突,重新配置端口
server.port=8081
# 服务名称
spring.ai.mcp.server.name=sse-weather-server
# 服务版本
spring.ai.mcp.server.version=0.0.1
# 定义同步模式(必须设置)
spring.ai.mcp.server.type=SYNC
# 是否关闭stdio模式,因为webmvc支持stdio和sse,这里演示sse,关闭stdio
spring.ai.mcp.server.stdio=false
# 用于客户端发送消息的自定义 SSE 消息端点路径,以供网络传输使用
spring.ai.mcp.server.sse-message-endpoint=/mcp/message
# 用于网络传输的自定义 SSE 端点路径
spring.ai.mcp.server.sse-endpoint=/sse

注意:这里有几个点需要注意
1)spring.ai.mcp.server.stdio=false,关闭stdio模式,实际上webmvc也支持stdio模式,这里只是为了演示sse模式
2)这里没有把banner和控制台输出屏蔽,是因为sse模式不是通过控制台输出,因此没必要屏蔽

3)新建天气预报服务WeatherService:

import org.springframework.ai.tool.annotation.Tool;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;@Service
public class WeatherService {@Autowiredprivate RestTemplate restTemplate;private static final String adcode = "adcode";@Tool(description = "获取中国城市的天气情况")public String getWeatherForecastByCity(String city) {// 获取城市的adcodeString result = restTemplate.getForObject("https://restapi.amap.com/v3/geocode/geo?address=西安&key=你的高德APIKEY", String.class);// 这里为了方便简单处理一下字符串获取adcode,正式的话需要解析json格式int startIndex = result.indexOf(adcode);String code = result.substring(startIndex+adcode.length()+3,result.indexOf(",",startIndex)-1);// 通过城市的adcode,进行获取天气预报result = restTemplate.getForObject("https://restapi.amap.com/v3/weather/weatherInfo?extensions=all&key=你的高德APIKEY&city="+code, String.class);return result;}}

4)由于使用RestTemplate访问API,因此配置RestTemplateConfig:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;@Configuration
public class RestTemplateConfig {@Beanpublic RestTemplate restTemplate() {return new RestTemplate();}
}

5)配置启动类Lesson10MCPServerSSEApplication :

import com.demo.lesson10.sse.server.service.WeatherService;
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;@SpringBootApplication
public class Lesson10MCPServerSSEApplication {@Beanpublic ToolCallbackProvider weatherTools(WeatherService weatherService) {return MethodToolCallbackProvider.builder().toolObjects(weatherService).build();}public static void main(String[] args) {SpringApplication.run(Lesson10MCPServerSSEApplication.class, args);}
}

2.2.2 sse-client子模块

1)在lesson10子模块下,新建sse-client子模块,其pom引入如下:

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-model-zhipuai</artifactId></dependency><!-- 引入的是mcp-client --><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-mcp-client</artifactId></dependency>
</dependencies>

2)配置application.properties文件

# 聊天模型
spring.ai.zhipuai.api-key=你的智谱模型的API KEY
spring.ai.zhipuai.chat.options.model=GLM-4-Flash-250414
spring.ai.zhipuai.chat.options.temperature=0.7# 指定mcp-servers的URL
spring.ai.mcp.client.type=SYNC
spring.ai.mcp.client.sse.connections.server1.url=http://localhost:8081

说明:由于sse-server没有特意配置路径,因此无需配置sse-endpoint

3)新建MCPClientSSEController 类进行访问

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;@RestController
public class MCPClientSSEController {private ChatClient chatClient;public MCPClientSSEController(ChatClient.Builder chatClientBuilder, ToolCallbackProvider tools) {this.chatClient = chatClientBuilder.defaultToolCallbacks(tools).build();}/*** @param message 问题*/@GetMapping("/ai/sse/client")public String amapMaps(@RequestParam(value = "message", required = true) String message) {return this.chatClient.prompt().user(message).call().content();}}

4)新建Lesson10MCPClientSSEApplication 启动类

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class Lesson10MCPClientSSEApplication {public static void main(String[] args) {SpringApplication.run(Lesson10MCPClientSSEApplication.class, args);}}

2.2.3 演示效果

1)启动sse-server子模块

在这里插入图片描述

2)启动sse-client子模块

在这里插入图片描述

3)访问地址:

http://localhost:8080/ai/sse/client?message=广州市的天气预报

在这里插入图片描述

可以去看sse-server控制台的日志,有调用的日志
在这里插入图片描述

结语:本章演示通过2种方式建立了MCP-Server服务,对于MCP的内容还比较多,这里只是一个入门。如果对这一块感兴趣的朋友,需要先研究一下MCP协议,里面涉及的配置也可以参考官方网站。下一章将讲更为复杂的RAG框架。

http://www.dtcms.com/a/273359.html

相关文章:

  • NFS文件存储及部署论坛(小白的“升级打怪”成长之路)
  • (鱼书)深度学习入门2:手搓感知机
  • PostgreSQL创建新实例并指定目录
  • 下一代防火墙混合模式部署
  • Jupyter介绍
  • MySQL事务实现原理
  • SpringCloud系列 - 分布式锁(八)
  • html页面,当鼠标移开A字标就隐藏颜色框
  • Spring Boot项目中大文件上传的优化策略与实践
  • 华为鸿蒙3.0 4.0完全关闭纯净模式的方法以及临时绕过纯净模式检测的方法
  • 接口(上篇)
  • 基于深度学习的自动调制识别网络(持续更新)
  • 亚洲牧原:活跃行业交流,延伸公益版图,市场拓展再结硕果
  • 布隆过滤器原理
  • 我的世界模组开发——机械动力的渲染(4)
  • java-io流
  • 对象序列化与反序列化
  • 【PyTorch】PyTorch 自动微分与完整手动实现对比
  • vue3 element plus table 使用固定列,滑动滚动会错位、固定列层级异常、滑动后固定列的内容看不到了
  • Java多线程 V1
  • AIStarter 3.2.0正式上线!高速下载+离线导入+一键卸载新功能详解【附完整使用教程】✅ 帖子正文(字数:约 400 字)
  • 静态路由综合实验
  • WiFi技术深度研究报告:从基础原理到组网应用与未来演进
  • python+django/flask基于微信小程序的农产品管理与销售APP系统
  • CTFshow-PWN-栈溢出(pwn62-pwn64)
  • JAVA面试宝典 -《新潮技术:协程与响应式编程实践》
  • 【Ubuntu】编译sentencepiece库
  • next.js打包后的前端资源如何进行部署和访问,为什么没有index.html
  • Vue响应式原理六:Vue3响应式原理
  • Java 17 新特性解析:密封类与模式匹配的完美协作