Spring AI 框架中集成 MCP 的完整指南:从服务端到客户端的全流程实践

目录

  • [1. 引言:为什么需要 MCP](#1. 引言:为什么需要 MCP)
  • [2. 整体架构与运行模式](#2. 整体架构与运行模式)
  • [3. 构建 MCP 服务端](#3. 构建 MCP 服务端)
    • [3.1 引入依赖](#3.1 引入依赖)
    • [3.2 使用 @Tool 注解声明工具](#3.2 使用 @Tool 注解声明工具)
    • [3.3 手动注册工具回调(可选)](#3.3 手动注册工具回调(可选))
    • [3.4 验证服务端](#3.4 验证服务端)
  • [4. 构建 MCP 客户端](#4. 构建 MCP 客户端)
    • [4.1 引入依赖](#4.1 引入依赖)
    • [4.2 配置服务端连接](#4.2 配置服务端连接)
    • [4.3 注入工具并调用大模型](#4.3 注入工具并调用大模型)
    • [4.4 验证完整链路](#4.4 验证完整链路)
  • [5. 实战:带数据库查询的完整案例](#5. 实战:带数据库查询的完整案例)
    • [5.1 服务端:商品库存工具](#5.1 服务端:商品库存工具)
    • [5.2 客户端:业务提问](#5.2 客户端:业务提问)
  • [6. 常见问题与排障](#6. 常见问题与排障)
    • [6.1 客户端提示工具未注册](#6.1 客户端提示工具未注册)
    • [6.2 大模型不调用工具](#6.2 大模型不调用工具)
    • [6.3 STDIO 模式连接失败](#6.3 STDIO 模式连接失败)
    • [6.4 版本兼容问题](#6.4 版本兼容问题)
  • [7. 总结与最佳实践](#7. 总结与最佳实践)

1. 引言:为什么需要 MCP

随着大语言模型(LLM)在企业级应用中的落地,一个长期困扰开发者的问题是:模型如何安全、标准化地访问外部工具与数据?

早期做法通常是给每个模型厂商单独写一套工具回调适配器,导致代码与厂商强绑定,工具生态难以复用。Model Context Protocol(MCP)正是为了解决这一痛点而诞生的开放协议:它定义了模型与「工具(Tool)、资源(Resource)、提示(Prompt)」之间的统一交互规范,使得任何 MCP 兼容的客户端都能无缝调用任何 MCP 兼容的服务端。

Spring AI 从 1.0 版本开始对 MCP 提供了一等公民支持,通过 spring-ai-starter-mcp-serverspring-ai-starter-mcp-client 两个 Starter,开发者可以用几乎纯声明式的方式完成服务端工具暴露与客户端工具调用。

本文将从零开始,带你走通「构建 MCP 服务端 → 构建 MCP 客户端 → 打通大模型调用」的完整链路,并给出可直接运行的 Java 代码。

2. 整体架构与运行模式

在动手写代码之前,先理解 Spring AI MCP 的整体协作模型。
#mermaid-svg-5GopvdFMCdxMSBTY{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-5GopvdFMCdxMSBTY .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-5GopvdFMCdxMSBTY .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-5GopvdFMCdxMSBTY .error-icon{fill:#552222;}#mermaid-svg-5GopvdFMCdxMSBTY .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5GopvdFMCdxMSBTY .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-5GopvdFMCdxMSBTY .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5GopvdFMCdxMSBTY .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5GopvdFMCdxMSBTY .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-5GopvdFMCdxMSBTY .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5GopvdFMCdxMSBTY .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5GopvdFMCdxMSBTY .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5GopvdFMCdxMSBTY .marker.cross{stroke:#333333;}#mermaid-svg-5GopvdFMCdxMSBTY svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5GopvdFMCdxMSBTY p{margin:0;}#mermaid-svg-5GopvdFMCdxMSBTY .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-5GopvdFMCdxMSBTY .cluster-label text{fill:#333;}#mermaid-svg-5GopvdFMCdxMSBTY .cluster-label span{color:#333;}#mermaid-svg-5GopvdFMCdxMSBTY .cluster-label span p{background-color:transparent;}#mermaid-svg-5GopvdFMCdxMSBTY .label text,#mermaid-svg-5GopvdFMCdxMSBTY span{fill:#333;color:#333;}#mermaid-svg-5GopvdFMCdxMSBTY .node rect,#mermaid-svg-5GopvdFMCdxMSBTY .node circle,#mermaid-svg-5GopvdFMCdxMSBTY .node ellipse,#mermaid-svg-5GopvdFMCdxMSBTY .node polygon,#mermaid-svg-5GopvdFMCdxMSBTY .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5GopvdFMCdxMSBTY .rough-node .label text,#mermaid-svg-5GopvdFMCdxMSBTY .node .label text,#mermaid-svg-5GopvdFMCdxMSBTY .image-shape .label,#mermaid-svg-5GopvdFMCdxMSBTY .icon-shape .label{text-anchor:middle;}#mermaid-svg-5GopvdFMCdxMSBTY .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-5GopvdFMCdxMSBTY .rough-node .label,#mermaid-svg-5GopvdFMCdxMSBTY .node .label,#mermaid-svg-5GopvdFMCdxMSBTY .image-shape .label,#mermaid-svg-5GopvdFMCdxMSBTY .icon-shape .label{text-align:center;}#mermaid-svg-5GopvdFMCdxMSBTY .node.clickable{cursor:pointer;}#mermaid-svg-5GopvdFMCdxMSBTY .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-5GopvdFMCdxMSBTY .arrowheadPath{fill:#333333;}#mermaid-svg-5GopvdFMCdxMSBTY .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-5GopvdFMCdxMSBTY .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-5GopvdFMCdxMSBTY .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5GopvdFMCdxMSBTY .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-5GopvdFMCdxMSBTY .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5GopvdFMCdxMSBTY .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-5GopvdFMCdxMSBTY .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-5GopvdFMCdxMSBTY .cluster text{fill:#333;}#mermaid-svg-5GopvdFMCdxMSBTY .cluster span{color:#333;}#mermaid-svg-5GopvdFMCdxMSBTY div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-5GopvdFMCdxMSBTY .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-5GopvdFMCdxMSBTY rect.text{fill:none;stroke-width:0;}#mermaid-svg-5GopvdFMCdxMSBTY .icon-shape,#mermaid-svg-5GopvdFMCdxMSBTY .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5GopvdFMCdxMSBTY .icon-shape p,#mermaid-svg-5GopvdFMCdxMSBTY .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-5GopvdFMCdxMSBTY .icon-shape .label rect,#mermaid-svg-5GopvdFMCdxMSBTY .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5GopvdFMCdxMSBTY .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-5GopvdFMCdxMSBTY .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-5GopvdFMCdxMSBTY :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 大模型 LLM
MCP Client
MCP 协议
MCP Server
业务工具
数据库
外部 API
本地文件

  • MCP Server(服务端) :负责暴露具体业务能力(查询数据、调用接口、读写文件等),每个能力通过 @Tool 注解声明为工具。
  • MCP Client(客户端):连接到一个或多个 MCP Server,把服务端工具注册为大模型的函数调用能力。
  • 大模型:根据用户提问自主决策调用哪个工具,并把工具返回结果整合成最终回答。

MCP 支持多种传输方式,Spring AI 官方主要支持以下两种:

传输方式 适用场景 说明
STDIO 本地进程、单机工具 通过标准输入输出通信,客户端以子进程方式启动服务端
SSE / Streamable HTTP 远程服务、微服务架构 通过 HTTP 长连接传输 JSON-RPC 消息,适合跨网络部署

下面我们先从服务端开始,采用最常用的 WebMVC + Streamable HTTP 模式,这也是微服务架构下推荐的部署方式。

3. 构建 MCP 服务端

3.1 引入依赖

首先创建一个 Spring Boot 项目,pom.xml 中引入 MCP 服务端 Starter:

xml 复制代码
<properties>
    <java.version>17</java.version>
    <spring-ai.version>1.0.0</spring-ai.version>
</properties>

<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>

<dependencies>
    <!-- MCP Server:WebMVC + Streamable HTTP 传输 -->
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
    </dependency>

    <!-- 如果只需要本地 STDIO 模式,改用下面这个 -->
    <!--
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-starter-mcp-server</artifactId>
    </dependency>
    -->
</dependencies>

注意:MCP 相关 Starter 不在 Maven 中央仓库的默认 BOM 中,需确保正确引入 spring-ai-bom,并配置 Spring 官方快照/里程碑仓库(如使用正式版则无需额外仓库)。

3.2 使用 @Tool 注解声明工具

Spring AI 提供了 @Tool 注解,配合 @ToolParam 即可把一个普通方法暴露为 MCP 工具,无需继承任何基类

java 复制代码
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Service;

@Service
public class WeatherService {

    @Tool(description = "根据城市名称查询实时天气,返回温度与天气状况")
    public String getWeather(
            @ToolParam(description = "城市名称,例如:北京") String city) {

        // 这里模拟查询过程,实际项目可替换为调用第三方天气 API
        return switch (city) {
            case "北京" -> "北京:晴,26℃,微风";
            case "上海" -> "上海:多云,28℃,东风 3 级";
            default -> city + ":暂无天气数据";
        };
    }
}

关键点说明

  • description 是给大模型看的「工具说明书」,必须清晰描述工具用途、适用场景,否则模型可能不知道该何时调用。
  • @ToolParam 的参数描述同样重要,它帮助模型正确填充参数。
  • 方法入参支持基本类型、String、POJO 等,返回类型支持 String、POJO、List 等,框架会自动序列化为 JSON。

3.3 手动注册工具回调(可选)

除了 @Tool 自动扫描,也可以通过 ToolCallbackProvider 手动注册,适合动态组装工具的场景:

java 复制代码
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 ToolConfig {

    @Bean
    public ToolCallbackProvider weatherToolCallbackProvider(WeatherService weatherService) {
        return MethodToolCallbackProvider.builder()
                .toolObjects(weatherService)
                .build();
    }
}

一般情况下,@Tool 注解已经足够,手动注册仅在需要精细控制或统一管理多个对象时使用。

3.4 验证服务端

启动应用后,MCP 端点默认暴露在以下路径:

  • SSE 端点http://localhost:8080/mcp/sse
  • Streamable HTTP 端点http://localhost:8080/mcp

可以使用 MCP 官方调试工具 mcp-inspector 或 curl 直接验证工具列表是否注册成功。正确注册后,应在初始化握手时看到 weather_getWeather 工具及其入参 Schema。

4. 构建 MCP 客户端

客户端负责连接服务端,并把远端工具「桥接」给大模型。我们依旧使用 Spring Boot,新建一个独立项目。

4.1 引入依赖

xml 复制代码
<dependencies>
    <!-- MCP Client:支持 Streamable HTTP -->
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-starter-mcp-client</artifactId>
    </dependency>

    <!-- 大模型客户端,示例使用 OpenAI 兼容接口 -->
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-starter-model-openai</artifactId>
    </dependency>
</dependencies>

4.2 配置服务端连接

application.yml 中声明 MCP 服务端连接信息:

yaml 复制代码
spring:
  ai:
    mcp:
      client:
        enabled: true
        toolcallback:
          enabled: true
        name: weather-client
        request-timeout: 30s
        type: SYNC
        servers:
          # 通过 Streamable HTTP 连接远程 MCP 服务端
          weather-server:
            url: http://localhost:8080/mcp
          # 如需连接 STDIO 模式的本地服务端,使用 command 配置
          # local-tools:
          #   command: java
          #   args:
          #     - -jar
          #     - /path/to/mcp-server.jar

  ai:
    openai:
      api-key: ${OPENAI_API_KEY}
      base-url: https://api.openai.com
      chat:
        options:
          model: gpt-4o-mini

配置要点

  • spring.ai.mcp.client.servers 下每个 key 是一个自定义的服务端别名,可配置多个服务端。
  • 远程模式使用 url,本地 STDIO 模式使用 command + args
  • type: SYNC 表示同步客户端(最常用),1.0 版本也支持 ASYNC 异步客户端。

4.3 注入工具并调用大模型

Spring AI 会自动把配置好的 MCP 服务端工具注入到 ChatClient,开发者只需正常构建 ChatClient 即可。

java 复制代码
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ChatController {

    private final ChatClient chatClient;

    @Autowired
    public ChatController(ChatClient.Builder builder) {
        this.chatClient = builder
                .defaultAdvisors(new SimpleLoggerAdvisor())
                .build();
    }

    @GetMapping("/chat")
    public String chat(@RequestParam(defaultValue = "北京今天天气怎么样?") String message) {
        return chatClient.prompt()
                .user(message)
                .call()
                .content();
    }
}

这里的关键 :MCP 客户端 Starter 在自动配置时,会读取所有已声明的 servers,把它们的工具合并注册为一个 ToolCallbackProvider,并自动装配进 ChatClient。因此你在业务代码中无需手动绑定工具,大模型即可看到并使用远端 MCP 工具。

4.4 验证完整链路

启动客户端应用,访问:

text 复制代码
http://localhost:8080/chat?message=上海今天天气怎么样?

预期返回类似结果:

text 复制代码
根据工具查询结果,上海今天多云,气温 28℃,东风 3 级,整体体感舒适。

当大模型判断需要工具时,请求链路为「客户端 → MCP 服务端 → 业务工具 → 返回结果 → 大模型整合回答」,你可以在服务端日志与 SimpleLoggerAdvisor 日志中清晰看到工具调用的完整过程。

5. 实战:带数据库查询的完整案例

为了更贴近真实业务,我们把「天气示例」升级为「商品库存查询」,演示 MCP 服务端如何访问数据库。

5.1 服务端:商品库存工具

java 复制代码
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;

@Service
public class InventoryService {

    private final JdbcTemplate jdbcTemplate;

    public InventoryService(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @Tool(description = "根据商品名称模糊查询库存,返回商品编号、名称与可售数量")
    public List<Map<String, Object>> queryStock(
            @ToolParam(description = "商品名称关键字,例如:手机") String keyword) {

        String sql = """
                SELECT sku, name, stock
                FROM product
                WHERE name LIKE ?
                ORDER BY stock DESC
                LIMIT 10
                """;

        return jdbcTemplate.queryForList(sql, "%" + keyword + "%");
    }
}

5.2 客户端:业务提问

用户问「帮我查一下手机相关的库存情况」,大模型会自动调用 inventory_queryStock 工具,拿到结构化列表后,再用自然语言生成一段便于阅读的总结。

这种「库表结构对模型透明 + 工具完成真实查询 + 模型负责表达」的组合,正是 MCP 在 Agent 场景下的典型价值:既保证了数据访问的可控性与安全性,又充分发挥了大模型的理解与表达能力。

6. 常见问题与排障

6.1 客户端提示工具未注册

  • 确认服务端应用已启动且 mcp 端点可访问。
  • 检查客户端 application.ymlspring.ai.mcp.client.serversurl 是否正确。
  • 确认服务端没有被安全框架拦截 /mcp/** 路径。

6.2 大模型不调用工具

  • 检查 @Tooldescription 是否足够明确,模型只有理解了工具用途才会调用。
  • 确认所用模型具备函数调用(Function Calling)能力,且模型名称配置正确。
  • 开启 SimpleLoggerAdvisor 日志,观察模型返回的是普通文本还是工具调用请求。

6.3 STDIO 模式连接失败

  • 确认 command 指向的是可执行文件(如 javanode),args 中的路径是否正确。
  • STDIO 模式下服务端不能同时运行在 Web 容器中,需使用独立的启动类或 spring-ai-starter-mcp-server(非 webmvc 版本)。

6.4 版本兼容问题

Spring AI 从 1.0 开始对 MCP 做了较大重构(@Tool 取代旧的 @Tool 所在包路径、同步/异步客户端分离等)。若你参考的是旧版本教程,请以 1.0.x 正式版的官方文档为准,避免包名与配置项混用。

7. 总结与最佳实践

通过本文的实践,我们完成了 Spring AI 集成 MCP 的完整链路:

  1. 服务端 :使用 spring-ai-starter-mcp-server-webmvc + @Tool 注解暴露业务能力。
  2. 客户端 :使用 spring-ai-starter-mcp-client 配置远端服务地址,自动注入工具。
  3. 大模型 :通过 ChatClient 直接使用 MCP 工具,无需手动编写函数调用协议。

在实践中,建议遵循以下原则:

  • 工具粒度要合理:一个工具做一件事,描述清晰,输入输出尽量结构化。
  • 安全边界要严格:对外暴露的工具应做权限校验与参数校验,避免模型诱导出越权操作。
  • 远程优先:微服务架构下优先使用 Streamable HTTP,便于独立部署、独立扩缩容。
  • 日志与可观测性:开启工具调用日志,记录每次调用的入参、耗时与返回,便于排查问题。
  • 版本锁定 :通过 spring-ai-bom 统一管理版本,避免多模块间版本冲突。

MCP 的意义在于把「模型能力」与「工具生态」解耦,让一次工具开发可以被所有兼容模型复用。随着 MCP 生态的持续完善,掌握 Spring AI 的这套集成方式,将帮助你在企业 AI 应用中构建更灵活、更安全的 Agent 架构。

相关推荐
甲维斯1 小时前
全TM草台班子,DSH毒瘤目录卡死OpenCode!
人工智能
Eloudy1 小时前
用 OpenROAD 验证设计的 PPA(功耗、面积、性能)指标
人工智能·ai ic agent
精彩AI说1 小时前
ChatGPT总是答非所问怎么办?提示词歧义、上下文干扰与提问方式排查
chatgpt·prompt·提示词·ai工具·chatgpt教程
weixin_446260851 小时前
拆解再复用:大模型智能体的跨任务技能迁移
人工智能·深度学习·算法
QC777LX1 小时前
大模型、RAG、Agent和云平台方向,如何组合认证构建AI工程师的复合竞争力?
人工智能
WL_arm1 小时前
Vibe Coding(氛围编程)入门
javascript·css·人工智能·html5
YH55269841 小时前
ChatGPT Plus 与 OpenAI API 的区别:API Key、Token 和计费机制详解
人工智能·chatgpt
计算机魔术师1 小时前
第二届世界人形机器人运动会开幕:2056 台机器人齐聚“冰丝带“,666 支队伍竞技 51 赛项
数据库·人工智能·机器人
@insist1231 小时前
系统集成项目管理工程师-配置管理角色与活动
数据库·软考·系统集成项目管理工程师·软考中项·软件水平考试