云计算百科
云计算领域专业知识百科平台

大模型开发 - 基于Spring AI 的 MCP Server 开发(中)

文章目录

  • Pre
  • 概述
  • MCP协议:为AI应用连接外部世界的桥梁
  • MCP Server:上下文与能力的提供者
  • 基于Spring AI 1.0.0的开发之路
    • 1. 使用Spring AI构建MCP客户端
    • 2. 使用Spring AI构建MCP服务器
  • 实战:构建基于Spring AI的MCP图书管理系统
    • 核心组件分析
      • 1. 项目依赖配置
      • 2. 数据模型设计
      • 3. 业务服务实现
      • 4. MCP配置
      • 5. 应用配置
    • 功能亮点
      • 1. 多维度图书查询
      • 2. 智能化推荐
      • 3. AI工具集成
    • 使用场景
    • 小结

在这里插入图片描述

Pre

大模型开发 – 基于Spring AI 借助MCP Client 通过STDIO和SSE协议调用MCP Server (上)


概述

自2024年11月由Anthropic公司发布以来,模型上下文协议(Model Context Protocol, MCP)作为一项开放标准,正迅速成为人工智能领域,特别是大型语言模型(LLM)应用开发中的一个重要基石。它旨在为LLM应用与外部数据源和工具之间提供一个标准化的“通用接口”,从而打破模型仅能依赖其内部训练数据的局ăpadă,使其能够获取和利用实时、动态的外部世界信息。

接下来我们将深入介绍MCP协议的核心概念,重点解析其服务器(MCP Server)的角色与功能,并进一步探讨如何利用日趋成熟的Java AI开发框架——Spring AI 1.0.0,来构建支持MCP的应用。


MCP协议:为AI应用连接外部世界的桥梁

MCP协议的核心思想是,在LLM应用(客户端)和提供上下文信息、工具或数据的服务(服务器)之间建立一个统一的通信规范。这极大地简化了集成过程,开发者不再需要为每一个数据源或API编写定制化的连接代码,而是可以遵循一个通用的协议标准。

MCP协议的主要构成与特点:

  • 开放标准与协作: 由Anthropic发起,并得到了包括OpenAI、Google DeepMind和微软在内的行业巨头支持,保证了其广泛的适用性和未来的发展潜力。
  • 基于成熟技术: MCP在设计上借鉴了语言服务器协议(Language Server Protocol, LSP),并采用JSON-RPC 2.0作为其消息传输格式,可通过stdio、HTTP(支持Server-Sent Events)等多种方式进行传输。
  • 核心概念:
    • 主机(Host): 指的是LLM应用程序本身,它负责发起与MCP服务器的连接。
    • 客户端(Client): 位于主机应用内部,是实现MCP通信的具体连接器。
    • 服务器(Server): 负责提供上下文信息,是外部数据和能力的提供者。
  • 安全与隐私: 协议在设计上强调了用户授权和控制,要求在访问用户数据或执行操作前必须获得明确的用户同意,并提供了相应的安全指导原则。

MCP Server:上下文与能力的提供者

在MCP生态中,MCP Server扮演着至关重要的角色。它是一个遵循MCP规范的服务,其主要职责是向MCP客户端(即LLM应用)暴露其拥有的资源和能力。

MCP Server提供的核心能力:

  • 资源(Resources): 服务器可以提供类似文件的只读数据。这可以是本地文件系统的文件、数据库查询结果、API响应内容等。LLM应用可以通过标准化的请求来读取这些资源,从而获得回答问题或执行任务所需的背景信息。

  • 工具(Tools): 这是MCP Server最强大的功能之一。服务器可以定义一系列可供LLM调用的函数或方法。例如,一个MCP Server可以提供“发送邮件”、“查询订单状态”、“执行代码”等工具。当LLM在其推理过程中判断需要使用某个工具时,它会向服务器发起一个工具调用请求,服务器执行相应的功能后将结果返回给LLM。这极大地扩展了LLM的应用场景,使其从一个“对话者”转变为一个“行动者”。

  • 提示(Prompts): 服务器还可以提供预定义的、可复用的提示模板。这些模板可以包含参数,允许LLM应用动态地生成更复杂、更具针对性的指令。这有助于提高与模型交互的效率和一致性。

  • MCP Server的实现与部署:

    MCP Server的实现是多样化的。开发者可以使用官方或社区提供的SDK(目前已有Go, C#, Rust, TypeScript等版本)来构建自己的服务器。根据部署需求,服务器可以:

    • 以子进程(stdio)形式运行: 适用于本地开发或桌面应用集成,服务器与主应用在同一台机器上运行。
    • 以远程服务(HTTP/SSE)形式运行: 这是更常见的部署方式,服务器作为一个独立的Web服务运行,可通过URL被多个客户端访问。

    微软的Copilot Studio等产品已经集成了对MCP的支持,允许用户直接连接到外部的MCP服务器,并将其提供的工具无缝地集成到自己的AI助手中。

    基于Spring AI 1.0.0的开发之路

    Spring AI作为Spring生态系统的一部分,旨在简化Java开发者构建AI应用的复杂性。它提供了一套高级API,用于与各种大型语言模型进行交互,并集成了向量数据库、ETL框架等常用工具。

    尽管在当前时间点(2025年8月),Spring AI与MCP协议之间尚未出现官方的、开箱即用的直接集成,但这并不妨碍我们利用Spring AI的强大功能来开发一个符合MCP规范的应用。开发者可以扮演MCP客户端或MCP服务器的角色。

    1. 使用Spring AI构建MCP客户端

    当你的Java应用需要连接一个已有的MCP服务器时,你可以利用Spring AI的核心能力来消费MCP服务。

    开发步骤概览:

  • 理解MCP通信: 首先需要熟悉MCP的JSON-RPC 2.0消息格式和通信流程。你需要知道如何构建初始化请求、工具调用请求等。

  • HTTP客户端实现: 使用Spring框架中强大的WebClient或RestClient来与远程的MCP Server(通常是HTTP服务)进行通信。你需要手动构建符合MCP规范的JSON请求体,并解析服务器返回的JSON响应。

  • 集成Spring AI ChatClient: 你的应用核心部分可能仍在使用Spring AI的ChatClient与LLM进行交互。当ChatClient返回的AssistantMessage表明需要调用一个外部工具时,你的代码需要:

    • 解析出需要调用的工具名称和参数。
    • 判断该工具是否由某个已连接的MCP Server提供。
    • 通过前述的HTTP客户端,向对应的MCP Server发起工具调用请求。
    • 将MCP Server返回的结果包装成Spring AI的ToolResponseMessage,再次提交给ChatClient,让LLM根据工具执行结果继续生成回应。

  • 2. 使用Spring AI构建MCP服务器

    当你想将你的Java应用所拥有的数据或能力通过MCP协议暴露出去时,你可以构建一个MCP Server。

    开发步骤概览:

  • 创建Spring Boot应用: 使用Spring Boot快速搭建一个Web服务,这将是你的MCP Server的基础。

  • 定义MCP的API端点: 根据MCP规范,创建处理JSON-RPC请求的Controller。你需要实现如initialize、tool/run等核心端点。

  • 利用Spring AI实现工具功能: 这是将Spring AI与MCP Server结合的关键。你的MCP Server所暴露的“工具”,其内部逻辑可以完全由Spring AI来驱动。

    • 示例:创建一个“智能文档问答”工具
      • 工具定义: 在你的MCP Server中,你会向客户端声明一个名为askDocument的工具,它接受一个question作为参数。
      • 内部实现: 当收到tool/run请求调用askDocument时,你的Spring Boot应用会:
      • 使用Spring AI的VectorStore接口,将用户的question进行向量化,并在你预先加载好的文档向量库中进行相似性搜索,检索出最相关的文档片段。
      • 将这些相关的文档片段作为上下文,连同用户的原始问题,一起构建成一个更丰富的提示(Prompt)。
      • 调用Spring AI的ChatClient,将这个丰富的提示发送给LLM(如GPT-4, Claude 3等)。
      • 将LLM返回的答案作为askDocument工具的执行结果,通过MCP的响应格式返回给客户端。
  • 通过这种方式,你将Spring AI在文档检索、与LLM交互等方面的能力,封装成了一个符合MCP标准的、可被任何MCP客户端调用的强大工具。


    实战:构建基于Spring AI的MCP图书管理系统

    该项目是一个基于Spring Boot的MCP服务器实现,专门用于图书管理。通过Spring AI的MCP功能,我们可以将图书服务中的各种查询方法暴露为AI模型可以调用的工具,从而实现智能化的图书查询和推荐功能。

    核心组件分析

    在这里插入图片描述

    1. 项目依赖配置

    <dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
    </dependency>

    项目使用了spring-ai-starter-mcp-server-webmvc依赖,这是Spring AI提供的MCP服务器WebMVC版本的starter,它为我们提供了构建MCP服务器所需的核心功能。

    2. 数据模型设计

    Book实体类定义了图书的基本信息:

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Book {
    private Long id;
    private String title;
    private String category;
    private String author;
    private LocalDate publicationDate;
    private String isbn;
    }

    3. 业务服务实现

    BookService类包含了丰富的图书查询功能,每个方法都使用了@Tool注解标记,使其可以作为MCP工具被AI模型调用:

    • findBooksByTitle: 根据书名模糊查询
    • findBooksByAuthor: 根据作者精确查询
    • findBooksByCategory: 根据分类查询
    • findBooksByISBN: 根据ISBN查询
    • findBooksByPublicationDateRange: 根据出版日期范围查询
    • findBookRecommendations: 图书推荐功能
    • getBookCategories: 获取所有图书分类
    • getPopularBooks: 获取热门图书列表

    package com.example.server.service;

    import com.example.server.entity.Book;
    import org.springframework.ai.tool.annotation.Tool;
    import org.springframework.ai.tool.annotation.ToolParam;
    import org.springframework.stereotype.Service;

    import java.time.LocalDate;
    import java.util.Arrays;
    import java.util.List;
    import java.util.stream.Collectors;

    @Service
    public class BookService {

    @Tool(name = "findBooksByTitle", description = "根据书名模糊查询图书,支持部分标题匹配")
    public List<Book> findBooksByTitle(@ToolParam(description = "书名关键词") String title) {
    System.out.println(">>> 调用工具: findBooksByTitle, 参数: " + title);
    return findByTitleContaining(title);
    }

    @Tool(name = "findBooksByAuthor", description = "根据作者精确查询图书")
    public List<Book> findBooksByAuthor(@ToolParam(description = "作者姓名") String author) {
    System.out.println(">>> 调用工具: findBooksByAuthor, 参数: " + author);
    return findByAuthor(author);
    }

    @Tool(name = "findBooksByCategory", description = "根据图书分类精确查询图书")
    public List<Book> findBooksByCategory(@ToolParam(description = "图书分类") String category) {
    System.out.println(">>> 调用工具: findBooksByCategory, 参数: " + category);
    return findByCategory(category);
    }

    @Tool(name = "findBooksByISBN", description = "根据ISBN查询图书")
    public Book findBooksByISBN(@ToolParam(description = "图书ISBN号码") String isbn) {
    System.out.println(">>> 调用工具: findBooksByISBN, 参数: " + isbn);
    return findByISBN(isbn);
    }

    @Tool(name = "findBooksByPublicationDateRange", description = "根据出版日期范围查询图书")
    public List<Book> findBooksByPublicationDateRange(
    @ToolParam(description = "开始日期 (格式: yyyy-MM-dd)") String startDate,
    @ToolParam(description = "结束日期 (格式: yyyy-MM-dd)") String endDate) {
    System.out.println(">>> 调用工具: findBooksByPublicationDateRange, 参数: " + startDate + " 到 " + endDate);
    return findByPublicationDateRange(LocalDate.parse(startDate), LocalDate.parse(endDate));
    }

    @Tool(name = "findBookRecommendations", description = "根据指定图书推荐相关图书")
    public List<Book> findBookRecommendations(@ToolParam(description = "图书ID") Long bookId) {
    System.out.println(">>> 调用工具: findBookRecommendations, 参数: " + bookId);
    return getRecommendations(bookId);
    }

    @Tool(name = "getBookCategories", description = "获取所有图书分类列表")
    public List<String> getBookCategories() {
    System.out.println(">>> 调用工具: getBookCategories");
    return getAllCategories();
    }

    @Tool(name = "getPopularBooks", description = "获取热门图书列表")
    public List<Book> getPopularBooks(@ToolParam(description = "返回图书数量,默认为5本") int limit) {
    System.out.println(">>> 调用工具: getPopularBooks, 参数: " + limit);
    return getPopularBooksList(limit);
    }

    /*
    * 模拟数据库查询
    */

    // 模拟数据库查询:根据书名模糊查询
    private List<Book> findByTitleContaining(String title) {
    return sampleBooks.stream()
    .filter(book -> book.getTitle().contains(title))
    .toList();
    }

    // 模拟数据库查询:根据作者查询
    private List<Book> findByAuthor(String author) {
    return sampleBooks.stream()
    .filter(book -> book.getAuthor().equals(author))
    .toList();
    }

    // 模拟数据库查询:根据分类查询
    private List<Book> findByCategory(String category) {
    return sampleBooks.stream()
    .filter(book -> book.getCategory().equals(category))
    .toList();
    }

    // 模拟数据库查询:根据ISBN查询
    private Book findByISBN(String isbn) {
    return sampleBooks.stream()
    .filter(book -> book.getIsbn() != null && book.getIsbn().equals(isbn))
    .findFirst()
    .orElse(null);
    }

    // 模拟数据库查询:根据出版日期范围查询
    private List<Book> findByPublicationDateRange(LocalDate startDate, LocalDate endDate) {
    return sampleBooks.stream()
    .filter(book -> !book.getPublicationDate().isBefore(startDate) && !book.getPublicationDate().isAfter(endDate))
    .toList();
    }

    // 模拟数据库查询:获取推荐图书
    private List<Book> getRecommendations(Long bookId) {
    // 简单实现:返回同分类的其他图书
    Book book = sampleBooks.stream()
    .filter(b -> bookId.equals(b.getId()))
    .findFirst()
    .orElse(null);

    if (book == null) {
    return List.of();
    }

    return sampleBooks.stream()
    .filter(b -> !bookId.equals(b.getId()) && b.getCategory().equals(book.getCategory()))
    .limit(3)
    .collect(Collectors.toList());
    }

    // 模拟数据库查询:获取所有分类
    private List<String> getAllCategories() {
    return sampleBooks.stream()
    .map(Book::getCategory)
    .distinct()
    .sorted()
    .collect(Collectors.toList());
    }

    // 模拟数据库查询:获取热门图书
    private List<Book> getPopularBooksList(int limit) {
    // 简单实现:返回前几本图书作为热门图书
    return sampleBooks.stream()
    .limit(Math.max(1, limit)) // 确保至少返回1本书
    .collect(Collectors.toList());
    }

    // 准备示例数据
    List<Book> sampleBooks = Arrays.asList(
    new Book(1L, "Spring实战(第6版)", "编程", "Craig Walls",
    LocalDate.of(2022, 1, 15), "9787115582247"),
    new Book(2L, "深入理解Java虚拟机", "编程", "周志明",
    LocalDate.of(2019, 12, 1), "9787111641247"),
    new Book(3L, "Java编程思想(第4版)", "编程", "Bruce Eckel",
    LocalDate.of(2007, 6, 1), "9787111213826"),
    new Book(4L, "算法(第4版)", "计算机科学", "Robert Sedgewick",
    LocalDate.of(2012, 10, 1), "9787115293800"),
    new Book(5L, "云原生架构", "架构设计", "张三",
    LocalDate.of(2023, 3, 15), "9781234567890"),
    new Book(6L, "微服务设计模式", "架构设计", "张三",
    LocalDate.of(2021, 8, 20), "9789876543210"),
    new Book(7L, "领域驱动设计", "架构设计", "Eric Evans",
    LocalDate.of(2010, 4, 10), "9787111214748"),
    new Book(8L, "高性能MySQL", "数据库", "Baron Schwartz",
    LocalDate.of(2013, 5, 25), "9787111464747"),
    new Book(9L, "Redis实战", "数据库", "Josiah L. Carlson",
    LocalDate.of(2015, 9, 30), "9787115419378"),
    new Book(10L, "深入浅出Docker", "容器技术", "李四",
    LocalDate.of(2022, 11, 20), "9787123456789"),
    new Book(11L, "Kubernetes权威指南", "容器技术", "龚正等",
    LocalDate.of(2023, 1, 10), "9787121340123"),
    new Book(12L, "设计模式:可复用面向对象软件的基础", "编程", "Gang of Four",
    LocalDate.of(2010, 10, 1), "9787115295571"),
    new Book(13L, "重构:改善既有代码的设计", "编程", "Martin Fowler",
    LocalDate.of(2017, 5, 15), "9787115511055"),
    new Book(14L, "代码大全", "编程", "Steve McConnell",
    LocalDate.of(2012, 3, 20), "9787115295572"),
    new Book(15L, "计算机网络:自顶向下方法", "计算机科学", "James F. Kurose",
    LocalDate.of(2020, 8, 1), "9787111389030"),
    new Book(16L, "操作系统概念", "计算机科学", "Abraham Silberschatz",
    LocalDate.of(2019, 1, 1), "9787111295573"),
    new Book(17L, "数据结构与算法分析", "计算机科学", "Mark Allen Weiss",
    LocalDate.of(2017, 9, 15), "9787111395574"),
    new Book(18L, "大型网站技术架构", "架构设计", "李智慧",
    LocalDate.of(2019, 9, 1), "9787115295575"),
    new Book(19L, "MongoDB实战", "数据库", "Kyle Banker",
    LocalDate.of(2019, 11, 1), "9787111395576"),
    new Book(20L, "深入理解Nginx", "Web服务器", "陶辉",
    LocalDate.of(2021, 7, 1), "9787111395577"));
    }

    4. MCP配置

    McpServerConfig类负责将BookService中的工具方法注册到MCP服务器:

    package com.example.server.mcp;

    import com.example.server.service.BookService;
    import org.springframework.ai.tool.ToolCallbackProvider;
    import org.springframework.ai.tool.method.MethodToolCallbackProvider;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;

    /**
    * MCP服务器配置类,负责注册MCP工具
    */

    @Configuration
    public class McpServerConfig {

    /**
    * 注册工具回调提供者,将BookQueryService中的@Tool方法暴露为MCP工具
    *
    * @param bookService 图书服务
    * @return 工具回调提供者
    */

    @Bean
    public ToolCallbackProvider bookToolCallbackProvider(BookService bookService) {
    return MethodToolCallbackProvider.builder()
    .toolObjects(bookService)
    .build();
    }

    }

    5. 应用配置

    在application.properties 中配置了MCP服务器的相关参数:

    # MCP服务端开启
    spring.ai.mcp.server.enabled=true

    # MCP服务端配置
    spring.ai.mcp.server.name=book-management-server
    spring.ai.mcp.server.version=1.0.0
    spring.ai.mcp.server.type=SYNC
    spring.ai.mcp.server.sse-message-endpoint=/mcp/message

    功能亮点

    1. 多维度图书查询

    系统提供了多种图书查询方式,包括按标题、作者、分类、ISBN、出版日期范围等维度进行查询,满足不同场景下的查询需求。

    2. 智能化推荐

    通过findBookRecommendations 工具,系统可以根据指定图书推荐同分类的其他图书,实现简单的推荐功能。

    3. AI工具集成

    所有查询方法都通过@Tool注解暴露为AI工具,使得AI模型可以直接调用这些方法执行特定任务,实现自然语言到具体操作的转换。

    使用场景

    该系统可以作为AI助手的后端服务,用户可以通过自然语言询问图书信息,例如:

    • “查找关于Spring的书籍”

    在这里插入图片描述

    • “推荐一些编程类的热门图书”

    在这里插入图片描述

    • “查找Craig Walls写的书”

    在这里插入图片描述

    • “查找2022年出版的图书”

    在这里插入图片描述

    AI助手会根据用户的问题选择合适的工具进行调用,并返回相应的结果。

    小结

    这个基于Spring AI MCP的图书管理系统展示了如何将传统业务服务与AI能力相结合。通过简单的注解和配置,我们就能将现有的服务方法暴露为AI可调用的工具,极大地提升了系统的智能化水平。这种架构模式可以广泛应用于各种业务系统中,为用户提供更自然、更智能的交互体验。

    在这里插入图片描述

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 大模型开发 - 基于Spring AI 的 MCP Server 开发(中)
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!