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

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 也是支持的


http://www.kler.cn/a/581456.html

相关文章:

  • yolov8自定义实例分割
  • 清华大学出品《DeepSeek从入门到精通》超详细使用手册pdf
  • React学习笔记14
  • 从零构建 KNN 分类: sklearn 与自定义实现对比
  • JMeter使用BeanShell断言
  • 训练大模型LLM选择哪种开发语言最好
  • 前端开发中的设计模式:策略模式的应用与实践
  • C#的判断语句总结
  • Ubuntu开荒
  • llvm数据流分析
  • 【1688】崖山集群YAC安装备忘
  • 【软件设计】23 种设计模式解析与实践指南
  • bash---括号之间的区别
  • 『PostgreSQL』PGSQL备份与还原实操指南
  • kubernetes——part3-5 核心概念 Service
  • 《基于深度学习的图像修复技术研究与应用-图像修复》—3000字论文模板
  • golang从入门到做牛马:第十五篇-Go语言切片(Slice):动态数组的“魔法”
  • 【编程题】7-6 列出连通集
  • 电力行业能源管理系统(Energy Management System, EMS)的技术实现方案
  • gitlab add an ssh key 多个ssh配置