ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

MCP协议是什么?Java后端五分钟搞懂Model Context Protocol,把自己变成LLM的工具

MCP协议是什么?Java后端五分钟搞懂Model Context Protocol,把自己变成LLM的工具 MCP的本质是LLM调工具的标准化协议。对Java后端来说开发MCP Server就是写Spring Bean加​​Tool​​注解简单得很。难点在协议理解、传输模式选择、客户端适配、踩坑处理。写过Spring AI的Function Calling是不是觉得LLM调工具这事儿已经搞定了我去年也是这么想的。直到Anthropic推了MCPCursor、Claude Desktop、Windsurf全跟进我才反应过来--Function Calling是能调MCP是标准地调。这俩不是替代关系是层次不同。这篇文章讲Java后端怎么开发MCP Server让自家业务系统变成Claude/Cursor能直接用的工具。适合正在搞AI工程化的Java开发读完能跑通第一个MCP Server。MCP到底是个啥Model Context ProtocolAnthropic 2024年11月推出的开放协议。说白了就是LLM与应用系统之间的通信标准。后端视角理解MCP之于LLM相当于JDBC之于数据库。JDBC让Java代码用统一接口操作不同数据库MCP让LLM用统一协议调用不同应用系统。一次开发MCP Server所有支持MCP的客户端Claude Desktop、Cursor、Windsurf、Continue都能用你的工具。协议层基于JSON-RPC 2.0三大原语Tools工具调用、Resources资源读取、Prompts提示词模板。传输层两种stdio本地进程通信和SSE/HTTP远程通信。我的体感是MCP不是又一个Function Calling它是把LLM调工具这件事协议化、标准化。以前每家LLM厂商自己定义工具调用格式OpenAI一套、Anthropic一套、Google一套开发者要适配多次。MCP出来后写一次MCP Server所有客户端通吃。为啥需要MCPFunction Calling的问题很明显。第一每个LLM厂商格式不同OpenAI的tools参数结构和Anthropic的tool_use完全两套业务代码要写多份适配。第二工具定义散落在应用代码里没法复用--A项目写的查订单工具B项目要重新写。第三工具调用和业务系统耦合没法独立部署、独立升级。MCP的解法是解耦。把工具能力抽成独立的MCP Server业务系统专注于实现工具逻辑LLM客户端专注于调用。MCP Server就像微服务架构里的一个独立服务有自己的进程、自己的生命周期。举个具体场景。我们有个内部工单系统以前接Claude要写一套Function Calling接Cursor要再写一套接自家产品还要再适配。改成MCP Server后写一次三个客户端都能用维护成本降了三分之二。扯远了说回MCP本身。这玩意儿2024年底推出2025年生态爆发2026年Spring AI 1.0正式集成MCP Server支持。现在是上车的最佳时机--早了没受众晚了烂大街。Java开发MCP ServerSpring AI 1.0提供了spring-ai-mcp-server-spring-boot-starter开发MCP Server跟写普通Spring Bean一样简单。依赖就引这一个starter版本由Spring AI BOM统一管别单独指定版本号。application.yml关键配置spring: ai: mcp: server: name: order-query-server version: 1.0.0 # stdio模式适合本地客户端Claude Desktop # sse模式适合远程客户端Cursor云端 type: STDIO # 工具调用超时别设太长LLM等不起 request-timeout: 30s核心MCP Server代码用真实业务对象Service public class OrderQueryService { private final OrderMapper orderMapper; private final OrderStatusDecoder statusDecoder; public OrderQueryService(OrderMapper orderMapper, OrderStatusDecoder statusDecoder) { this.orderMapper orderMapper; this.statusDecoder statusDecoder; } /** * Tool注解标记这是MCP工具方法LLM能调用 * name和description必填LLM靠description判断何时调用 */ Tool(name query_order, description 根据订单号查询订单状态、金额、物流信息。输入订单号返回订单详情。) public OrderDetail queryOrder(ToolParam(description 订单号纯数字) String orderId) { // MCP工具方法本质就是普通Java方法LLM传入参数返回结果 // 别在这里写复杂逻辑LLM等不起超时30秒就断了 Order order orderMapper.selectById(orderId); if (order null) { throw new ToolExecutionException(订单不存在: orderId); } return OrderDetail.builder() .orderId(order.getId()) .status(statusDecoder.decode(order.getStatus())) .amount(order.getAmount()) .logisticsNo(order.getLogisticsNo()) .build(); } Tool(name refund_order, description 申请订单退款。需要订单号和退款原因。仅支持已发货前的订单。) public RefundResult refundOrder(ToolParam(description 订单号) String orderId, ToolParam(description 退款原因不超过200字) String reason) { // 退款涉及状态变更必须做幂等校验 // MCP工具被LLM重复调用是常事别假设只调一次 return refundService.process(orderId, reason); } }这里边有几个坑得提一下。Tool的description是LLM判断何时调用的唯一依据必须写清楚这个工具干什么、输入什么、输出什么。我一开始写得简略Claude老调错工具--用户问查物流它调了refund_order。后来把description改成根据订单号查询订单状态、金额、物流信息调用准确率从70%到95%。ToolParam的description同样重要LLM靠它理解参数含义。订单号要写纯数字否则Claude会把ORD-2026-001这种带前缀的字符串传进来数据库查不到。工具方法里别写复杂逻辑。MCP工具本质是LLM的外挂函数调用链路是LLM-MCP协议-Java方法-返回结果。中间任何一步慢了LLM会超时重试重复调用。30秒超时是我的经验值再长Claude Desktop会断开。异常必须用ToolExecutionException包装。我踩过坑直接抛RuntimeExceptionClaude收到的是空响应无法理解失败原因会一直重试。改成ToolExecutionException后错误信息会传回LLM它能看到订单不存在然后告诉用户。接入Claude Desktop测试MCP Server开发完要接到客户端测试。Claude Desktop是最常用的本地客户端。配置文件在claude_desktop_config.json位置Mac是~/Library/Application Support/Claude/Windows是%APPDATA%\Claude\。{ mcpServers: { order-query-server: { command: java, args: [-jar, /path/to/order-mcp-server.jar], env: { DEEPSEEK_API_KEY: sk-xxx } } } }配置完重启Claude Desktop在对话框里看到工具图标亮起就说明MCP Server连上了。实测效果。我问Claude帮我查下订单20260723001的状态它会自动调用query_order工具拿到结果后用自然语言回复您的订单已发货物流单号SF1234567890预计明天送达。整个过程用户无感知就像Claude自己知道订单信息一样。真香。第一次跑通的时候我在工位上乐了半天旁边同事以为我中奖了。踩过的坑坑一stdio模式下日志不能打到stdout。stdio传输用stdout传JSON-RPC消息日志打到stdout会污染协议流Claude Desktop直接断连。必须把日志打到stderr或文件。这个坑我查了两天日志一加就挂最后才反应过来。坑二工具方法不能有同名重载。MCP协议用方法名做唯一标识Java的重载在MCP层面会冲突。两个queryOrder方法一个传String一个传LongMCP Server启动报错。解法是改名--queryOrderById和queryOrderByName。坑三返回对象必须可序列化。LLM拿到的结果是JSON返回对象的字段要全部可序列化。我返回过一个含LocalDateTime的对象Claude收到的是空对象因为Jackson默认不认LocalDateTime。加JsonFormat或配jackson-datatype-jsr310模块。坑四sse模式要单独暴露端口。stdio模式是本地进程通信sse模式是HTTP服务要单独开端口。我一开始把MCP Server和业务系统塞一个进程结果MCP端口被业务流量冲垮。后来拆成独立进程问题解决。坑五权限沙箱。Claude Desktop对MCP Server有权限限制文件系统访问、网络访问都受控。我的工具要读/etc/config/order.json被沙箱拦了。解法是把配置改成环境变量传入或者用Claude Desktop的权限配置显式授权。MCP vs Function Calling这俩不是替代关系是层次不同。Function Calling是LLM厂商定义的工具调用格式OpenAI、Anthropic、Google各有各的。MCP是Anthropic推出的开放协议标准化了LLM怎么发现工具、怎么调用工具、怎么传参、怎么返回结果。选型上如果只用一家LLMFunction Calling够用简单直接。如果要支持多客户端Claude Desktop、Cursor、自家产品MCP一次开发通吃维护成本低。我的判断是MCP会成为事实标准就像REST之于Web API。现在投入MCP是给未来铺路。面试官会怎么问这块的问题套路我面过几个候选人整理一下。Q1MCP和Function Calling什么关系Function Calling是LLM厂商定义的工具调用格式每家不同。MCP是Anthropic推出的开放协议标准化了工具发现、调用、传参、返回的完整流程。MCP在协议层Function Calling在能力层。MCP Server一次开发所有支持MCP的客户端通吃。Q2MCP三大原语是什么Tools工具调用、Resources资源读取、Prompts提示词模板。Tools是LLM调用业务方法比如查订单。Resources是LLM读取静态资源比如配置文件。Prompts是LLM使用预定义的提示词模板比如代码review模板。生产中Tools用得最多。Q3stdio和sse两种传输模式怎么选stdio是本地进程通信适合Claude Desktop这种本地客户端延迟低但只能本机用。sse是HTTP服务适合远程客户端Cursor云端、自家产品能跨网络但延迟高。本地工具用stdio对外服务用sse。Q4MCP Server开发有什么坑五个坑。stdio模式日志不能打stdout会污染协议流。工具方法不能同名重载MCP按名识别。返回对象必须可序列化LocalDateTime要加注解。sse模式要单独端口别和业务塞一起。Claude Desktop权限沙箱限制文件和网络访问。Q5大厂追问字节/百度会问什么字节会问MCP协议的JSON-RPC 2.0底层、sse模式的长连接管理。百度会问MCP与Function Calling的协议差异、MCP在RAG中的应用。核心都是你有没有真的开发过MCP Server、踩过哪些坑。回头看MCP的本质是LLM调工具的标准化协议。对Java后端来说开发MCP Server就是写Spring Bean加Tool注解简单得很。难点在协议理解、传输模式选择、客户端适配、踩坑处理。我的判断是MCP会成事实标准。现在投入是给未来铺路。等Claude Desktop、Cursor、Windsurf都普及了再写MCP就跟现在写REST API一样自然。
返回列表