【AI智能体】Spring AI MCP 从使用到操作实战详解

目录

一、前言

[二、MCP 介绍](#二、MCP 介绍)

[2.1 什么是MCP](#2.1 什么是MCP)

[2.2 MCP 核心特点](#2.2 MCP 核心特点)

[2.3 MCP 核心价值](#2.3 MCP 核心价值)

[2.4 MCP 与Function Calling 区别](#2.4 MCP 与Function Calling 区别)

[三、Spring AI MCP 架构介绍](#三、Spring AI MCP 架构介绍)

[3.1 整体架构](#3.1 整体架构)

[3.1.1 三层架构实现说明](#3.1.1 三层架构实现说明)

[3.2 服务端与客户端](#3.2 服务端与客户端)

[3.2.1 MCP 服务端](#3.2.1 MCP 服务端)

[3.2.1 MCP 客户端](#3.2.1 MCP 客户端)

[3.3 MCP中SSE和STDIO区别](#3.3 MCP中SSE和STDIO区别)

[3.3.1 SSE模式介绍](#3.3.1 SSE模式介绍)

[3.3.2 STDIO模式介绍](#3.3.2 STDIO模式介绍)

[3.3.3 SSE 与STDIO对比](#3.3.3 SSE 与STDIO对比)

[四、Spring AI MCP 案例操作演示](#四、Spring AI MCP 案例操作演示)

[4.1 开发环境准备](#4.1 开发环境准备)

[4.2 构建MCP Server服务](#4.2 构建MCP Server服务)

[4.2.1 pom导入下面的依赖](#4.2.1 pom导入下面的依赖)

[4.2.2 添加配置文件](#4.2.2 添加配置文件)

[4.2.3 提供一个Tool 工具类](#4.2.3 提供一个Tool 工具类)

[4.2.4 注册工具类](#4.2.4 注册工具类)

[4.3 构建MCP Client服务](#4.3 构建MCP Client服务)

[4.3.1 导入核心依赖](#4.3.1 导入核心依赖)

[4.3.2 增加配置信息](#4.3.2 增加配置信息)

[4.3.3 增加一个对话接口](#4.3.3 增加一个对话接口)

[4.3.4 效果测试](#4.3.4 效果测试)

[4.4 MCP 底层原理解析](#4.4 MCP 底层原理解析)

[4.4.1 server端底层执行逻辑](#4.4.1 server端底层执行逻辑)

[4.4.2 client 端底层执行逻辑](#4.4.2 client 端底层执行逻辑)

五、写在文末


一、前言

Spring AI MCP 是 Spring 框架对模型上下文协议(Model Context Protocol, MCP)的官方实现,它为 Java 开发者提供了标准化集成大型语言模型(LLM)与外部数据源和工具的能力。本文将全面介绍 Spring AI MCP 的核心概念、架构设计以及实际应用方法。

二、MCP 介绍

2.1 什么是MCP

MCP ,即模型上下文协议(Model Context Protocol, MCP),由 Anthropic 公司(Claude 大模型母公司)在 2024 年 11 月推出的开放协议,旨在标准化大型语言模型与外部数据源和工具之间的交互方式。MCP 被形象地比喻为 AI 领域的"USB-C 接口",因为它提供了连接各类服务的统一框架。从而增强模型的实时交互和任务执行能力。

2.2 MCP 核心特点

MCP 具备如下核心特点:

  • 标准化集成

    • MCP统一了不同AI模型(如Claude、GPT等)与外部工具(如数据库、API、浏览器等)的交互方式,避免了传统定制化API开发的重复劳动
  • 双向通信

    • 不同于传统AI只能被动接收数据,MCP支持AI主动检索信息并执行操作(如更新数据库、触发工作流),实现真正的"代理式AI"
  • 动态上下文管理

    • MCP允许AI在会话中持续携带上下文信息(如用户偏好、历史记录),使多步骤任务(如"查询天气→推荐行程→预订酒店")能自动串联执行
  • 安全与灵活性

    • MCP支持本地或云端部署,通过OAuth 2.1认证和数据沙箱机制保障敏感信息的安全访问

2.3 MCP 核心价值

MCP 解决了 AI 应用开发中的几个关键问题:

  • 打破数据孤岛:让大模型可以直接访问实时数据和本地资源(如数据库、文件系统)

  • 降低开发成本:开发者只需编写一次 MCP 服务端,所有兼容 MCP 的模型都能调用

  • 提升安全性:内置权限控制和加密机制,比直接开放数据库更安全

  • 促进生态统一:类似 USB 接口,让不同厂商的工具能"即插即用"

2.4 MCP 与Function Calling 区别

与传统的 Function Calling 相比,MCP 具有以下优势:

  • 复用性:Function Calling 通常与具体 AI 应用紧密绑定,难以复用;而 MCP 设计用于跨系统、跨工具场景

  • 交互能力:MCP 着重解决系统间交互问题,使数据和功能能在不同系统间灵活传递

  • 协议标准化:MCP 通过协议标准化简化了分布式系统开发

三、Spring AI MCP 架构介绍

spring官网在大模型开始火热的时候,推出了Spring AI,与时俱进,随着MCP的热度走高,Spring AI MCP也随之推出与大众见面,官网入口:Model Context Protocol (MCP) :: Spring AI Reference

3.1 整体架构

Spring 官网对于MCP给出了下面的架构图

Spring AI MCP 采用模块化架构,包含以下核心组件:

  • Spring AI 应用程序:使用 Spring AI 框架构建的生成式 AI 应用

  • Spring MCP 客户端:MCP 协议的 Spring AI 实现,与服务器保持 1:1 连接

  • MCP 服务器:轻量级程序,通过标准化协议公开特定功能

  • 本地数据源:MCP 服务器可安全访问的计算机文件、数据库和服务

  • 远程服务:MCP 服务器可通过互联网(如 API)连接的外部系统

3.1.1 三层架构实现说明

Java MCP 实现遵循三层架构:

  • 客户端/服务器层:McpClient 处理客户端操作,McpServer 管理服务器端协议操作

  • 会话层(McpSession):管理通信模式和状态

  • 传输层(McpTransport):处理 JSON-RPC 消息的序列化和反序列化,支持多种传输方式

3.2 服务端与客户端

在实际开发和使用时,MCP的开发主要涉及到服务端与客户端,两者的作用各有差异,下面分别做说明。

3.2.1 MCP 服务端

MCP 服务端主要负责下面的功能:

  • 服务端协议操作实现

  • 工具暴露与发现

  • 基于 URI 的资源管理

  • 提示词模板的提供与处理

  • 客户端并发连接管理

3.2.1 MCP 客户端

MCP 客户端是架构中的关键组件,其主要负责下面的功能:

  • 协议版本协商

  • 能力协商以确定可用功能

  • 消息传输和 JSON-RPC 通信

  • 工具发现和执行

  • 资源访问和管理

  • 支持同步和异步操作

3.3 MCP中SSE和STDIO区别

在Spring AI的MCP实现中,SSE和STDIO是两种主要的通信方式,它们各自适用于不同的场景,下面我们来详细分析两者的区别。

3.3.1 SSE模式介绍

SSE全称,Server-Sent Events,是一种服务器推送技术,允许服务器向客户端推送实时更新。在MCP中,SSE主要用于流式返回AI模型生成的内容。其主要特点如下:

  • 单向通信:服务器向客户端发送数据,客户端不能通过SSE向服务器发送数据

  • 实时性:支持服务器实时推送数据到客户端

  • 自动重连:客户端断开连接后会自动尝试重新连接

  • 基于HTTP:使用标准HTTP协议,易于穿越防火墙

  • 轻量级:相比WebSocket更加轻量,适合单向通信场景

在AI大模型中的交互使用场景:

  • 流式返回模型生成的文本,实现类似打字机的效果;

  • 提供实时反馈,如生成进度、中间结果等;

  • 减少首字节时间(TTFB),提升用户体验;

SSE的HTTP格式示例:

复制代码
HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
 
data: {"content":"这是第一段对话"}
 
data: {"content":"这是第二段对话"}
 
data: {"content":"这是最后一段对话"}
 
data: [DONE]

3.3.2 STDIO模式介绍

STDIO全称,Standard Input/Output,STDIO 是指标准输入输出流,是一种最基本的程序通信机制。在MCP中,STDIO主要用于与本地部署的模型或通过命令行工具访问的模型进行交互。STDIO主要有如下特点:

  • 双向通信

    • 程序可以通过标准输入接收数据,通过标准输出发送数据
  • 进程间通信

    • 通常用于父子进程之间的通信
  • 无网络依赖

    • 不依赖网络协议,适合本地应用场景
  • 命令行友好

    • 与命令行工具天然集成
  • 低开销

    • 通信开销小,适合高性能场景

在AI大模型中的交互使用场景:

  • 与本地部署大模型交互,如Ollama、LLaMA.cpp等

  • 通过命令行工具访问AI大模型服务

  • 构建管道式AI处理流程

STDIO 通信流程如下示例:

应用程序 -> 标准输入 -> AI模型进程 -> 标准输出 -> 应用程序

3.3.3 SSE 与STDIO对比

SSE 与STDIO两种模式具有如下差异:

|---------|-------------|-----------|
| 特征项 | SSE | STDIO |
| 通信方向 | 单向 :服务器到客户端 | 双向 |
| 网络依赖 | 依赖HTTP | 不依赖网络 |
| 部署要求 | 需要网络服务 | 本地即可 |
| 性能开销 | 较高(HTTP开销) | 较低 |
| 扩展性 | 支持多客户端 | 一对一通信 |
| 实现复杂度 | 中等 | 简单 |
| 错误恢复 | 自动重连机制 | 需手动处理 |
| 应用场景 | 网络API通信 | 本地进程通信 |

四、Spring AI MCP 案例操作演示

接下来通过一个实际案例详细演示基于SSE 这种模式下Spring AI MCP是如何使用的。

mcp项目分为server服务端(提供mcp服务),client客户端(去请求mcp服务)

4.1 开发环境准备

本地开发环境相关的组件依赖版本如下:

  • JDK17

  • maven 3.6.2

  • springBoot 3.4.4

  • springAi 1.0.0-M7

4.2 构建MCP Server服务

下面以SSE模式为例演示下如何在springboot项目中集成并使用MCP

4.2.1 pom导入下面的依赖

创建一个springboot的server端项目,导入下面的核心依赖

java 复制代码
<properties>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <spring-ai.version>1.0.0-M7</spring-ai.version>
</properties>

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.4.4</version>
    <relativePath/>
</parent>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-bom</artifactId>
            <version>1.0.0-M7</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-starter-mcp-server-webflux</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

<repositories>
    <repository>
        <id>central</id>
        <url>https://maven.aliyun.com/repository/central</url>
        <releases>
            <enabled>true</enabled>
        </releases>
        <snapshots>
            <enabled>true</enabled>
        </snapshots>
    </repository>
    <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>

4.2.2 添加配置文件

在配置文件中添加如下信息

java 复制代码
spring:
  ai:
    mcp:
      server:
        name: mcp-server
        version: 1.0.0
        type: ASYNC
        sse-message-endpoint: /mcp/messages

4.2.3 提供一个Tool 工具类

在MCP的实现中,Tool 是一个很重要的连接点,借助Tool ,客户端调用具体的服务时,可以理解为MCP就能找到符合要求的Tool 实现,从而给客户端预期的响应结果,在server项目启动的时候,会将相关的Tool工具进行注册,统一汇聚在一个类似于服务注册中心的列表上去,客户端通过配置Server的地址后,就能进行调用,在下面的类中,提供了2个工具方法,第一个是模拟调用天气的API获取天气信息,第二个是获取用户所在的时区时间。

java 复制代码
package com.congge.tool;

import org.springframework.ai.tool.annotation.Tool;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;

@Service("weatherServiceTool")
public class WeatherServiceTool {

    @Tool(description = "获取指定城市的天气")
    public String getWeather(String cityName){

        if(cityName.equals("北京")){
            return "晴天";
        }else if(cityName.equals("上海")){
            return "阴天";
        }else {
            return "未知";
        }
    }

    @Tool(description = "获取当前用户所在的时区时间")
    String getCurrentDateTime() {
        return LocalDateTime.now().atZone(LocaleContextHolder.getTimeZone().toZoneId()).toString();
    }

}

4.2.4 注册工具类

提供一个配置类,将上述的Tool工具类进行注册

java 复制代码
package com.congge.config;

import com.congge.tool.WeatherServiceTool;
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;

@Configuration
public class ToolCallbackProviderConfig {

    @Bean
    public ToolCallbackProvider toolCallbackProvider(WeatherServiceTool weatherServiceTool) {
        return MethodToolCallbackProvider.builder().toolObjects(weatherServiceTool).build();
    }
}

其实到这里基本上就结束了,如果在server工程中,还需要提供接口,继续添加即可,在实际开发中,server端主要是提供针对各类场景下的tool服务,接下来启动工程,在控制台的启动日志中可以看到成功注册了两个服务。

4.3 构建MCP Client服务

4.3.1 导入核心依赖

在pom文件中导入下面的核心依赖,基本和server端的相同,注意版本号保持一致,在client端,主要增加了mcp-client的依赖,以及阿里的spring-ai的依赖,参考下面的依赖信息:

java 复制代码
<properties>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <spring-ai.version>1.0.0-M7</spring-ai.version>
</properties>

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.4.4</version>
    <relativePath/>
</parent>

<dependencies>

    <dependency>
        <groupId>com.alibaba.cloud.ai</groupId>
        <artifactId>spring-ai-alibaba-starter</artifactId>
        <version>1.0.0-M6.1</version>
    </dependency>

    <!--<dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
        <version>1.0.0-M6</version>
    </dependency>-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-starter-mcp-client</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
    </dependency>
</dependencies>

<repositories>
    <repository>
        <id>spring-snapshots</id>
        <name>Spring Snapshots</name>
        <url>https://repo.spring.io/snapshot</url>
        <releases>
            <enabled>false</enabled>
        </releases>
    </repository>
    <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>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-bom</artifactId>
            <version>1.0.0-M7</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>

4.3.2 增加配置信息

在配置文件中增加下面的信息

java 复制代码
server:
  port: 8082

spring:
  ai:
    dashscope:
      api-key: 你的apikey  #这里使用阿里云百炼平台的apikey
    mcp:
      client:
        sse:
          connections:
            server1:
              url: http://localhost:8080
        toolcallback:
          enabled: true

4.3.3 增加一个对话接口

增加一个测试用的聊天对话接口,用于测试客户端调用server的服务是否正常,参考下面的代码

java 复制代码
package com.congge.web;

import jakarta.annotation.Resource;
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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/client/ai")
public class ChatController {

    private final ChatClient chatClient;

    public ChatController(ChatClient.Builder chatClientBuilder) {
        this.chatClient = chatClientBuilder.build();
    }

    @Resource
    private ToolCallbackProvider toolCallbackProvider;

    //localhost:8082/client/ai/chat?message=今天北京的天气如何
    //localhost:8082/client/ai/chat?message=你是谁
    //localhost:8082/client/ai/chat?message=当前时间是多少
    @GetMapping("/chat")
    public String chat(String message){
        return chatClient
                .prompt()
                .user(message)
                .tools(toolCallbackProvider.getToolCallbacks())
                .call()
                .content();
    }
}

4.3.4 效果测试

启动上面client的服务,依次调用几个接口测试下是否能够成功调用到server端的服务。

1)测试效果一

调用接口:localhost:8082/client/ai/chat?message= *今天北京的天气如何 ,*在server端,提供了一个获取天气信息的模拟接口,所以在下面的接口响应中可以看到正常返回结果

2)测试效果二

调用接口:localhost:8082/client/ai/chat?message= *你是谁 ,*这是一个与server端2个工具方法无关的问题,所以最后由大模型返回的响应结果

3)测试效果三

调用接口:localhost:8082/client/ai/chat?message=当前时间是多少 ,server中提供了获取时间的工具方法,所以在调用结果中可以看到返回了预期的结果

4.4 MCP 底层原理解析

在上面的案例中,演示了如何在client端调用server端的tool服务,于是细心的伙伴会问,这是怎么实现的呢?clent端怎么就能准确的调用到server端提供的服务了呢?下面简单说明下其底层的实现原理。

4.4.1 server端底层执行逻辑

在server端工程中,代码中搜索这个McpAsyncServer类,server端项目启动的时候,通过这个类的执行,将tool的相关方法注册到一个指定的位置,然后暴露出去,从而客户端只要配置了与server端的连接,即可获取到这些tool列表,类似于我们在使用nacos之类的服务注册中心时候,只要服务消费者连上注册中心,就可以获取并使用上面注册的服务列表类似的道理。

在这个类中,提供了多个与Tool相关的方法,如下:

启动server工程,首先进入了该类的构造方法中,走到构造方法中,可以看到获取到了工程中提供的2个tool方法

同时在下面这段代码中,暴露了一个重要的端口: tools/list ,简单理解就是为客户端暴露了一个获取server端工具列表的接口

  • tools/list ,暴露工具列表;

  • tools/call,执行工具调用;

4.4.2 client 端底层执行逻辑

在客户端,在上面的案例演示中看到,在配置文件中配置了连接server端的IP+端口号,然后在接口中注入相关的类就可以调用了,简单理解就是,通过这样的配置之后,客户端在发起对server端的tool方法调用时,从tool列表中找到合适的tool,然后执行调用,从而返回预期的结果。

在client端代码中,通过ToolCallbackProvider这个接口进入,可以看到,这是一个接口,里面对应了多个具体的实现类,不同的实现对应了不同场景下对于tool的调用模式,在上述的案例代码中,对应的便是下面这个SyncMcpToolCallbackProvider类的代码执行

在启动工程之后,通过一次接口调用后,来到下面的McpSyncClient这个类的代码中,首先会执行下面的listTools方法,获取当前客户端能够拿到的tool列表,通过debug代码可以看到已经拿到了server端提供的2个tool

通过中间一些列的过滤、筛选得到具体执行的那个tool,然后执行本类中的callTool方法,而且是一个同步阻塞方法,需要等待执行结果的返回

继续往下走,就看到了在上一个解析server端源码中的那个 tools/call的接口了,在这里相当于是客户端找到了具体的执行tool之后,向server端发起request,然后等待server的执行结果

五、写在文末

本文详细介绍了Spring AI MCP的相关技术,并通过一个实际案例操作演示了在SSE模式下MCP的实战应用,事实上,使用Spring AI对接和整合MCP还有很多技能可以解锁,比如stdio模式下的使用,整合第三方的MCP服务等,限于篇幅原因,有兴趣的同学可以基于此继续深入研究,在后续的篇章中也会逐渐丰富和完善,本篇到此结束,感谢观看。