1. 先聊聊这次“测试发布 MCP”到底在搞什么
最近一直在折腾 MCP(Model Context Protocol,模型上下文协议),前前后后试了好几个方案,这次要把一个项目里的 REST 接口发布成 MCP Server,专门做了一次完整的测试发布流程。标题里的“mcp5”其实就是我第五轮测试发布的记录——前四轮踩了不少坑,这一轮终于把流程跑顺了,所以想把整个经验和踩坑过程整理出来,给准备入坑 MCP 的朋友一个参考。
先给还不熟悉的朋友简单说下背景。MCP 是 Anthropic 在 2024 年底提出的一套开放协议,核心目的就一个:让 AI 模型能够标准化地调用外部工具和数据源。你不需要为每个 AI 应用写一套定制化的工具接入代码,只要把工具封装成 MCP Server,所有支持 MCP 的客户端(Claude Desktop、各种 IDE 插件、自研 Agent 等)就能直接调用。业内有个比喻特别形象——MCP 之于 AI 工具接入,就像 USB 接口之于外设。以前接一个设备要单独写驱动,现在统一接口,插上就能用。
这个内容适合谁看?三类人:
- 后端开发,手上有现成的接口服务,想让 AI Agent 能直接调用的;
- 正在选型,纠结 MCP Server、Tool、Agent Skill 到底用哪个的同学;
- 以及已经配过 MCP 但在发布、调试环节反复出问题的朋友。
这一轮我主要做了三件事:把已有的 Java REST 接口包装成 MCP Server,用官方测试方式验证服务连通性,再把服务接到客户端里做真实调用。整个流程跑下来,我对 MCP 的理解比之前只看文档要深得多。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. MCP 协议核心概念拆解——先搞清楚“发布的是什么”
2.1 MCP 的三大核心原语:Tools、Resources、Prompts
在动手发布之前,一定要把 MCP 协议的基本模型吃透。MCP 协议里定义了三种核心原语,分别是 Tools(工具)、Resources(资源)和 Prompts(提示词模板)。这三者对应的是不同的 AI 交互场景:
-
Tools:可执行的函数,模型根据用户需求决定是否调用。比如“查询订单状态”“创建工单”“发送消息”这类有副作用或需要实时计算的操作。Tool 是 MCP 里用法最广、也是大家最常接触的一类。
-
Resources:只读的数据,通常是文件内容、数据库查询结果、API 返回等。模型不会主动去“执行”资源,而是把它当作上下文的一部分来读取。比如把一个项目的文档目录暴露为 Resource,AI 回答问题时就能自动引用这些内容。
-
Prompts:预定义的提示词模板,用户可以手动触发,也可以由模型根据对话自动匹配。主要用于把重复性的任务流程标准化。
这一轮我发布的 REST 接口属于典型的 Tools 场景——接口本身有输入输出,调用后会产生结果返回。理解这一点很重要,因为不同原语的发布方式和客户端体验差异很大。
2.2 MCP 的传输方式:stdio 与 Streamable HTTP
发布 MCP Server 之前,还必须搞清楚传输方式的选择。MCP 协议目前主流的传输方式有两类:
stdio 模式:MCP Server 以子进程方式启动,客户端和 Server 之间通过标准输入输出通信。这种方式配置简单,基本不需要考虑网络和鉴权,适合本地开发工具调用。但缺点也明显——Server 的生命周期和客户端绑定,客户端退出,Server 也就没了。
Streamable HTTP 模式:Server 以 HTTP 服务方式独立部署,客户端通过 URL 访问。这种方式适合远程服务、多客户端共享、生产环境部署。我这次测试发布的目标就是这种模式。
这里有个很容易踩的认知误区:很多人以为 MCP 只能配本地 stdio,看到“远程 MCP”就懵了。实际上,只要服务端实现了 Streamable HTTP 传输,远程访问完全没问题。现在 Claude Desktop、IDE 插件等主流客户端都同时支持这两种模式。
2.3 MCP 与 Tool、Agent Skill 的区别
最近关于“MCP 和 Agent Skill 有什么区别”的讨论特别多,我自己的理解是这样的:
- Tool 是 AI 应用里最底层的“函数”概念,可以是普通的内部函数、HTTP API 封装,也可以是 MCP 暴露出来的工具;
- MCP 是工具的一种标准化封装和传输协议,解决的是“工具怎么被发现、怎么被调用、怎么传参返回”的统一问题;
- Agent Skill(如 Claude Skills、Codex 的技能包)更侧重在工具之上叠加“使用方法和上下文知识”,比如告诉你这个工具适合什么场景、参数怎么填、返回怎么解析,本质是 Prompt + Tools + 工作流 的组合。
我的观点是:如果只是单一内部工具,直接写 Tool 函数就行;如果工具要被多个 AI 应用复用,或者要对外开放给其他团队,那就上 MCP;如果还希望 AI 在调用工具时具备更丰富的领域知识和决策逻辑,可以考虑把 MCP Server 作为承载层,再在客户端侧配合 Skill 使用。这轮测试发布,本质上就是在做“标准化承载层”。
3. 发布前的准备工作——工具选型与方案设计
3.1 语言与 SDK 选型:Java 是怎么做 MCP Server 的
我手头的接口是 Java 写的,所以选型第一考虑就是 Java 生态的 MCP SDK。目前社区主流的 MCP SDK 有 TypeScript、Python、Java(官方 SDK)三套,Java 这边有两个选择:
- 官方 Java SDK(modelcontextprotocol/java-sdk):MCP 官方维护,支持同步和异步客户端,提供 Server 端的抽象。基于 Spring Boot 的项目接入比较顺。
- Spring AI Alibaba 的 MCP 扩展:如果项目已经用了 Spring AI Alibaba,它提供了对 MCP 协议的开箱即用支持,包括 MCP Server 的自动配置和客户端接入。热词里提到的“spring ai alibaba 如何使用别人提供的 MCP 服务”就是这个场景。
因为我们的项目本身就有 Spring Boot 基础,我最终选了 Spring AI Alibaba 的方式。原因有三个:一是和现有技术栈融合度高,不用单独起一个 MCP 进程;二是它把 MCP Server 的声明、注册、鉴权都封装好了,业务代码侵入小;三是后续如果要接入自家 Agent,链路会短很多。
这里补充一个经验:不要一上来就追求“纯手写协议层”。MCP 规范迭代速度很快,手写协议解析容易跟不上版本变化。用官方 SDK 或成熟框架做标准封装,把精力留在业务接口适配和部署调优上,才是正确的投入方向。
3.2 设计 MCP Server 的工具骨架
发布之前,我画了一张工具清单,明确哪些接口要暴露给 AI、每个工具的名称、描述、输入参数、输出格式。这一步特别关键,原因在于 MCP 的 Tool 描述不是给人看的,是给模型看的。模型通过 description 决定要不要调用这个工具,通过参数 schema 决定传什么值,描述写得不清楚,再好的接口 AI 也不会用、不会用对。
我设计的工具骨架大致如下:
| 工具名 | 对应 REST 接口 | 描述(给 LLM 看) | 主要参数 |
|---|---|---|---|
| query_order | GET /api/order/ | 根据订单ID查询订单详细信息,返回订单状态、金额、商品列表 | orderId: string |
| create_ticket | POST /api/ticket | 创建一条新的工单记录,需要提供标题、描述和优先级 | title, description, priority |
| get_user_info | GET /api/user/info | 根据用户ID查询用户基本信息,包含昵称、等级、积分 | userId: string |
工具名的命名规范我建议统一用“动词_名词”,query、create、update、delete 这类动词开头,后面跟操作对象。这样模型在理解时成本最低。描述里一定要写清楚“什么场景用、输入怎么填、拿到结果后意味着什么”。
3.3 发布方式对比:本地直连还是远程部署
我再多说一句发布方式的选择。MCP Server 的“发布”在当前生态里有两种理解,一种是本地配置发布,在客户端里加一行配置指向本地启动的 MCP Server;另一种是服务端部署发布,把 MCP 能力做成一个可公网访问的 HTTP 服务。我这次做的是后者。
为什么坚持做远程部署?因为 MCP 最大的价值在于“一处封装、多处接入”。如果你的 MCP Server 只能跑在本地,那它就只能服务你这一台电脑上的几个客户端,没法被团队共享。一旦支持远程访问,无论是 CI/CD 工具、监控告警 Agent,还是微信机器人、办公套件,只要支持 MCP 协议,都能接入你这套能力。这才是 MCP 真正解放生产力的地方。
远程部署涉及的东西在配置那一节我会详细展开,这里先给提醒:认证鉴权一定要考虑。内网环境可以简单点,走内网地址;如果服务要跨网络访问,建议在 MCP Server 前面加一层网关,用 API Key 或 OAuth 做统一鉴权。
4. 实操过程:将 REST 接口发布为 MCP Server 的完整流程
4.1 初始化项目与引入依赖
我以 Spring Boot 项目为例。第一步是在 pom.xml 里引入 Spring AI Alibaba 的 MCP Server 相关依赖:
xml复制<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-mcp-server</artifactId>
<version>1.0.0-M3.1</version>
</dependency>
<!-- 使用默认的 WebMVC 传输(Streamable HTTP) -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-mcp-server-webmvc</artifactId>
<version>1.0.0-M3.1</version>
</dependency>
这里有个版本匹配问题,我第一轮就栽在上面。Spring AI Alibaba 的 MCP Starter 和 Spring Boot 版本需要兼容,建议直接用官方文档推荐的 BOM 版本组合,不要自己乱搭。我第一次用了比较新的 Spring Boot 3.4,结果和 Starter 里的自动配置冲突,启动直接报错,排查了好一阵子。
4.2 声明工具方法:用注解暴露业务接口
写好依赖后,核心就是声明工具。在 Spring AI Alibaba 的模型里,你只需要写一个普通 Bean,然后在方法上打 @Tool 注解,框架会自动把方法暴露为 MCP Tool。看代码:
java复制@Component
public class OrderToolService {
private final OrderService orderService;
public OrderToolService(OrderService orderService) {
this.orderService = orderService;
}
@Tool(description = "根据订单ID查询订单详细信息,返回订单状态、金额、商品列表")
public OrderInfo queryOrder(@ToolParam(description = "订单ID,格式为数字字符串") String orderId) {
return orderService.queryById(orderId);
}
@Tool(description = "创建一条新的工单记录,需要提供标题、描述和优先级")
public TicketInfo createTicket(
@ToolParam(description = "工单标题,简要描述问题") String title,
@ToolParam(description = "工单详细描述,越长越有利于后续处理") String description,
@ToolParam(description = "优先级:low/medium/high") String priority) {
return ticketService.create(title, description, priority);
}
}
这里我想强调几个细节:
-
@Tool注解里的 description 是给模型看的,一定要说清楚“什么场景用这个工具”,而不是简单写“查询订单”。模型判断逻辑基本靠读这段描述,写得越具体,调用准确率越高。我自己写描述时有个习惯:先写功能(做什么),再写参数含义,最后写返回结果能用来干什么。 -
@ToolParam的参数描述同样重要。模型需要根据描述来生成参数值,如果你只写一个orderId,模型不知道传什么格式,容易传错。写成“订单ID,格式为数字字符串”,模型就会去对话上下文里找符合这个描述的值。 -
返回值最好是一个结构化的 JSON 对象,而不是自由文本。模型拿到结构化数据后,后续的推理和回答都会更准确。
4.3 配置文件的发布参数
写完工具方法,接下来在 application.yml 里配置 MCP Server 的暴露方式:
yaml复制spring:
ai:
alibaba:
mcp:
server:
enabled: true
name: order-mcp-server
version: 1.0.0
transport: webmvc
path: /mcp
配置说明:
name和version是 MCP 协议握手时返回的服务标识,客户端会用来展示来源。transport: webmvc表示走 Spring MVC 的 HTTP 传输,也就是 Streamable HTTP 模式。path是 MCP 端点的访问路径,配置成/mcp后,最终地址就是http://你的服务地址/mcp。
启动项目后,控制台会输出 MCP Server 端点信息。架上可以直接用工具类来验证连通性,后面我会讲。
4.4 用官方方式验证 MCP 端点——先别急着上客户端
我第一次发布时犯了个错:直接让 Claude Desktop 去连远程地址,结果握手失败,然后开始乱猜是地址问题还是鉴权问题——其实问题根本不在客户端侧,而是服务端返回的协议格式不对。
后来我学乖了,先把 Server 当成普通接口来调试,确认协议没问题再接客户端。MCP 的 Streamable HTTP 端点支持 initialize 握手请求,可以先用 curl 模拟一次完整握手:
bash复制curl -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"capabilities": {},
"clientInfo": {"name": "curl-test", "version": "1.0.0"}
}
}'
如果服务正常,你会收到一个包含 serverInfo 和 capabilities 的 JSON 响应,代表协议握手成功。之后再发 tools/list 请求,就能看到你通过 @Tool 注解暴露的所有工具列表:
bash复制curl -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'
返回里会包含每个工具的名称、描述和参数 schema。这两步过了,基本可以确定 MCP Server 侧没问题,再去配客户端,排查范围就小多了。
这里有个容易忽略的响应类型问题:MCP 的 Streamable HTTP 响应可能是 application/json,也可能是 text/event-stream(SSE),取决于客户端请求头里的 Accept。所以 curl 测试时两个类型都要带上,否则服务端可能返回 406。
4.5 在客户端配置远程 MCP 地址
服务端验证通过后,就可以接客户端了。以 Claude Desktop 为例,在配置文件 claude_desktop_config.json 里添加:
json复制{
"mcpServers": {
"order-mcp": {
"type": "http",
"url": "http://你的服务地址/mcp"
}
}
}
如果你用 IDE 插件(如 Cline、Continue、Trae 等),一般都有 MCP 管理界面,直接用 http://你的服务地址/mcp 添加即可。重点是把 type 指定为 http,很多客户端默认按 stdio 解析,不写 type 就会尝试本地启动子进程,导致链接失败。
4.6 真实调用验证:让 AI 完成一个完整任务
我在客户端里做了一次完整验证,让 AI 完成“查询订单号 20250713001 的状态,并说明订单金额”——AI 正确识别出需要调用 query_order 工具,自动提取了订单号,调用接口拿到订单信息,然后根据返回结果组织成了自然语言回答。整个调用链路能跑通,说明:
- 工具发现(tools/list)正常;
- 工具调用(tools/call)正常;
- 参数传递和返回结果序列化正常;
- 模型正确理解了工具描述并按预期使用。
这一步走通之后,MCP 发布的核心流程就算是验证完成了。
5. 发布过程中高频踩坑与排查技巧实录
5.1 握手失败:服务端响应 406/415
这是我测试中出现最多的问题。406 基本都是 Accept 头没带对——客户端没有声明能接受 JSON 或 SSE 响应,服务端就拒绝返回。排查方式很简单,curl 加上 -H "Accept: application/json, text/event-stream" 再试。415 则是 Content-Type 不对,MCP 的 JSON-RPC 请求必须是 application/json。
5.2 工具调用返回大段报错:Bean 未注册或循环依赖
有一次我把工具类实现成一个内部类,结果 Spring 没有把它注册成 Bean,MCP Server 启动成功但 tools/list 里空无一物。排查时我先调了 tools/list,发现工具列表为空,再回头看代码,才发现类上只有 @Tool 而缺少 @Component,工具方法根本没有被扫描到。这里记住一个原则:先确认有没有,再确认能不能调用。
5.3 工具调用超时:接口响应时间过长
MCP 协议本身没有强制要求工具调用超时时间,但客户端通常有自己的超时机制。我测试时有个接口要跑 15 秒的关联查询,结果 AI 提示工具调用失败。这不一定是 MCP 配置问题,而是业务接口本身响应慢。解决思路有两条:一是优化接口逻辑,减少外部调用;二是增加线程池配置,不要让慢接口阻塞其他请求。我们最终把查询结果做了缓存,响应从 15 秒降到 1 秒以内。
5.4 “测试发布 mcp5”——发布版号管理的经验
标题里的 mcp5 其实是我自己的版本管理习惯。我每次发布测试都会记录一个版本号,不仅仅是为了区分迭代,更是为了回滚时能快速定位配置差异。比如第一版我用的协议版本是旧版的 2024-11-05,第三次发布时协议已经迭代,新版客户端对旧协议版本兼容性变差,导致部分工具调用不稳定。后来我把协议版本统一指定为服务端实测支持的版本,并把这个版本固定在发布的配置模板里,问题就解决了。
一个小建议:发布 MCP Server 时,把协议版本、SDK 版本、传输方式、认证方式都记录在服务根目录的 README 里,方便自己和团队后面排查。
5.5 常见问题速查表
| 现象 | 可能原因 | 排查命令 / 操作 |
|---|---|---|
| 连不上 MCP 地址 | 服务未启动 / 网络不通 / 路径错误 | curl -X POST 地址 -d '{"jsonrpc":"2.0","method":"initialize"}' |
| 握手 406 | Accept 头未声明 | 带上 Accept: application/json, text/event-stream |
| tools/list 为空 | 工具类未注册 / 扫描路径不对 | 检查类是否有 @Component,方法是否有 @Tool |
| 调用超时 | 业务接口慢 / 客户端超时短 | 优化接口、调缓存,或调整客户端超时配置 |
| 中文参数乱码 | 编码问题 | 确保服务端和客户端均使用 UTF-8 |
| 升级后不兼容 | 协议版本或 SDK 版本变化 | 查看服务端日志,确认 initialize 会话版本 |
6. MCP 生态扩展:发布之后还能接哪里
6.1 不只是聊天客户端:IDE、Agent、办公场景都能接
MCP 发布完以后,同样一套接口可以接入多种消费端。我先说几个我实际验证过或观察到的场景:
- IDE 插件:在 Cline、Continue、Trae 这类 IDE 的 MCP 配置里添加远程地址,AI 编程助手就能直接调用你发布的工具。适合在写代码时让 Agent 查询内部系统信息、创建任务、执行部署脚本。
- Agent 工作流:企业里如果用自研 Agent 做自动化运维或客服问答,MCP 可以作为标准工具接入层,外部团队实现 Agent 时只需要按 MCP 规范来适配,不需要理解你的内部 API。
- 设计协作工具:热词里有不少和 Figma、蓝湖相关的 MCP,这类主要是设计稿信息同步到开发工具。如果你的业务里有类似跨工具数据传递的需求,值得参考这个思路。
6.2 关于“MCP 工具市场”和生态分发
有朋友在问“MCP 工具市场在哪里”,这个问题反映出大家对 MCP 分发的关注。目前 MCP 生态还没有形成类似应用商店那样统一的工具市场,但已经有一些方向的探索:
- 客户端内置市场:部分 IDE 插件内置了 MCP 服务器列表,相当于官方筛选过的工具集合。
- 代码托管平台索引:热词里出现 gitee、GitHub 相关的 MCP 资源,很多 MCP Server 是以开源项目方式分发,通过仓库地址分享配置。
- 企业内部市场:越来越多团队会自建一个 MCP Server 注册中心,把内部工具统一登记、版本管理、权限控制。
我个人的看法是:短期内 MCP 的分发还会以“配置 URL 或命令”为主,真正统一的市场形态还需要时间。但对开发者来说,现在把工具做成 MCP Server 是明确的趋势,早做早受益。
6.3 如何评估是否要把接口发布为 MCP
最后聊聊方法论。不是所有接口都适合立刻发布成 MCP,我建议从三个维度判断:
- 复用性:这个接口是否会被多个 AI 应用或团队复用?只服务一个内部脚本,用普通函数就好;要开放给多方,MCP 值得。
- 标准化成本:要不要统一工具命名规范、参数描述和返回结构?规范做得不到位,调用质量会打折扣。
- 安全边界:暴露给 AI 的能力是否有风险?AI 调用工具是动态的,鉴权、限流、审计都要设计好,尤其写操作类的工具要谨慎。
我这轮测试发布选择的是查询类接口为主,创建类接口单独做了权限校验,就是基于这个考虑。
7. 最后再分享几个我今天总结的小经验
MCP 这个领域现在变化很快,文档、SDK、客户端支持都在持续迭代,如果你照着网上的旧教程配置遇到问题,大概率是版本不匹配,而不是你操作有误。解决这类问题最好的办法是:先看客户端官方文档对 MCP 配置的说明,再看服务端 SDK 的 Release Notes,最后才是去社区搜答案。
我个人的体会是:测试发布 MCP 的核心不是“把服务跑起来”,而是把“工具被模型正确理解和调用”这一步跑通。当你看到 AI 通过你的工具描述,自动填充参数、正确解析返回结果、甚至能在返回数据基础上做进一步推理的时候,你才会真正理解 MCP 为什么被这么多人看好。
如果你正准备把一个 REST 接口发布成 MCP Server,我建议你也按这个顺序走一遍:先确认核心原语类型,再选 SDK 封装工具,接着用 curl 验证握手和 tools/list,最后才接客户端做真实调用测试。这套流程虽然看起来多花几分钟,但能帮你省下大量在客户端和服务端之间来回猜问题的时间。
按这个思路去试,你大概率不会再被 MCP 发布的第一道坎拦住。
