【SpringAIAlibaba新手村系列】(14)MCP 本地服务与工具集成

简介: 本章从 MCP Server 视角出发,说明如何将本地天气查询能力整理并暴露为标准化工具服务。内容涵盖 @Tool、ToolCallbackProvider、MethodToolCallbackProvider 的作用,以及 Streamable-HTTP 协议下服务端的能力注册与对外提供逻辑。

第十四章 MCP 本地服务与工具集成

版本标注

  • Spring AI: 1.1.2
  • Spring AI Alibaba: 1.1.2.0

章节定位

  • 第十三章讨论的是 Tool Calling 的工作机制。
  • 这一章转到 MCP Server 视角,重点不再是“模型如何调用工具”,而是“本地能力如何作为服务提供出去”。
  • 下一章再继续写 MCP Client,连接并调用这里提供的本地服务。

s01 > s02 > s03 > s04 > s05 > s06 > s07 > s08 > s09 > s10 > s11 > s12 > s13 > [ s14 ] s15 > s16 > s17 > s18


一、本章要解决的问题

第十三章中,工具调用发生在当前应用内部,链路大致如下:

用户问题 -> ChatClient -> 模型决策 -> 本地 @Tool 方法

这种方式适合在单个项目中快速接入工具,但它仍然属于应用内部调用。到了 MCP 场景,关注点会前移到服务提供侧:如果已经有天气、文件、地图等能力,如何将它们做成一个本地 MCP 服务,供其他客户端统一连接和调用。

因此,这一章的主语是 MCP Server,下一章的主语才是 MCP Client。两章分别对应“提供服务”和“使用服务”两个视角。

二、本地 MCP Server 的含义

本地 MCP Server 指的是运行在本机上的 MCP 服务端。它负责把项目中的业务能力按 MCP 协议暴露出去,使这些能力不再只是当前工程里的内部方法,而是可以被外部客户端发现和调用的标准化工具。

放到这一章里,可以把它理解为三层结构:

  1. 业务能力层:例如天气查询服务、地图查询服务、文件读写服务。
  2. Spring Bean 层:这些能力被交给 Spring 管理。
  3. MCP 暴露层:MCP Server 发现其中可暴露的方法,并将其作为工具提供给客户端。

这里最容易混淆的地方在于,@Tool 本身并不等于 MCP Server。@Tool 只负责描述“这个方法可以作为工具能力”,真正将其暴露为 MCP 服务,还需要 MCP Server 依赖和自动配置参与。

三、代码结构

本章直接相关的代码只有两部分。

3.0 服务端配置

除了业务类和配置类之外,服务端还需要在 application.yml 中显式声明使用 Streamable-HTTP 协议。配置可以写成这样:

server:
  port: 8014

spring:
  application:
    name: mcp-server-demo

  ai:
    mcp:
      server:
        type: async
        protocol: STREAMABLE
        name: customer-define-mcp-server
        version: 1.0.0

这段配置的作用是把当前应用明确切到 Streamable-HTTP 方式。这样下一章客户端侧配置中的:

  • url: http://localhost:8014
  • endpoint: /mcp

就和本章服务端形成了一一对应的关系。

3.1 业务服务类

package cn.edu.nnu.opengms.service;

import org.springframework.ai.tool.annotation.Tool;
import org.springframework.stereotype.Service;

import java.util.Map;

@Service
public class WeatherService {
   
    @Tool(description = "根据城市名称获取天气预报")
    public String getWeatherByCity(String city)
    {
   
        Map<String, String> map = Map.of(
                "北京", "11111降雨频繁,其中今天和后天雨势较强,部分地区有暴雨并伴强对流天气,需注意",
                "上海", "22222多云,15℃~27℃,南风3级,当前温度27℃。",
                "深圳", "333333多云40天,阴16天,雨30天,晴3天"
        );
        return map.getOrDefault(city, "没有该城市的天气信息");
    }
}

WeatherService 是一个普通的 Spring 业务类,真正有意义的是其中这个 @Tool 方法。它向框架表达的是:getWeatherByCity 可以作为一个工具被暴露出去。

3.2 配置类

package cn.edu.nnu.opengms.config;

import cn.edu.nnu.opengms.service.WeatherService;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.ai.tool.method.MethodToolCallbackProvider;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Bean;

/**
 * MCP Server 配置
 * 作用:把 WeatherService 中的工具方法整理为可对外暴露的工具提供者
 */
@Configuration
public class McpServerConfig
{
   
    @Bean
    public ToolCallbackProvider weatherTools(WeatherService weatherService)
    {
   
        return MethodToolCallbackProvider.builder()
                .toolObjects(weatherService)
                .build();
    }
}

这段配置的含义,比单纯注册一个 WeatherService Bean 更明确。

WeatherService 负责提供业务能力,MethodToolCallbackProvider 负责从这个对象中提取 @Tool 方法,并将它们组织成 ToolCallbackProvider。这样一来,MCP Server 拿到的就不再只是一个普通业务 Bean,而是一组已经整理好的工具定义。

如果项目中已经加入下面这个依赖:

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-mcp-server</artifactId>
</dependency>

那么当前工程就已经具备 MCP Server 的运行基础。此时 WeatherService 中被 @Tool 标记的方法,不再只是本地工具候选,而是会通过 ToolCallbackProvider 进入 MCP Server 的能力暴露链路。

四、这两段代码到底算不算“已经实现了 MCP”

从章节定位和工程意图上看,这已经不是单纯的本地 Tool Calling 示例了。因为项目已经引入了 spring-ai-starter-mcp-serverWeatherService 通过 @Tool 声明了可暴露的工具能力,配置类又通过 ToolCallbackProvider 显式将这些工具交给服务端管理。这说明已经进入了 MCP Server 的实现范围。

但如果从“是否已经完全跑通”这个角度判断,仅凭这两个类还不够。严格来说,还需要满足下面几个条件:

  1. MCP Server 相关自动配置正常生效。
  2. @Tool 方法被服务端正确发现并注册。
  3. 应用启动后,客户端能够通过 MCP 方式连接并调用该工具。

到这里,本地 MCP Server 的基础实现已经具备,天气服务也已经具备作为 MCP 工具对外暴露的条件。是否完全跑通,则以下一章客户端成功连接和调用为最终验证。

五、为什么它看起来仍然像 Tool Calling

这里确实很容易产生一种感觉:@ToolToolCallbackProviderMethodToolCallbackProvider 这些名字,看上去还是和 Tool Calling 很像。

原因并不复杂。MCP Server 在 Spring AI 里的底层工具抽象,本来就是沿着 Tool 回调体系组织起来的。也就是说,@Tool 并不是只服务于上一章的本地 Tool Calling,它同时也是 MCP Server 暴露工具能力时使用的基础描述方式。

两者的区别不在注解长得像不像,而在于这些工具最终交给谁使用:

  1. 如果工具被注册给 ChatClient.defaultTools(...),主语就是当前应用内部的调用方,这更接近第十三章的 Tool Calling。
  2. 如果工具被整理成 ToolCallbackProvider 交给 MCP Server,主语就是服务提供方,这就是第十四章的 MCP Server。

因此,代码形态上会有相似之处,但章节含义已经变了。第十三章关注的是“模型在当前应用中怎么调工具”,第十四章关注的是“服务端怎么把工具能力整理并暴露出去”。

六、MCP Server 暴露工具的原理

这一章如果只停留在“把 WeatherService 包装成 ToolCallbackProvider”,还是不够完整。更重要的是理解:服务端到底是怎么把一个普通业务方法变成可被客户端发现和调用的 MCP 能力的。

Spring AI 对 MCP 的支持,底层依赖的是 MCP Java SDK。官方文档把它拆成三层:

  1. 客户端/服务端层
    这一层由 McpClientMcpServer 负责,前者管理连接和调用,后者负责提供工具、资源和提示模板。

  2. 会话层
    这一层由 McpSession 管理会话状态。服务端不是简单接收一条 HTTP 请求就结束,而是在协议交互过程中维护和客户端之间的会话状态。

  3. 传输层
    这一层负责 JSON-RPC 消息的序列化、反序列化以及具体的传输方式实现。本章使用的是 WebFlux 版 MCP Server,当前场景采用的是 Streamable-HTTP 传输方式,它同样建立在 HTTP 连接之上,相比SSE的核心优势在于它能够提供更高效的全双工通信能力,解决了 SSE 仅支持服务端向客户端单向推送数据的局限性(单工通信)。

6.1 服务端是怎样发现工具的

在本章代码里,真正的业务能力来自:

@Tool(description = "根据城市名称获取天气预报")
public String getWeatherByCity(String city)

这个方法本身还是普通 Java 方法,但 @Tool 给了框架一层额外的语义:它可以被作为工具暴露出去。

随后,MethodToolCallbackProvider 会扫描 WeatherService,提取其中带有 @Tool 的方法,并整理出一组工具定义。这里被整理出来的不只是方法名,还包括:

  1. 工具名称
  2. 工具描述
  3. 参数结构
  4. 调用入口

这些信息会进一步被 MCP Server 注册为对外能力。客户端最终看到的,不再是一个 Java 类,而是一组符合 MCP 协议描述方式的工具元数据。

6.2 服务端是如何对外提供这些能力的

当前工程引入的是:

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

根据 Spring AI 官方文档,WebFlux Server Starter 会自动完成 MCP Server 相关组件的配置,并在当前应用中启用对应的服务端能力。对于本章这种本地服务场景,它做的事情可以概括为:

  1. 初始化 MCP Server
  2. 注册工具能力
  3. 挂载 Streamable-HTTP 端点
  4. 等待客户端连接

也就是说,本章的服务端不是通过手写 Controller 去暴露天气接口,而是通过 MCP Server 统一对外提供工具能力。

6.3 数据是怎样传输的

MCP 协议在消息层使用的是 JSON-RPC。服务端和客户端之间真正交换的,不是 WeatherService 对象本身,而是 JSON-RPC 消息。

这条链路可以理解成这样:

WeatherService 中的 @Tool 方法
  -> MethodToolCallbackProvider 提取工具定义
  -> MCP Server 注册工具能力
  -> 通过 `Streamable-HTTP` 端点对外暴露
  -> 客户端建立连接并发现工具
  -> 客户端发起工具调用消息
  -> 服务端执行本地方法并返回结果

所以服务端真正暴露出去的是:

  • 工具名称
  • 参数 schema
  • 调用能力
  • 返回结果

而不是某个 Java Bean 本身。

6.4 为什么客户端后面可以直接发现这些工具

因为服务端在启动时已经把 @Tool 方法注册成了 MCP 能力。客户端连接上来后,会先完成协议初始化和能力协商,然后拉取服务端已经暴露的工具列表。下一章里看到的 ToolCallbackProvider,本质上就是客户端把这些远端工具整理后的结果。

这一来一回,刚好对应了服务端和客户端的职责分工:

  1. 这一章负责把能力暴露出去
  2. 下一章负责发现这些能力并调用它们

七、代码里还有一个需要说明的细节

当前 WeatherService 同时写了 @Service,又在 McpServerConfig 中通过 @Bean 手动注册了一次。这通常会造成重复注册问题。

也就是说,下面两种方式本质上是在做同一件事:

  1. @Service
  2. @Bean public WeatherService weatherService()

在一般情况下,保留一种即可。对当前这个模块来说,更自然的写法是保留 @Service,删除配置类中的手动 @Bean 注册。这样代码更简单,也更符合 Spring 的常规使用方式。

如果继续保留两者,后续在 Bean 注入、组件扫描或自动配置过程中容易引出不必要的歧义。

对当前这一版写法来说,配置类中已经不需要再手动创建 WeatherService 了。保留 @Service 即可,配置类只负责把它包装成 ToolCallbackProvider

八、和第十三章的边界

为了避免和上一章混淆,这里把两章的侧重点并排列一下。

对比项 第13章 Tool Calling 第14章 MCP Server
主语 当前应用中的调用方 本地服务提供方
重点 模型如何决定并执行工具调用 本地能力如何被组织成 MCP 服务
关键对象 ChatClientToolCallback WeatherServiceToolCallbackProvider、MCP Server 暴露链路
结果 模型能在当前应用中调用工具 客户端可以连接本地 MCP 服务

第十三章解释的是调用机制,这一章建立的是服务端能力。这也是为什么下一章可以自然接着写 MCP Client,而不是和前两章重复。

九、本章小结

这一章的重点不在 ChatClient,而在服务提供侧。真正关键的是两件事:

  1. 项目已经引入 spring-ai-starter-mcp-server,具备了 MCP Server 的运行基础。
  2. WeatherService 中的 @Tool 方法已经具备被服务端发现并暴露的条件。

从这个意义上说,第十四章已经站在了 MCP Server 的语境里。下一章继续转到 MCP Client 视角,去连接并调用这里提供的本地服务,整个链路就完整了。


本章重点

  1. 明确第14章写的是 MCP Server,而不是本地 Tool Calling 的重复讲解。
  2. 理解 @Tool、Spring Bean 和 MCP Server 暴露之间的层次关系。
  3. 为第15章的 MCP Client 调用本地服务建立前提。

下章剧透(s15):

下一章开始切到使用方视角,重点看 MCP Client 如何连接并调用这一章提供的本地服务。


📝 编辑者:Flittly
📅 更新时间:2026年4月
🔗 相关资源MCP 官方文档 | Spring AI MCP

目录
相关文章
|
21天前
|
人工智能 JSON 编解码
【SpringAIAlibaba新手村系列】(15)MCP Client 调用本地服务
本章从 MCP Client 视角说明如何连接上一章提供的本地服务,并把远端工具接入 ChatClient。重点讲解 Streamable-HTTP 配置、ToolCallbackProvider 的注入方式,以及模型如何通过 JSON-RPC 消息完成工具调用与结果回传。
268 21
|
30天前
|
人工智能 前端开发 Java
【SpringAIAlibaba新手村系列】(4)流式输出与响应式编程
本文围绕 Spring AI 中的流式输出与响应式编程展开,重点解释了传统一次性响应与流式返回的差异,以及 Flux 在异步数据流中的核心作用。文章结合 ChatModel.stream() 与 ChatClient 的多种代码示例,说明如何实现 AI 内容的边生成边返回,并帮助读者理解流式调用在用户体验、性能和长文本场景中的实际价值。
620 4
【SpringAIAlibaba新手村系列】(4)流式输出与响应式编程
|
20天前
|
人工智能 Java 定位技术
【SpringAIAlibaba新手村系列】(16)调用百度 MCP 服务
本章展示如何在客户端接入第三方百度 MCP 服务。通过 spring-ai-starter-mcp-client、application.yml 与 mcp-server.json5 完成 stdio 方式连接,自动发现并注册远端工具到 ChatClient,实现天气、IP 归属地、路线规划等能力调用。
296 9
|
30天前
|
存储 人工智能 Java
吃透 Spring AI Alibaba 多智能体|四大协同模式+完整代码
本文详细讲解 Spring AI Alibaba Multi-Agent 多智能体架构,包含顺序执行、并行执行、LLM 路由、监督者四大协同模式,搭配可运行代码示例与真实业务场景,从零带你上手多智能体开发。
981 3
|
22天前
|
人工智能 Java API
【SpringAIAlibaba新手村系列】(13)Tool Calling 函数工具调用技术
本文详细解析 Spring AI 的 Tool Calling 技术,阐明其如何弥补大模型“会说不会做”的局限。通过 @Tool 注解,开发者可轻松将 Java 方法暴露为 AI 工具。文中深入讲解了 ToolCallbacks.from() 注册工具的原理,以及工具方法在当前 Spring Boot 进程内通过反射动态执行的底层逻辑,强调了模型决策与框架执行的协同过程,为理解 AI 赋能实际操作奠定基础。
514 0
|
26天前
|
NoSQL Java 数据库
【SpringAIAlibaba新手村系列】(11)Embedding 向量化与向量数据库
本文围绕 Embedding 与向量数据库展开,讲解了文本向量化、相似度检索和 VectorStore 的基本用法,并结合 SimpleVectorStore 示例说明了 Spring 中自动装配与手动注册 Bean 的区别,为后续学习 RAG 打下基础。
404 4
【SpringAIAlibaba新手村系列】(11)Embedding 向量化与向量数据库
|
22天前
|
人工智能 运维 Java
【SpringAIAlibaba新手村系列】(12)RAG 检索增强生成技术
本文深入探讨 RAG 技术在 AiOps 场景中的应用,以基于 ops.txt 构建运维知识库为例,讲解了如何通过将文本切分、向量化并存入向量数据库,实现 AI 故障查询。内容涵盖 EmbeddingModel、VectorStore 的基本概念,以及利用 Redis 的 SETNX 机制防止知识库重复导入的工程实践。核心在于让 AI 结合外部知识库,更准确地回答运维问题。
280 0
|
16天前
|
人工智能 Java API
【SpringAIAlibaba新手村系列】(17)百炼 RAG 知识库应用
本章基于 Spring AI Alibaba 落地百炼 RAG,完成 DashScopeApi、ChatModel、ChatClient 配置,并通过检索器与 DocumentRetrievalAdvisor 组装检索增强问答链路,实现可运行的知识库问答接口。
292 1
|
16天前
|
人工智能 Java API
【SpringAIAlibaba新手村系列】(18)Agent 智能体与今日菜单应用
本章以 ReactAgent 为入口,将本地菜单工具与 MCP 外部工具合并注册,统一通过 /eatAgent 执行任务,展示 Agent 在多工具协同下的意图理解、工具调用与结果整合能力。
236 3
|
存储 人工智能 Java
【SpringAIAlibaba新手村系列】(3)ChatModel 与 ChatClient 的深度对比
本章深度解析 Spring AI 中 `ChatModel`(底层接口)与 `ChatClient`(高级封装)的本质区别:前者如“手动挡”,精准控制但需写大量样板代码;后者似“智能点餐机”,链式调用、支持系统提示、模板、工具调用等,开发高效。初学者推荐优先使用 `ChatClient`。
410 0
【SpringAIAlibaba新手村系列】(3)ChatModel 与 ChatClient 的深度对比

热门文章

最新文章