首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >三个 Spring 注解,让 Claude 直接调你的业务接口

三个 Spring 注解,让 Claude 直接调你的业务接口

作者头像
码哥字节
发布2026-04-28 13:27:32
发布2026-04-28 13:27:32
630
举报
文章被收录于专栏:后端架构师后端架构师

去年底一个同事问我,怎么让 Claude 直接查我们系统的订单状态。我的第一反应是:写个 Function Calling,把接口描述塞进 Prompt 里,让模型按格式返回参数,再在业务层拦截调用。折腾了两天,跑通了,但代码丑得不忍看——换一个模型就要重新适配一套格式,换 Cursor 就要再折腾一遍。

后来发现 MCP,五分钟把同一个接口接上了 Claude Desktop,Cursor 里也直接可用,代码就加了三个注解。

这篇把这个过程完整写出来,代码可以直接复制跑。

MCP 和 Function Calling 差在哪?不搞清楚后面会踩坑

两个技术经常被混为一谈,但本质上解决的不是同一个问题。

Function Calling 是 Prompt 级别的能力。你在请求里告诉模型「有这几个函数,参数格式如下」,模型决定要不要调用,返回一个结构化调用指令,你的代码去执行,再把结果塞回 Prompt。整个过程绑定在一次模型请求里,工具描述随 Prompt 走,换个模型得重写一遍格式,换个 AI 客户端得重新适配一套。

MCP(Model Context Protocol)是 协议级别的能力。你的服务独立部署成一个 MCP Server,任何支持 MCP 协议的客户端——Claude Desktop、Cursor、自己写的 Agent——都能发现和调用你的工具,不依赖具体模型,一次开发到处复用。

Anthropic 在 2024 年 11 月开源 MCP 规范,目前 Claude、ChatGPT、VS Code Copilot、Cursor、Gemini 等主流 AI 工具都已支持,已有 5000+ 个公开 MCP Server。可以理解为 AI 工具调用的 USB-C 接口:以前每根线的接口不一样,现在统一了。

Function Calling vs MCP 架构对比示意图
Function Calling vs MCP 架构对比示意图

Function Calling vs MCP 架构对比示意图

MCP 协议里有三种原语:Tool(AI 可以执行的操作,有副作用)、Resource(AI 可以读取的数据,幂等只读)、Prompt(预置的提示词模板)。Spring AI 为三者都提供了对应注解。

环境准备:加依赖就行

Spring AI 1.0.0-M6 开始提供 MCP Server Starters,根据传输协议选一个(协议怎么选后面讲):

代码语言:javascript
复制
<!-- 生产首选:WebMVC + SSE 协议,支持多客户端同时连接 -->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
</dependency>
代码语言:javascript
复制
<!-- 响应式技术栈用这个 -->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-mcp-server-webflux</artifactId>
</dependency>
代码语言:javascript
复制
<!-- 纯本地工具,不需要网络,Claude Desktop 用子进程方式拉起 -->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-mcp-server</artifactId>
</dependency>

BOM 版本管理(推荐,避免版本冲突):

代码语言:javascript
复制
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-bom</artifactId>
            <version>1.0.0-M7</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

前置要求:Java 17+,Spring Boot 3.2+。

@McpTool:把现有方法变成 AI 可调用的工具

在任何 Spring Bean 的方法上加 @McpTool,Spring AI 自动扫描注册成 MCP Tool,JSON Schema 也是自动生成的,不需要手写。

实际例子,把订单查询接口暴露出去:

代码语言:javascript
复制
import org.springframework.ai.mcp.spring.annotation.McpTool;
import org.springframework.ai.mcp.spring.annotation.McpToolParam;
import org.springframework.stereotype.Component;

@Component
publicclass OrderMcpTools {

    privatefinal OrderService orderService;

    public OrderMcpTools(OrderService orderService) {
        this.orderService = orderService;
    }

    @McpTool(
        name = "queryOrder",
        description = "根据订单号查询订单状态和详情,支持查询最近 90 天内的订单。" +
                      "返回字段包括:订单状态、实付金额、下单时间、收货地址、物流单号。"
    )
    public OrderDetail queryOrder(
        @McpToolParam(description = "订单号,格式为 ORD-XXXXXXXXXX,10 位数字后缀", required = true)
        String orderId,

        @McpToolParam(description = "是否返回商品明细列表,默认 false,true 时额外返回每个商品的名称和数量")
        boolean includeItems
    ) {
        // 直接复用现有业务逻辑,不需要改一行
        return orderService.getOrderDetail(orderId, includeItems);
    }

    @McpTool(
        name = "listRecentOrders",
        description = "查询用户最近的订单列表,返回最多 20 条,按下单时间倒序排列"
    )
    public List<OrderSummary> listRecentOrders(
        @McpToolParam(description = "用户 ID,Long 类型", required = true) Long userId,
        @McpToolParam(description = "查询天数范围,1-90 之间,默认 30") int days
    ) {
        return orderService.listRecentOrders(userId, days);
    }
}

description 的写法很关键——这是模型决定「要不要调用这个工具、传什么参数」的主要依据,不是给开发者看的注释。「订单号,格式为 ORD-XXXXXXXXXX,10 位数字后缀」比「the order ID」有用得多,模型在推断参数时会参考这个描述。

application.yml 里开启注解扫描:

代码语言:javascript
复制
spring:
  ai:
    mcp:
      server:
        type: SYNC          # SYNC 同步 / ASYNC 响应式
        protocol: SSE       # 传输协议,后面详细讲
        annotation-scanner:
          enabled: true

正常启动 Spring Boot 应用,MCP Server 就跑起来了。

Spring AI MCP Server 内部架构图
Spring AI MCP Server 内部架构图

Spring AI MCP Server 内部架构图

踩坑记录:返回值必须可 JSON 序列化。如果 OrderDetail 里有 LocalDateTime,要确保 Jackson 加了时间模块(jackson-datatype-jsr310),否则模型收到的是序列化异常而不是工具执行结果。更隐蔽的是 Optional<T> 类型,Jackson 默认不处理,直接返回 Tnull 比较稳。

传输协议选型:STDIO vs SSE vs Streamable-HTTP

Spring AI MCP Server 支持三种传输协议,选错了要么本地能用生产跑不了,要么部署方式不对:

协议

适用场景

Starter 依赖

yml 配置

STDIO

本地工具、Claude Desktop 直接拉起进程

spring-ai-starter-mcp-server

stdio: true

SSE

生产部署,多 AI 客户端同时连接

spring-ai-starter-mcp-server-webmvc

protocol: SSE

Streamable-HTTP

云原生、K8s 横向扩容、负载均衡场景

spring-ai-starter-mcp-server-webmvc

protocol: STREAMABLE

STDIO 最容易踩的坑:Claude Desktop 用 STDIO 协议时,是把你的 Spring Boot 应用当成子进程直接拉起的。

这意味着服务不能有额外的 HTTP 端口监听(会报冲突),而且绝对不能用 System.out.println——标准输出被 MCP 协议占用,乱写会直接污染消息帧,导致工具调用解析失败。把所有日志配到文件或 stderr 就好:

代码语言:javascript
复制
<!-- logback.xml:STDIO 模式下把控制台输出全部转到 STDERR -->
<appender name="STDERR" class="ch.qos.logback.core.ConsoleAppender">
    <target>System.err</target>
    <encoder>
        <pattern>%d{HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
</appender>

生产环境用 SSE:保持持久 HTTP 连接,服务端可以主动推送工具执行进度。多个 AI 客户端可以同时连,服务正常作为 Spring Boot 应用独立部署,和现有 REST 接口共用同一个端口。

Streamable-HTTP 适合无状态部署:每次请求独立,没有持久连接,K8s 横向扩容时不需要考虑连接亲和性。如果你的服务部署在有负载均衡的环境里,优先考虑这个协议。

让 Claude Desktop 找到你的 MCP Server

STDIO 模式(本地调试最方便):

找到 Claude Desktop 配置文件——macOS 在 ~/Library/Application Support/Claude/claude_desktop_config.json,Windows 在 %APPDATA%\Claude\claude_desktop_config.json——加入:

代码语言:javascript
复制
{
  "mcpServers": {
    "order-service": {
      "command": "java",
      "args": [
        "-jar",
        "/absolute/path/to/your-mcp-server.jar"
      ]
    }
  }
}

SSE 模式(服务已独立部署):

代码语言:javascript
复制
{
  "mcpServers": {
    "order-service": {
      "url": "http://localhost:8080/sse"
    }
  }
}

如果服务需要鉴权,在请求头里带 Token:

代码语言:javascript
复制
{
  "mcpServers": {
    "order-service": {
      "url": "http://your-server/sse",
      "headers": { "Authorization": "Bearer your-token" }
    }
  }
}

重启 Claude Desktop 后,工具栏会出现锤子图标,点开能看到你注册的所有 MCP Tool 名称和描述。直接在对话框里问「帮我查一下订单 ORD-20260425001 的状态」,Claude 会自动识别出需要调用 queryOrder 工具,填入参数,执行,再把结果转成自然语言回复。

Claude Desktop 调用 MCP Server 完整时序图
Claude Desktop 调用 MCP Server 完整时序图

Claude Desktop 调用 MCP Server 完整时序图

进阶:@McpResource 暴露只读数据

@McpTool 适合「执行操作」(下单、更新状态),@McpResource 适合「读取数据」(商品详情、配置项)。区别在语义和使用时机:Resource 是幂等只读的,AI 客户端在构建上下文时会主动拉取,而不是等到要执行操作时才调用。

代码语言:javascript
复制
@Component
publicclass ProductMcpResources {

    @McpResource(
        uri = "product://{productId}/info",
        name = "商品信息",
        description = "根据商品 ID 获取商品基本信息,包括价格、库存状态、分类"
    )
    public String getProductInfo(String productId) {
        Product product = productService.findById(productId);
        // Resource 返回字符串,JSON 格式或结构化文本都行
        return objectMapper.writeValueAsString(product);
    }

    @McpResource(
        uri = "config://feature-flags",
        name = "功能开关配置",
        description = "返回当前所有功能开关的状态,供 AI 了解系统当前支持的能力"
    )
    public String getFeatureFlags() {
        return featureFlagService.getAllFlags().toString();
    }
}

实际用下来,@McpResource 在「让 AI 了解你的系统状态」这个场景特别有用。比如先让 AI 读一遍功能开关配置,再决定推荐哪些操作,逻辑上比每次调 Tool 干净。

常见问题

Q:现有服务改造需要动多少代码?

加依赖、写一个 @Component 包装类调用现有 Service,在方法上加注解。不需要修改任何现有业务逻辑,原来的 REST 接口照常工作,两套并存,互不影响。

Q:description 怎么写才有效?

受众是 AI 模型,不是开发者。要说清楚:工具做什么、适合什么场景、参数有什么限制(格式、范围、枚举值、默认值)。「查询订单」比「根据订单号查询,仅支持 90 天内,返回状态和物流信息」少了太多有效信息,模型填参数时更容易出错。

Q:和 Spring AI 的 FunctionCallback 是什么关系?

完全独立的两套机制。FunctionCallback 是你自己写 AI 应用时,给模型 Prompt 绑定工具(Function Calling);@McpTool 是把你的服务暴露给外部 AI 客户端(MCP 协议)。可以同时用——比如你的服务既是一个 MCP Server 供外部客户端调,内部同时用 FunctionCallback 驱动自己的业务 AI 流程。

Q:STDIO 和 SSE 模式下日志怎么配?

STDIO 模式下 System.out 会污染 MCP 消息流,必须把所有日志重定向到文件或 stderr,把 logback.xml 里的 Console Appender target 改成 System.err。SSE 模式没这个限制,按正常配置就好。

Q:生产环境需要鉴权吗?

MCP 协议本身没有内置鉴权,默认任何人都能调你的 MCP Server。生产环境建议在 Spring Security 层加 HTTP Bearer Token 或 API Key 验证,然后在客户端配置文件里的 headers 字段带上凭证。

MCP 现在还在快速演进,Streamable-HTTP 是最近才定稿的传输协议,比 SSE 更适合云原生无状态部署场景。Spring AI 的注解 API 在各个 milestone 版本之间有调整,遇到问题先确认用的版本和文档是否对应。

说到底,MCP 解决的核心问题是「工具定义和模型解耦」——你写一次,任何支持 MCP 的 AI 客户端都能用,不用跟着每个模型的格式调整。这个方向是对的,生态成熟度也在快速追上来。

下一篇打算写 MCP 的认证鉴权实战,以及多个 MCP Server 组合的 Agent 编排——这两块坑比较多,整理好了会推送。如果身边有同事在做 AI 工具接入的选型,这篇可以直接转给他,省得从头翻文档。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-04-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码哥跳动 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • MCP 和 Function Calling 差在哪?不搞清楚后面会踩坑
  • 环境准备:加依赖就行
  • @McpTool:把现有方法变成 AI 可调用的工具
  • 传输协议选型:STDIO vs SSE vs Streamable-HTTP
  • 让 Claude Desktop 找到你的 MCP Server
  • 进阶:@McpResource 暴露只读数据
  • 常见问题
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档