构建一个商务网站的步骤有哪些如何做区块链网站
构建一个商务网站的步骤有哪些,如何做区块链网站,体育评论做的好的网站,上传到服务器的网站打开是空白1. 开篇#xff1a;为什么你需要了解 MCP 协议#xff1f;
如果你正在开发一个AI应用#xff0c;比如一个智能客服机器人或者一个代码助手#xff0c;你可能会遇到一个头疼的问题#xff1a;你的应用核心可能是用 Java 写的#xff0c;但网上某个特别好用的工具库却是 Py…1. 开篇为什么你需要了解 MCP 协议如果你正在开发一个AI应用比如一个智能客服机器人或者一个代码助手你可能会遇到一个头疼的问题你的应用核心可能是用 Java 写的但网上某个特别好用的工具库却是 Python 的。难道要为了这一个功能把整个项目重构成 Python 吗或者你的团队里既有 Java 高手也有 Python 专家怎么才能让大家写的工具无缝地集成到一起这就是MCPModel Context Protocol要解决的痛点。你可以把它想象成一个“万能翻译官”或者“通用插座”。它定义了一套标准化的通信协议让不同语言、不同框架写的 AI 工具我们称之为 Server能够被任何遵循 MCP 的客户端Client发现和调用。无论你的后端是 Java、Go 还是 Node.js都可以通过 MCP 去调用一个用 Python 写的天气查询工具、一个用 Rust 写的数据库操作工具而无需关心它们内部的具体实现。今天我就以一个真实的实战案例带你手把手走一遍 MCP 协议最核心的通信流程。我们会用Java 写的 Client去调用一个Python 写的 Server并通过详细的日志把从握手到工具调用的每一步都掰开揉碎讲清楚。这个过程就像两个说不同语言的人通过一本共同的词典和固定的手势协议进行合作。理解了这套流程你就能在自己的项目中轻松集成各种 MCP 工具快速扩展应用的能力边界。无论你是前端、后端还是算法工程师只要你的工作涉及 AI 应用集成这篇文章都能帮你打通任督二脉。2. 实战环境搭建与项目初始化在深入协议细节之前我们先得把“实验场地”搭起来。这次实战我们需要两个角色一个用 Python 写的 MCP 服务器Server它提供了几个工具另一个是用 Java 写的 MCP 客户端Client它负责发现并调用这些工具。2.1 Python MCP 服务器端准备首先我们准备 Server 端。这里我使用了一个简单的 Python MCP 服务器示例它提供了两个工具echo-simple简单回显和echo-with-notifications带通知的回显。后者特别有意思它会在执行过程中发送不同级别的日志通知完美演示 MCP 的异步通知机制。你需要安装mcp这个 Python 库通常可以通过 pip 安装。一个最简单的服务器代码可能长这样这里做概念性展示# 示例性代码展示工具定义 from mcp.server import Server from mcp.server.models import Tool server Server(python-mcp-server) server.list_tools() async def handle_list_tools(): return [ Tool( nameecho-simple, description简单的Echo工具直接返回输入的消息, inputSchema{ type: object, properties: {message: {type: string}}, required: [message] } ), # ... 另一个工具定义 ] # 运行服务器假设在 localhost:8080 提供 SSE 端点在实际操作中你可能会使用fastmcp或其他框架来快速创建服务器。关键点是这个服务器启动后会在http://localhost:8080/sse/这个地址提供一个Server-Sent Events (SSE)端点。SSE 是 MCP 常用的一种传输方式它允许服务器主动向客户端推送消息比如我们的通知非常适合这种需要双向通信的场景。2.2 Java MCP 客户端端准备客户端这边我们使用基于 Java 的 MCP SDK。以 Spring AI 的 MCP 客户端为例我们需要在项目中引入相关依赖。如果你用的是 Mavenpom.xml里可能需要添加这样的依赖具体版本请以官方仓库为准dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-mcp-client/artifactId version0.3.1/version /dependency客户端的核心是McpAsyncClient。在初始化时我们需要告诉它服务器的地址并配置一个传输层Transport。从日志里可以看到我们使用了一个自定义的、带日志记录的 HTTP 传输层CustomLoggingHttpClientTransport它底层封装了 HTTP Client 和 SSE 连接管理。初始化客户端的代码骨架大致如下// 示例性代码 import io.mcp.client.McpAsyncClient; import io.mcp.client.transport.http.CustomLoggingHttpClientTransport; public class McpDemo { public static void main(String[] args) { String serverUrl http://localhost:8080; // 1. 创建带日志的HTTP传输层 var transport new CustomLoggingHttpClientTransport(serverUrl); // 2. 构建MCP异步客户端 McpAsyncClient client McpAsyncClient.builder() .transport(transport) .build(); // 3. 接下来就是初始化、调用工具等流程 } }好了现在舞台已经搭好两位演员Java Client 和 Python Server各就各位。接下来好戏正式开场让我们聚焦于它们之间的第一次对话——初始化握手。3. 第一步握手initialize 请求与响应万事开头难通信也是如此。initialize是 MCP 协议规定的第一个必须发送的请求它的作用类似于两个人见面时的自我介绍和确认沟通规则。3.1 客户端发送 initialize 请求当我们的 Java 客户端调用client.initialize()方法时幕后发生了很多事情。首先传输层会建立到服务器 SSE 端点 (http://localhost:8080/sse/) 的长连接。然后客户端会精心构造一个JSON-RPC 2.0格式的请求消息。让我们仔细看看日志里截获的这个请求体它包含了建立连接所需的所有核心信息{ jsonrpc: 2.0, method: initialize, id: 5ed91d29-0, params: { protocolVersion: 2024-11-05, capabilities: {}, clientInfo: { name: Spring AI MCP Client, version: 0.3.1 } } }我来给你拆解一下这几个关键字段jsonrpc: “2.0”声明使用的 JSON-RPC 协议版本这是 MCP 的底层通信基石。method: “initialize”指明要调用的方法。id: “5ed91d29-0”请求的唯一标识符。客户端用它来匹配后续的响应这对于异步通信至关重要。你可以看到它像是一个“会话ID-序列号”的组合。params: 这才是真正传递初始化参数的地方。protocolVersion:“2024-11-05”。这是 MCP 协议本身的版本号采用日期格式。客户端告诉服务器“我理解并遵循的是 2024年11月5日 这个版本的协议规则”。如果服务器不支持这个版本理论上可以拒绝或协商。capabilities:{}。在这个例子里客户端暂时声明自己没有任何特殊的“能力”。但在更复杂的场景下这里可以声明客户端支持的特性比如是否支持某种资源订阅模式。clientInfo: 客户端的自我介绍包括名称和版本号。这有助于服务器端进行日志记录或兼容性处理。这个请求会通过一个 HTTP POST 请求发送到服务器的 SSE 端点。注意虽然连接是 SSE 用于服务器推送但客户端的请求仍然是普通的 HTTP POST。3.2 服务器响应 initialize服务器收到initialize请求后会进行校验比如协议版本是否支持然后返回一个响应。这个响应同样是一个 JSON-RPC 响应{ jsonrpc: 2.0, id: 5ed91d29-0, result: { protocolVersion: 2024-11-05, capabilities: { experimental: {}, prompts: { listChanged: true }, resources: { subscribe: false, listChanged: true }, tools: { listChanged: true } }, serverInfo: { name: python mcp server, version: 1.10.1 } } }这个响应必须使用和请求相同的id(“5ed91d29-0”)这样客户端才能知道这个响应对应的是哪个请求。result字段包含了服务器的“回礼”protocolVersion: 服务器确认使用的协议版本这里和客户端一致。capabilities: 这是重中之重。服务器详细列出了它支持哪些 MCP 功能以及这些功能的行为特性tools.listChanged:true。这非常关键它意味着服务器支持“工具列表变更通知”。如果后续有新的工具被动态加载或移除服务器会主动通过通知告诉客户端客户端就不需要频繁轮询tools/list了。resources.listChanged:true。同理表示资源列表变更也会通知。prompts.listChanged:true。提示词列表变更也会通知。resources.subscribe:false。表示这个服务器不支持客户端订阅资源的动态更新另一种更实时的资源获取模式。serverInfo: 服务器的自我介绍包含名称和版本。当 Java 客户端收到这个响应后日志显示它成功解析了这些信息并记录了下来“Server response with Protocol: 2024-11-05, Capabilities: ...”。至此握手成功双方就通信版本和基本能力达成一致。3.3 发送 initialized 通知握手成功之后协议还规定了一个小步骤客户端需要向服务器发送一个notifications/initialized通知。注意这是一个通知Notification而不是请求。JSON-RPC 中的通知没有id字段因为不需要响应。从日志中可以看到客户端发送的消息{jsonrpc:2.0,method:notifications/initialized}这个通知就像是客户端在说“嗨你刚才发的初始化参数我都收到了并且我这边也准备好了咱们可以正式开始合作了” 发送这个通知标志着初始化阶段彻底完成连接进入就绪状态。有些服务器可能会等待这个通知后才认为客户端完全激活。4. 连接保活与健康检查ping 的作用初始化完成后连接已经建立。但在长连接场景下我们怎么知道这条连接还“活着”呢网络可能不稳定服务器进程也可能挂掉。这就是ping方法的用武之地。它就像一个轻量级的心跳检测用于确认连接的健康状态。4.1 ping 请求与响应的极简美学让我们看看日志里的ping交互。客户端发送的请求非常简单{ jsonrpc: 2.0, method: ping, id: 5ed91d29-1 }可以看到params字段是空的因为ping不需要任何额外参数它的唯一目的就是探活。id变成了 “5ed91d29-1”序列号递增。服务器的响应同样极其简洁{ jsonrpc: 2.0, id: 5ed91d29-1, result: {} }result是一个空对象{}。这个空对象本身就是信号意味着“我收到了并且我活着。” 如果连接已断开或服务器无响应客户端就会收到错误或超时。在实际项目中ping可以用于连接初始化后的即时健康检查就像我们日志里展示的在initialize之后立即ping一下确保万无一失。定时保活在空闲时段客户端可以定期比如每30秒发送ping防止中间的网络设备如负载均衡器、防火墙因为连接长时间无活动而将其切断。故障诊断当工具调用失败时先发一个ping可以帮助快速定位问题是出在连接层面还是具体的工具逻辑层面。这个设计体现了 MCP 协议的务实性用最小的开销完成最关键的健康状态确认。5. 探索工具箱tools/list 获取可用工具清单确认连接健康后客户端下一步要做的就是“摸清家底”服务器到底提供了哪些工具可以调用这需要通过tools/list请求来获取工具清单。这个过程就像你去一个陌生的工具房首先要看看墙上挂着的工具清单一样。5.1 发起 tools/list 请求客户端发送的请求同样很简洁{ jsonrpc: 2.0, method: tools/list, id: 5ed91d29-2, params: {} }目前params也是空的但协议为未来预留了扩展空间比如可以用于过滤特定类型的工具。5.2 解析服务器返回的工具元数据服务器的响应则是信息量巨大它返回了一个包含所有工具定义的数组。我们结合日志重点看一下返回的两个工具{ jsonrpc: 2.0, id: 5ed91d29-2, result: { tools: [ { name: echo-simple, description: 简单的Echo工具直接返回输入的消息不包装JSON格式\n\nArgs:\n message: 要回显的消息\n \nReturns:\n 相同的输入消息, inputSchema: { properties: { message: { title: Message, type: string } }, required: [message], type: object }, outputSchema: { properties: { result: { title: Result, type: string } }, required: [result], title: _WrappedResult, type: object, x-fastmcp-wrap-result: true } }, { name: echo-with-notifications, description: 带不同级别通知的Echo工具演示MCP日志通知功能\n\nArgs:\n message: 要回显的消息\n notification_level: 通知级别\n \nReturns:\n 处理后的消息, inputSchema: { properties: { message: { description: 要回显的消息, title: Message, type: string }, notification_level: { default: info, description: 通知级别 (debug/info/warning/error), title: Notification Level, type: string } }, required: [message], type: object }, outputSchema: { ... } // 结构与第一个工具类似 } ] } }这份工具清单不仅仅是名字列表它遵循JSON Schema标准提供了完整的“使用说明书”name: 工具的唯一标识符调用时必须指定。description: 人类可读的描述甚至包含了参数说明和返回值说明非常清晰。inputSchema:输入参数的严格模式定义。这是实现跨语言调用的关键对于echo-simple它要求一个名为message的字符串类型参数且是必须的 (required)。对于echo-with-notifications它要求必须的message参数和一个可选的notification_level参数默认值为info且描述中限定了可选值范围。客户端在调用前可以根据这个 schema 验证自己准备传入的参数是否合法。outputSchema: 定义了工具返回值的结构。两个工具都返回一个包含result字符串字段的对象。x-fastmcp-wrap-result这样的扩展属性可能被特定的 SDK如 fastmcp用于内部处理。客户端 SDK如 Spring AI MCP Client在收到这个列表后通常会将这些工具元数据缓存起来并可能将其动态注册为本地可调用的方法或函数让开发者可以像调用本地 API 一样方便地调用远程工具。至此客户端已经掌握了服务器的全部“技能列表”接下来就可以按需取用了。6. 核心操作tools/call 执行工具与处理异步通知准备工作全部就绪现在进入最激动人心的环节实际调用一个工具。我们选择调用功能更丰富的echo-with-notifications工具因为它会触发 MCP 另一个强大的特性——异步通知。6.1 构造并发送 tools/call 请求调用工具需要告诉服务器两件事调用哪个工具以及传入什么参数。对应的 JSON-RPC 请求如下{ jsonrpc: 2.0, method: tools/call, id: 5ed91d29-3, params: { name: echo-with-notifications, arguments: { notification_level: warning, message: 测试通知功能 } } }method固定为tools/call。params对象包含两个核心属性name: 指定要调用的工具名称必须与tools/list返回的名称完全一致。arguments: 一个对象其键值对必须符合该工具inputSchema的定义。这里我们传入了message和notification_level指定为warning。这个请求被发送后服务器端的 Python 工具函数开始执行。根据工具描述它会根据notification_level参数发送不同级别的日志通知。重点来了这些通知是在工具执行过程中、最终结果返回之前由服务器主动、异步地推送给客户端的。6.2 处理异步通知notifications/message在等待工具执行结果的同时客户端陆续收到了三条notifications/message通知。请注意这些是JSON-RPC 通知没有id字段因为它们是服务器单向发起的不需要客户端回应。我们看第一条通知{ jsonrpc: 2.0, method: notifications/message, params: { level: debug, data: 这是一个调试级别的通知 } }method:notifications/message表明这是一个日志/消息通知。params:level: 通知级别这里是debug。MCP 通常支持类似日志的级别如 debug, info, warning, error。data: 通知的具体内容。紧接着客户端又收到了level为info和warning的通知。日志显示Java 客户端的McpAsyncClient有一个LoggingConsumer处理了这些通知并将它们输出到了客户端的日志系统中。这种机制非常有用它允许服务器端工具在长时间运行的计算中向客户端反馈进度、中间状态或调试信息极大地改善了用户体验和调试便利性。你可以想象一个文件处理工具在处理过程中发送“开始读取”、“处理中 50%”、“完成写入”等通知。6.3 接收最终调用结果在所有通知发送完毕后服务器送出了本次工具调用的最终结果{ jsonrpc: 2.0, id: 5ed91d29-3, result: { content: [ { type: text, text: {\result\: true, \message\: \带warning级别通知的Echo成功\, \timestamp\: \2025-07-19T22:13:36.819802Z\, \tool_name\: \echo_with_notifications\, \data\: {\echo_message\: \测试通知功能\, \notification_level\: \warning\, \notifications_sent\: [\debug\, \info\, \warning\]}} } ], structuredContent: { result: {\result\: true, \message\: \带warning级别通知的Echo成功\, \timestamp\: \2025-07-19T22:13:36.819802Z\, \tool_name\: \echo_with_notifications\, \data\: {\echo_message\: \测试通知功能\, \notification_level\: \warning\, \notifications_sent\: [\debug\, \info\, \warning\]}} }, isError: false } }这个响应结构是 MCP 为工具调用结果设计的标准格式content: 一个数组通常包含一个或多个内容块。这里是一个type为text的块其text字段是一个 JSON 字符串。这种设计允许返回富文本、图像引用等多种类型的内容。structuredContent: 一个更结构化的表示。在这个例子里它把上述 JSON 字符串直接放在了result字段里。在实际应用中这里可以是任何符合outputSchema的结构化数据。isError: 明确指示本次调用是否出错。客户端解析这个响应提取出structuredContent.result里的 JSON 字符串再反序列化就能得到工具执行的实际结果一个包含成功标志、消息、时间戳和详细数据的对象。至此一次完整的工具调用流程结束。客户端成功地将参数跨语言传递给 Python 工具执行后不仅拿到了结果还实时接收了执行过程中的状态通知。7. 优雅收尾连接关闭与资源清理所有操作完成后需要优雅地关闭连接释放资源。客户端调用closeGracefully()方法。从日志 CLOSING MCP CONNECTION 可以看到客户端会发起 graceful shutdown。关闭底层的 HTTP 传输层。关闭 SSE 连接。对于服务器端当检测到客户端连接关闭时也应该清理与该会话相关的资源。这种显式的关闭机制确保了网络连接和内存资源能够被及时释放避免资源泄漏尤其是在高频或长生命周期的应用场景中非常重要。8. 实战踩坑与关键点总结走完整个流程看起来挺顺畅的但在实际集成中我也遇到过几个典型的“坑”。这里分享给你希望能帮你节省时间。第一坑协议版本与能力协商。初期我用的客户端 SDK 版本比较老其默认的protocolVersion与较新的服务器不兼容导致initialize失败。一定要检查客户端和服务器各自支持/声明的协议版本。capabilities字段也很重要如果你依赖tools/listChanged通知来动态更新工具列表但服务器返回的是false那你的客户端就需要实现轮询机制作为备选方案。第二坑SSE 连接管理与超时。MCP 常用 SSE 作为传输层它基于 HTTP 长连接。在一些网络环境如公司代理背后或云服务如某些 Serverless 环境中长连接可能被意外中断。务必在客户端实现重连逻辑和心跳ping机制。我们的示例中在初始化后立即 ping 了一次但在生产环境可能需要一个定时器周期性发送 ping。第三坑通知的处理与消费。就像我们例子中的echo-with-notifications工具可能在result返回前发送多条通知。客户端的 SDK 需要妥善处理这些异步消息避免阻塞等待最终结果。要确保你的通知处理器LoggingConsumer是线程安全或非阻塞的防止它拖慢整个消息处理循环。第四坑参数格式与 JSON Schema 验证。在构造tools/call的arguments时一定要严格按照tools/list返回的inputSchema来。数据类型string, number, boolean、是否必需required、默认值等都要匹配。我建议在客户端调用前先用一个轻量级的 JSON Schema 验证器对参数做一次预校验这样可以提前发现参数错误而不是等到服务器返回一个晦涩的验证错误。理解了从initialize握手、ping保活、tools/list发现到tools/call调用并处理notifications的完整闭环你就掌握了 MCP 协议最核心的交互模式。这套模式清晰、解耦使得跨语言的工具集成变得标准化和可管理。下次当你需要让 Java 服务去调用一个 Python 的机器学习模型或者让 Node.js 应用使用一个 Go 写的数据库工具时不妨考虑一下 MCP 这个“万能翻译官”。