1. 项目概述为什么我们需要 MCP如果你最近在折腾 AI 应用开发尤其是想让大模型比如 Claude、GPT-4去操作你电脑上的文件、查询数据库或者调用某个特定的 API那你大概率会遇到一个头疼的问题怎么让 AI 安全、可控地“伸手”去干这些活过去我们通常有两种做法。一种是写死在应用代码里硬编码一堆工具函数告诉 AI “你可以调用这个、那个”。这种做法极其僵化每加一个新功能都得改代码、重新部署。另一种是让 AI 直接去执行代码或命令这听起来很强大但无异于把系统 root 权限交给了 AI安全风险高到令人睡不着觉。就在这个节点上MCPModel Context Protocol出现了。你可以把它理解为 AI 应用连接外部世界的“USB 标准协议”。在 MCP 出现之前每个 AI 应用想连接外部工具都得自己造一套“插口”和“数据线”彼此不通用开发效率低用户体验割裂。MCP 的目标就是定义一套标准化的“插口”规格让工具Server和应用Client可以即插即用。我第一次深入接触 MCP 是在尝试为团队内部的一个数据分析助手添加自定义数据源时。当时我们试过各种临时方案要么权限管理混乱要么扩展起来极其麻烦。直到看到 MCP 的协议设计那种“终于有人把这事儿想明白了”的感觉非常强烈。它不仅仅是一个技术规范更是一种对 AI 应用架构范式的重新思考——将工具能力与 AI 主体解耦通过标准协议进行安全、声明式的交互。简单来说MCP 解决的核心痛点是如何让 AI 应用动态、安全、标准化地获取和使用外部工具与数据而无需为每一个工具重写一遍集成代码。它适合所有正在构建或使用复杂 AI 应用的开发者、产品经理和技术决策者。无论你是想给 Cursor、Claude Desktop 这类 AI 智能体添加新能力还是想为自己公司内部的 AI 平台构建可插拔的工具生态MCP 都提供了一个优雅且强大的基础。2. MCP 核心架构与设计哲学拆解要理解 MCP不能只看它定义了哪些 API 接口更要理解其背后的设计哲学。它的核心思想是“资源Resources与工具Tools的声明式供给”。2.1 核心组件与交互模型MCP 的架构非常清晰主要包含三个角色MCP 客户端Client通常是 AI 应用本身比如 Claude Desktop、Cursor IDE或者你自己写的 AI 助手。Client 的核心职责是运行大模型并根据模型的需求向 Server 请求资源或调用工具。MCP 服务器Server提供具体能力和数据的服务端。一个 Server 可以暴露多种资源如文件、数据库表、API 文档和工具如执行命令、发送邮件、查询天气。例如一个filesystem-mcpServer 可以提供文件读写能力一个sqlite-mcpServer 可以提供数据库查询能力。MCP 协议Protocol连接 Client 和 Server 的标准化通信层。它基于 JSON-RPC 2.0定义了 Server 如何向 Client 宣告“我有什么”资源列表、工具列表以及 Client 如何请求“我要什么”读取资源、调用工具。这种架构带来的最大好处是解耦和组合。AI 应用Client不需要知道工具的具体实现它只需要懂得 MCP 协议。同样工具提供方Server也只需要实现 MCP 协议就可以被任何兼容 MCP 的 Client 使用。这就像你买了一个 USB 接口的硬盘可以插在电脑、电视或者游戏机上使用而不需要为每个设备定制驱动。2.2 两大核心概念资源Resources与工具Tools这是 MCP 协议中最精髓的部分理解了它们就理解了 MCP 的威力。资源Resources是静态或半静态的数据可以被 AI 读取以丰富其上下文Context。每个资源有一个唯一的uri如file:///path/to/doc.md或db://customers/schema和mimeType。Client 可以通过resources/list和resources/read来发现和获取资源内容。设计意图将外部数据“注入”到 AI 的提示词Prompt中。例如Server 可以将项目目录下的README.md和requirements.txt作为资源暴露给 AIAI 在回答关于项目的问题时就能自动引用这些文件的内容。与普通文件读取的区别MCP 的资源是声明式的、带语义的。Server 可以决定暴露哪些资源、以什么格式mimeType暴露。比如一个 SQLite Server 不是暴露整个.db文件而是将数据库的表结构DDL作为text/plain资源将查询结果作为application/json资源暴露这样对 AI 更友好。工具Tools是动态的能力可以被 AI 调用来执行操作、产生副作用。每个工具有一个name、description和输入参数的inputSchema遵循 JSON Schema。Client 通过tools/call来调用工具。设计意图让 AI 能够安全地执行动作。工具的inputSchema是对 AI 的强约束它明确告诉 AI“调用我这个工具你需要提供哪些参数每个参数是什么类型、有什么格式要求。” 这极大地提高了调用的准确性和安全性。与直接执行代码的区别工具调用是经过 Server 封装和校验的。例如一个execute_command工具Server 可以限制只能执行某些安全命令列表里的内容并对参数进行严格的清洗和校验避免了 AI 直接生成rm -rf /这种灾难性命令。资源和工具的关系它们常常配合使用。例如AI 先通过读取数据库模式资源了解了表结构然后通过调用执行SQL查询工具来获取具体数据。这种“先看说明书再操作机器”的流程非常符合人类和AI的认知习惯。2.3 协议层基于 JSON-RPC 2.0 的通信MCP 选择 JSON-RPC 2.0 作为底层通信协议是一个务实且高明的选择。简单通用JSON 格式几乎被所有编程语言支持RPC远程过程调用模型也非常符合“Client 调用 Server 提供的方法”这一心智模型。双向通信JSON-RPC 2.0 支持请求Request、响应Response、通知Notification和错误Error。这使得 MCP 不仅能处理 Client 的主动请求还能支持 Server 主动推送例如通知 Client 某个文件资源发生了变更。标准化传输MCP 协议本身不绑定于特定的传输层Transport。它可以通过标准输入输出stdio、HTTP或SSEServer-Sent Events来传输 JSON-RPC 消息。这使得 MCP Server 可以以多种形式部署本地进程stdio最常见的方式。Client 作为一个父进程启动 Server 子进程两者通过管道stdin/stdout通信。这种方式隔离性好部署简单。远程服务HTTP/SSEServer 作为一个独立的 HTTP 服务运行Client 通过网络连接。这便于工具能力的集中管理和跨网络调用。注意在实践中最常用的是 stdio 模式因为它天然适合将工具作为本地辅助进程集成到桌面 AI 应用中。你需要确保你的 Server 程序能够正确处理来自 stdin 的请求并将响应写入 stdout。3. 从零实现一个 MCP 服务器以“待办事项列表”为例理论讲得再多不如动手实现一个。我们来实现一个最简单的todo-mcp-server它提供一个资源当前待办列表和一个工具添加待办项。我们将使用 Node.js 和官方modelcontextprotocol/sdk来开发。3.1 环境准备与项目初始化首先确保你安装了 Node.js版本 18 或以上。然后创建一个新目录并初始化项目mkdir todo-mcp-server cd todo-mcp-server npm init -y安装 MCP SDKnpm install modelcontextprotocol/sdk创建一个入口文件index.js。我们先引入 SDK 并创建一个 Server 实例import { Server } from modelcontextprotocol/sdk/server/index.js; import { StdioServerTransport } from modelcontextprotocol/sdk/server/stdio.js; // 初始化一个内存中的待办列表 let todos [ { id: 1, task: 学习 MCP 协议, completed: false }, { id: 2, task: 编写示例 Server, completed: false } ]; // 创建 Server 实例 const server new Server( { name: todo-mcp-server, version: 0.1.0, }, { capabilities: { // 声明本 Server 支持资源Resources和工具Tools resources: {}, tools: {}, }, } );3.2 实现资源Resources供给资源的核心是提供uri和内容。我们要暴露一个资源其uri为todo://list内容是目前所有的待办事项。我们需要为 Server 设置处理程序handler。当 Client 请求resources/list时我们返回资源列表当 Client 请求resources/read时我们根据uri返回具体内容。// 处理 resources/list 请求列出所有可用的资源 server.setRequestHandler(resources/list, async () { return { resources: [ { // 资源的唯一标识符 uri: todo://list, // 资源的媒体类型这里我们用 JSON mimeType: application/json, // 资源的名称便于 Client 展示 name: Current Todo List, // 可选描述 description: The current list of all todo items., }, ], }; }); // 处理 resources/read 请求根据 uri 读取特定资源的内容 server.setRequestHandler(resources/read, async (request) { const { uri } request.params; if (uri todo://list) { // 将内存中的 todos 数组序列化为 JSON 字符串作为资源内容 return { contents: [ { uri: uri, mimeType: application/json, // 注意text 字段需要是字符串 text: JSON.stringify(todos, null, 2), }, ], }; } // 如果请求了不存在的资源抛出一个错误 throw new Error(Resource not found: ${uri}); });3.3 实现工具Tools调用工具的核心是定义输入模式inputSchema和执行函数。我们要创建一个add_todo工具它接收一个字符串参数task然后向待办列表添加一个新项。首先我们需要在 Server 初始化时声明的capabilities中更详细地定义我们提供的工具const server new Server( { name: todo-mcp-server, version: 0.1.0, }, { capabilities: { resources: {}, tools: { // 声明本 Server 提供的工具列表 toolList: [ { name: add_todo, description: Add a new item to the todo list., // 定义工具的输入参数模式使用 JSON Schema inputSchema: { type: object, properties: { task: { type: string, description: The description of the new todo item., }, }, required: [task], }, }, ], }, }, } );然后设置处理tools/call请求的 handler// 处理 tools/call 请求执行具体的工具 server.setRequestHandler(tools/call, async (request) { const { name, arguments: args } request.params; if (name add_todo) { const { task } args; if (!task || typeof task ! string) { throw new Error(Invalid argument: task must be a non-empty string.); } // 创建新的待办项 const newTodo { id: todos.length 1, task: task, completed: false, }; todos.push(newTodo); // 返回执行结果给 Client return { content: [ { type: text, text: Successfully added todo: ${task}. Total todos: ${todos.length}, }, // 我们也可以选择将更新后的列表作为资源内容一并返回 { type: resource, resource: { uri: todo://list, mimeType: application/json, text: JSON.stringify(todos, null, 2), }, }, ], }; } throw new Error(Unknown tool: ${name}); });3.4 启动服务器与连接测试最后我们需要启动 Server并指定使用 stdio 传输方式这样它才能通过管道与 Client如 Claude Desktop通信。// 启动 Server使用标准输入输出作为传输层 async function runServer() { const transport new StdioServerTransport(); await server.connect(transport); console.error(Todo MCP Server is running on stdio...); } runServer().catch((error) { console.error(Server error:, error); process.exit(1); });现在一个最简单的 MCP Server 就完成了。你可以通过node index.js来运行它但它现在只是等待来自 stdin 的输入。真正的测试需要在一个 MCP Client 中进行。实操心得在开发 MCP Server 时一个非常实用的调试技巧是你可以暂时将传输层Transport从StdioServerTransport换成一个简单的测试脚本该脚本能模拟 Client 发送标准的 JSON-RPC 请求。这能帮助你在独立环境下验证 Server 的逻辑是否正确而不用每次都启动完整的 AI 应用。4. 在主流 AI 应用中配置与使用 MCPServer 写好了怎么让它真正被 AI 用到呢这取决于你使用的 Client。目前Claude Desktop和Cursor IDE是对 MCP 支持最友好、也是最流行的两个客户端。4.1 在 Claude Desktop 中配置 MCP ServerClaude Desktop 是 Anthropic 官方推出的 Claude 客户端它内置了 MCP Client 功能。找到配置文件macOS:~/Library/Application Support/Claude/claude_desktop_config.jsonWindows:%APPDATA%\Claude\claude_desktop_config.jsonLinux:~/.config/Claude/claude_desktop_config.json编辑配置文件如果文件不存在就创建它。我们需要在mcpServers对象下添加我们的 Server 配置。{ mcpServers: { todo-list: { command: node, args: [/ABSOLUTE/PATH/TO/YOUR/todo-mcp-server/index.js] } } }todo-list是你给这个 Server 起的任意名字。command启动 Server 的命令这里是node。args命令的参数第一个参数是你的 Server 入口文件的绝对路径。重要提示必须使用绝对路径。在 macOS/Linux 上你可以用pwd命令获取当前目录的绝对路径。例如如果你的项目在/Users/you/projects/todo-mcp-server那么args就应该是[/Users/you/projects/todo-mcp-server/index.js]。重启 Claude Desktop保存配置文件后完全退出并重新启动 Claude Desktop。验证与使用重启后当你新建一个对话你应该能在输入框附近看到一个新的图标通常是一个插头或工具图标点击它可以看到可用的工具列表。如果配置成功你应该能看到add_todo工具。你可以直接对 Claude 说“请使用 add_todo 工具帮我添加一个待办事项‘写项目报告’。” Claude 会识别出这是一个工具调用并弹出参数框让你确认执行后即可看到结果。4.2 在 Cursor IDE 中配置 MCP ServerCursor 是一个集成了 AI 的代码编辑器它也支持 MCP。配置方式与 Claude Desktop 类似但配置文件位置不同。找到或创建 Cursor 规则文件在 Cursor 中MCP 配置通常放在项目根目录下的.cursor/rules/mcp.json文件中。你需要创建这个目录和文件。mkdir -p .cursor/rules touch .cursor/rules/mcp.json编辑mcp.json文件{ mcpServers: { todo-list: { command: node, args: [/ABSOLUTE/PATH/TO/YOUR/todo-mcp-server/index.js], env: {} } } }格式与 Claude Desktop 几乎一致。同样需要注意使用绝对路径。重启 Cursor 或重载项目保存文件后你可能需要重启 Cursor或者使用命令面板Cmd/Ctrl Shift P执行Cursor: Reload Context来重载配置。在 Cursor 中使用配置成功后当你在 Cursor 的聊天框中与 AI 对话时AI 将能够感知到todo-list服务器提供的资源和工具。你可以让 AI “查看当前的待办列表”或“添加一个新任务”。4.3 配置过程中的常见问题与排查即使按照步骤操作你也可能会遇到 Server 不工作的情况。以下是几个最常见的坑和排查方法“Server failed to start” 或连接失败检查绝对路径这是最最常见的问题。再次确认args中的文件路径是否正确无误并且没有使用~这样的家目录缩写在 JSON 配置中通常不展开。检查 Node.js 和环境确保你的node命令在系统 PATH 中。可以在终端中直接运行node /ABSOLUTE/PATH/TO/index.js看 Server 是否能独立启动并等待输入。检查文件权限确保你的脚本文件有可执行权限。工具或资源列表不显示检查 Server 日志MCP Server 的错误输出console.error会打印到标准错误流。你可以直接运行 Server并手动模拟一个 Client 请求来调试。或者在配置 Client 时有时可以在其日志中找到来自 Server 的错误信息例如 Claude Desktop 的日志文件位置。验证协议握手MCP 连接开始时有一个初始化握手过程。确保你的 Server 在connect后正确响应了initialize请求。SDK 通常会处理这些但如果你自己实现底层协议这里容易出错。检查 capabilities 声明确认在创建 Server 时在capabilities中正确声明了resources和tools。如果声明为空对象{}Client 会认为你不提供任何能力。工具调用无反应或报错检查输入模式inputSchemaClientAI会根据你定义的inputSchema来生成调用参数。如果 Schema 定义有误比如required字段缺失AI 可能无法正确构造请求。在 Handler 中增加日志在tools/call的 handler 里用console.error打印接收到的name和arguments这能帮你确认请求是否到达以及参数格式是否正确。处理异步错误确保你的 handler 是async函数并且所有可能的错误路径都抛出了Error或返回了正确的 JSON-RPC 错误响应。未捕获的异常可能导致连接中断。避坑技巧在开发初期强烈建议先使用一个简单的测试 Client 来验证你的 Server。你可以写一个几行代码的 Node.js 脚本通过child_process.spawn启动你的 Server然后通过 stdin/stdout 管道发送一个手写的resources/list请求看看返回是否正确。这能帮你快速定位是协议逻辑问题还是客户端集成问题。5. 高级主题与生态现状当你掌握了基础 Server 的开发后可以进一步探索 MCP 更强大的能力和整个生态。5.1 动态资源与变更通知我们之前的待办列表资源是静态的每次读取都是返回当前快照。但 MCP 支持动态资源和变更通知这能让 AI 获得实时数据。动态资源在resources/list的响应中可以为资源设置uri模板或通过上下文动态生成列表。例如一个文件系统 Server 可以根据当前目录列出所有.md文件作为资源。变更通知Notifications这是 MCP 的一个高级特性。Server 可以主动向 Client 发送notifications/resources/updated通知告诉 Client 某个资源的内容已经变了。Client 收到后可以选择重新读取该资源以更新 AI 的上下文。这对于监控日志文件、数据库表变化等场景非常有用。实现变更通知需要 Server 在初始化时声明支持notifications能力并在数据变化时调用server.notify()方法。这稍微复杂一些但它是构建响应式、实时 AI 应用的关键。5.2 现有 MCP Server 生态概览你不需要什么都自己从头造轮子。MCP 社区已经涌现出大量高质量的 Server 实现覆盖了常见需求文件系统filesystem-mcp允许 AI 读写指定目录下的文件。搜索引擎brave-search-mcp,tavily-mcp为 AI 添加实时网络搜索能力。数据库sqlite-mcp,postgres-mcp让 AI 可以查询和分析数据库。代码仓库github-mcp允许 AI 读取仓库信息、Issue 甚至提交 PR。浏览器自动化playwright-mcp赋予 AI 控制浏览器进行网页抓取或操作的能力。多媒体youtube-mcp可获取视频信息、字幕等。这些现成的 Server 可以直接配置到你的 Claude Desktop 或 Cursor 中瞬间扩展 AI 的能力边界。例如配置了filesystem-mcp和sqlite-mcp后你可以直接对 AI 说“帮我分析一下项目data.db数据库里users表的增长趋势并把结论写到analysis.md文件里。” AI 会自主调用相应的工具来完成这一系列操作。5.3 安全性与生产环境考量将系统能力暴露给 AI 必须慎之又慎。MCP 在设计上提供了一些安全基础但真正的安全取决于实施。最小权限原则每个 MCP Server 应该只拥有完成其特定任务所需的最小权限。例如一个文件读写 Server 应该被配置为只能访问某个特定的项目目录而不是整个硬盘。输入验证与净化在工具的inputSchema中严格定义参数类型和格式只是第一道防线。在 Server 的工具 handler 内部必须对传入的参数进行二次验证和净化防止注入攻击。例如对于执行命令的工具绝不能直接将用户输入拼接成命令。沙箱化运行尽可能在沙箱环境如 Docker 容器、虚拟环境中运行 MCP Server尤其是那些执行代码或系统命令的 Server。审计与日志记录所有工具调用的详细信息谁、何时、调用什么、参数是什么、结果如何便于事后审计和问题排查。用户确认在重要的工具调用如删除文件、发送邮件前Client 端应该设置用户确认环节避免 AI 误操作。MCP 协议本身是中立的它提供了能力暴露的通道但管道的两端Client 的权限管理、Server 的实现安全需要开发者精心设计。在考虑将 MCP Server 部署到生产环境时务必进行严格的安全评审。6. MCP 与其他 AI 扩展协议的对比在 MCP 之前已经存在一些让 AI 与外部交互的方案了解它们的区别能更好地定位 MCP 的价值。OpenAI 的 Function Calling / Tools这是大模型原生支持的“工具调用”功能。它定义了 AI 如何“表达”想要调用一个工具包括工具名和参数但没有定义工具如何被实现、如何被注册、如何与 AI 应用通信。它更像是一个“调用约定”而 MCP 则是一个完整的“通信协议”。你可以把 OpenAI 的 Function Calling 看作是 MCP 协议中tools/call那部分在模型层面的抽象。LangChain ToolsLangChain 是一个流行的 AI 应用开发框架它提供了丰富的“Tool”抽象和大量内置工具。它的工具主要是在 Python 后端代码中定义和注册的与 LangChain 的链Chain或智能体Agent紧密耦合。MCP 则更底层、更通用它不依赖于任何特定的框架或语言任何能处理 JSON-RPC 的程序都可以实现 MCP Server。MCP 的目标是成为跨平台、跨框架的“基础协议”而 LangChain Tools 是构建在特定框架之上的“高级组件”。自定义 API 集成这是最传统的方式即为每个需要的能力单独开发一个 API然后在 AI 应用代码中硬编码调用逻辑。这种方式耦合度最高扩展和维护成本也最高。MCP 的优势在于标准化和动态发现新工具的增加不需要修改 Client 的核心代码。简而言之MCP 填补了“模型层面的工具调用意向”与“系统层面工具具体实现”之间的空白提供了一个标准化的、安全的、可插拔的中间层。它让工具生态的构建者Server 开发者和消费者AI 应用开发者能够基于统一的协议协作极大地提升了效率。从我个人的实践来看MCP 最大的魅力在于它带来的“组合性”。一旦你为你的 AI 应用配置了几个核心的 MCP Server如文件、数据库、搜索你会发现 AI 能够完成的任务复杂度是阶跃式上升的。它不再是一个只能聊天的玩具而是一个真正能帮你操作数字世界、串联不同信息孤岛的智能助手。虽然它目前还在快速发展中工具生态和客户端支持都在不断丰富但其设计理念已经为 AI 应用的未来架构指明了一个非常清晰的方向。