MCP 2.0 协议详解:从资源与工具范式到实战服务器搭建
1. 先搞清楚 MCP 2.0 到底要解决什么问题如果你在开发 AI 应用特别是那些需要让大模型比如 Claude、GPTs去操作外部工具、读取数据库或调用 API 的场景那你大概率听说过或者用过 MCPModel Context Protocol。简单说它就是一个让 AI 模型和外部工具、数据源安全、标准化通信的协议。这次提到的 2026-07-28 的重大变更指向的是 MCP 2.0。这个版本不是小修小补而是一次架构上的重要演进。最核心的变化是从过去相对松散的“客户端-服务器”模型转向了更清晰、更强大的“资源Resources与工具Tools” 范式。这意味着什么在 1.x 时代你可能需要为不同的数据源比如数据库、文件系统、API写各种自定义的通信逻辑模型调用起来也比较“原生态”。而 2.0 的核心思想是把一切外部能力都抽象成两类东西资源Resources可以理解为“只读”的数据视图。比如一个数据库表、一个文件夹下的文件列表、一个网页的当前内容。AI 模型可以查询read这些资源来获取信息但不能直接修改。工具Tools可以理解为“可执行”的操作。比如执行一个 SQL 查询、写入一个文件、调用一个特定的 API 端点。AI 模型可以调用call这些工具来改变外部状态。这种抽象带来的最大好处是标准化和安全性。开发者现在可以用统一的方式向 AI 模型暴露能力而模型侧如 Claude Desktop、各类 AI 应用框架也能用统一的方式来发现、理解和使用这些能力。对于使用者来说最直观的感受就是AI 助手能更稳定、更可控地使用你配置好的工具而不是“自由发挥”可能带来风险。所以如果你在关注 Claude 插件、LangChain Tools、或是自己搭建 AI Agent 系统理解 MCP 2.0 的这次变更是决定你未来技术栈是否跟得上节奏的关键一步。它解决的正是 AI 应用落地时那个最头疼的“如何让模型安全、可靠地使用外部能力”的问题。2. 从 1.x 到 2.0核心概念迁移与实操影响这次变更不是简单的 API 版本号升级很多底层概念都变了。如果你有基于 MCP 1.x 的现有项目或者正在评估相关技术需要重点关注以下几个迁移点。2.1 “采样Sampling”彻底成为历史在 MCP 1.x 中有一个核心概念叫sampling服务器可以主动向客户端推送内容。在 2.0 中这个概念被完全移除了。所有的数据交互现在都通过read_resource读资源和call_tool调工具这两个核心操作来完成。这对开发者的影响是你的服务器代码MCP Server需要重新设计。以前你可能写了一个主动推送日志或事件的逻辑现在必须改造成一个“资源”。比如你可以创建一个名为recent_logs的资源AI 客户端通过read_resource来获取它而不是服务器主动sampling过去。示例对比1.x 风格已过时服务器“我这里有新的日志推给你。”2.0 风格客户端“请把recent_logs这个资源的内容读给我。”这种改变使得数据流更加清晰和“拉取式”pull-based符合 RESTful 的设计思想也更容易控制和管理。2.2 全新的协议初始化与能力协商流程MCP 2.0 引入了一个更正式的握手和能力协商阶段。服务器启动时需要向客户端明确宣告自己支持哪些capabilities能力比如支持哪些资源类型、哪些工具。客户端也会宣告自己需要什么。实操步骤连接建立客户端如 Claude Desktop连接到你的 MCP 服务器。初始化交换双方交换initialize请求和响应。在这个响应里服务器必须明确列出它提供的所有resources和tools。能力协商客户端根据服务器宣告的能力决定如何展示和使用这些功能和工具。这意味着你的服务器代码在启动时必须能动态或静态地生成一份完整的“能力清单”。这份清单就是 AI 模型理解你能做什么的“菜单”。2.3 资源Resources的详细定义与使用资源是 2.0 的基石。一个资源必须包含以下几个关键属性uri资源的唯一标识符类似一个 URL。例如file:///path/to/doc.md或db://sales/customers。mimeType资源的媒体类型告诉客户端如何解析内容。例如text/plainapplication/json。name和description人类可读的名称和描述用于 AI 模型理解这个资源是什么。如何暴露一个资源在你的 MCP Server 代码中你需要在初始化响应或后续的list_resources调用中返回资源列表。当客户端调用read_resource时你根据传入的uri返回实际的内容。示例概念性代码# 假设在 MCP Server 的初始化逻辑中 async def handle_initialize(): return { capabilities: {...}, serverInfo: {...}, resources: [ { uri: file:///projects/README.md, name: 项目说明文档, description: 主项目的 README 文件, mimeType: text/markdown }, { uri: weather://beijing, name: 北京天气, description: 获取北京当前的天气信息, mimeType: application/json } ], tools: [...] }2.4 工具Tools的输入输出标准化工具的定义也更加规范。每个工具需要明确其name工具名。description工具描述这个描述至关重要AI 模型主要靠它来决定是否以及如何调用这个工具。inputSchema输入参数的 JSON Schema。这定义了调用工具时需要传递什么样的数据。调用流程客户端AI模型决定调用某个工具。它根据inputSchema生成结构化的输入参数。向服务器发送call_tool请求。服务器执行实际逻辑如运行脚本、查询数据库。服务器返回结构化的结果通常包含content数组里面可以是文本、图片等。这种标准化使得AI 模型调用更准确清晰的 Schema 减少了模型“猜”参数的情况。开发调试更方便输入输出格式固定易于测试。工具组合更灵活因为接口一致工具之间更容易串联。3. 环境准备与实战搭建你的第一个 MCP 2.0 服务器理论讲完了我们动手搭一个。这里以 Python 为例因为 Python 的生态在 AI 和工具集成方面非常丰富。TypeScript/Node.js 和 Go 的社区也有很好的支持原理相通。3.1 前置条件与依赖选择首先你需要一个 Python 环境3.8。不建议用系统自带的 Python容易产生依赖冲突。使用 Conda 或 venv 创建独立环境是更好的选择。核心 SDKMCP 官方和社区提供了多个 SDK 来简化开发。对于 Python最主流的是mcp库。你可以通过 pip 安装pip install mcp这个库提供了编写 MCP Server 和 Client 所需的所有底层协议处理和类型定义。对于快速原型你也可以考虑使用mcp-cli或一些高阶框架如基于 FastAPI 封装的库它们能帮你处理更多样板代码。但对于理解原理我们从基础的mcp库开始。开发工具测试客户端你需要一个 MCP 客户端来测试你的服务器。Claude Desktop是目前最方便的选择它内置了 MCP 客户端支持。你也可以用mcp库写一个简单的测试客户端。代码编辑器VS Code 配合 Python 插件即可。确保你的 VS Code Python 环境配置指向了正确的虚拟环境。3.2 项目结构与最小化服务器代码创建一个新的项目目录例如my-mcp-server。my-mcp-server/ ├── server.py # 你的 MCP 服务器主文件 ├── pyproject.toml # 项目依赖声明可选但推荐 └── README.md下面是一个最简单的 MCP 2.0 服务器示例它暴露了一个“获取服务器时间”的工具和一个“服务器信息”的资源。# server.py import asyncio from datetime import datetime from mcp import Server, StdioServerParameters from mcp.types import Tool, Resource, TextContent # 1. 定义我们提供的工具列表 TOOLS [ Tool( nameget_current_time, description获取服务器的当前日期和时间。, inputSchema{ type: object, properties: { format: { type: string, description: 时间格式例如 %Y-%m-%d %H:%M:%S。留空则使用默认格式。, default: %Y-%m-%d %H:%M:%S } } } ) ] # 2. 定义我们提供的资源列表 RESOURCES [ Resource( urimcp://myserver/info, name服务器信息, description关于这个 MCP 服务器的基本信息。, mimeTypetext/plain ) ] async def handle_list_tools(): 处理客户端请求工具列表 return TOOLS async def handle_list_resources(): 处理客户端请求资源列表 return RESOURCES async def handle_read_resource(uri: str): 处理客户端读取资源的请求 if uri mcp://myserver/info: # 返回服务器信息 content TextContent( typetext, text这是一个示例 MCP 2.0 服务器。\n提供时间查询工具和本信息资源。 ) return [content] else: # 资源未找到 raise ValueError(f未知资源: {uri}) async def handle_call_tool(name: str, arguments: dict | None): 处理客户端调用工具的请求 if name get_current_time: fmt arguments.get(format, %Y-%m-%d %H:%M:%S) if arguments else %Y-%m-%d %H:%M:%S current_time datetime.now().strftime(fmt) content TextContent(typetext, textf服务器当前时间: {current_time}) return [content] else: # 工具未找到 raise ValueError(f未知工具: {name}) async def main(): 创建并运行 MCP 服务器 # 创建 Server 实例 server Server( # 这里我们使用标准输入输出stdio作为传输层这是与 Claude Desktop 等客户端通信的常见方式。 server_paramsStdioServerParameters( commandpython, # 解释器 args[server.py], # 脚本 ) ) # 注册请求处理器 server.set_request_handlers( list_toolshandle_list_tools, list_resourceshandle_list_resources, read_resourcehandle_read_resource, call_toolhandle_call_tool, ) # 运行服务器这会阻塞直到连接关闭 async with server: await server.run() if __name__ __main__: asyncio.run(main())3.3 运行与测试连接 Claude Desktop配置 Claude Desktop打开 Claude Desktop 设置Settings。找到 “Developer” 或 “MCP Servers” 部分。点击 “Add Server” 或编辑配置文件通常是claude_desktop_config.json。添加你的服务器配置。配置方式因版本而异常见的是指定一个命令行{ mcpServers: { my-time-server: { command: /path/to/your/python, args: [/path/to/your/my-mcp-server/server.py], env: { PYTHONPATH: /path/to/your/my-mcp-server } } } }保存配置并重启 Claude Desktop。验证连接重启后在 Claude 的聊天界面你应该能看到一个提示表示已连接新的 MCP 服务器。你可以直接问 Claude“你能用什么工具” 或者 “有什么资源”。Claude 应该会列出你定义的get_current_time工具和mcp://myserver/info资源。尝试让 Claude 调用工具“请使用 get_current_time 工具获取当前时间格式用 ISO 格式。” Claude 应该能成功调用并返回结果。成功的关键标志Claude 能正确列出你的工具和资源。能成功调用工具并返回预期格式的结果。能成功读取资源内容。在 Claude Desktop 的后台或你的服务器日志中没有持续的报错。4. 进阶实战构建实用的文件系统浏览器服务器一个“获取时间”的服务器演示了概念但不够实用。我们构建一个更实用的例子一个简单的文件系统浏览器 MCP 服务器。它允许 AI 模型列出指定目录下的文件并读取文本文件的内容。设计思路工具提供一个list_directory工具输入路径返回文件列表。资源将每个文件视为一个资源。list_directory工具返回的结果中包含每个文件的资源 URI如file:///path/to/file.txt。AI 模型随后可以通过read_resource来读取具体文件内容。# file_server.py import asyncio import os from pathlib import Path from mcp import Server, StdioServerParameters from mcp.types import Tool, Resource, TextContent, ResourceTemplate from typing import List # 安全考虑定义一个允许访问的根目录防止 AI 访问系统敏感文件。 ALLOWED_ROOT Path.home() / Documents / mcp_accessible # 例如用户文档下的一个子目录 ALLOWED_ROOT.mkdir(parentsTrue, exist_okTrue) # 确保目录存在 def is_path_allowed(requested_path: Path) - bool: 检查请求的路径是否在允许的根目录下 try: # 解析规范路径防止通过 ../ 跳出限制 resolved requested_path.resolve() return resolved.is_relative_to(ALLOWED_ROOT.resolve()) except ValueError: return False async def handle_list_tools(): tools [ Tool( namelist_directory, description列出指定目录下的文件和子目录。, inputSchema{ type: object, properties: { path: { type: string, description: 要列出的目录路径。默认为可访问的根目录。, } }, required: [path] } ) ] return tools async def handle_list_resources(): # 初始状态下我们可以只返回根目录作为一个资源或者返回空。 # 更动态的做法是在 list_directory 被调用后将列出的文件作为资源“宣告”出去。 # MCP 2.0 支持动态资源这里我们先返回一个根目录资源。 resources [ Resource( uriffile://{ALLOWED_ROOT}, name可访问文件根目录, descriptionf允许 MCP 访问的根目录: {ALLOWED_ROOT}, mimeTypetext/plain ) ] return resources async def handle_read_resource(uri: str): 读取文件资源 if not uri.startswith(file://): raise ValueError(f不支持的资源 URI 协议: {uri}) file_path Path(uri[7:]) # 去掉 file:// 前缀 if not is_path_allowed(file_path): raise PermissionError(f无权访问路径: {file_path}) if not file_path.is_file(): raise FileNotFoundError(f不是文件或文件不存在: {file_path}) # 简单处理只读取文本文件 try: content file_path.read_text(encodingutf-8) return [TextContent(typetext, textcontent)] except UnicodeDecodeError: return [TextContent(typetext, textf[二进制文件无法直接显示] {file_path})] async def handle_call_tool(name: str, arguments: dict | None): if name list_directory: if not arguments or path not in arguments: target_path ALLOWED_ROOT else: target_path Path(arguments[path]) # 安全检查 if not is_path_allowed(target_path): raise PermissionError(f无权访问路径: {target_path}) if not target_path.exists() or not target_path.is_dir(): raise ValueError(f路径不存在或不是目录: {target_path}) items [] for item in target_path.iterdir(): item_type directory if item.is_dir() else file items.append({ name: item.name, type: item_type, uri: ffile://{item} if item.is_file() else None, # 文件才有可读的 URI size: item.stat().st_size if item.is_file() else None }) # 将结果格式化为易读的文本并附上资源信息 result_text f目录 {target_path} 下的内容\n for item in items: result_text f- {item[name]} ({item[type]}) if item[uri]: result_text f [可读资源] result_text \n # 除了返回文本我们还可以在 content 中携带结构化数据如果客户端支持。 # 这里我们主要返回文本内容。 return [TextContent(typetext, textresult_text)] else: raise ValueError(f未知工具: {name}) async def main(): server Server(server_paramsStdioServerParameters( commandpython, args[file_server.py], )) server.set_request_handlers( list_toolshandle_list_tools, list_resourceshandle_list_resources, read_resourcehandle_read_resource, call_toolhandle_call_tool, ) async with server: await server.run() if __name__ __main__: asyncio.run(main())如何使用这个服务器在~/Documents/mcp_accessible目录下放一些.txt或.md文件。将上述配置添加到 Claude Desktop。在 Claude 中你可以说“请使用 list_directory 工具列出可访问根目录下的文件。”Claude 会调用工具并返回列表。你可以接着说“请读取文件file:///.../example.txt的内容。” Claude 就会通过read_resource获取文件内容并展示给你。这个例子体现了 MCP 2.0 的核心价值工具与资源分离list_directory是工具它执行一个操作列出文件。资源作为数据载体列出的每个文件本身是一个资源可以通过标准化的read_resource接口读取。安全性通过路径检查限制了 AI 的访问范围。5. 迁移、调试与生产化考量如果你有旧的 MCP 1.x 服务器或者打算将新服务器用于更严肃的场景以下几点需要重点考虑。5.1 从 MCP 1.x 迁移到 2.0 的检查清单移除所有sampling相关代码查找并删除任何主动推送数据的逻辑。重构能力暴露将你的服务能力重新分类为“资源”或“工具”。资源用于查询状态、获取数据如数据库表、配置文件、日志。工具用于执行动作、修改状态如运行命令、发送请求、写入文件。重写初始化逻辑确保在initialize响应中正确填写resources和tools列表。更新请求处理器实现新的list_resourcesread_resourcelist_toolscall_tool处理器。测试协议兼容性使用最新的 Claude Desktop 或 MCP SDK 的测试客户端进行全面测试。5.2 常见问题与调试技巧问题Claude Desktop 连接失败提示“初始化错误”或“协议不支持”。排查首先检查你的服务器initialize响应格式是否符合 MCP 2.0 规范。确保resources和tools字段存在且是正确格式的数组。使用print或日志将初始化响应输出到控制台仔细核对。技巧可以先写一个极简的、只返回固定资源的服务器确认基础协议通信正常。问题AI 模型看不到我提供的工具/资源。排查检查handle_list_tools和handle_list_resources返回的列表是否正确。特别注意description字段AI 模型严重依赖它来理解工具用途。描述要清晰、具体。技巧在 Claude 中直接提问“你能用什么工具”或“列出所有资源”看它的回答是否完整。问题调用工具时参数错误或结果不符合预期。排查在handle_call_tool函数内部打印接收到的name和arguments确认 AI 传递的参数与你定义的inputSchema匹配。确保你的工具逻辑能处理边界情况如参数为空、路径不存在等。技巧为你的工具设计清晰的、结构化的inputSchema。使用typepropertiesrequireddescription等字段充分定义输入。问题服务器进程意外退出或卡住。排查检查是否有未捕获的异常。在main()函数和各个async handler中添加 try-catch 块记录错误日志。确保异步函数正确使用了await。技巧使用logging模块替代print可以更好地记录不同级别的信息便于在后台查看。5.3 生产环境部署建议安全性是第一位的权限最小化像上面的文件服务器例子一样严格限制 MCP 服务器能访问的系统资源文件、网络、命令。输入验证与清理对所有来自客户端的输入如工具参数、资源 URI进行严格的验证、转义和权限检查。沙箱考虑对于执行任意代码或命令的工具考虑在容器或沙箱环境中运行。健壮性与可观测性完善的错误处理服务器不能因为一个错误请求而崩溃。返回结构化的错误信息给客户端。日志记录记录重要的操作、错误和性能指标。这对于调试和审计至关重要。资源管理如果你的工具操作耗时或耗资源如大文件处理、复杂查询考虑实现超时、取消机制和并发控制。性能与扩展连接管理MCP 服务器可能是常驻进程处理好客户端连接的生命周期。状态管理MCP 协议本身是无状态的但你的服务器可能需要管理一些会话或缓存状态。设计好状态清理机制。考虑使用高阶框架当你的服务器变得复杂时可以考虑使用社区提供的、基于流行 Web 框架如 FastAPI的 MCP 封装库它们能帮你处理更多基础设施问题。MCP 2.0 的这次变革为 AI 与外部世界的交互奠定了更坚实、更标准化的基础。它要求开发者以更结构化的方式思考 AI 的能力边界。对于使用者而言未来我们配置给 AI 助手的“技能包”将会更加模块化、安全且易于管理。虽然迁移有成本但长远来看拥抱这个新范式能让你的 AI 应用更可靠、更强大。