夜雨聆风学习资料网

ARTICLE · 1158613

把 Java 接口变成 AI 工具,我只用了 3 步

把 Java 接口变成 AI 工具,我只用了 3 步

     点击上方蓝字关注我们   

上篇讲五层架构时,我留了个钩子:下一篇展开讲讲 MCP 实操,怎么把 Java 后端的 REST 接口发布成 MCP 能力。今天兑现。

先说这段研究是怎么开始的。团队提了个需求:让 AI 助手直接读本地代码仓库、查数据库、再调设计软件把界面改一版。听起来很酷,但真做起来才发现,每个 AI 工具都要单独写一套接入逻辑——同一个工具在 Claude 里能用,换到 Cursor 又得重写一遍。那是典型的 N×M 适配地狱:N 个 AI 应用 × M 个外部工具,每个组合都要写适配代码,维护成本随两端数量乘积上涨。

MCP(Model Context Protocol)解决的就是这个。它把 N×M 压缩成 N+M:工具方做一次标准化暴露,所有 AI 方都能复用。2024 年 11 月 Anthropic 开源 MCP,随后 OpenAI、Google、Microsoft 相继跟进。到 2026 年,MCP 1.0 进入 Stable 阶段,Java SDK 2.0.0 正式 GA,月 SDK 下载量突破 4 亿次,注册 Server 超 10 万个,财富 500 强里 60% 把它当成内部 AI 系统连接外部数据源的强制标准。

作为写了十多年 Java 后端的人,我盯着这套规范看了半天,悟出一个感觉:这不就是给接口加个"AI 版 OpenAPI"吗?本质跟 Spring Boot 把 Service 暴露成 REST Controller 一回事,区别在于"说给谁听"——以前说给浏览器,现在说给 LLM。

先理解它的"语言"。MCP 底层走 JSON-RPC 2.0,几个核心操作一眼能懂:tools/list(列出全部工具)、tools/call(调用某个工具)、resources/read(读一个资源)。你可以把 MCP 想象成 AI 界的"HTTP"——HTTP 定义了 GET/POST 怎么发,MCP 定义了 AI 怎么发现和调用工具。下面跟着我把最熟的订单查询接口,用 3 步变成 AI 能直接用的 MCP Server。

· · ·

01

加依赖、起服务、定义自己的头一个 Tool

和加一个 Spring Boot Starter 一样,官方 MCP Java SDK 2.0.0 的依赖就一个坐标:

<dependency>   <groupId>io.modelcontextprotocol.sdk</groupId>   <artifactId>mcp</artifactId>   <version>2.0.0</version> </dependency>

MCP 的核心抽象就三个,后端工程师看一眼就能对上号:Server 相当于 Controller 容器,Tool 相当于一个接口方法,Transport 相当于通信协议(选 Stdio 还是 HTTP)。除此之外,Server 还能暴露 Resource(URI 寻址的静态数据,类似 Spring Resource)和 Prompt(模板化的提示词)。

为什么说这事 Java 后端干起来有天然优势?因为企业里大量业务逻辑本来就在 Java 微服务里,订单、库存、CRM、支付,一套套都现成。MCP 允许这些服务零代码改动,只加一层暴露配置,就能让 AI 直接调用——Java 的强类型、连接池、事务这些工程能力,直接顺着协议透传给 AI。你不需要为了 AI 专门学 Python 写一套新服务。

定义一个 Tool 只需要四件套:name(工具名)、description(干什么用)、inputSchema(参数结构)、handler(执行逻辑)。拿订单查询举例:

var queryOrderTool = Tool.builder()   .name("query_order")   .description("根据订单号查询订单状态、金额和物流信息")   .inputSchema("{ ... JSON Schema ... }")   .handler((ctx, args) -> {     OrderDTO order = orderService.queryByNo(args.get("orderId"));     return ToolResult.success(JSON.toString(order));   })   .build();

真实的调用长这样——一个 tools/call 的 JSON-RPC 请求,name 指工具名,arguments 传参数,跟调 REST 接口没区别:

{ "jsonrpc": "2.0", "id": 2, "method": "tools/call",  "params": { "name": "query_order",     "arguments": { "orderId": "MT20260918001" } } }

再把 Tool 挂到 Server 上,选一个 Transport 启动。本地调试用 Stdio,对外服务用 Streamable HTTP(2.0 起推荐,SSE 已弃用)。到这一步,一个能对外提供工具的 MCP Server 就跑起来了。  

如果想偷懒,还有个更省事的路子:Spring AI 内置 MCP 适配器,能把 MCP Server 上的 Tool 自动转成 Spring AI 的 FunctionCallback,@Tool 注解和 MCP 可以共存。也就是说,你甚至不需要改业务代码,靠配置就能把微服务暴露给 AI。不过我建议先把 MCP 自己的协议栈摸清楚,再去用框架封装——出了问题才知道去哪查。

但注意——能跑和好用之间,差着一个 inputSchema 的距离。

· · ·

02

把 inputSchema 写到 AI 看得懂

这是 Java 后端最容易踩的坑。写 REST 接口时,参数注释是给人看的,写得随意点问题不大;但 Tool 的 inputSchema 是给 LLM 看的——它决定了模型"能不能生成合规的调用参数"。参数定义含糊,AI 就会自由发挥。

举个真实例子。"退货原因"这个字段,如果只标成 string,AI 会把"质量不好""发错了""7天无理由"什么值都往里塞,下游转换逻辑就崩。改成显式枚举之后,情况完全不一样:

"reasonCode": {   "type": "string",   "enum": ["DAMAGED", "WRONG_ITEM", "NOT_AS_DESCRIBED"],   "description": "退货原因,枚举值:破损/错发/与描述不符" }

我们团队做过一次统计:把约束写进 enum 和 description 后,非法参数的比例从 12% 降到 1% 以下。这不是玄学——LLM 的"听话程度"取决于你给它的定义有多精确。description 里写"枚举值:破损/错发/与描述不符",比写"退货原因"四个字好用得多。有团队还给每个字段加了示例值(example),准确率还能再上一个台阶。

顺带澄清一个高频混淆:MCP 和 Function Calling 不是一回事。Function Calling 是模型层的"要不要调"——它决定 LLM 输出结构化的函数调用意图;MCP 是协议层的"怎么标准调"——它规定工具怎么被描述、发现和执行。可以把 Function Calling 当成模型能力,把 MCP 当成工具界的"普通话",两者配合使用,不是二选一。

维度
REST 参数注释
MCP inputSchema
阅读对象
人(前端/对接方)
LLM(模型自主理解)
约束方式
代码强校验兜底
Schema 语义 + enum 显式约束
描述要求
能读懂即可
得带业务语义和示例值
写不好后果
联调返工
AI 瞎传参数,业务链路崩

还有个实战教训:不要把 Spring Boot Controller 直接当 MCP 能力暴露。给一个客户做风控时见过,他们直接把 Controller 映射成 Tool,任意 HTTP 请求都能触发业务逻辑,参数校验形同虚设。正确做法是 MCP 这层套薄沙箱:入参校验、调用方身份核对、执行超时兜底。某银行的风控 Agent 甚至在沙箱里嵌了 AST 分析器,扫一遍传入的脚本,拦掉 os.system() 这类危险调用。这些在传统后端都是常识,但换了技术栈,新手很容易漏。

给 AI 的工具描述,要像给新人写接口文档一样认真。你含糊,它就替你"自由发挥"。

· · ·

03

选对传输方式,守好鉴权三红线

传输层就三个选项:Stdio(本地进程间 RPC,Claude Desktop、Cursor 这类 IDE 用)、SSE(HTTP 远程,已弃用)、Streamable HTTP(服务端推荐)。做企业服务端,直接用 Streamable HTTP,配一个 /mcp 端点即可:

McpConfigurer.builder()   .transport(new StreamableHttpServerTransport("/mcp"))   .exposeTools(List.of("query_order", "create_refund"))   .build();

但端口一旦上了公网,安全就从"要不要做"变成"怎么做"。MCP 规范强制 HTTP 传输走 OAuth 2.1 + PKCE,Token 受众绑定、短期时效、禁止透传下游。我把最常见的三个翻车场景拎出来:

红线一:裸奔。远程 Server 在公网开放了 execute_db_sql,零鉴权,攻击者扫到端口直接调用,等于把数据库敞开。

红线二:共享 Token。全公司共用一个静态 API Key,分不清是"财务部 Agent"还是"实习生的测试 Agent"在调高危工具。

红线三:Token 透传。把客户端 Token 直接转发给下游 API,一旦下游泄露,主凭证跟着全灭。

合规的做法并不复杂,都是微服务里玩剩下的:OAuth 2.1 发短期 Token(建议 1 小时),配合 PKCE;RBAC 按角色分权限,每个工具声明允许的角色集合,越权直接 403;日志一律脱敏,手机号、身份证号这类敏感字段记成掩码,只留大小和结果。有团队在 MCP 网关层做了这套,工具越权调用几乎全部拦截。

Client 侧调用也很简单,把 Server 地址指过去就能连上:

Client.builder()   .name("java-spring-host")   .transport(new StreamableHttpTransport("https://mcp.example.com/mcp"))   .build(); client.callTool("query_order", Map.of("orderId", "MT20260918001"));

  Client 侧调用也很简单,把 Server 地址指过去就能连上并调用:       

Client.builder()   .name("java-spring-host")   .transport(new StreamableHttpTransport("https://mcp.example.com/mcp"))   .build(); client.callTool("query_order", Map.of("orderId", "MT20260918001"));

2026-07-28

MCP 新规范移除会话握手,改为无状态Server 可以直接跑在 AWS Lambda / 云函数上

顺带说一个对后端特别友好的新变化:2026-07-28 版本把 MCP 从有状态改成了无状态。以前每次交互都要 initialize 握手、维护 Mcp-Session-Id,横向扩展得配粘性会话;现在每个请求独立自包含,协议版本自带协商。这意味着 MCP Server 可以直接上 Serverless(AWS Lambda、云函数),K8s 里做无粘性负载均衡,弹性伸缩的门槛一下子没了。          

  做多了分布式系统的 Java 后端,应该懂这个演进有多舒服。

还有工具粒度值得单独拎出来说。工具太细,一个"查订单"拆成"查状态""查物流""查金额"三个,Agent 得来回调三次,决策负担翻倍;工具太粗,一个"处理订单"塞进十个操作,Agent 又搞不清你要哪个。实践下来的标准是:一个工具对应一个完整的业务动作,"查询订单"是一个工具,"生成退款"是另一个工具,粒度跟用户能说出口的指令对齐。工具超过 20 个就按业务域分组或加路由,别让 Agent 在几十个选项里做选择题。

调用失败的重试策略,也跟微服务里的套路一致:把错误分成两类——可重试(HTTP 503、429、连接超时)和不可重试(400、401、404)。可重试的用指数退避加抖动:1 秒、2 秒、4 秒,上限 30 秒,再给每次等待加 ±20% 随机值,避免惊群效应;不可重试的直接抛异常,别浪费重试预算。头一版 Server 时我把 400 当临时错误重试了三次,下游日志刷屏,用户还看到了三次重复提示——这个学费交得挺值。

最后提醒一件容易被忽略的事:日志和安全审计。MCP Server 一旦被 AI 反复调用,你得能回答"上周谁调了我们的哪个工具"。建议每个工具调用都记一条结构化日志:trace_id、server_id、tool_name、latency_ms、输入输出大小、结果状态。参数内容默认别落盘——里面可能有手机号、身份证这类敏感信息,真要查问题时再按需采样,还要做脱敏。可观测性和日志,是 MCP Server 从"能跑"走向"能运营"的分水岭。

上线部署也有讲究,别把它当普通单体扔上去就完事:至少 2 个副本走负载均衡;暴露 /health 和 /ready 两个探针,/ready 在初始化完成前不返回 200;收到 SIGTERM 后先等正在执行的 Tool 调用收尾(最多 10 秒),再拒绝新请求优雅退出;用资源配额把单个 Server 的 CPU 内存限制住,防止某个工具死循环拖垮整个集群。这些和你们线上微服务的做法一模一样,只是换了个组件名。

  上线部署也有讲究,别把它当普通单体扔上去就完事:至少 2 个副本走负载均衡;暴露 /health 和 /ready 两个探针,/ready 在初始化完成前不返回 200;收到 SIGTERM 后先等正在执行的 Tool 调用收尾(最多 10 秒),再拒绝新请求优雅退出;用资源配额把单个 Server 的 CPU 内存限制住,防止某个工具死循环拖垮整个集群。这些和你们线上微服务的做法一模一样,只是换了个组件名。

· · ·

04

上线后的收益,和一句大实话

按这套流程,我们把订单、库存、CRM 三个核心服务暴露成了 MCP Server,Cursor 和内部 AI 助手共用同一套 Tool。效果很直观:不用再手写 N×M 适配层,开发效率提升 60% 以上;新工具上线周期从一周缩到一天。身边一个 500 强企业的办公助手更夸张,接了 JIRA、Confluence、ERP、HR 等 20 多个系统,日均调用 10 万次,工具调用成功率 99.5%。

头一行收益:AI 工具复用率拉满,Cursor 和内部助手共享一套 Tool

第二行收益:Java 微服务零代码改动,加 MCP 配置即暴露给 AI

第三行收益:LLM 厂商更新接口,不再影响你的工具层

但我也说句实在话。MCP 生态仍在快速迭代,成熟度不及 REST:工具粒度怎么定、跨组织怎么审计、SDK 版本怎么跟,都需要花时间磨。而且工具数量上来之后,Agent 的决策负担会指数级上升——业内普遍建议工具超过 20 个就得分组路由。Gartner 也预测,到 2026 年底 40% 的企业应用会内置任务型 Agent,75% 的 API 网关供应商会支持 MCP。方向没问题,但别一上来就把整个系统都暴露出去,挑两三个核心价值接口起步最稳。

如果你在团队里负责这事,还可以再往前想一步:企业里 Server 一多,就得有个"注册中心"来管——类似你们熟悉的 Nacos 或 Eureka。MCP Registry 就干这个:统一登记所有 Server 的元数据(Schema、能力、SLA),按标签路由(stable 进生产、beta 进测试),记录每个 Server 的调用次数做成本归因。社区已经有不少开源的 MCP 网关方案,Spring AI Alibaba 的 MCP Gateway 也接进了 Nacos。规模上来之前先把这层想好,能省很多回头路。

如果你也在纠结怎么入手,我的建议和上篇一样:先别急着啃大模型原理,把自己手头那个业务接口用这 3 步发布成 MCP Server,在 Claude Desktop 或 Cursor 里接进去用一天。体感比看十篇教程都实在。

· · ·

这 3 步走完,你的 Java 接口就正式进了 AI 的"工具箱"。下一篇计划拆 MCP Server 的可观测性——如何回答"上周谁调了我们的哪个工具",包括调用审计、延迟指标和日志脱敏的设计。

觉得有用,点个关注

我是"二里弄",一个写了十多年代码、正在 AI 浪潮里摸爬滚打的技术人

下篇见

相关学习资料