Spring AI 工具调用详解:Function Calling 与 MCP 客户端 / 服务器实战

摘要 :本文系统讲解 Spring AI 中的工具调用机制,从基础的 Function Calling 概念出发,逐步深入到 MCP(模型上下文协议)的标准化实现。文章首先介绍如何通过 @Tool 注解定义工具并注册到 Spring 容器,随后详细阐述 MCP 的客户端-服务器架构、三种通信机制(STDIO、SSE、WebFlux SSE)以及 Spring AI 提供的各类启动器。最后通过完整的代码示例,分别演示 MCP Server 与 MCP Client 的开发流程,帮助读者掌握从工具定义、服务端打包到客户端配置调用的全链路实践方法。

1. Function Calling 函数调用

函数调用,是让大语言模型(LLM)在对话中"调用"外部定义的工具或 API 的机制,通过这个机制,模型可以在生成回答前提出需要执行的操作(如获取实时天气、设置数据库记录、触发业务流程等),然后由应用端执行工具,结果再反馈回模型,最终给用户完整回复。

2. Spring AI 中 Function Calling(工具)调用流程

1️⃣ 开发者定义工具,并将其注册到 Spring 容器中。

2️⃣ 模型在生成响应时,识别到需要调用工具,会生成包含工具调用信息的响应。

3️⃣ 应用程序接收到模型的响应后,解析其中的工具调用信息,执行相应的工具。

4️⃣ 工具执行完成后,将结果返回给应用程序。

5️⃣ 应用程序将工具执行结果作为上下文信息,传递给模型。

6️⃣ 模型使用工具执行结果,生成最终的响应。

3. 工具定义

开发者可以通过在方法上添加 @Tool 注解定义工具,该注解允许提供工具的名称、描述和输入参数等信息,模型在调用工具时,会根据这些信息生成相应的调用请求。

1️⃣ @Tool 标注;工具名称默认就是方法本身,也可以通过 @Tool(name="tool name") 方式来指定。

2️⃣ @Tool(description="...") 中,这里的 description 是工具描述,大模型根据该描述决定要不要调用该工具。

3️⃣ 工具方法中需要传入参数的,通过使用 @ToolParam 标注,大模型自动决定调用时机和自动根据语义传入参数,调用完成后生成最终对话。

4️⃣ 工具不支持如下类型作为参数或返回值:Optional 、异步类型 (如 CompletableFuture、Future)、响应式类型 (如 Flow、Mono、Flux)、函数式类型(如 Function、Supplier、Consumer)。

java 复制代码
@Component
public class MyTools {

    Logger log = LoggerFactory.getLogger(MyTools.class);

    @Tool(name = "getCurrentTime", description = "返回当前系统时间")
    public String getCurrentTime() {
        log.info("调用 getCurrentTime 工具......");
        return LocalDateTime.now().toString();
    }

    @Tool(description = "对两个数字执行加(add)、减(subtract)、乘(multiply)、除(divide)运算")
    public double calculate(
            @ToolParam(description = "第一个数字") double a,
            @ToolParam(description = "第二个数字") double b,
            @ToolParam(description = "运算类型") String operation
    ) {
        log.info("调用 calculate 工具,第一个参数:" + a + ",第二个参数:" + b + ",第三个参数:" + operation);
        double result = 0;
        switch (operation) {
            case "add":
                result = a + b;
                break;
            case "subtract":
                result = a - b;
                break;
            case "multiply":
                result = a * b;
                break;
            case "divide":
                if (b != 0) {
                    result = a / b;
                }
                break;
            default:
                result = 0;
        }


        return result;
    }  
}

也可以在 ToolUseController.java 中设置不使用工具,将 defaultTools(new MyTools) 注释掉再进行测试,会发现以上访问不会调用工具,都由大模型根据已有知识进行回复。

java 复制代码
import com.example.springaitoolcalling.tools.MyTools;
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.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/ai")
public class ToolUseController {
    private final ChatClient chatClient;

    public ToolUseController(ChatModel chatModel, MyTools tools) {
        this.chatClient = ChatClient.builder(chatModel)
                .defaultSystem("你是一个非常有帮助的助手,你可以使用工具来帮助回答问题")
                .defaultTools(tools)
                .build();
    }

    @GetMapping("/chat")
    public String chat(@RequestParam("message") String message) {
        return chatClient.prompt()
                .user(message)
                .call()
                .content();
    }
}

4. MCP 模型上下文协议

目前,各大 LLM 平台(如 DeepSeek、ChatGPT、Claude)普遍支持"函数调用",允许模型在需要时调用特定函数(如访问网络、查询数据库等)来扩展能力。然而,不同平台的"函数调用"存在实现差异,导致开发者在切换平台时需要重新适配。

MCP(Model Context Protocol,模型上下文协议)是 Anthropic 于 2024 年 11 月推出的开放标准,为大模型调用外部工具建立了一个标准化流程。MCP 基于"函数调用",进一步定义了从请求构建、发送、执行到结果返回的标准化流程。

4.1 MCP 与 Function Calling 的区别和联系

1️⃣ Function Calling:是 LLM 内部定义的一组函数,通过 JSON Schema 让 LLM 知道有哪些功能能调用。

2️⃣ MCP:在 Function Calling 基础上,进一步标准化了函数调用的完整流程,包括请求的构建、发送、执行以及结果的返回。

4.2 MCP 遵循客户端-服务器架构,角色主要包含三部分

1️⃣ MCP Host

运行 LLM(如 Claude、ChatGPT、DeepSeek)的实体节点,如果使用的 LLM 为线上模型,可以忽略这部分。

2️⃣ MCP Client

运行着与大模型对话的客户端(可能会使用工具)叫做 MCP Client。其与 MCP Server 保持 1:1 连接,负责解析模型请求,如果使用工具会将请求转发到对应 MCP Server。

3️⃣ MCP Server

实际运行外部工具(如访问文件系统、发送邮件、查询日历)的服务端叫做 MCP Server。负责处理请求并将结果返回给 Client。

4.3 MCP Client 与 MCP Server 之间有两种通信机制

1️⃣ STDIO(标准输入/输出):当服务器和客户端同时运行在本机时,可以使用 STDIO 机制。

2️⃣ SSE(Server-Sent-Event):当服务器部署在远程服务器上,客户端通过 HTTP 请求发送消息使用这种方式。

4.4 MCP Java SDK 架构

MCP Client 处理客户端操作,MCP Server 管理服务端操作,两者都使用 MCP Session 进行通信管理。传输层(MCP Transport)负责处理 JSON-RPC 消息的序列化和反序列化,支持三种传输实现:STDIO、Spring MVC SSE、Spring WebFlux SSE。

1️⃣ STDIO:基于进程间的标准输入/输出(STDIO)传输,支持单进程,同步交互处理消息。适用于 MCP 服务端和客户端都在同一节点上集成。

2️⃣ Spring MVC SSE(HTTP SSE):基于 Spring MVC 的 SSE 传输,支持 Servlet 线程池,阻塞式处理消息。适用于普通的 Web 应用。

3️⃣ Spring WebFlux SSE:官方建议方式。基于 Spring WebFlux 的反应式 SSE,支持高并发、低延迟,响应式处理消息。适用于高并发的 Web 微服务。

4.5 Spring AI MCP 启动器

Spring AI 提供了多个启动器(starter),简化 MCP 在 Spring Boot 中的使用。

客户端 Starter:

1️⃣ spring-ai-starter-mcp-client:支持 STDIO 与 HTTP-SSE。

2️⃣ spring-ai-starter-mcp-client-webflux:基于 WebFlux 的 SSE 客户端实现。

服务端 Starter:

1️⃣ spring-ai-starter-mcp-server:支持 STDIO 传输。

2️⃣ spring-ai-starter-mcp-server-webmvc:基于 Spring MVC 的 SSE 服务端实现。

3️⃣ spring-ai-starter-mcp-server-webflux:基于 WebFlux 的 SSE 服务端实现。

5. MCP Server 开发

1️⃣ MCP Server 端和 Function Calling 中构建工具的方法一样,即:使用 @Service、@Tool 注解构建工具

2️⃣ @SpringBootApplication 主应用启动类中通过 @Bean 注解创建 ToolCallbackProvider 类型,该类型是 Spring AI 提供的接口,其实现类负责将指定 Service 类中带有 @Tool 注解的方法注册为可供 AI 模型调用的工具。

3️⃣ MCP Client 与 MCP Server 使用 STDIO 传输时,我们需要将 MCP Server 项目进行打包,然后在 MCP Client 中进行配置,无需单独启动 MCP Server。这里直接通过 Maven 工具进行打包即可。

java 复制代码
import com.example.springaimcpstdioserver.service.WeatherService;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.ai.tool.method.MethodToolCallbackProvider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class SpringAimcpStdioServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringAimcpStdioServerApplication.class, args);
    }

    /**
     * ToolCallbackProvider 接口:Spring AI 提供的接口,负责将带有@Tool注解的方法注册为AI LLM 可以调用的工具
     * @param weatherService
     * @return
     */
    @Bean
    public ToolCallbackProvider weatherTools(WeatherService weatherService) {
        return MethodToolCallbackProvider.builder().toolObjects(weatherService).build();
    }
}

6. MCP Client 开发

MCP Client 通过 STDIO 方式连接到 MCP Server 需要在项目的 resources/application.properties 文件中配置如下内容,指定的文件中需要进行 MCP Server 配置。

shell 复制代码
spring.application.name=SpringAIStdioMcpClient

server.port=8080

# 配置 Deepseek URL、API Key、模型
spring.ai.deepseek.base-url=https://api.deepseek.com
spring.ai.deepseek.api-key=your_api_key
spring.ai.deepseek.chat.options.model=deepseek-chat

# 配置日志
logging.pattern.console= %-5level %logger - %msg%n

# STDIO 模式(MCP Client 和 MCP Server 都是在同一机器上):通过配置文件找到 Server Jar 并执行
spring.ai.mcp.client.stdio.servers-configuration=classpath:/mcp-server-config.json
# SSE 模式,配置名为 server1 的 MCP 服务器连接,远程连接到指定的服务器地址
# spring.ai.mcp.client.sse.connections.server1.url=http://localhost:8089

1️⃣ spring.ai.mcp.client.stdio.servers-configuration 参数用来让 MCP Client 找到 MCP Server 相应配置,进而启动 MCP Server 使用工具。

2️⃣ resources/mcp-servers-config.json 文件内容如下:

java 复制代码
{
  "mcpServers": {
    "spring-ai-mcp-weather": {
      "command": "D:\\Program Files\\Java\\jdk17\\jdk\\bin\\java.exe",
      "args": [
        "-Dspring.ai.mcp.server.transport=STDIO",
        "-jar",
        "D:\\idea_space\\StudySpringAI\\SpringAIMCPStdioServer\\target\\SpringAIMCPStdioServer-0.0.1-SNAPSHOT.jar"
      ]
    }
  }
}
6.1 SyncMcpToolCallbackProvider 工具回调提供者
java 复制代码
import io.modelcontextprotocol.client.McpSyncClient;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.mcp.SyncMcpToolCallbackProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.List;

@Configuration
public class Config {
    /**
     * SyncMcpToolCallbackProvider:自动集成 MCP Server 暴露的工具到 ChatClient,可以使 LLM 使用工具
     *
     * @param mcpClients
     * @return
     */
    @Bean
    public SyncMcpToolCallbackProvider toolCallbackProvider(List<McpSyncClient> mcpClients) {
        return new SyncMcpToolCallbackProvider(mcpClients);
    }

    @Bean
    public ChatClient chatClient(ChatModel chatModel, SyncMcpToolCallbackProvider toolCallbackProvider) {
        ChatClient client = ChatClient.builder(chatModel)
                .defaultSystem("你是一个非常有帮助的助手,可以调用工具来回答用户问题")
                .defaultToolCallbacks(toolCallbackProvider) // 配置工具回调,让 LLM 能调用外部工具
                .build();
        return client;

    }
}

说明 :SyncMcpToolCallbackProvider属于MCP‑Client 侧的工具回调提供者 ,和 MCP‑Server 端的ToolCallbackProvider职责不一样。

Server 端ToolCallbackProvider用于把本地@Tool注解的方法暴露成为 MCP 对外服务的工具;

Client 端SyncMcpToolCallbackProvider负责**对接远端 MCP‑Server,将远端工具适配为 Spring AI 体系内可用的ToolCallback。

6.1 STDIO 模式完整加载与调用时序(Windows)

1️⃣ Spring Boot 启动读取 application.properties;读到 servers‑configuration 配置项,指向 classpath 下的 mcp‑server‑config.json;

2️⃣ MCP‑Client starter 加载 mcp‑server‑config.json;解析 command 和 args;Windows 环境下创建新的 Java 子进程,拉起 MCP‑Server 可执行 jar 包;完成之后建立 STDIO 管道的 MCP Session 会话;此时仅仅完成底层通信会话建立,尚未发起 list‑tools 查询工具列表;

3️⃣ 主进程(MCP‑Client)与子进程(MCP‑Server)之间依靠标准输入输出 STDIO 管道完成进程间通信,不占用 TCP 网络端口;

4️⃣ Spring 容器自动实例化 McpSyncClient 对象,存入 List<McpSyncClient> 集合;该集合自动注入到 SyncMcpToolCallbackProvider 构造函数;此时 Provider 内部已经持有全部 MCP 客户端连接,但仍然不知道远端有哪些工具;

5️⃣ 在构建 ChatClient 实例的时候执行 provider.getToolCallbacks();通过已经就绪的 MCP 会话向远端 MCP‑Server 发起 list‑tools RPC 请求,拉取远端工具 JSON‑Schema 元数据 ,在本地动态生成虚拟 ToolCallback 回调对象,注册进 ChatClient;

💡关键点:工具列表不是应用启动时预先拉取!是 ChatClient 构建阶段才远程获取远端工具定义。如果没有把 Provider 注册进 ChatClient,即使 MCP‑Server 子进程正常运行,大模型依旧无法使用远端工具。

6️⃣ 用户提交业务提问;LLM 识别当前问题需要调用远端工具,生成工具调用指令;底层通过 McpSyncClient 经由 STDIO 管道向后台 MCP‑Server 子进程发送工具调用请求;MCP‑Server 完成业务逻辑执行之后将工具结果原路返回给 Client;Client 把工具输出作为对话上下文提交给大模型,模型结合返回数据生成最终应答返回用户。

6.2 SSE 模式完整加载与调用时序

说明:SSE 属于网络长连接模式,MCP‑Server 必须 预先独立启动(手动启动,客户端不会 fork 子进程),MCP‑Server 启动后监听 localhost:8089,暴露默认 /sse SSE 端点;底层 Transport 是 HTTP‑SSE;上层 SyncMcpToolCallbackProvider、ChatClient 的逻辑和 STDIO 完全一致。

1️⃣ Spring Boot 启动读取 application.properties;读取 SSE 连接配置:spring.ai.mcp.client.sse.connections.server1.url=http://localhost:8089;

2️⃣ MCP‑Client starter 根据配置发起网络请求,建立HTTP‑SSE 长连接,创建 MCP Session 会话 ;此时仅仅建立网络会话通道,还没有发起 list‑tools 请求查询远端工具列表 ;(⚠️注意:这里不会新开 java 子进程!MCP‑Server 进程是我们事先手动启动完成的)

3️⃣ 主进程 MCP‑Client (8080) 和远端 MCP‑Server (8089) 之间基于 TCP 网络、SSE 长连接进行 JSON‑RPC 通信;占用 TCP 端口;不再使用 stdio 进程管道 IPC;支持多个 Client 连接同一个 MCP‑Server;

4️⃣ Spring 自动实例化McpSyncClient对象,存入List<McpSyncClient>集合;该集合注入到SyncMcpToolCallbackProvider构造函数;此时 Provider 持有网络连接,但仍然不知道远端提供哪些工具;

5️⃣ 构建ChatClient实例的时候调用 provider.getToolCallbacks();复用已经建立好的 SSE 网络会话向远端 MCP‑Server 发起list‑toolsRPC 请求;拉取远端工具 JSON‑Schema 元数据;本地动态生成虚拟ToolCallback回调对象,注册进 ChatClient;

💡关键点:和 STDIO 时序完全一样!工具列表不是 Spring Bean 初始化阶段拉取,是 ChatClient 构建阶段才远程发现工具;如果忘记把 Provider 注册进 ChatClient,即便 SSE 长连接已经连通,大模型依旧无法调用远端工具。

6️⃣ 用户提交业务提问;LLM 识别问题需要调用远端工具,生成工具调用指令;底层通过 McpSyncClient 经由 SSE 长连接向 MCP‑Server 发送工具调用请求;MCP‑Server 执行业务逻辑之后,将工具执行结果沿 SSE 链路回传给 Client;Client 把工具返回结果追加到对话上下文提交给大模型;模型结合返回数据生成最终应答返回用户。

相关推荐
计算机源码社1 小时前
【27届大数据毕设】基于Spark的AI艺术创作者价值评估与市场趋势预测研究 基于Python与Spark的AI生成艺术受众画像及异常热度识别可视化平台
大数据·人工智能·hadoop·python·spark·毕业设计·课程设计
嵌入式er1 小时前
经纬恒润 自动驾驶嵌入式软件-秋招面经
人工智能·机器学习·自动驾驶
向上的车轮3 小时前
AI音乐创作实战:用Workbuddy定制专属BGM并仿写《Star Sky》
人工智能·ai音乐·workbuddy
宸津-代码粉碎机7 小时前
OpenAI 连夜迎战 Grok Bot 和 Muse:AI 智能体从 “会聊天” 到 “能办事”,现在入场还来得及吗
java·大数据·人工智能·分布式·python
做萤石二次开发的哈哈9 小时前
视频解码器怎么对接?解码上墙、电视墙开窗与场景切换的ISAPI接入实战
人工智能·物联网·监控·视频编解码·大屏端·萤石开放平台·蓝海aiot一站式工作台
Leo.yuan9 小时前
2026年本地化Data Agent优质厂商盘点:哪些产品更适合企业生产环境
大数据·数据库·人工智能
长谷深风1119 小时前
Tool与Skill:AI能力设计的分水岭
java·人工智能·ai·大模型·aiagent
科技观察哨9 小时前
六足平台选型与纳米定位系统集成:HEB-640六自由度位移台在半导体光刻对准中的参数边界与国产替代评估
前端·人工智能
云上先途9 小时前
任务智能体可以自动完成哪些类型工作,是不是只能做简单重复操作?
大数据·人工智能