基于 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 应用开发的核心方案。

相关推荐
会编程的吕洞宾1 小时前
Spring Boot多环境配置实战 配置文件加载顺序与切换不再翻车
java·后端
额鹅恶饿呃1 小时前
随着CentOS官方停服的时间越来越久,大量仍在使用CentOS7的企业和运维从业者
java·python·算法·c#·ruby
秋名RG1 小时前
Java 异常处理全攻略:从入门到实战(JDK 21 版)
java·开发语言
SQL-First布道者2 小时前
我为什么把 MyBatis 从项目中删了?
java·spring·tomcat·mybatis·mybatis plus·spring jdbc
公爵爱学习2 小时前
无人机多点导航笔记
java·前端·笔记
萧瑟余晖2 小时前
Java深入解析篇五十五之Unsafe 机制详解
java
kakawzw2 小时前
mybatis源码笔记1——JDBC和整体架构
java·mybatis
sunshine22 girl2 小时前
Java学习一 环境配置1 安装JDK,配置环境变量
java·开发语言·学习
秋饼2 小时前
Spring AI Session API 深度实战:从 ChatMemory 平滑迁移到事件溯源的企业级短期记忆
java·ai·技术分享·后端开发