接手过企业系统集成的人,应该都有同感:业务侧天天喊着“让 AI 把活干了”,可真要把大模型接进内部系统,第一个挡路的往往就是那堆跑了好多年的遗留 API。命名规则五花八门就不说了,有的接口到今天还是 SOAP,返回结构有包 data 的、有直接裸返回的、有套 RPC 信封的,文档和代码早就各走各的路。这种环境里,别说让 Agent 自动调度,连写个稳定的客户端脚本都费劲。我这两年在企业内部做 AI 原生化改造,反复试验下来,最实用的解法就是用 MCP(Model Context Protocol)做标准化重构——把散落的遗留 API 包装成统一、可发现、可审计的 AI 原生接口中心。这篇就把整体思路、方案选型、落地步骤和踩坑记录完整讲一遍,适合正在做系统改造的架构师、后端负责人,以及所有想把手头老系统接入 AI 应用的同学参考。
1. 遗留 API 为什么成了 AI 集成的第一个瓶颈
在动手写 MCP 之前,先把问题定性清楚。我在不少团队里看到过同样的场景:业务方拿一张需求表,说“让 AI 帮我们查库存、看订单、发起流程”,真正落到 IT 这边,却发现根本没法定直接干。
1.1 先看三类典型的“碎片化”问题
第一类是接口契约碎片化。企业内部系统经过多年建设,接口风格通常不是一套体系。有的模块是标准 RESTful,有的还是老 SOAP 封装,有的走内部 RPC 框架,连调用的 SDK 都是特定语言版本的。更麻烦的是响应结构不统一:库存服务返回 {resultCode: 0, data: {stock: 100}},订单服务返回 {code: 200, message: "ok", bill: {...}},支付回调可能直接返回纯文本甚至 HTML。对于人写的代码,可以通过硬编码适配,但换一个调用方就得重新写一遍,时间一长就没人说得清全貌。
第二类是认证授权碎片化。同一个企业内部,可能同时存在 Basic Auth、API Key、OAuth2、单点登录等多种鉴权方式。AI 应用要访问三个不同系统,就得维护三套凭证和三套刷新逻辑。更头疼的是,凭证一旦放在 Agent 运行环境里,安全团队看到就想否决项目。我在一个项目里就因为这个问题,前后跟安全部门沟通了快一个月,最后用独立的只读服务账号才把方案落下来。
第三类是元数据缺失。很多老服务没有 OpenAPI 文档,或者文档停留在几年前。我们曾在一个订单服务里发现,实际响应字段比文档多出十几个,其中几个还是后来临时加的。对自动化代码来说,文档失真最多是调试麻烦;但对需要“理解工具用途”的 LLM 来说,描述错误可能让它用错参数,问题就严重了。
1.2 为什么不能直接让 AI 硬调这些老接口
有人会说,AI 应用不也是代码吗?直接发 HTTP 请求不行吗?表面上看行,实际操作中处处碰壁。
第一,LLM 需要的是“结构化工具定义”。模型不是靠读 HTML 文档来了解接口的,它依赖的是工具名、描述、参数 JSON Schema 这些机器可读的信息。老接口没有这套元数据,你必须先人工整理。而且不同 AI 平台对工具定义的格式各有一套——OpenAI 的 function calling、Anthropic 的 tool use、自研 Agent 又可能是另一套 schema。每个平台单独适配一遍,工作量直接翻倍。
第二,错误处理不标准。老接口的出参没有统一约定,有的用 HTTP 200 包业务错误码,有的用 HTTP 500 裸奔 HTML 页面。对同样一个查询,Agent 这次收到 {success: false},下次收到 {"code": 20400},再下次直接收到超时。它没法判断“到底成没成”,更不敢安全重试,因为不确定重试是否幂等。这种不确定性在传统代码里最多算个小坑,在 AI 自动决策的场景里就是致命的。
第三,安全与审计缺失。AI 调用具备不确定性,同一个用户问题,模型可能触发完全不同的工具序列。如果写操作没有审计和审批,出了问题连回放都做不了。这是我在不少项目里看到最容易被低估的地方,往往等到线上出了事故才回过头来补。
1.3 现有的集成方案为什么不够
常规思路是套一个 API 网关,把老接口统一代理一遍。网关确实解决了路由、限流、熔断,但它是给“人写的代码”用的。AI 应用需要的不仅仅是网络通路,而是“我该怎么描述这个工具、传什么参数、怎么处理返回”的完整契约,网关给不了这一层。
另一条路是每个 AI 应用各自写一套接口封装 SDK。团队 A 的 Agent 封装一遍,团队 B 的 ChatBot 再封装一遍,前端 AI 工具又封装一遍——结果就是碎片化从 API 层扩散到了 AI 应用层,每次接入都重复造轮子。所以问题就很清楚了:我们缺的不是又一个封装,而是统一的“AI 工具协议层”。MCP 恰好补的就是这一层。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. MCP 到底解决了什么:从“一家一套对接方式”到“一种协议”
MCP 全称是 Model Context Protocol,最初由 Anthropic 提出并开源,现在已经是不少 AI 应用接入外部工具的事实标准接口。你可以把它理解成 AI 领域的“USB 接口”:设备厂商不用关心你的电脑是 Windows 还是 Mac,只要都支持 USB,插上就能用。
2.1 MCP 的核心概念拆解
一个 MCP 系统里有三个角色:
- MCP Host:AI 应用本身,比如 Claude Desktop、Codex CLI、Cursor、自研的 Agent 框架。它负责管理连接、持有对话上下文、决定什么时候调用工具。
- MCP Client:Host 内部的协议客户端,负责与 Server 建立连接、发送请求。
- MCP Server:暴露工具、数据、提示词的服务端进程或服务。一个 Server 可以对应一个遗留系统,也可以对应一个业务域。
MCP Server 对外提供三种能力原语:
- Tools:可执行的函数,每个工具包含 name、description、inputSchema,AI 应用通过
tools/call来调用它。这是接入遗留 API 最常用的一类。 - Resources:可读取的数据,用 URI 定位,类似 REST 里的 GET。适合暴露配置、报表、知识库内容。
- Prompts:可复用的提示词模板,本质是让 AI 应用在特定场景下使用固定的指令和上下文。
协议底层走的是 JSON-RPC 2.0。一个 MCP Server 启动后,Client 会先发 initialize 请求完成握手,然后发 tools/list 拉取工具清单,需要时再发 tools/call 触发某个工具。整个过程对 AI 应用来说是完全标准的,不管 Server 背后接的是 SAP、Oracle 还是自研老系统。
2.2 传输方式:stdio 和 Streamable HTTP 怎么选
MCP 的传输层经历了一个演进过程。最早普遍用的是 stdio,也就是 MCP Server 以本地子进程方式运行,通过标准输入输出与 Client 通信。这对本地 CLI 工具(比如 Codex、Claude Desktop)没问题,但在企业场景里,AI 应用和 Server 可能不在同一台机器,就得靠网络通信。
目前官方主推的远程方案是 Streamable HTTP。它把 MCP 的 JSON-RPC 消息封装成 HTTP 请求,支持 POST + SSE 的流式响应,一个端点同时处理请求和响应流,部署起来比传统 SSE 简单。早期那种单独开一个 SSE transport 的方式官方已经不太推荐新项目用了,存量项目在迁移时要注意协议版本兼容。
企业里我通常建议用 Streamable HTTP。这样做的核心原因是运维体验好:MCP Server 可以部署在内部网络,通过网关统一暴露,链路走常规的负载均衡、TLS 终止、日志采集,不需要为 MCP 单独设计一套运维体系。团队现有基础设施都能直接复用。
2.3 skill 和 MCP 到底有什么区别
这个话题最近问的人特别多。很多同学看 Claude 出了 Agent Skills,又看大家在接 MCP Server,分不清两者是不是一回事。我尽量说得简单点。
Skill(以 Claude Agent Skills 为例)本质上是一组“怎么做事”的知识包:一个 SKILL.md 对应一个技能,里面包含任务拆解步骤、示例、脚本、约束条件,告诉模型在遇到某类任务时“按这个套路干”。它解决的是“脑子怎么转”的问题。
MCP 解决的是“手能伸到哪”的问题:模型要查数据库,MCP Server 给一个 query_database 工具;模型要画图,MCP Server 给一个 render_chart 工具。它定义的是能力边界和调用协议。
两者不但不冲突,还能配合使用。举个实际例子:你在 Skill 里定义一个“月度经营分析”流程,要求模型先调用 MCP 的销售数据查询工具拿数,再做清洗和去重,最后调用图表工具出图。Skill 负责把流程串起来,MCP 提供原子能力。
顺便说一句,经常有人问“mcp tools inputschema 是否支持类型嵌套”,答案是支持。JSON Schema 本身支持嵌套对象和数组,你可以在工具的 inputSchema 里定义复杂结构。不过我后面会讲,嵌套结构里如果有大量必填字段,模型填错参数的概率会明显上升,要谨慎设计。
2.4 MCP 和 API 网关、ESB 是不是重复建设
不是。ESB 和 API 网关解决的是服务间的消息路由、协议转换、限流熔断;MCP 解决的是“AI 应用如何用统一协议发现和调用工具”。这两层可以共存:MCP Server 在多数情况下只是薄薄的一层适配器,它收到 Agent 的 tools/call 请求后,内部还是要通过 API 网关转发到遗留服务。
所以你不需要推倒现有的服务治理体系,而是在现有体系之上加一层“AI 工具协议层”。这一点想通了,后续架构设计就不会跑偏。很多人一听到新协议就以为老基础设施要推翻重建,实际落地下来,MCP 适配层占用的改动量很小,大部分时候就是一个几十行的包装函数。
3. 三种把遗留 API 接入 MCP 的实战路径
接下来是大家最关心的部分:到底怎么把已有的老接口变成 MCP Server?我基于实际项目经验,整理了三种路径,覆盖了多数情况。
3.1 方案一:用 FastMCP 写一层“薄代理”(最快落地)
先说最简单、也最通用的方式:写一个 MCP Server,内部去调遗留 API,把返回结果原样再包一层。这种“薄代理”适合老服务代码完全改不动、接口文档不完整、或者新老系统语言不一致的场景。
我常用的是 Python 生态的 FastMCP。先装库:
bash复制pip install fastmcp
然后写一个最简 Server:
python复制# server.py
from fastmcp import FastMCP
import httpx
mcp = FastMCP("inventory-adapter")
LEGACY_INVENTORY_URL = "https://inventory.internal.example/stock"
@mcp.tool()
def query_stock(sku: str, warehouse: str = "default") -> dict:
"""查询 SKU 在指定仓库的实时库存。
Args:
sku: 商品SKU编码,例如 "SKU-2024-001"。
warehouse: 仓库编码,缺省为 "default",例如 "east-1"。
"""
resp = httpx.get(
LEGACY_INVENTORY_URL,
params={"sku": sku, "wh": warehouse},
timeout=10,
)
resp.raise_for_status()
return resp.json()
if __name__ == "__main__":
mcp.run(transport="streamable-http")
启动之后,这个 Server 就会暴露一个 query_stock 工具。FastMCP 会根据函数签名和 docstring 自动生成 inputSchema,这就是 AI 应用拿到的结构化契约。
这里有个很多人忽略的细节:函数名和参数名会成为工具的对外名称和参数名。老接口里如果参数叫 wh、sk,你最好在适配层改成具有业务含义的名字(warehouse、sku),并在 docstring 里写清楚示例值。模型对“这个名字看起来是什么意思”的依赖程度,远超很多人想象,我后面会再展开。
3.2 方案二:从 OpenAPI/Swagger 自动生成 MCP Server
如果你的遗留服务有相对完整的 OpenAPI/Swagger 文档,可以考虑用现成工具自动生成 MCP Server,减少手工编码量。
目前社区里比较常用的做法是用 Node 生态的 openapi-mcp-server,或者参考一些 REST 转 MCP 的项目,比如 @krzko/rest-api-mcp-server。基本思路都是解析 swagger.json 或 openapi.json,把每个 operation 映射成 MCP tool,再通过配置的 baseURL 发起真实 HTTP 请求。
具体步骤大概是:
- 获取老服务的 OpenAPI 描述文件;
- 按工具要求配置 baseURL、默认请求头、鉴权凭证;
- 启动 MCP Server,检查
tools/list返回的工具清单; - 用 MCP Inspector 或简单 Client 做冒烟测试。
我自己用过这类工具,最大的问题是自动生成的工具描述普遍“营养不良”。老接口的操作 ID 经常是 getOrderInfo_1,参数可能是 a1、a2 这种无意义缩写。自动生成后,LLM 既猜不透参数含义,也容易传错型。所以我的建议是:自动生成只作为初稿,上线前一定要对核心工具做二次润色,补充 description 和参数示例。如果老接口连 OpenAPI 都没有,也可以用代码注释或者网关配置反向生成一份,但工作量会大一些。
3.3 方案三:让 Spring Boot 老业务“零成本”接入 MCP
热搜里有个很具体的问题:“如何让现有 Spring 2.x 业务零成本接入 MCP”。这确实是 Java 存量系统很常见的诉求。
Spring AI 官方提供了一套 MCP Server 的 Boot Starter,支持把普通 Service 方法直接暴露成 MCP 工具。核心思路就是加一个依赖,然后在方法上打一个 @Tool 注解。
以 Maven 项目为例:
xml复制<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-mcp-server-webmvc</artifactId>
<version>1.0.0</version>
</dependency>
然后在业务类里加注解:
java复制@Service
public class OrderToolService {
private final OrderService orderService;
public OrderToolService(OrderService orderService) {
this.orderService = orderService;
}
@Tool(description = "根据订单号查询订单状态,订单号为纯数字字符串,例如 20250101001")
public OrderStatus queryOrder(String orderNo) {
// 直接复用原有业务方法,零改动
return orderService.queryByNo(orderNo);
}
}
Spring AI 启动时会扫描带有 @Tool 注解的方法,自动生成 MCP Server,并暴露标准的 initialize、tools/list、tools/call HTTP 端点。对已有业务代码来说,确实接近“零侵入”。
要提醒三点:一是不是所有方法都适合暴露,涉及写操作、敏感数据、内部状态变更的方法,不要无脑加注解;二是 Spring Boot 2.x 场景要确认 Starter 版本兼容,Servlet/WebMvc 的依赖选择和响应式版本完全不同;三是如果 AI 应用跑在浏览器端,需要提前配置 CORS,不然 tools/list 能通但 `tools/c
