MCP协议详解:AI Agent工具调用的标准化解决方案

📅 2026/8/11 9:16:53
MCP协议详解:AI Agent工具调用的标准化解决方案
1. 项目概述为什么我们需要 MCP如果你最近在折腾 AI Agent 或者大模型应用开发大概率会听到一个词MCP。它不是什么新出的芯片也不是某个神秘组织全称是Model Context Protocol翻译过来叫“模型上下文协议”。简单说它就是一套让 AI 大模型比如 GPT-4、Claude 3能够安全、标准化地调用外部工具和数据的“接线手册”和“通信规范”。听起来有点抽象我给你打个比方。你家里有空调、电视、扫地机器人每个电器都有自己的遥控器你想开空调得找空调遥控器想看电视又得换一个很麻烦。后来你买了一个“万能遥控器”或者接入了“智能家居中枢”它懂得所有电器的“语言”你只需要对中枢说“我热了”或者“看新闻”它就能自动帮你打开空调、调到合适的频道。这个让中枢和所有电器能互相听懂、安全协作的“共同语言”和“接线标准”就是 MCP 想做的事。在 AI 的世界里大模型就是这个“智能中枢”它很聪明但“手无寸铁”——它无法直接操作你的文件系统、不能执行命令行、不能查询实时数据库、也不能控制浏览器。而 MCP Server 就是那些“电器”工具比如一个能读你电脑文件的工具、一个能执行git命令的工具、一个能查询公司数据库的工具。MCP 协议就是定义中枢AI Agent和电器工具之间如何打招呼、如何描述自己能干什么、如何发送指令、如何返回结果、以及如何保证安全的那个核心协议。没有 MCP 之前每个 AI 应用开发者都在重复造轮子为 ChatGPT 写一套插件系统为 Claude 再写一套为自家开发的 Agent 框架又写一套。工具开发者也很痛苦他们的工具要想被不同的 AI 使用得适配各种不同的接口。MCP 的出现就是为了统一这个混乱的局面。它由 Anthropic 等公司牵头推动目标就是成为 AI 与工具交互的“USB 标准”或“HTTP 协议”让一次开发处处可用。所以这个项目的核心价值在于为 AI Agent 的“手”和“脚”制定一套通用、安全、高效的连接标准彻底解决工具调用的碎片化问题释放 AI 真正的生产力。2. MCP 协议核心架构与工作原理拆解要理解 MCP不能只看表面调用得深入它的“五脏六腑”。整个协议的设计哲学围绕着声明式、双向流、强类型这几个关键词展开。2.1 核心组件与交互模型MCP 的架构非常清晰主要包含三个角色MCP Client客户端通常是 AI 应用或 Agent 框架本身。例如Cursor IDE、Claude Desktop、你自行开发的 Agent 系统。Client 的角色是“大脑”它发起请求决定要调用哪个工具、传递什么参数。MCP Server服务器端这是具体的工具提供方。一个 Server 可以提供一个或多个“工具”Tools或“资源”Resources。例如一个“文件系统 MCP Server”可以提供“读取文件”、“写入文件”、“列出目录”等工具一个“SQLite MCP Server”可以提供“执行 SQL 查询”的工具。Server 也可以是数据源比如提供一个实时股票价格的资源流。MCP Protocol协议本身定义 Client 和 Server 之间通信的格式、序列化方式默认 JSON-RPC over stdio/SSE、消息类型和生命周期。它们之间的工作流程可以想象成一次餐厅点餐Client顾客进入餐厅先拿到一份菜单Server 声明的工具列表。顾客看菜单后决定点“宫保鸡丁”调用某个工具并告诉服务员“微辣”参数。Server厨房收到订单请求开始烹饪执行工具。烹饪完成后服务员将“宫保鸡丁”结果端给顾客。如果是复杂的菜可能需要分几次上流式响应。整个过程中餐厅和顾客使用同一种语言协议并且菜单清晰明确强类型定义避免了“来个随便炒炒”这种模糊指令。在技术实现上MCP 使用 JSON-RPC 2.0 作为消息传输层。通信方式主要有两种stdio标准输入输出最常用的方式Server 作为一个独立的本地进程启动通过 stdin/stdout 与 Client 交换 JSON-RPC 消息。这种方式简单、高效适合大多数本地工具。SSEServer-Sent Events用于 HTTP 环境Server 部署为一个 HTTP 服务Client 通过 SSE 长连接接收来自 Server 的推送消息如资源更新。2.2 协议核心概念详解工具、资源与提示词MCP 协议定义了三种主要的内容类型这是其强大能力的基石工具Tools这是最核心的概念。一个 Tool 代表一个可执行的操作。每个 Tool 都有name: 工具名称如read_file。description: 人类可读的描述AI 主要靠这个理解工具用途。描述的质量直接决定 AI 调用的准确性必须清晰、无歧义。inputSchema: 输入参数的 JSON Schema 定义。这是“强类型”的关键它严格定义了参数的名字、类型、是否必填、枚举值等。例如read_file工具可能有一个必填参数path类型是string。{ name: read_file, description: 读取指定路径的文本文件内容。, inputSchema: { type: object, properties: { path: { type: string, description: 待读取文件的绝对路径。 } }, required: [path] } }实操心得定义inputSchema时description字段不仅给开发者看更是 AI 理解参数含义的窗口。像写产品文档一样写它避免使用技术黑话。例如用“用户邮箱地址”而不是“email_str”。资源Resources代表可读取的静态或动态数据如文件、数据库表、API 端点数据。Resource 通过 URI 标识Client 可以“读取”资源内容。这对于为 AI 提供上下文信息特别有用。例如一个 Server 可以将file:///etc/hosts声明为一个 ResourceAI 需要时可以直接请求读取而不必调用一个“读文件”工具。提示词模板Prompts这是 MCP 一个很巧妙的设计。Server 可以预定义一些高质量的提示词模板ClientAI可以直接调用并填充变量。这相当于把最佳实践“固化”下来。比如一个“代码审查助手” Server 可以提供一个review_python_function的 Prompt里面已经写好了专业的代码审查指令和占位符{code}AI 只需传入代码片段即可生成高质量的审查意见。2.3 通信流程与消息类型一次完整的 MCP 交互遵循以下基本流程初始化握手Client 启动 Server 进程或连接 SSE 端点。双方交换initialize和initialized消息协商协议版本、能力等。列出可用内容Client 发送tools/list、resources/list、prompts/list请求Server 返回各自列表。这是 AI 获取“能力菜单”的时刻。调用工具AI 决定行动后Client 发送tools/call请求包含工具名和参数字典。Server 执行后通过tools/call的响应返回结果。结果可以是简单的文本也可以是复杂的结构化数据。流式响应对于耗时长或需要持续输出的工具如执行一个长时间运行的脚本Server 可以使用partialResult消息进行流式返回最后用completion消息结束。这能极大提升交互体验让 AI 边执行边思考。读取资源Client 发送resources/read请求Server 返回资源内容。调用提示词Client 发送prompts/get请求并传入变量Server 返回填充好的完整提示词文本。整个协议的消息是异步的并且支持 Server 主动向 Client 推送通知如notifications/resources/updated告知某个资源已更新这使得 MCP 能支持实时性很强的应用场景。3. 实战从零构建一个自定义 MCP Server理解了原理最好的学习方式就是动手造一个。我们以构建一个“系统信息查询 MCP Server”为例它可以提供获取 CPU 使用率、内存信息、磁盘空间等工具。我们将使用官方推荐的TypeScript SDK进行开发这是目前最成熟、生态最好的选择。3.1 环境准备与项目初始化首先确保你的环境有 Node.js (18) 和 npm。# 创建一个新目录并初始化项目 mkdir mcp-server-system-info cd mcp-server-system-info npm init -y # 安装 MCP 核心 SDK 和类型定义 npm install modelcontextprotocol/sdk npm install --save-dev typescript tsx types/node # 初始化 TypeScript 配置 npx tsc --init --target ES2022 --module NodeNext --moduleResolution NodeNext --outDir ./dist --strict编辑生成的tsconfig.json确保rootDir设置为./src。然后创建项目结构mcp-server-system-info/ ├── package.json ├── tsconfig.json ├── src/ │ └── index.ts # 主入口文件 └── README.md3.2 编写核心 Server 逻辑现在打开src/index.ts开始编写我们的 Server。import { Server } from modelcontextprotocol/sdk/server/index.js; import { StdioServerTransport } from modelcontextprotocol/sdk/server/stdio.js; import { CallToolRequestSchema, ListToolsRequestSchema, Tool, } from modelcontextprotocol/sdk/types.js; import os from os; import fs from fs/promises; import { exec } from child_process; import { promisify } from util; const execAsync promisify(exec); // 1. 创建 Server 实例 const server new Server( { name: system-info-server, version: 0.1.0, }, { capabilities: { tools: {}, // 声明我们支持工具 }, } ); // 2. 定义我们的工具列表 const tools: Tool[] [ { name: get_cpu_info, description: 获取当前系统的CPU架构、核心数和负载信息。, inputSchema: { type: object, properties: {}, // 此工具无需参数 additionalProperties: false, }, }, { name: get_memory_info, description: 获取当前系统的总内存、空闲内存和使用率。, inputSchema: { type: object, properties: {}, additionalProperties: false, }, }, { name: get_disk_usage, description: 获取指定路径的磁盘使用情况总空间、已用空间、可用空间。, inputSchema: { type: object, properties: { path: { type: string, description: 需要查询的磁盘路径例如 / 或 C:\\。默认为当前工作目录。, default: process.cwd(), }, }, required: [], }, }, { name: get_process_list, description: 获取当前运行中的进程列表简化版包含PID和命令。, inputSchema: { type: object, properties: { limit: { type: number, description: 返回的进程数量上限。默认为20。, minimum: 1, maximum: 100, default: 20, }, }, required: [], }, }, ]; // 3. 实现工具处理逻辑 server.setRequestHandler(ListToolsRequestSchema, async () { return { tools, }; }); server.setRequestHandler(CallToolRequestSchema, async (request) { const { name, arguments: args } request.params; try { switch (name) { case get_cpu_info: { const cpus os.cpus(); const loadAvg os.loadavg(); const content [ **CPU 架构**: ${os.arch()}, **CPU 核心数**: ${cpus.length}, **型号**: ${cpus[0]?.model || 未知}, **系统负载 (1/5/15分钟)**: ${loadAvg.map(l l.toFixed(2)).join(, )}, **运行时间**: ${(os.uptime() / 3600).toFixed(2)} 小时, ].join(\n); return { content: [{ type: text, text: content }], }; } case get_memory_info: { const total os.totalmem(); const free os.freemem(); const used total - free; const usagePercent ((used / total) * 100).toFixed(1); const content [ **总内存**: ${(total / 1024 ** 3).toFixed(2)} GB, **已用内存**: ${(used / 1024 ** 3).toFixed(2)} GB, **空闲内存**: ${(free / 1024 ** 3).toFixed(2)} GB, **内存使用率**: ${usagePercent}%, ].join(\n); return { content: [{ type: text, text: content }], }; } case get_disk_usage: { const path (args as { path?: string })?.path || process.cwd(); // 注意fs.statfs 仅在 Node.js 某些版本或需要 polyfill。这里使用更通用的方法。 // 实际生产环境建议使用 df -k 命令解析或 diskusage 第三方包。 try { await fs.access(path); // 简化演示返回一个占位信息。真实实现需要跨平台磁盘信息获取。 const content 已接收到查询路径: ${path}。\n*提示在真实实现中此处会调用系统API获取该路径的磁盘使用详情总大小、已用空间、可用空间。*; return { content: [{ type: text, text: content }], }; } catch (error) { return { content: [{ type: text, text: 错误路径 ${path} 不可访问。 }], isError: true, }; } } case get_process_list: { const limit (args as { limit?: number })?.limit || 20; let command; let parseFunction: (stdout: string) string; if (process.platform win32) { command tasklist /FO CSV /NH | Select-Object -First ${limit}; parseFunction (stdout) Windows 进程列表前${limit}个:\n stdout.split(\n).filter(line line).map(line - ${line}).join(\n); } else { // Linux/Mac command ps -eo pid,comm --no-headers | head -n ${limit}; parseFunction (stdout) 进程列表PID, 命令:\n stdout; } try { const { stdout } await execAsync(command, { shell: true }); const content parseFunction(stdout); return { content: [{ type: text, text: content }], }; } catch (error: any) { return { content: [{ type: text, text: 获取进程列表失败: ${error.message} }], isError: true, }; } } default: return { content: [{ type: text, text: 未知工具: ${name} }], isError: true, }; } } catch (error: any) { // 统一错误处理 return { content: [{ type: text, text: 工具执行出错: ${error.message} }], isError: true, }; } }); // 4. 启动 Server使用 stdio 传输 async function main() { const transport new StdioServerTransport(); await server.connect(transport); console.error(MCP System Info Server 已启动并运行在 stdio 上。); } main().catch((error) { console.error(Server 启动失败:, error); process.exit(1); });3.3 编译、运行与调试编写完成后我们需要编译并运行它。# 编译 TypeScript npx tsc # 运行 Server (编译后的JS文件在dist目录) node dist/index.js此时这个 Server 会静静地等待来自 stdio 的输入。但我们需要一个 MCP Client 来测试它。最快捷的方式是使用MCP Inspector这是一个官方提供的调试工具。首先全局安装 MCP CLI 工具如果尚未安装npm install -g modelcontextprotocol/sdk然后创建一个 Server 配置文件server-config.json{ command: node, args: [/你的绝对路径/mcp-server-system-info/dist/index.js], env: {} }最后使用 MCP CLI 启动 Inspector 进行测试npx modelcontextprotocol/inspector server-config.jsonInspector 会启动一个本地网页通常是在http://localhost:5173。打开后你就能在网页界面中看到你的 Server 注册的所有工具get_cpu_info,get_memory_info等并可以手动输入参数进行调用测试实时查看请求和响应的 JSON-RPC 消息。这是开发和调试 MCP Server 不可或缺的利器。注意事项错误处理Server 中必须进行完善的错误处理并在isError: true时返回清晰的错误信息。AI 需要根据错误信息决定下一步动作。安全性你的工具能做什么Server 就能做什么。像exec这样的操作极其危险。在生产环境中必须对输入参数进行严格的验证、过滤和沙箱化。例如get_process_list工具如果允许用户传入任意命令将是严重的安全漏洞。资源清理如果工具创建了临时文件或网络连接确保在完成后妥善清理。描述清晰再次强调description和参数描述是 AI 的“眼睛”务必用自然语言准确描述工具的功能、参数含义和返回值的格式。4. 如何将 MCP Server 集成到 AI 应用Client构建好 Server 只是第一步让它被 AI 使用才是目的。目前多个主流 AI 应用已支持或正在积极集成 MCP。4.1 在 Claude Desktop 中使用Claude Desktop 是官方最早支持 MCP 的客户端之一。配置非常简单只需编辑其配置文件。macOS:~/Library/Application Support/Claude/claude_desktop_config.jsonWindows:%APPDATA%\Claude\claude_desktop_config.json在配置文件中添加你的 Server{ mcpServers: { system-info: { command: node, args: [/绝对路径/to/your/mcp-server-system-info/dist/index.js], env: {} } // 可以同时配置多个 Server // filesystem: { ... }, // sqlite: { ... } } }重启 Claude Desktop你就可以在对话中直接让 Claude 使用这些工具了。例如输入“帮我看看当前系统的内存使用情况。” Claude 会自动识别并调用get_memory_info工具。4.2 在 Cursor IDE 中使用Cursor 是另一个深度集成 MCP 的明星产品。它的配置更灵活可以通过项目根目录下的.cursor/mcp.json文件进行配置这使得工具可以按项目定制。在你的项目根目录创建.cursor/mcp.json{ mcpServers: { system-info: { command: node, args: [/项目相对路径或绝对路径/dist/index.js] } } }配置完成后在 Cursor 的聊天框中AI 助手如 Claude 3.5 Sonnet就能利用这些工具来帮助你。比如你可以说“我怀疑这个项目目录占用了太多空间检查一下磁盘使用情况。” AI 会调用get_disk_usage工具并返回结果。4.3 在自行开发的 Agent 框架中集成如果你正在构建自己的 AI Agent 应用集成 MCP 意味着你的 Agent 瞬间获得了接入庞大 MCP 工具生态的能力。你需要实现一个 MCP Client。以 Node.js 环境为例简化步骤包括创建 Client 并连接 Server使用StdioClientTransport启动 Server 进程并建立连接。获取工具列表调用listTools()方法。让 LLM 决定调用将工具列表名称和描述作为上下文提供给 LLM如通过 OpenAI API让 LLM 根据用户问题决定调用哪个工具及参数。执行调用根据 LLM 的决策调用callTool()方法。处理结果将工具返回的结果再次喂给 LLM生成最终回复给用户。这个过程封装了工具调用的复杂性你无需为每个工具编写硬代码只需管理好与 MCP Server 的通信和与 LLM 的交互逻辑即可。5. 高级主题与生态展望掌握了基础我们可以看看 MCP 更高级的用法和未来的可能性。5.1 资源Resources与提示词Prompts的进阶用法动态资源资源不一定是一个静态文件。一个 MCP Server 可以声明一个weather://beijing的资源当 Client 读取它时Server 可以实时调用天气 API 返回数据。这为 AI 提供了“实时感知”能力。资源更新通知Server 可以主动推送resources/updated通知。例如一个监控日志文件的 Server可以在日志更新时通知 ClientClient 可以决定是否让 AI 立即读取新日志进行分析。提示词链式调用Prompt 可以组合使用。例如一个“数据分析” Prompt 的输出可以作为另一个“报告生成” Prompt 的输入。Server 可以设计这样的工作流 Prompt极大提升复杂任务的完成质量。5.2 安全性考量与最佳实践MCP 将能力暴露给 AI安全是重中之重。最小权限原则每个 Server 应只提供完成其特定任务所需的最小工具集。不要做一个“超级 Server”。输入验证与净化对所有来自 Client 的输入进行严格的类型、范围、路径遍历检查。防止注入攻击。沙箱环境对于执行代码或命令的工具务必在沙箱如 Docker 容器、nsjail中运行。访问控制Server 可以实现基于令牌Token或上下文的访问控制。例如文件系统 Server 可以限制只能访问项目目录下的文件。审计日志记录所有的工具调用请求和结果便于事后审查和问题排查。5.3 现有生态与热门 Server 推荐MCP 生态正在飞速增长已经有很多高质量的开源 Server官方示例modelcontextprotocol/servers包中包含文件系统、HTTP 获取、SQLite 等基础 Server是学习的最佳范本。brave-search-mcp/tavily-mcp网络搜索 Server让 AI 能获取实时信息。github-mcp集成 GitHub API管理 Issue、PR、仓库。notion-mcp连接 Notion 数据库读写页面内容。slack-mcp在 Slack 频道中发送消息、读取历史。playwright-mcp用 Playwright 控制浏览器进行自动化操作功能极其强大。你可以通过 npm 直接安装这些 Server并配置到你的 Claude Desktop 或 Cursor 中立即扩展 AI 的能力边界。5.4 未来趋势MCP 将如何重塑 AI 应用开发我个人认为MCP 协议正在成为 AI 基础设施层的关键拼图它会带来几个深远影响工具开发生态的标准化与繁荣就像 npm 之于 JavaScriptPyPI 之于 PythonMCP 将催生一个庞大的、可复用的“AI 工具包”市场。开发者可以专注于编写一个精悍的 Server然后就能被所有兼容 MCP 的 AI 应用使用。AI 应用开发范式的转变未来的 AI 应用开发者可能更像“乐高大师”。核心工作是设计 Agent 的决策逻辑和用户体验而具体能力则通过组合不同的 MCP Server 来搭建。开发效率会大幅提升。本地化与隐私保护MCP Server 可以完全运行在本地。你的文件操作、数据库查询、内部系统交互都可以通过本地 Server 完成数据无需上传到云端 AI 服务。这为在企业敏感环境或注重隐私的场景下部署 AI 助理扫清了障碍。多模态与具身智能的桥梁协议本身不限于软件工具。理论上可以开发控制机械臂、智能家居、实验室设备的 MCP Server。这为 AI 连接物理世界提供了一个标准化的软件接口。回过头看MCP 解决的远不止是“让 AI 调用工具”这个技术问题它更是在定义下一代人机交互中智能体Agent如何与数字世界乃至物理世界安全、高效、灵活互作的基石协议。现在入手学习和实践正是站在了这个浪潮的起点。