Spring AI 1.0.0 M6新特性MCP
Spring AI 1.0.0 M6新特性MCP
- 前言
- 一、MCP是什么?(Model Context Protocol)
- 二、它的发展历程
- 三、核心架构
- 四、MCP Java SDK的核心能力
- Java MCP实现遵循三层架构:
- MCP客户端
- MCP服务器
- 总结MCP 的核心能力
- 总结多种传输选项
- 搭建服务端与客户端
- 服务端
- 引入maven依赖
- 配置YML
- 编写服务工具
- 注册为ToolCallbackProvider
- 客户端
- 引入maven依赖
- 配置YML
- 编写controller
前言
在人工智能技术日新月异的今天,Spring AI作为Spring家族的新星,正以前所未有的速度发展与进化。从最初的简单应用到如今复杂多样的功能集成,Spring AI不断突破边界,为用户提供更加智能、高效的解决方案。而在这一系列的迭代升级中,1.0.0 M6版本无疑成为了一个备受瞩目的里程碑。本文将深入探讨这一新版本所带来的新特性MCP(Model Context Protocol),以及它如何为开发者和企业带来全新的机遇与挑战。通过详细的解析与实例展示,我们希望能够帮助您更好地理解并应用这些前沿技术,开启智能化新篇章。
是不是感觉很熟悉又陌生Cursor与Trae编辑器的人都清楚,为什么他能操作我本地的系统并执行代码下载依赖等问题?那么我们来讲解一下MCP
一、MCP是什么?(Model Context Protocol)
MCP,全称是Model Context Protocol(模型上下文协议),可以把它想象成一个让AI模型更顺畅工作的“桥梁”和“助手”。
连接数据源和工具:就像我们要做饭需要从不同地方买来食材和厨具一样,AI模型工作也需要很多不同的数据和工具。MCP就提供了一个统一的方式,让AI模型能轻松地连接到各种数据来源和使用各种工具,不需要每次都重新折腾怎么连接,让整个过程变得无缝又一致。
助力构建智能代理和复杂工作流程:咱们开发的时候,经常得基于大型语言模型(LLMs)来创建一些能自主干活的智能代理,还有很复杂的工作流程。这时候MCP就派上用场了,它能让这些开发工作变得更简单高效。因为大型语言模型常常要和好多数据、工具打交道,MCP就像个贴心小助手,提供了预建好的集成方式,你的LLM可以直接用;还能灵活地在不同LLM供应商和厂商之间切换;并且有标准的接口用于发现和使用工具。
二、它的发展历程
去年11月,Model Context Protocol发布了,而且在AI社区里反响特别好,这可是去年的大惊喜呢,大家都很期待它能带来新变化。
从去年11月开始,spring - ai - mcp这个实验性项目就启动了,之后一直在不断发展。Spring AI团队还和Anthropic公司的David Soria Parra等人一起合作,把这个实验性项目变成了正式的MCP Java SDK。
三、核心架构
了解MCP如何连接客户端、服务器和LLM
模型上下文协议(MCP)建立在一个灵活的、可扩展的体系结构上,可以实现LLM应用程序和集成之间的无缝通信。本文档涵盖了核心架构组件和概念。
MCP遵循客户端-服务器架构,其中:
启动连接的LLM应用程序 Claude Desktop或IDE
客户端在主机应用程序内与服务器保持1:1连接
服务器为客户端提供上下文、工具和提示
四、MCP Java SDK的核心能力
Java MCP实现遵循三层架构:
客户端/服务器层:McpClient处理客户端操作,而McpServer管理服务器端协议操作。两者都使用McpSession进行通信管理。
会话层(McpSession):通过DefaultMcpSession实现管理通信模式和状态。
传输层(McpTransport):处理JSON-RPC消息序列化和非序列化,支持多种传输实现。
MCP客户端
MCP客户端是模型上下文协议(MCP)架构中的关键组件,负责建立和管理与MCP服务器的连接。它实现了协议的客户端,处理:
- 协议版本协商以确保与服务器的兼容性
- 能力协商以确定可用功能
- 消息传输和JSON-RPC通信
- 工具发现和执行
- 资源获取和管理
- 提示系统交互
MCP服务器
MCP服务器是模型上下文协议(MCP)体系结构中的基础组件,为客户端提供工具、资源和功能。它实现了协议的服务器端,负责:
- 服务器端协议操作实现
- 工具暴露和发现
- 使用基于URI的访问进行资源管理
- 迅速提供和处理模板
- 与客户进行能力协商
- 结构化日志和通知
- 并发客户端连接管理
- 同步和异步API支持
- 传输实现:
用于基于进程的通信的基于stdio的传输
基于Servlet的SSE服务器传输
- WebFlux SSE服务器传输用于响应式HTTP流
- 用于基于servlet的HTTP流的WebMVC SSE服务器传输
总结MCP 的核心能力
- 同步和异步客户端/服务器实现: 这个功能可以让不同的操作在不同时间、以不同方式去执行,有时候需要同步操作,有时候异步操作更方便,它都能支持。
- 协议版本兼容性协商: 就好比我们要和不同版本软件打交道一样,它能商量好大家用哪个版本的协议来交流,确保沟通无障碍。
- 工具发现和执行以及变更通知: 它能像侦探一样去发现有哪些工具可以用,然后安排它们去干活,而且如果有了什么变化,还会及时通知大家。
- 带URI模板的资源管理: 可以很方便地管理资源,就像把东西分门别类放在不同的文件夹里,通过URI模板能快速找到和管理它们。
- Roots列表管理和通知: 这个就像是管理一个清单,知道哪些是重要的“根”元素,并且如果有变化会及时告知。
- 提示处理和管理: 能更好地处理和管理给AI模型的提示信息,让模型更准确地理解和回应我们的需求。
- AI模型交互的采样支持: 在和AI模型互动的时候,还可以进行采样,就像是从一大群人里抽一部分代表出来了解情况一样,让互动更有针对性。
总结多种传输选项
- 基于Stdio的进程通信传输:这是比较基础的一种传输方式,就像两个人面对面说话一样直接,适合基于进程的通信。
- 基于Java HttpClient的SSE客户端传输:利用Java里的HttpClient,通过服务器发送事件(SSE)这种方式来进行数据传输,就像一个网络实时广播,客户端能实时收到消息。-
- 基于Servlet的SSE服务器传输:这是在服务器端的Servlet基础上进行的SSE传输,让服务器能主动把数据推送给客户端,就像老师在黑板上实时写最新的消息,学生能马上看到。
- Spring特定的传输方式:
对于反应式HTTP流媒体,有WebFlux SSE传输,就像水流一样,数据源源不断地实时流动,适合反应式的编程场景。
对于基于Servlet的HTTP流媒体,有WebMVC SSE传输,这也是在Servlet基础上实现的实时数据推送,方便在传统的Web应用里使用。
搭建服务端与客户端
本文只用JAVA进行演示我这里使用的是webflux STDIO服务器传输
服务端
引入maven依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.qijc</groupId>
<artifactId>ai-springboot-mcp-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>ai-springboot-mcp-server</name>
<description>ai-springboot-mcp-server</description>
<properties>
<java.version>17</java.version>
<spring-ai.version>1.0.0-M6</spring-ai.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-mcp-server-webflux-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
配置YML
spring:
output:
ansi:
enabled: never
application:
name: ai-springboot-mcp-server
main:
banner-mode: off
ai:
mcp:
server:
name: ai-springboot-mcp-server
version: 0.0.1
# 服务端配置
logging:
level:
root: OFF # 关闭所有日志输出到控制台
file:
name: server.log # 将日志重定向到文件
编写服务工具
package com.qijc.service;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
@Service
public class DateService {
public static class AddressRequest {
private String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
public static class DateResponse {
private String result;
public DateResponse(String result) {
this.result = result;
}
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
}
@Tool(description = "获取指定地点的当前时间")
public DateResponse getAddressDate(AddressRequest request) {
String result = String.format("%s的当前时间是%s",
request.getAddress(),
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
return new DateResponse(result);
}
}
注册为ToolCallbackProvider
package com.qijc;
import com.qijc.service.DateService;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.ai.tool.function.FunctionToolCallback;
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 AiSpringbootMcpServerApplication {
public static void main(String[] args) {
SpringApplication.run(AiSpringbootMcpServerApplication.class, args);
}
@Bean
public ToolCallbackProvider addressDateTools(DateService dateService) {
return MethodToolCallbackProvider.builder()
.toolObjects(dateService).build();
}
}
进行将server服务器进行打包,在下面直接引用jar包即可
客户端
引入maven依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.qijc</groupId>
<artifactId>ai-springboot-mcp-client</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>ai-springboot-mcp-client</name>
<description>ai-springboot-mcp-client</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>17</java.version>
<spring-ai.version>1.0.0-M6</spring-ai.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-mcp-client-webflux-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<repositories>
<repository>
<name>Central Portal Snapshots</name>
<id>central-portal-snapshots</id>
<url>https://central.sonatype.com/repository/maven-snapshots/</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<releases>
<enabled>false</enabled>
</releases>
</repository>
</repositories>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
配置YML
spring:
application:
name: ai-springboot-mcp-client
#main:
# web-application-type: none
ai:
mcp:
client:
stdio:
connections:
ai-springboot-mcp-server:
command: java
args:
- -Dspring.ai.mcp.server.stdio=true
- -Dspring.main.web-application-type=none
- -Dspring.main.banner-mode=off
- -jar
- C:\Users\qijch\ai-project\spring-ai-project\ai-springboot-mcp-server\target\ai-springboot-mcp-server-0.0.1-SNAPSHOT.jar
openai:
api-key: sk-xxxxxx
base-url: https://dashscope.aliyuncs.com/compatible-mode/
chat:
options:
model: qwen-plus
embedding:
api-key: sk-xxxxxx
base-url: https://dashscope.aliyuncs.com/compatible-mode/
options:
model: text-embedding-v2
server:
port: 8090
编写controller
package com.qijc.controller;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.memory.InMemoryChatMemory;
import org.springframework.ai.chat.messages.SystemMessage;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.model.function.FunctionCallback;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.Arrays;
import java.util.List;
/***
* @projectName spring-ai-project
* @packageName com.qijc.controller
* @author qjc
* @description TODO
* @Email qjc1024@aliyun.com
* @date 2025-03-10 15:57
**/
@RestController
public class McpController {
@Autowired
private OpenAiChatModel openAiChatModel;
@Autowired
private ToolCallbackProvider tools;
@GetMapping("/functionCallback")
public ResponseEntity<String> functionCallback(@RequestParam String message) {
try {
SystemMessage systemMessage = new SystemMessage("你是一个助手,请用中文回答。");
UserMessage userMessage = new UserMessage(message);
FunctionCallback[] toolCallbacks = tools.getToolCallbacks();
Prompt prompt = new Prompt(List.of(systemMessage, userMessage));
String response = ChatClient.builder(openAiChatModel)
.defaultAdvisors(new MessageChatMemoryAdvisor(new InMemoryChatMemory()))
.defaultTools(toolCallbacks)
.build()
.prompt(prompt)
.call().content();
return ResponseEntity.ok(response);
} catch (Exception e) {
return ResponseEntity.status(500).body("服务器错误: " + e.getMessage() + "\n" + Arrays.toString(e.getStackTrace()));
}
}
}
我们只需要引入 ToolCallbackProvider 即可使用访问
http://localhost:8090/functionCallback?message=中国的首都是哪里,现在时间是多少
那我们就完成了服务端用来解耦,这样我们就可以进行开发Tools,不同的语言也是支持的服务端不管是js或python 也是支持的