手把手教你实现MCP Server:解锁大模型开发必备技能(收藏版)
本文将带你深入了解MCP模型上下文协议的工作原理并通过实例讲解如何从零开始实现MCP Server。MCP是连接大模型与外部工具的关键协议掌握它对于开发高效的AI应用至关重要。文章将详细解析SSE和JSON-RPC 2.0在MCP中的应用并提供完整的Java代码示例帮助你快速上手并收藏学习。一、MCP介绍什么是MCPQMCP 究竟是什么AMCPModel Context Protocol模型上下文协议是规范应用程序向大语言模型提供上下文的开放协议。当AI客户端MCP Host需要获取上下文时会通过集成的MCP Client向MCP Server发送请求由Server返回模型所需的上下文数据。该协议的核心价值在于标准化交互流程——反之如果AI客户端都不集成MCP Client那该协议就和AI没有任何关系了。 一句话MCP本质上是MCP Client和MCP Server之间的通信协议实现了Agent开发与工具开发的解耦。Q协议具体如何运作A由三个核心组件协作1. MCP Client负责发送请求/接收响应2. MCP Server处理请求并返回上下文数据3. MCP HostMCP协议的执行者。负责接收用户问题 → 选择工具 → 构建参数 → 通过Client调用Server → 解析结果 → 继续对话 集成MCP Client的智能体执行平台如 IDEA LAB或模型厂商的 Agent/AI 客户端如 Cherry Studio均可承担MCP Host职能。QMCP Host、MCP Server、MCP Client到底长什么样啊以阿里云百练平台为例MCP Host可以简单理解为平台上的智能体应用MCP Host长这样子MCP Client直接集成在MCP Host中了所以我们是看不到的。但是我们可以看看自己写一个MCP Client的例子Slf4j publicclassMcpClient { private String baseUri; // MCP 服务器基础地址 private String sseEndpoint; // SSE 连接端点 private String messageEndpoint; // HTTP POST 消息端点 private SseClient sseClient new SseClient(); // SSE 客户端实例 // 异步任务管理 - 用于同步连接状态和请求响应 private CompletableFutureVoid connectFuture new CompletableFuture(); // 连接建立完成信号 private CompletableFutureVoid initFuture new CompletableFuture(); // 初始化完成信号 // 待处理的请求映射表requestId - 对应的Future对象 privatefinal MapString, CompletableFutureMapString, Object pendingRequests new ConcurrentHashMap(); privatefinal ObjectMapper objectMapper new ObjectMapper(); // JSON 序列化工具 /** * MCP 客户端构造函数 * param baseUri MCP 服务器地址 */ publicMcpClient(String baseUri){ this.baseUri baseUri; this.sseEndpoint baseUri; // MCP 协议规定的三步初始化流程 this.connect(); // 1. 建立 SSE 连接获取 POST 端点 this.sendInitialize(); // 2. 发送初始化请求协商协议版本和能力 this.sendNotification(notifications/initialized, null); // 3. 发送初始化完成通知 } }MCP Server长这样子MCP Host上注册好MCP Server以后我们这个智能体应用就可以调用MCP Server了。Q那这个协议和Type-C接口、统一function call有什么关系呢A当所有的Agent/AI客户端集成了MCP Client并且通过MCP协议去实现调用MCP Server提供的工具从Agent开发的角度来说MCP就变成了一个Agent进行function call的协议了如果大家都按MCP的方式去给Agent添加工具不就是统一了function call吗所以如果把Agent比作手机接口比作充电线那MCP Server牌充电线不就是Type-C接口类型的充电线了吗MCP解决的核心问题1. 让模型调用接口更简单把接口上下文一键喂给LLM有了统一协议IDEA LAB、通义千问等平台的AI客户端MCP Host内置MCP Client按协议通信。我们只需把自己的接口封装成MCP Server接入平台即可选工具、拼参数、调接口、解析结果等链路由平台自动完成。2. 实现了Agent开发与工具开发的解耦类似于前后端解耦的意义所有平台支持MCP协议后工具按MCP Server标准发布Agent即可直接调用无需再关心选-拼-调-解全流程真正做到工具开发与Agent开发分离。二、MCP技术原理深度解析1. SSEServer-Sent EventsMCP Client和MCP Server之间的数据传输方式SSE全称Server-Sent Events是一种基于HTTP协议实现的Web技术专门用于服务器向客户端实时推送数据。它通过建立持久的HTTP连接让客户端监听事件流从而实时接收服务器推送的数据。那么sse是如何做到实时推送的呢其实SSE采用了一种巧妙的变通方法虽然底层仍然是请求-响应模式但响应的不是一个简单的数据包而是一个持续不断的流信息stream。这就像是一次耗时很长的下载过程让数据源源不断地流向客户端。正因为SSE基于HTTP实现所以浏览器天然支持这一技术无需额外配置。【一句话结论】在 MCP 体系里SSE 就是用一次超长时间的 HTTP 下载来伪装成服务器主动推送规范极简4 个固定字段浏览器原生支持Spring 既可用同步阻塞的SseEmitter也可用响应式的FluxServerSentEvent实现。一、SSE在MCP中的定位定位实现MCP Server → MCP Client 的单向实时数据流解决「怎么传」本质依旧走 HTTP/1.1复用现有基础设施长连接 text/event-stream响应把请求-响应拉成长时间下载。二、协议格式只需记 4 个字段HeaderContent-Type: text/event-stream Cache-Control: no-cache Connection: keep-aliveBodyfield: value\n\n /* 每条消息以两个换行结束 */服务器发送给浏览器的body必须按上述k-v格式符合其中field的类型是固定的四种字段data 实际负载id 事件序号断线续传用event 事件类型前端addEventListener(‘event名’)retry 断线重连间隔msMCP 实战示例MCP Server返回给MCP Client的SSE数据包如下event: endpoint data: /message?sessionIdfc92c0fc-6222-466c-b353-bdc4931980a6 event: message data: {jsonrpc:2.0,id:0,result:{capabilities:{tools:{listChanged:true}},serverInfo:{name:SpringBoot MCP Server,version:1.0.0},protocolVersion:2024-11-05}} event: message data: {jsonrpc:2.0,id:1,result:{tools:[{inputSchema:{type:object,properties:{name:{description:Name to greet (optional),type:string}}},name:hello_world,description:Returns a Hello World message},{inputSchema:{type:object,properties:{}},name:get_time,description:Returns current server time},{inputSchema:{type:object,properties:{message:{description:Message to echo back,type:string}}},name:echo,description:Echoes back the provided message}]}} event: message data: {jsonrpc:2.0,id:2,result:{content:[{text:Hello, 宸游!,type:text}]}}三、实现路线对比四、代码级 DemoJava实现SSE方式一Spring MVC阻塞式通过返回一个SseEmitter对象作为事件发射器给到前端然后我们在后端调用emitter.send()方法发送的数据会被封装为SSE事件流的形式前端可以通过监听该事件流来进行数据更新。GetMapping(value/stock-price, producesTEXT_EVENT_STREAM_VALUE) public SseEmitter stream(){ SseEmitter e new SseEmitter(); new Thread(() - { while(true){ e.send(SseEmitter.event().data(price)); sleep(1000); } }).start(); return e; }前端在js中通过new EventSource建立一个EventSource对象它会与/stock-price这个路径建立sse长连接并监听事件。当后端发送event事件以后会触发eventSource.onmessage这个回调函数将数据写入到页面中。const es new EventSource(/stock-price); es.onmessage e document.body.innerText e.data;Java实现SSE方式二Spring WebFlux响应式返回publisher给到前端进行订阅然后我们在后端通过Sinks.ManyServerSentEvent?这个类的tryEmitNext方法来手动发送事件给到前端前端可以通过监听该事件流来进行数据更新。GetMapping(/stream-sse) public FluxServerSentEventString stream() { return Flux.interval(Duration.ofSeconds(1)) .map(i - ServerSentEvent.builder() .id(i.toString()) .event(tick) .data(now LocalTime.now()) .build()); }前端同上。2. JSON-RPC 2.0MCP Client和MCP Server之间的传输的内容格式JSON-RPC 2.0 是一种无状态、轻量级的远程过程调用(RPC)协议定义了MCP Client和MCP Server之间进行通信的格式。JSON-RPC 2.0协议定义了请求和响应的格式以及错误处理机制。【一句话结论】在 MCP 里Client与 Server永远只交换一条四字段 JSON-RPC 2.0 消息版本jsonrpc、方法method、编号id、载荷params/result|error一、JSON-RPC 2.0 在 MCP 中的定位定位定义MCP Server和MCP Client之间的统一的无状态消息格式解决「说什么」本质通过无状态、轻量级的四字段 JSON 契约实现两端零差异解析。二、消息骨架记住 4 个固定键三、完整范式1. 请求{ jsonrpc: 2.0, method: ..., params: {...}, id: unique }2. 响应{ jsonrpc: 2.0, id: same, result: {...} } // 成功 { jsonrpc: 2.0, id: same, error: {code, message, data?} } // 失败四、MCP 实战示例initialize请求客户端 → 服务器{ 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 }服务器回同样id的响应宣告双方能力即可进入正式会话。3. MCP协议MCP Client和MCP Server之间的通信调用协议MCP本质上是MCP Client和MCP Server之间的通信协议那么这个MCP协议具体定义了写什么内容呢定义了MCP Server和MCP Client之间的连接建立过程定义了MCP Client和MCP Server之间如何进行信息上下文交换定义了MCP Client和MCP Server之间如何连接关闭过程【一句话结论】MCP 协议就是客户端先拉一条 SSE 长连接收通知再用普通 HTTP POST 发请求到服务端Client和Server之间必须先发 initialize 打招呼才能调工具最后Client关 SSE 就通信结束了。一、协议定位 本质定位在 SSE JSON-RPC 2.0 之上定义两通道、四步骤、全生命周期的MCP Server与 MCP Client 通信协议本质把怎么连、怎么握手、怎么调工具、怎么结束写死成固定流程MCP Host只需照表填空就可以实现LLM获取MCP Server提供的上下文。二、MCP Server 和 MCP Client 之间的通信两通道服务器启动时必须同时暴露这两个端点缺一不可。三、MCP Server 和 MCP Client 之间的通信四步骤1. 连 客户端 发送GET /sse请求建立sse长连接 用于后续服务端推送数据给客户端 2. 取 服务器回event: endpoint的消息给出后续客户端发送信息给服务端的POST 端点 URI 3. 握 客户端向该 URI 发两包 JSON-RPCinitialize请求 → 服务器返回 capabilitiesnotifications/initialized通知 → 握手完成 4. 用 正常会话tools/list可选拉可用工具tools/call 调用具体工具服务器随时在 SSE 流里推送状态更新 断 任一端关闭 SSE会话结束。四、MCP Server 和 MCP Client 之间的通信生命周期三、自己动手实现一个MCP ServerTalk is cheap, show me the code。当我们尝试实现一个MCP Server时我们就会对MCP彻底搞懂而不是仅仅一个写在纸上的协议了。那么怎么实现一个MCP Server呢任何语言/框架实现 MCP Server 时只要开一个 SSE 端点开一个可接收 POST 的 URI实现通信四步骤所需要的接口即可开发出一个MCP Server。下面我们将通过具体的代码示例来实现一个简单的MCP Server1. 项目准备和依赖技术栈Spring Boot WebFlux Jackson测试工具MCP Inspector项目结构springboot-mcp-server/ ├── pom.xml ├── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── example/ │ │ │ ├── HelloWorldApplication.java │ │ │ ├── config/ --http配置 │ │ │ │ └── McpConfig.java │ │ │ └── mcp/ │ │ │ ├── protocol/ --jsonrpc2.0格式的请求和响应 │ │ │ │ ├── McpRequest.java │ │ │ │ └── McpResponse.java │ │ │ ├── transport/ -- sse与http post传输端点 │ │ │ │ └── SseTransport.java │ │ │ ├── server/ -- mcp协议处理 │ │ │ │ └── McpServerHandler.java │ │ │ └── tools/ -- 工具注册、调用 │ │ │ └── McpToolRegistry.java │ │ └── resources/ │ │ ├── application.properties │ │ └── static/ │ │ └── mcp-test.html │ └── test/ │ └── java/ │ └── com/ │ └── example/ │ └── HelloWorldApplicationTests.java └── README.md2. 实现核心组件两通道的构建(SseTransport)SSE端点实现GET/sse创建SSE长连接端点用于服务器向客户端推送数据/** * 标准 MCP SSE 端点 - 符合 MCP 协议规范 * 客户端连接此端点接收服务器消息 */ GetMapping(value /sse, produces MediaType.TEXT_EVENT_STREAM_VALUE) public FluxServerSentEventString sseEndpoint(RequestParam(required false) String clientId, ServerHttpRequest request) { // 安全检查验证 Origin 头防止 DNS 重绑定攻击 String origin request.getHeaders().getFirst(Origin); if (origin ! null !isValidOrigin(origin)) { System.err.println(Invalid origin rejected: origin); return Flux.error(new SecurityException(Invalid origin)); } String sessionId clientId ! null ? clientId : client- System.currentTimeMillis(); // 为每个客户端创建独立的消息流 Sinks.ManyServerSentEventString sink Sinks.many().multicast().onBackpressureBuffer(); clientSinks.put(sessionId, sink); System.out.println(MCP SSE client connected: sessionId from origin: origin); // 创建心跳流每30秒发送一次心跳 FluxServerSentEventString heartbeat Flux.interval(Duration.ofSeconds(30)) .map(tick - ServerSentEvent.Stringbuilder() .event(ping) .data({\type\:\ping\}) .build()); // 合并消息流和心跳流 return Flux.merge( sink.asFlux(), heartbeat ) .doOnSubscribe(subscription - { // 根据 MCP 协议必须发送 endpoint 事件 try { // 1. 发送 endpoint 事件 - MCP 协议要求 String endpointUri getBaseUrl(request) /message/ sessionId; // 直接发送 URI 字符串不包装在对象中 ServerSentEventString endpointEvent ServerSentEvent.Stringbuilder() .event(endpoint) .data(endpointUri) .build(); sink.tryEmitNext(endpointEvent); System.out.println(Sent endpoint event to client: sessionId with URI: endpointUri); } catch (Exception e) { 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()); return Flux.empty(); }); }HTTP POST端点实现POST/message/{sessionId}创建处理客户端请求的端点/** * 处理客户端发送的 MCP 请求 - 支持会话特定端点 * 客户端通过 POST 请求发送 MCP 消息到指定的端点 */ PostMapping(value /message/{sessionId}, consumes MediaType.APPLICATION_JSON_VALUE) public MonoVoid handleSessionMessage(PathVariable String sessionId, RequestBody String messageJson, ServerHttpRequest request) { return handleMessageInternal(sessionId, messageJson, request); } private MonoVoid handleMessageInternal(String sessionId, String messageJson, ServerHttpRequest request){ return Mono.fromRunnable(() - { try { // 安全检查验证 Origin 头 String origin request.getHeaders().getFirst(Origin); if (origin ! null !isValidOrigin(origin)) { System.err.println(Invalid origin rejected for message: origin); return; } // 解析消息 McpRequest mcpRequest objectMapper.readValue(messageJson, McpRequest.class); System.out.println(Received MCP request: mcpRequest.getMethod() (id: mcpRequest.getId() ) from session: sessionId); // 处理请求 McpResponse response serverHandler.handleRequest(mcpRequest); // 通过 SSE 发送响应 if (response ! null) { sendMessageToClient(sessionId, response); } } catch (Exception e) { System.err.println(Error processing MCP message: e.getMessage()); e.printStackTrace(); // 发送错误响应 try { McpRequest mcpReq objectMapper.readValue(messageJson, McpRequest.class); McpResponse errorResponse new McpResponse(); errorResponse.setId(mcpReq.getId()); errorResponse.setError(new McpResponse.McpError(-32603, Internal error: e.getMessage())); sendMessageToClient(sessionId, errorResponse); } catch (Exception ex) { System.err.println(Error sending error response: ex.getMessage()); } } }); } /** * 向指定客户端发送 MCP 消息 */ publicvoidsendMessageToClient(String sessionId, Object message){ Sinks.ManyServerSentEventString sink clientSinks.get(sessionId); if (sink ! null) { try { String jsonData objectMapper.writeValueAsString(message); ServerSentEventString event ServerSentEvent.Stringbuilder() .event(message) .data(jsonData) .build(); sink.tryEmitNext(event); System.out.println(Sent MCP message to client: sessionId); } catch (Exception e) { System.err.println(Error sending message to client sessionId : e.getMessage()); } } else { System.err.println(No active connection found for session: sessionId); } }3. 实现通信四步骤所需要的接口McpServerHandler呼应前面讲到的MCP四步骤生命周期步骤一建立连接连客户端通过GET请求建立SSE连接服务器准备并维护连接状态步骤二获取端点信息取服务器通过SSE流发送event: endpoint事件提供客户端后续通信所需的POST URI步骤三初始化握手握处理客户端的initialize请求返回服务器能力信息处理notifications/initialized通知步骤四工具调用用实现tools/list接口返回可用工具列表实现tools/call接口处理具体工具调用请求在McpServerHandler中处理请求的方法handleRequest()实现了步骤三和步骤四所需要的接口和逻辑。public McpResponse handleRequest(McpRequest request){ String method request.getMethod(); String id request.getId(); MapString, Object params request.getParams(); try { switch (method) { caseinitialize: return handleInitialize(id, params); casetools/list: return handleListTools(id); casetools/call: return handleCallTool(id, params); casenotifications/initialized: // 客户端初始化完成通知不需要响应 this.initialized true; System.out.println(Client initialization completed); return null; default: // 创建错误响应确保只包含 error 字段不包含 result McpResponse errorResponse new McpResponse(); errorResponse.setId(id); errorResponse.setError(new McpResponse.McpError(-32601, Method not found: method)); return errorResponse; } } catch (Exception e) { System.err.println(Error handling request method : e.getMessage()); // 创建错误响应确保只包含 error 字段不包含 result McpResponse errorResponse new McpResponse(); errorResponse.setId(id); errorResponse.setError(new McpResponse.McpError(-32603, Internal error: e.getMessage())); return errorResponse; } }4. 测试步骤使用 MCP Inspector启动服务器:mvn spring-boot:run打开 MCP Inspector v0.9.0选择 “SSE Transport”输入 URL:http://localhost:8080/sse点击 “Connect”执行标准 MCP 流程:InitializeList ToolsCall Tools MCP Inspector只需要安装好node.js环境以后直接运行命令npx modelcontextprotocol/inspector0.9即可使用内置测试客户端访问:http://localhost:8080/mcp-test.html点击 “Connect to MCP Server”按顺序执行:InitializeList ToolsCall Hello WorldGet TimeEcho Message5. 总结通过以上代码实现我们就完成了一个基本的MCP Server它能够通过SSE端点建立连接并发送endpoint事件通过POST端点处理initialize、tools/list、tools/call等请求提供具体的工具实现供客户端调用这与我们前面介绍的MCP协议的两通道和四步骤完全对应帮助我们深入理解MCP的工作原理。如何学习大模型 AI 由于新岗位的生产效率要优于被取代岗位的生产效率所以实际上整个社会的生产效率是提升的。但是具体到个人只能说是“最先掌握AI的人将会比较晚掌握AI的人有竞争优势”。这句话放在计算机、互联网、移动互联网的开局时期都是一样的道理。我在一线科技企业深耕十二载见证过太多因技术卡位而跃迁的案例。那些率先拥抱 AI 的同事早已在效率与薪资上形成代际优势我意识到有很多经验和知识值得分享给大家也可以通过我们的能力和经验解答大家在大模型的学习中的很多困惑。我们整理出这套AI 大模型突围资料包✅ 从零到一的 AI 学习路径图✅ 大模型调优实战手册附医疗/金融等大厂真实案例✅ 百度/阿里专家闭门录播课✅ 大模型当下最新行业报告✅ 真实大厂面试真题✅ 2026 最新岗位需求图谱所有资料 ⚡️ 朋友们如果有需要《AI大模型入门进阶学习资源包》下方扫码获取~① 全套AI大模型应用开发视频教程包含提示工程、RAG、LangChain、Agent、模型微调与部署、DeepSeek等技术点② 大模型系统化学习路线作为学习AI大模型技术的新手方向至关重要。 正确的学习路线可以为你节省时间少走弯路方向不对努力白费。这里我给大家准备了一份最科学最系统的学习成长路线图和学习规划带你从零基础入门到精通③ 大模型学习书籍文档学习AI大模型离不开书籍文档我精选了一系列大模型技术的书籍和学习文档电子版它们由领域内的顶尖专家撰写内容全面、深入、详尽为你学习大模型提供坚实的理论基础。④ AI大模型最新行业报告2025最新行业报告针对不同行业的现状、趋势、问题、机会等进行系统地调研和评估以了解哪些行业更适合引入大模型的技术和应用以及在哪些方面可以发挥大模型的优势。⑤ 大模型项目实战配套源码学以致用在项目实战中检验和巩固你所学到的知识同时为你找工作就业和职业发展打下坚实的基础。⑥ 大模型大厂面试真题面试不仅是技术的较量更需要充分的准备。在你已经掌握了大模型技术之后就需要开始准备面试我精心整理了一份大模型面试题库涵盖当前面试中可能遇到的各种技术问题让你在面试中游刃有余。以上资料如何领取为什么大家都在学大模型最近科技巨头英特尔宣布裁员2万人传统岗位不断缩减但AI相关技术岗疯狂扩招有3-5年经验大厂薪资就能给到50K*20薪不出1年“有AI项目经验”将成为投递简历的门槛。风口之下与其像“温水煮青蛙”一样坐等被行业淘汰不如先人一步掌握AI大模型原理应用技术项目实操经验“顺风”翻盘这些资料真的有用吗这份资料由我和鲁为民博士(北京清华大学学士和美国加州理工学院博士)共同整理现任上海殷泊信息科技CEO其创立的MoPaaS云平台获Forrester全球’强劲表现者’认证服务航天科工、国家电网等1000企业以第一作者在IEEE Transactions发表论文50篇获NASA JPL火星探测系统强化学习专利等35项中美专利。本套AI大模型课程由清华大学-加州理工双料博士、吴文俊人工智能奖得主鲁为民教授领衔研发。资料内容涵盖了从入门到进阶的各类视频教程和实战项目无论你是小白还是有些技术基础的技术人员这份资料都绝对能帮助你提升薪资待遇转行大模型岗位。以上全套大模型资料如何领取
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2428768.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!