前言
大家好呀~ 我是菲菲,一名刚入门大模型开发的学妹!在跟着教程探索 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 协议核心概念解析
- 什么是 MCP?
MCP(Model 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:实时数据传输的核心载体
SSE(Server-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 MVC+Servlet | 同步阻塞 | 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 协议在 SSE+JSON-RPC 2.0 基础上,定义了 “两通道、四步骤” 的全生命周期通信流程,明确了 “怎么连、怎么握手、怎么调工具、怎么结束” 的标准流程。
通信两通道(缺一不可)
| 通信 | 作用 | 通道类型 | 方向 |
|---|---|---|---|
| SSE Stream (GET /sse) | 服务器→客户端推送事件 | Server-Sent Events | 单向 |
| HTTP POST Endpoint | 客户端→服务器发起调用 | HTTP | 双向请求 - 响应 |
通信四步骤
连:客户端发送 GET /sse 请求,建立 SSE 长连接,用于接收服务器推送
取:服务器返回 event: endpoint 消息,提供客户端后续通信的 POST 端点 URI
握:客户端向 POST 端点发送两包 JSON-RPC:
initialize 请求:协商协议版本和能力
notifications/initialized 通知:确认握手完成
用:正常会话阶段,支持 tools/list(获取工具列表)、tools/call(调用具体工具),服务器通过 SSE 流推送状态更新
断:任一端关闭 SSE 连接,会话结束
三、从零实现 MCP Server(实战教程)
任何语言 / 框架实现 MCP Server,只需满足三个条件:开启 SSE 端点、开启 POST 端点、实现 “四步骤” 所需接口。以下将用 Spring Boot+WebFlux+Jackson 技术栈,带您实现一个完整的 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/ // 传输端点(SSE+POST)
│ │ │ │ └── SseTransport.java
│ │ │ ├── server/ // MCP协议处理逻辑
│ │ │ │ └── McpServerHandler.java
│ │ │ └── tools/ // 工具注册与调用
│ │ │ └── McpToolRegistry.java
│ │ └── resources/
│ │ ├── application.properties
│ │ └── static/
│ │ └── mcp-test.html // 内置测试页面
│ └── test/
│ └── java/com/example/HelloWorldApplicationTests.java
└── README.md
- 核心组件实现
(1)SSE 端点与 POST 端点构建(SseTransport.java)
首先实现 MCP 协议要求的两通道,确保客户端能建立连接并发送请求。
SSE 端点(GET /sse):
/** * 标准MCP SSE端点 - 客户端连接此端点接收服务器推送消息 */@GetMapping(value="/sse",produces=MediaType.TEXT_EVENT_STREAM_VALUE)publicFlux<ServerSentEvent<String>>sseEndpoint(@RequestParam(required=false)StringclientId,ServerHttpRequestrequest){// 安全检查:验证Origin头防止DNS重绑定攻击Stringorigin=request.getHeaders().getFirst("Origin");if(origin!=null&&!isValidOrigin(origin)){System.err.println("Invalid origin rejected: "+origin);returnFlux.error(newSecurityException("Invalid origin"));}StringsessionId=clientId!=null?clientId:"client-"+System.currentTimeMillis();// 为每个客户端创建独立消息流Sinks.Many<ServerSentEvent<String>>sink=Sinks.many().multicast().onBackpressureBuffer();clientSinks.put(sessionId,sink);System.out.println("MCP SSE client connected: "+sessionId+" from origin: "+origin);// 30秒心跳流,维持连接Flux<ServerSentEvent<String>>heartbeat=Flux.interval(Duration.ofSeconds(30)).map(tick->ServerSentEvent.<String>builder().event("ping").data("{\"type\":\"ping\"}").build());// 合并消息流与心跳流,发送endpoint事件(MCP协议要求)returnFlux.merge(sink.asFlux(),heartbeat).doOnSubscribe(subscription->{try{StringendpointUri=getBaseUrl(request)+"/message/"+sessionId;// 发送endpoint事件,提供POST通信地址ServerSentEvent<String>endpointEvent=ServerSentEvent.<String>builder().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}",consumes=MediaType.APPLICATION_JSON_VALUE)publicMono<Void>handleSessionMessage(@PathVariableStringsessionId,@RequestBodyStringmessageJson,ServerHttpRequestrequest){returnhandleMessageInternal(sessionId,messageJson,request);}privateMono<Void>handleMessageInternal(StringsessionId,StringmessageJson,ServerHttpRequestrequest){returnMono.fromRunnable(()->{try{// 安全校验与消息解析Stringorigin=request.getHeaders().getFirst("Origin");if(origin!=null&&!isValidOrigin(origin)){System.err.println("Invalid origin rejected for message: "+origin);return;}McpRequestmcpRequest=objectMapper.readValue(messageJson,McpRequest.class);System.out.println("Received MCP request: "+mcpRequest.getMethod()+" (id: "+mcpRequest.getId()+") from session: "+sessionId);// 处理请求并通过SSE返回响应McpResponseresponse=serverHandler.handleRequest(mcpRequest);if(response!=null){sendMessageToClient(sessionId,response);}}catch(Exceptione){System.err.println("Error processing MCP message: "+e.getMessage());e.printStackTrace();// 发送错误响应try{McpRequestmcpReq=objectMapper.readValue(messageJson,McpRequest.class);McpResponseerrorResponse=newMcpResponse();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.Many<ServerSentEvent<String>>sink=clientSinks.get(sessionId);if(sink!=null){try{StringjsonData=objectMapper.writeValueAsString(message);ServerSentEvent<String>event=ServerSentEvent.<String>builder().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){Stringmethod=request.getMethod();Stringid=request.getId();Map<String,Object>params=request.getParams();try{switch(method){// 处理初始化请求:协商协议版本与能力case"initialize":returnhandleInitialize(id,params);// 处理工具列表请求:返回可用工具case"tools/list":returnhandleListTools(id);// 处理工具调用请求:执行具体工具逻辑case"tools/call":returnhandleCallTool(id,params);// 客户端初始化完成通知:无需返回响应case"notifications/initialized":this.initialized=true;System.out.println("Client initialization completed");returnnull;// 未知方法处理default:McpResponseerrorResponse=newMcpResponse();errorResponse.setId(id);errorResponse.setError(newMcpResponse.McpError(-32601,"Method not found: "+method));returnerrorResponse;}}catch(Exceptione){System.err.println("Error handling request "+method+": "+e.getMessage());McpResponseerrorResponse=newMcpResponse();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”,输入 URL:http://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 开发领域构建更灵活、更具兼容性的应用系统,为业务创新提供强大支撑。