2026/1/10 14:16:43
网站建设
项目流程
站长工具综合查询系统,怎么打开公众号,做外贸没有网站需要,网站名称和备案公司名称不一样前言
大家好呀#xff5e; 我是菲菲#xff0c;一名刚入门大模型开发的学妹#xff01;在跟着教程探索 Agent 与工具协作的过程中#xff0c;“MCP 协议” 这个词频繁出现 —— 官网白皮书看着清晰易懂#xff0c;可真要动手实操时#xff0c;却总陷入 “一看就会、一写就…前言大家好呀 我是菲菲一名刚入门大模型开发的学妹在跟着教程探索 Agent 与工具协作的过程中“MCP 协议” 这个词频繁出现 —— 官网白皮书看着清晰易懂可真要动手实操时却总陷入 “一看就会、一写就懵” 的困境。后来发现不只是我很多初入行的小伙伴都对这个 “连接 Agent 与工具的桥梁” 感到困惑。其实在 AI 时代通过 MCP 协议让 Agent 获取业务上下文早已成为行业标配“会调接口” 也成了 Agent 开发者的必备技能。为了帮自己和更多像我一样的初学者彻底吃透 MCP我决定从 0 到 1 动手实现一个 MCP Server。这篇文章里我会把自己的学习心得和实操过程毫无保留地分享出来不仅会用通俗的语言拆解 MCP 协议的核心原理还会手把手带大家完成代码实现让每一位和我一样的学妹、学弟都能告别 “只听说过 MCP” 的尴尬真正理解它的底层逻辑 跟着步骤一步步来相信你也能轻松入门 MCP 开发在 AI 时代“会调接口” 已成为 Agent 开发者的必备技能。通过 MCP 协议让 Agent 获取业务上下文早已成为行业标配但官网白皮书往往 “一看就懂一写就懵”。本文将手把手带您从 0 到 1 实现 MCP Server全量剖析 MCP 协议及底层技术原理让您彻底告别 “只听说过 MCP” 的尴尬。一、MCP 协议核心概念解析什么是 MCPMCPModel Context Protocol模型上下文协议是规范应用程序向大语言模型提供上下文的开放协议。其核心是实现 MCP Client 与 MCP Server 之间的标准化通信让 Agent 开发与工具开发彻底解耦 —— 就像 Type-C 接口统一充电标准一样MCP 统一了 Agent 调用工具的方式。简单来说当 AI 客户端MCP Host需要获取上下文时会通过集成的 MCP Client 向 MCP Server 发送请求由 Server 返回模型所需的上下文数据。若 AI 客户端未集成 MCP Client该协议则与 AI 无任何关联。三大核心组件协作机制MCP 协议的正常运行依赖三个核心组件的协同工作MCP Client负责发送请求与接收响应通常集成在 MCP Host 中无需单独部署MCP Server核心处理单元负责接收请求、处理逻辑并返回上下文数据MCP Host协议执行者完整流程为接收用户问题→选择工具→构建参数→通过 Client 调用 Server→解析结果→继续对话集成了 MCP Client 的智能体执行平台如 IDEA LAB或模型厂商的 Agent/AI 客户端如 Cherry Studio均可作为 MCP Host。以阿里云百练平台为例MCP Host 即平台上的智能体应用支持低代码、高代码等多种开发模式可通过简单配置实现 MCP 服务调用。与 Type-C、统一 function call 的关联若将 Agent 比作手机工具接口比作充电线那么 MCP Server 就相当于 Type-C 接口 —— 当所有 Agent/AI 客户端都集成 MCP Client并用 MCP 协议调用工具时就实现了 function call 的统一。这种标准化带来的价值正如 Type-C 接口统一充电标准一样打破了不同平台间的兼容性壁垒。核心解决的两大问题简化模型调用流程统一协议后IDEA LAB、通义千问等平台的 AI 客户端内置 MCP Client开发者只需将接口封装为 MCP Server 即可接入选工具、拼参数、调接口、解析结果等流程由平台自动完成实现开发解耦工具按 MCP Server 标准发布后所有支持 MCP 协议的平台均可直接调用无需关心 “选 - 拼 - 调 - 解” 全流程实现工具开发与 Agent 开发的分离二、MCP 技术原理深度拆解MCP 协议的底层技术支撑主要包括 SSE数据传输方式、JSON-RPC 2.0数据格式规范和 MCP 自身的通信流程定义三者共同构成了完整的技术体系。SSE实时数据传输的核心载体SSEServer-Sent Events是基于 HTTP 协议的 Web 技术专门用于服务器向客户端实时推送数据。其核心原理是通过建立持久 HTTP 连接将 “请求 - 响应” 模式伪装成 “长时间下载”让数据源源不断流向客户端。关键特性与规范基于 HTTP/1.1复用现有基础设施浏览器原生支持响应格式固定Content-Type 为 text/event-stream需设置 Cache-Control: no-cache 和 Connection: keep-alive消息体包含四个固定字段data实际负载数据id事件序号用于断线续传event事件类型前端通过 addEventListener 监听retry断线重连间隔毫秒实现方式灵活Spring 框架可通过同步阻塞的 SseEmitter 或响应式的 Flux实现两种技术栈实现对比技术栈模型API对象适用场景备注Spring MVCServlet同步阻塞SseEmitter连接数可控、开发简单Tomcat 线程数≈连接数Spring WebFlux Reactor异步非阻塞Flux超高并发、低延迟Netty 事件循环少量线程支撑海量连接JSON-RPC 2.0标准化消息格式JSON-RPC 2.0 是无状态、轻量级的远程过程调用协议定义了 MCP Client 与 MCP Server 之间的通信格式确保两端能零差异解析数据。核心消息结构四固定字段字段要求类型作用jsonrpc必选固定值 “2.0”协议版本号method必选string远端要执行的函数名jsonrpc可选object/array入参数据jsonrpc可选string/number/null请求匹配符无 id 视为通知不期待回复jsonrpc二选一-成功返回 result失败返回 error完整范式示例请求示例{jsonrpc:2.0,method:initialize,params:{protocolVersion:2024-11-05,capabilities:{sampling:{},roots:{listChanged:true}},clientInfo:{name:mcp-inspector,version:0.9.0}},id:0}成功响应示例{jsonrpc:2.0,id:0,result:{capabilities:{tools:{listChanged:true}},serverInfo:{name:SpringBoot MCP Server,version:1.0.0},protocolVersion:2024-11-05}}MCP 协议全生命周期通信规范MCP 协议在 SSEJSON-RPC 2.0 基础上定义了 “两通道、四步骤” 的全生命周期通信流程明确了 “怎么连、怎么握手、怎么调工具、怎么结束” 的标准流程。通信两通道缺一不可通信作用通道类型方向SSE Stream (GET /sse)服务器→客户端推送事件Server-Sent Events单向HTTP POST Endpoint客户端→服务器发起调用HTTP双向请求 - 响应通信四步骤连客户端发送 GET /sse 请求建立 SSE 长连接用于接收服务器推送取服务器返回 event: endpoint 消息提供客户端后续通信的 POST 端点 URI握客户端向 POST 端点发送两包 JSON-RPCinitialize 请求协商协议版本和能力notifications/initialized 通知确认握手完成用正常会话阶段支持 tools/list获取工具列表、tools/call调用具体工具服务器通过 SSE 流推送状态更新断任一端关闭 SSE 连接会话结束三、从零实现 MCP Server实战教程任何语言 / 框架实现 MCP Server只需满足三个条件开启 SSE 端点、开启 POST 端点、实现 “四步骤” 所需接口。以下将用 Spring BootWebFluxJackson 技术栈带您实现一个完整的 MCP Server。项目准备技术栈与依赖核心框架Spring Boot WebFlux序列化工具Jackson测试工具MCP Inspector、内置 HTML 测试页面项目结构springboot-mcp-server/├── pom.xml├── src/│ ├── main/│ │ ├── java/│ │ │ └── com/example/│ │ │ ├── HelloWorldApplication.java│ │ │ ├── config/ // HTTP配置│ │ │ │ └── McpConfig.java│ │ │ └── mcp/│ │ │ ├── protocol/ // JSON-RPC 2.0请求响应模型│ │ │ │ ├── McpRequest.java│ │ │ │ └── McpResponse.java│ │ │ ├── transport/ // 传输端点SSEPOST│ │ │ │ └── SseTransport.java│ │ │ ├── server/ // MCP协议处理逻辑│ │ │ │ └── McpServerHandler.java│ │ │ └── tools/ // 工具注册与调用│ │ │ └── McpToolRegistry.java│ │ └── resources/│ │ ├── application.properties│ │ └── static/│ │ └── mcp-test.html // 内置测试页面│ └── test/│ └── java/com/example/HelloWorldApplicationTests.java└── README.md核心组件实现1SSE 端点与 POST 端点构建SseTransport.java首先实现 MCP 协议要求的两通道确保客户端能建立连接并发送请求。SSE 端点GET /sse/** * 标准MCP SSE端点 - 客户端连接此端点接收服务器推送消息 */GetMapping(value/sse,producesMediaType.TEXT_EVENT_STREAM_VALUE)publicFluxServerSentEventStringsseEndpoint(RequestParam(requiredfalse)StringclientId,ServerHttpRequestrequest){// 安全检查验证Origin头防止DNS重绑定攻击Stringoriginrequest.getHeaders().getFirst(Origin);if(origin!null!isValidOrigin(origin)){System.err.println(Invalid origin rejected: origin);returnFlux.error(newSecurityException(Invalid origin));}StringsessionIdclientId!null?clientId:client-System.currentTimeMillis();// 为每个客户端创建独立消息流Sinks.ManyServerSentEventStringsinkSinks.many().multicast().onBackpressureBuffer();clientSinks.put(sessionId,sink);System.out.println(MCP SSE client connected: sessionId from origin: origin);// 30秒心跳流维持连接FluxServerSentEventStringheartbeatFlux.interval(Duration.ofSeconds(30)).map(tick-ServerSentEvent.Stringbuilder().event(ping).data({\type\:\ping\}).build());// 合并消息流与心跳流发送endpoint事件MCP协议要求returnFlux.merge(sink.asFlux(),heartbeat).doOnSubscribe(subscription-{try{StringendpointUrigetBaseUrl(request)/message/sessionId;// 发送endpoint事件提供POST通信地址ServerSentEventStringendpointEventServerSentEvent.Stringbuilder().event(endpoint).data(endpointUri).build();sink.tryEmitNext(endpointEvent);System.out.println(Sent endpoint event to client: sessionId with URI: endpointUri);}catch(Exceptione){System.err.println(Error sending endpoint event: e.getMessage());}}).doOnCancel(()-{System.out.println(MCP SSE client disconnected: sessionId);clientSinks.remove(sessionId);sink.tryEmitComplete();}).doOnError(error-{System.err.println(MCP SSE error for client sessionId: error.getMessage());clientSinks.remove(sessionId);}).onErrorResume(error-{System.err.println(SSE stream error, attempting to recover: error.getMessage());returnFlux.empty();});}POST 端点POST /message/{sessionId}/** * 处理客户端MCP请求 - 客户端通过POST发送JSON-RPC消息 */PostMapping(value/message/{sessionId},consumesMediaType.APPLICATION_JSON_VALUE)publicMonoVoidhandleSessionMessage(PathVariableStringsessionId,RequestBodyStringmessageJson,ServerHttpRequestrequest){returnhandleMessageInternal(sessionId,messageJson,request);}privateMonoVoidhandleMessageInternal(StringsessionId,StringmessageJson,ServerHttpRequestrequest){returnMono.fromRunnable(()-{try{// 安全校验与消息解析Stringoriginrequest.getHeaders().getFirst(Origin);if(origin!null!isValidOrigin(origin)){System.err.println(Invalid origin rejected for message: origin);return;}McpRequestmcpRequestobjectMapper.readValue(messageJson,McpRequest.class);System.out.println(Received MCP request: mcpRequest.getMethod() (id: mcpRequest.getId()) from session: sessionId);// 处理请求并通过SSE返回响应McpResponseresponseserverHandler.handleRequest(mcpRequest);if(response!null){sendMessageToClient(sessionId,response);}}catch(Exceptione){System.err.println(Error processing MCP message: e.getMessage());e.printStackTrace();// 发送错误响应try{McpRequestmcpReqobjectMapper.readValue(messageJson,McpRequest.class);McpResponseerrorResponsenewMcpResponse();errorResponse.setId(mcpReq.getId());errorResponse.setError(newMcpResponse.McpError(-32603,Internal error: e.getMessage()));sendMessageToClient(sessionId,errorResponse);}catch(Exceptionex){System.err.println(Error sending error response: ex.getMessage());}}});}/** * 向指定客户端发送MCP响应消息 */publicvoidsendMessageToClient(StringsessionId,Objectmessage){Sinks.ManyServerSentEventStringsinkclientSinks.get(sessionId);if(sink!null){try{StringjsonDataobjectMapper.writeValueAsString(message);ServerSentEventStringeventServerSentEvent.Stringbuilder().event(message).data(jsonData).build();sink.tryEmitNext(event);System.out.println(Sent MCP message to client: sessionId);}catch(Exceptione){System.err.println(Error sending message to client sessionId: e.getMessage());}}else{System.err.println(No active connection found for session: sessionId);}}2协议核心逻辑处理McpServerHandler.java实现 MCP 通信 “四步骤” 中的握手与工具调用逻辑处理 initialize、tools/list、tools/call 等关键请求。publicMcpResponsehandleRequest(McpRequestrequest){Stringmethodrequest.getMethod();Stringidrequest.getId();MapString,Objectparamsrequest.getParams();try{switch(method){// 处理初始化请求协商协议版本与能力caseinitialize:returnhandleInitialize(id,params);// 处理工具列表请求返回可用工具casetools/list:returnhandleListTools(id);// 处理工具调用请求执行具体工具逻辑casetools/call:returnhandleCallTool(id,params);// 客户端初始化完成通知无需返回响应casenotifications/initialized:this.initializedtrue;System.out.println(Client initialization completed);returnnull;// 未知方法处理default:McpResponseerrorResponsenewMcpResponse();errorResponse.setId(id);errorResponse.setError(newMcpResponse.McpError(-32601,Method not found: method));returnerrorResponse;}}catch(Exceptione){System.err.println(Error handling request method: e.getMessage());McpResponseerrorResponsenewMcpResponse();errorResponse.setId(id);errorResponse.setError(newMcpResponse.McpError(-32603,Internal error: e.getMessage()));returnerrorResponse;}}测试验证方式一使用 MCP Inspector 测试启动 Spring Boot 应用默认端口 8080打开 MCP Inspector v0.9.0选择 “SSE Transport”输入 URLhttp://localhost:8080/sse点击 “Connect”按顺序执行Initialize初始化握手List Tools获取工具列表Call Tools调用具体工具方式二使用内置测试页面访问http://localhost:8080/mcp-test.html点击 “Connect to MCP Server” 建立连接依次执行Initialize → List Tools → Call Hello World → Get Time → Echo Message验证全流程实现效果通过上述代码我们完成的 MCP Server 具备以下能力支持 SSE 长连接建立与心跳维持按 MCP 协议要求发送 endpoint 事件处理 initialize 握手流程协商协议能力支持工具列表查询与具体工具调用异常处理与错误响应反馈四、总结MCP 协议通过 SSE传输通道 JSON-RPC 2.0数据格式 标准化流程通信规范的组合成功解决了 Agent 与工具之间的协作难题。通过亲手实现 MCP Server我们不仅掌握了 “两通道、四步骤” 的核心流程更理解了其 “解耦开发、统一标准” 的设计理念。在大模型应用开发日益普及的今天MCP 作为连接 Agent 与工具的 “桥梁”正在重塑行业开发模式。掌握 MCP 技术原理将帮助开发者在 Agent 开发领域构建更灵活、更具兼容性的应用系统为业务创新提供强大支撑。