老梁聊IT头像
Day97-MCP 协议实战:把 Spring Boot 服务变成 AI 可调用工具源封面图

Day97-MCP 协议实战:把 Spring Boot 服务变成 AI 可调用工具源

上篇我们把 Agent 的"脾气"管住了——超时掐断、降级保命、高风险动作锁在人工审批。但还有一个根本问题没解决:Agent 到底能调哪些工具、这些工具从哪来?这篇用 MCP 协议把你的 Spring Boot 服务变成 AI 能直接调用的工具源,顺便厘清它和 Day 92 讲的 Function Calling 到底什么关系。这是 Agent 工程化这一周的最后一块拼图。


一、MCP 到底解决什么问题

先泼盆冷水:Function Calling 不是不能用。Day 92 我们就是直接拿 @Tool / @P 把工具挂到模型上。问题在于 Function Calling 是"模型厂商私有"的:

  • OpenAI 一套工具 Schema,DeepSeek 又一套,Anthropic 还有自己的格式;

  • 工具定义耦合在一次模型调用里,换模型要重写,换场景要重接;

  • 企业内部十个业务系统各写一套工具给各自的 Agent——重复造轮子,还没法复用。

MCP(Model Context Protocol)干的事,就是把"工具提供方"和"模型消费方"解耦。Server 用一套统一协议把能力暴露出来,任何支持 MCP 的 Client(Claude Desktop、Cursor、你自己写的 Agent)都能即插即用。类比最直观:Function Calling 像每个 App 自己焊接口,MCP 像统一 USB 口——一个 Server,全生态的 Client 都能插。

传统 Function Calling(每对都私连)
  Agent-A ──私有格式──▶ 工具1
  Agent-B ──另一套────▶ 工具1(重写)
  Agent-C ──又一套────▶ 工具1(再写)
​
  MCP(统一协议,一次暴露多处复用)
  ┌─────────────┐
  │ MCP Server  │  订单工具 / 库存工具 / 知识资源
  └──────┬──────┘
    统一协议 │  stdio / SSE / HTTP
   ┌──────┼──────────┬──────────┐
  Client-A  Client-B   Client-C   (Agent 复用同一套能力)

二、MCP 的能力模型:三类原语

MCP 把 Server 能对外提供的能力分三类,记住这仨就够了:

原语性质例子
ToolsAI 可调用、带副作用的动作查订单、发邮件、改库存
ResourcesAI 可读、无副作用的上下文文档、配置快照、状态页
Prompts预置的提示词模板"生成周报"的固定套路

实战里 Tools 用得最频繁,Resources 适合挂知识文档,Prompts 用得少。下面重点搞 Tools,Resources 给个样例。


三、Server 端:用 Spring AI MCP 暴露一个"查订单"工具

Spring AI 1.0+ 自带 MCP Server starter,写法和 Day 92 的 @Tool 几乎一样——区别是这次工具不再绑死某个 Agent,而是独立成一个 Server 进程,谁都能连。

// OrderQueryTool.java —— 用 Spring AI MCP 暴露一个"查订单"工具
// 依赖:spring-ai-starter-mcp-server-webmvc (Spring AI 1.0+) + JDK 17
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Service;
​
@Service
public class OrderQueryTool {
​
    // @Tool 让该方法自动注册为 MCP Tool,AI 通过协议即可发现并调用
    @Tool(description = "根据用户ID查询最近一笔订单的状态和金额,下单、售后场景常用")
    public String queryLatestOrder(
            @ToolParam(description = "用户唯一ID,如 U10086") String userId) {
        Order o = orderRepository.findLatestByUser(userId);  // 实际查库
        if (o == null) return "该用户暂无订单";
        return String.format("订单%s 状态=%s 金额=%.2f元",
                o.getId(), o.getStatus(), o.getAmount());
    }
}
# application.yml —— MCP Server 配置(WebMVC 同步传输,默认 /mcp 端点)
spring:
  ai:
    mcp:
      server:
        name: order-mcp-server
        version: 1.0.0
        type: sync                      # sync=同步;用 webflux starter 则 async
        enabled: true
server:
  port: 8081                           # MCP Server 独立端口,与业务服务分离

启动这个服务,任何 MCP Client 连 http://localhost:8081/mcp(也支持 stdio / SSE)就能自动发现并调用 queryLatestOrder。注意 @Tool 和 @ToolParam 的 description 不是写给人看的——是写给模型看的,模型靠它判断"什么时候该调这个工具"。描述含糊,模型就乱调。


四、Client 端:把远程 Server 的工具接进 Agent

Server 有了,Agent 这边怎么消费?Spring AI 的 MCP Client starter 会自动连上配置的 Server,把远程工具转成本地 ToolCallback,直接灌进 ChatClient。你不再需要为每个工具手写 @Tool,Server 加一个工具,Client 自动多一个能力——这才是"解耦复用"的爽点。

// McpClientConfig.java —— 把远程 MCP Server 的工具接进本地 Agent
// 依赖:spring-ai-starter-mcp-client(Spring AI 1.0+)
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.mcp.McpToolCallback;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
​
@Configuration
public class McpClientConfig {
​
    // Spring AI 自动按配置连好 MCP Server;这里只把工具回调注入 ChatClient
    @Bean
    public ChatClient agentChatClient(ChatClient.Builder builder,
                                       java.util.List<McpSyncClient> mcpClients) {
        var callbacks = mcpClients.stream()
                .flatMap(client -> client.getToolCallbacks().stream())
                .toList();
        return builder.defaultTools(callbacks).build();   // Agent 自动拥有所有远程工具
    }
}
# application.yml —— Client 连接远程 MCP Server(支持 stdio / sse 两种传输)
spring:
  ai:
    mcp:
      client:
        enabled: true
        name: agent-client
        request-timeout: 30s
        sse:                              # 走 HTTP+SSE 连远程 Server
          connections:
            order-server:
              url: http://localhost:8081

接好之后,你的 Agent 问"U10086 最近订单咋样",模型会自己决定调用 queryLatestOrder,全程你没在 Agent 代码里写过这个工具——它来自那个独立的 8081 Server。


五、顺带:用 Resources 暴露只读上下文

Tools 是"动作",Resources 是"背景"。比如把一份"系统状态"挂成 Resource,AI 在推理前先读它了解环境,比每次在 Prompt 里硬塞省事得多。

// SystemStatusResource.java —— 暴露一份只读"系统状态"给 AI 当上下文
// Resource 无副作用,生产常用来挂知识文档/配置快照/状态页
@McpResource(uri = "system://status", name = "系统状态",
             description = "当前服务版本与依赖状态,调用订单工具前可先读")
public String systemStatus() {
    return """
           service=order-service
           version=2.3.1
           db=connected
           mq=connected
           说明:调用订单工具前可先读此资源了解环境
           """;
}

注:@McpResource 注解名与参数随 Spring AI 版本略有差异,核心是"Resource = 可读取、无副作用的上下文",概念不随 API 变。


六、MCP vs Function Calling:到底怎么选

这是被问得最多的问题,直接上对照:

维度Function CallingMCP
耦合度工具绑定在某次模型调用Server/Client 解耦,独立部署
复用性换模型/换 Agent 常要重写一套 Server,多 Client 复用
适用规模单 Agent、单模型、demo多 Agent/多模型/跨团队工具中台
上手成本低,注解即用中,要起 Server、配传输
本质模型驱动工具调用的"机制"工具分发的"协议层"

关键认知:MCP 不替代 Function Calling。底层最终还是靠 FC(或等效调用)驱动模型去调工具,MCP 是架在上面的"工具分发层"。它解决的是"工具怎么被多系统标准化发现、复用、治理",不是"模型怎么调工具"。

选型一句话:一个 Agent、一个模型、跑个 demo——Function Calling 足矣,别上 MCP 增加负担;工具要被多个 Agent / 多模型 / 跨团队复用——上 MCP,建工具中台。


七、实战建议

  1. 工具的 description 要认真写,粒度要"动词化+单一职责"。模型全靠描述判断调不调、怎么调。一个工具又查又改又发邮件,模型必乱。一个动作一个工具,描述写清"何时用、参数含义、返回什么"。

  2. MCP Server 独立部署 + 加鉴权,别和业务混端口。它本质是"给 AI 开后门调你系统",鉴权、限流、审计一个不能少;工具调用链路记得接 Day 94 的 OpenTelemetry,否则谁调了什么你又瞎了。

  3. 别为了 MCP 而 MCP。先问自己"这套工具会被几个系统复用"。复用度低,Function Calling 更轻更快;等真出现"三个 Agent 都要调同一批工具"的痛点,再抽成 MCP Server 不迟。


八、收尾

MCP 的价值不在"又能调工具了",而在于把工具从"某个 Agent 的私产"变成"全公司的公共设施"——当你的订单工具、库存工具、知识资源都挂在一个协议下,AI 应用的拼装速度会质变。但记住:协议是骨架,工具本身的健壮性(Day 92 的校验重试、Day 96 的降级兜底)才是血肉。

下篇(Day 98)我们切到新主题——RAG 深水区:高级 RAG 全景,Query 改写、多路检索、上下文压缩与自纠错,把 Day 71-75 那个能跑的 RAG 真正推向生产级。


本篇代码基于 JDK 17 + Spring AI 1.0+(MCP Server/Client starter),注解名随版本微调,概念与接线方式稳定。版本以官方 GA 为准。

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/liangjinhu/article/details/169337591

文章来源转载

评论

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--