基于 Spring AI+MCP 协议实现大模型调用本地自定义工具

一、前言

随着 Spring AI 生态不断完善,MCP(Model Context Protocol) 成为了 AI 模型与本地自定义工具通信的核心协议。通过 MCP 协议,我们可以将本地自定义的业务工具方法暴露为 AI 可调用的工具,让大模型自动调用本地接口完成业务逻辑,极大拓展 AI 应用的落地能力。

本文将从零搭建 MCP 服务端(WebFlux 异步)+ MCP 客户端 完整工程,实现:

  • 对比「启用 MCP 工具调用」和「普通大模型调用」的差异
  • 解决 MCP 启动失败、Web 容器冲突等经典踩坑问题

整套案例基于 Spring Boot + Spring AI + 阿里 DashScope 实现,可直接落地复用。

二、核心原理与踩坑前置说明

2.1 MCP 核心作用

MCP 协议实现了 大模型 <-> 本地工具 的双向通信,客户端向 MCP 服务端发现工具、传递参数,服务端执行本地业务逻辑并返回结果,大模型基于结果生成最终回答。

2.2 关键避坑点

spring-ai-starter-mcp-server-webflux 依赖 绝对不能与 spring-boot-starter-web 共存!

  • web 依赖会强制使用 Tomcat 容器启动
  • MCP WebFlux 依赖需要 Netty 容器支撑
  • 两者共存时,程序可正常启动,但 MCP 服务端异常,客户端无法连接调用工具

✅ 解决方案:MCP 服务端仅引入 webflux 相关依赖,剔除 web 依赖;客户端可正常引入 web 依赖。

三、MCP 服务端搭建

服务端核心职责:定义自定义工具、注册 MCP 工具回调、开启异步 MCP 服务,对外暴露工具调用能力。

3.1 Pom 核心依赖

服务端禁止引入 spring-boot-starter-web,仅保留核心启动器和 MCP WebFlux 依赖:

复制代码
<dependencies>
    <!-- Spring Boot 核心启动器 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <!-- MCP 服务端 WebFlux 异步依赖 -->
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-starter-mcp-server-webflux</artifactId>
    </dependency>
</dependencies>

3.2 服务端配置文件 application.yml

复制代码
# 服务端口
server.port=8014

# 全局编码配置
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true
server.servlet.encoding.charset=UTF-8

# 应用名称
spring.application.name=SAA-14LocalMcpServer

# MCP 服务端核心配置
spring.ai.mcp.server.type=async
spring.ai.mcp.server.name=customer-define-mcp-server
spring.ai.mcp.server.version=1.0.0

3.3 自定义工具业务类(天气查询)

通过 @Tool 注解标记工具方法,配置方法描述,用于大模型识别工具能力:

复制代码
package com.atguigu.study.service;

import org.springframework.ai.tool.annotation.Tool;
import org.springframework.stereotype.Service;
import java.util.Map;

/**
 * 自定义MCP工具服务:天气查询工具
 */
@Service
public class WeatherService
{
    /**
     * @Tool 注解:声明为AI可调用工具,description为工具描述,供大模型识别用途
     */
    @Tool(description = "根据城市名称获取天气预报")
    public String getWeatherByCity(String city)
    {
        Map<String, String> weatherMap = Map.of(
                "北京", "11111降雨频繁,其中今天和后天雨势较强,部分地区有暴雨并伴强对流天气,需注意",
                "上海", "22222多云,15℃~27℃,南风3级,当前温度27℃。",
                "深圳", "333333多云40天,阴16天,雨30天,晴3天"
        );
        return weatherMap.getOrDefault(city, "抱歉:未查询到对应城市!");
    }
}

3.4 MCP 工具注册配置类

将自定义的工具类注册为 ToolCallbackProvider,暴露给 MCP 客户端调用:

复制代码
package com.atguigu.study.config;

import com.atguigu.study.service.WeatherService;
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服务配置:注册自定义工具,对外暴露调用能力
 */
@Configuration
public class McpServerConfig
{
    /**
     * 将自定义工具方法注册为MCP可调用工具
     */
    @Bean
    public ToolCallbackProvider weatherTools(WeatherService weatherService)
    {
        return MethodToolCallbackProvider.builder()
                .toolObjects(weatherService)
                .build();
    }
}

四、MCP 客户端搭建

客户端核心职责:连接远程 MCP 服务端、加载远程工具、结合大模型实现自动工具调用、提供接口测试能力。客户端可正常引入 web 依赖。

4.1 Pom 核心依赖

复制代码
<dependencies>
    <!-- Web依赖:客户端需要提供接口测试能力 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- 阿里通义千问大模型依赖 -->
    <dependency>
        <groupId>com.alibaba.cloud.ai</groupId>
        <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
    </dependency>
    <!-- MCP 客户端依赖 -->
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-starter-mcp-client</artifactId>
    </dependency>
</dependencies>

4.2 客户端配置文件 application.yml

复制代码
# 服务端口
server.port=8015

# 全局编码配置
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true
server.servlet.encoding.charset=UTF-8

# 应用名称
spring.application.name=SAA-15LocalMcpClient

# 阿里通义千问API密钥(替换为自己的密钥)
spring.ai.dashscope.api-key=${aliQwen-api}

# MCP客户端核心配置
spring.ai.mcp.client.type=async
spring.ai.mcp.client.request-timeout=60s
# 开启MCP工具回调
spring.ai.mcp.client.toolcallback.enabled=true
# 绑定本地MCP服务端地址
spring.ai.mcp.client.sse.connections.mcp-server1.url=http://localhost:8014

4.3 ChatClient 集成 MCP 工具配置

将 MCP 远程工具注入 ChatClient,实现大模型自动感知并调用远程工具:

复制代码
package com.atguigu.study.config;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * AI 客户端配置:集成MCP远程工具
 */
@Configuration
public class SaaLLMConfig
{
    /**
     * 构建集成MCP工具的ChatClient
     * 自动加载MCP服务端暴露的所有工具方法
     */
    @Bean
    public ChatClient chatClient(ChatModel chatModel, ToolCallbackProvider tools)
    {
        return ChatClient.builder(chatModel)
                // 注入MCP远程工具回调
                .defaultToolCallbacks(tools.getToolCallbacks())
                .build();
    }
}

4.4 测试控制器(对比MCP启用/关闭效果)

提供两个接口:分别为启用MCP工具调用、原生大模型调用(无工具),直观对比差异:

复制代码
package com.atguigu.study.controller;

import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

/**
 * MCP客户端测试控制器
 */
@RestController
public class McpClientController
{
    // 集成MCP工具的AI客户端
    @Resource
    private ChatClient chatClient;

    // 原生AI模型(无MCP工具)
    @Resource
    private ChatModel chatModel;

    /**
     * 带MCP工具调用的流式对话
     * 大模型会自动调用MCP服务端的天气工具
     */
    @GetMapping("/mcpclient/chat")
    public Flux<String> chat(@RequestParam(name = "msg",defaultValue = "北京") String msg)
    {
        System.out.println("【使用MCP工具调用】");
        return chatClient.prompt(msg).stream().content();
    }

    /**
     * 原生大模型调用(无本地工具)
     * 大模型仅通过自身知识库回答,无法获取本地自定义数据
     */
    @GetMapping("/mcpclient/chat2")
    public Flux<String> chat2(@RequestParam(name = "msg",defaultValue = "北京") String msg)
    {
        System.out.println("【未使用MCP工具调用】");
        return chatModel.stream(msg);
    }
}

五、项目启动与测试

5.1 启动顺序

  1. 先启动 MCP 服务端(8014),保证 MCP 服务正常监听
  2. 再启动 MCP 客户端(8015),自动连接服务端加载工具

5.2 接口测试

1、启用 MCP 工具调用

请求地址:http://localhost:8015/mcpclient/chat?msg=上海天气怎么样

效果:大模型自动识别需要调用本地天气工具,请求 8014 服务端接口,返回自定义的本地天气数据。

2、未启用 MCP 工具调用

请求地址:http://localhost:8015/mcpclient/chat2?msg=上海天气怎么样

效果:大模型仅通过自身知识库回答实时天气,无法读取我们自定义的本地天气数据。

六、常见问题总结

6.1 MCP客户端连接不上服务端

原因:服务端引入了 spring-boot-starter-web,导致容器变为 Tomcat,MCP 基于 Netty 的 WebFlux 服务失效。

解决:服务端删除 web 依赖,仅保留 MCP webflux 依赖。

6.2 大模型不会自动调用工具

  • 检查 @Tool 注解的 description 是否清晰准确,大模型依赖描述识别工具用途
  • 检查客户端是否正确注入 ToolCallbackProvider
  • 确认 MCP 客户端配置的服务端地址正确、网络通畅

6.3 工具参数匹配失败

保证工具方法参数名、参数类型与大模型推断的参数一致,简单字符串参数为最稳适配方案。

七、总结

本文完整实现了 Spring AI MCP 异步服务端+客户端 落地案例,核心要点如下:

  1. MCP 服务端基于 WebFlux 异步实现,严格规避 web 依赖冲突
  2. 通过 @Tool 注解+ToolCallbackProvider 实现自定义工具对外暴露
  3. 客户端集成 MCP 远程工具,让大模型具备调用本地业务接口的能力
  4. 区分原生大模型调用与工具调用的核心差异,适配业务落地场景

该方案可快速拓展至数据库查询、接口调用、文件处理等各类本地业务工具,是 Spring AI 本地化 AI 应用开发的核心方案。

相关推荐
IT枫斗者枫哥3 小时前
MyBatis一对多分页:LIMIT 20,为什么凑不齐20个订单?
java·数据库
知守观3 小时前
@Transactional 事务失效排查,try-catch 吞异常导致回滚失败(附源码分析)
后端·spring
代码方舟3 小时前
Java数据工程:利用天远全网运营商三要素优化线上实名认证合规体验
java·人工智能
BBmmo3 小时前
我的 Java 学习笔记 · 第 7 篇:泛型与通配符
java
独泪了无痕3 小时前
Hutool之RandomUtil:随机数生成的终极利器
java·后端
花开路口3 小时前
深入理解 Java/Kotlin 协变与逆变
java·kotlin
运行时异常3 小时前
【WMS 仓储系统集成 AI Agent 实战】第 7 讲:Vue3 前端工程化——Token 刷新锁、Markdown 渲染踩坑与权限体系
java
她的男孩3 小时前
开放接口限流从 20 改到 200 还是每分钟 20 次:拆完防重放+幂等+限流,我找到 5 个静默失效的坑
java·后端·架构
天天被压力3 小时前
【Python 量化取数指南 #13】Python 把行情落库:sqlite 一键存,回测随用随取
java·人工智能·python
天天被压力3 小时前
【Python 量化取数指南 #14】Python 清洗行情数据:复权停牌对齐,回测不翻车
java·人工智能·python