ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Agent中MCP协议详解:从“为什么要它”到“怎么用它”,最全最完整

Agent中MCP协议详解:从“为什么要它”到“怎么用它”,最全最完整 1. 为什么 Agent 需要一个统一的工具协议先说一个我踩过的坑。去年做客服 Agent 的时候团队里三个人分别写了三个工具一个查订单的 REST 接口、一个跑 Python 脚本的本地命令行、一个连内部知识库的 gRPC 服务。结果 Agent 要调这三个工具得写三套适配代码参数格式、错误处理、超时逻辑全不一样。后来想换一个搜索工具发现又得重写一遍适配层。这种每个工具一套接口的状态就是 MCP 要解决的问题。MCP 全称 Model Context Protocol是 Anthropic 提出的 AI 工具通信标准。你可以把它理解成 AI 应用世界的 USB 接口以前每个设备一个充电口现在统一成 Type-C插上就能用。对 Agent 来说MCP 让工具这件事从每个工具单独适配变成所有工具走同一个协议说话。Claude Desktop、Cursor、以及你自己写的 Agent只要支持 MCP就能直接调用任何符合规范的 MCP Server零适配。它适合谁三类人最该关注。第一类是正在写 Agent 的开发者你手里有一堆工具想接进 AgentMCP 能省掉大量胶水代码。第二类是做内部工具平台的团队把工具封装成 MCP Server 后公司里所有 AI 应用都能复用。第三类是普通用户想给 Claude Desktop 或 Cursor 加个文件读取、数据库查询能力社区已经有现成的 MCP Server一行 npx 命令就能跑起来。这篇文章不讲空泛概念我会把 MCP 的通信机制拆开从 JSON-RPC 消息格式讲到 Stdio 子进程通信再给你可复制的配置片段和一次端到端调用验证。看完你应该能自己跑通一个 MCP Server 并接进 Agent 工作流。2. MCP 协议核心机制JSON-RPC 与 Stdio 通信详解MCP 底层用的是 JSON-RPC 2.0这是理解整个协议的关键。JSON-RPC 的规则很简单每条消息就是一行 JSON请求带 id响应带同一个 id通知不带 id。就像发微信一条消息一个气泡不会粘在一起。三种消息类型先搞清楚。请求是 Client 发给 Server 的带 id等回复比如点菜等服务员确认{jsonrpc: 2.0, id: 1, method: tools/list, params: null}响应是 Server 回给 Client 的id 必须和请求一致Client 才知道这条回复对应哪个请求{jsonrpc: 2.0, id: 1, result: {tools: []}}通知是不等回复的没有 id发了就完比如握手完成后的确认{jsonrpc: 2.0, method: notifications/initialized}连接建立后不是直接干活先握手。Client 发 initialize 请求告诉 Server 自己支持的协议版本和客户端信息Server 回 initialize response告诉 Client 自己的协议版本、能力和服务端信息最后 Client 发 notifications/initialized 通知表示可以开始干活了。整个握手有 30 秒超时防止 Server 卡住导致 Client 无限等待。握手请求长这样{ jsonrpc: 2.0, id: 1, method: initialize, params: { protocolVersion: 2024-11-05, capabilities: {}, clientInfo: {name: my-agent, version: 0.1.0} } }Server 的响应{ jsonrpc: 2.0, id: 1, result: { protocolVersion: 2024-11-05, capabilities: {tools: {}}, serverInfo: {name: my-mcp-server, version: 1.0.0} } }传输层有两种Stdio 和 SSE。Stdio 是最常见的Client 启动 Server 子进程通过 stdin/stdout 交换 JSON。你启动一个命令行程序给它输命令它打印结果就是这个模式。SSE 用于远程场景Server 独立运行Client 通过 HTTP 连过去先 GET /sse 建立长连接Server 推送一个 endpoint 事件告诉 Client 发消息用哪个 URL之后 Client 用 HTTP POST 往那个 URL 发请求。Stdio 有个关键细节叫 request_lock。并发请求时写 stdin 然后读 stdout这个操作必须原子不然两个请求同时写回复错位就乱了。所以 StdioTransport 里会有一个请求级互斥锁保证同一时刻只有一个请求在通信。stderr 单独开后台任务打印不阻塞主通信。一次完整的工具调用从 JSON 角度看是这样的。Agent 要读 /tmp/hello.txtClient 发出{jsonrpc:2.0,id:3,method:tools/call,params:{name:read_file,arguments:{path:/tmp/hello.txt}}}Server 回复{jsonrpc:2.0,id:3,result:{content:[{type:text,text:Hello, World!}],is_error:false}}一行 JSON 过去一行 JSON 回来。没有 HTTP 头、没有 WebSocket 帧、没有 gRPC 序列化纯粹的文本行协议。这就是 MCP 简单的地方也是它容易被各种语言实现的原因。3. 可复制的 MCP Server 配置与 Stdio 启动命令这一节给你能直接抄的配置。先看 Claude Desktop 的配置文件路径在 macOS 上是~/Library/Application Support/Claude/claude_desktop_config.jsonWindows 上是%APPDATA%\Claude\claude_desktop_config.json。内容格式{ mcpServers: { filesystem: { command: npx, args: [ -y, modelcontextprotocol/server-filesystem, /Users/yourname/project ] }, calculator: { command: /usr/local/bin/my-calculator, args: [] } } }这里command是要启动的可执行文件args是参数。filesystem 这个 Server 用 npx 启动允许访问/Users/yourname/project目录。calculator 是你自己编译的二进制直接跑。如果你用的是 Cursor配置在~/.cursor/mcp.json格式一样{ mcpServers: { filesystem: { command: npx, args: [-y, modelcontextprotocol/server-filesystem, /tmp] } } }手动测试 Stdio Server 能不能跑可以直接在终端里启动它然后手动喂 JSON。比如启动 filesystem Servernpx -y modelcontextprotocol/server-filesystem /tmp启动后它会等 stdin 输入。你手动输入一行 initialize 请求回车{jsonrpc:2.0,id:1,method:initialize,params:{protocolVersion:2024-11-05,capabilities:{},clientInfo:{name:test,version:1.0}}}如果 Server 正常会立刻在 stdout 打印一行 initialize response。这一步能跑通说明 Server 本身没问题问题就出在 Client 配置上。如果你要接的是 TaoToken 这类兼容 OpenAI 接口的服务在 Agent 侧配置 Base URL 和 Key 的时候MCP Server 的配置是独立的两者不冲突。MCP 管的是工具怎么暴露模型管的是推理怎么走。你可以把 MCP Server 理解成 Agent 的手模型是大脑手和大脑通过不同的通道连接。对于 Codex 用户~/.codex/auth.json里配置的是模型认证信息MCP Server 配置在 Codex 的 settings 里单独写。Cline 的话MCP 配置在 VS Code 的 settings.json 里搜cline.mcpServers就能找到。CC Switch 这类工具切换的是模型端点MCP Server 列表是另一份配置别搞混。一个完整的 MCP Server 配置三件套是Base URL如果是远程 SSE Server、Key如果需要认证、Model IDAgent 侧用的模型。Stdio Server 不需要 Base URL 和 Key因为它就是本地子进程通过 stdin/stdout 通信不经过网络。4. 端到端验证从 tools/list 到 tools/call 跑通一次调用配置写好了怎么确认真的通了我一般分三步验证先看 Server 能不能列出工具再手动调一次工具最后看 Agent 能不能自动调。第一步用 Python 写个最小 Client 验证 tools/list。这段代码可以直接跑import subprocess import json # 启动 MCP Server 子进程 proc subprocess.Popen( [npx, -y, modelcontextprotocol/server-filesystem, /tmp], stdinsubprocess.PIPE, stdoutsubprocess.PIPE, stderrsubprocess.PIPE, textTrue ) def send_request(method, paramsNone, req_id1): msg {jsonrpc: 2.0, id: req_id, method: method} if params is not None: msg[params] params proc.stdin.write(json.dumps(msg) \n) proc.stdin.flush() line proc.stdout.readline() return json.loads(line) # 握手 init_resp send_request(initialize, { protocolVersion: 2024-11-05, capabilities: {}, clientInfo: {name: test-client, version: 1.0} }) print(initialize:, init_resp) # 发 initialized 通知无 id proc.stdin.write(json.dumps({jsonrpc: 2.0, method: notifications/initialized}) \n) proc.stdin.flush() # 列出工具 tools_resp send_request(tools/list, None, req_id2) print(tools:, json.dumps(tools_resp, indent2, ensure_asciiFalse)) proc.terminate()跑起来应该能看到类似这样的输出{ jsonrpc: 2.0, id: 2, result: { tools: [ {name: read_file, description: Read the contents of a file}, {name: write_file, description: Write content to a file}, {name: list_directory, description: List directory contents} ] } }第二步调一次 read_file。先往 /tmp/hello.txt 写点内容echo Hello, MCP! /tmp/hello.txt然后在上面代码基础上加一段call_resp send_request(tools/call, { name: read_file, arguments: {path: /tmp/hello.txt} }, req_id3) print(call result:, json.dumps(call_resp, indent2, ensure_asciiFalse))预期输出{ jsonrpc: 2.0, id: 3, result: { content: [{type: text, text: Hello, MCP!}], is_error: false } }看到is_error: false和文件内容说明端到端通了。第三步接进 Agent。如果你用的是支持 MCP 的 Agent 框架把上面配置里的 Server 加进去然后问 Agent读一下 /tmp/hello.txt 的内容。Agent 会自动走 tools/list 发现 read_file然后 tools/call 调用它。你可以在 Agent 的日志里看到完整的 JSON-RPC 消息流。这一步验证通过后你就有了一个可用的 MCP 工具链。后面加新工具只需要在 Server 侧注册Client 侧不用改任何代码。5. 常见报错排查401、local proxy failed、reading choices、OAuth跑 MCP 的过程中报错基本集中在几类。我按真实遇到的频率排一下。401 Unauthorized。这个通常出现在远程 SSE Server 上Server 要求认证但 Client 没带 Key。检查你的 MCP 配置里有没有headers字段比如{ mcpServers: { remote-tools: { url: https://your-server.com/sse, headers: { Authorization: Bearer your-token-here } } } }如果是 Stdio Server 报 401那多半是 Server 内部去调了某个需要认证的 API跟 MCP 协议本身无关去看 Server 的日志。local proxy failed。这个报错一般出现在 Client 尝试连接本地 Server 但连不上。可能原因有三个Server 进程没启动、端口被占用、或者配置里的 command 路径写错了。先手动在终端跑一遍配置里的 command看能不能启动。如果 command 是相对路径改成绝对路径试试。端口占用的话换个端口或者杀掉占用进程。reading choices 相关报错。这个通常出现在 Agent 侧解析模型返回时模型返回的格式不符合预期。如果你用的是兼容 OpenAI 接口的服务检查 Base URL 有没有写对Model ID 是不是服务商支持的。有些服务返回的 choices 字段结构和标准 OpenAI 不一样需要在 Agent 侧做适配。TaoToken 的接口是兼容 OpenAI 格式的Base URL 填https://taotoken.net/apiModel ID 填你实际用的模型名一般不会有这个问题。OAuth 相关报错。远程 MCP Server 如果用 OAuth 认证Client 需要先走一遍授权流程。报错通常是 token 过期或者 scope 不对。检查你的 token 有没有过期scope 是不是包含了要调用的工具权限。有些 Server 要求特定的 scope比如tools:read、tools:call配置的时候要看清楚。Method not found (-32601)。这个说明你调的方法 Server 没实现。MCP 定义了六大原语tools、resources、prompts、completion、elicitation、roots、sampling。很多 Server 只实现了 tools你调 resources/list 就会报这个错。解决方法是先 tools/list 看看 Server 到底支持什么别调没实现的方法。Parse error (-32700)。这个说明你发的 JSON 不合法。最常见的是少了个引号、多了个逗号、或者换行符处理不对。Stdio 通信要求每条消息一行消息内部不能有裸换行。用 json.dumps 生成消息的时候确保没有 indent 参数不然会插入换行。握手超时。30 秒内没完成 initialize 握手就会超时。检查 Server 启动是不是太慢比如 npx 第一次跑要下载包可能超过 30 秒。可以先手动跑一次 npx 把包缓存下来或者改用本地安装的二进制。排查的时候有个通用技巧把 MCP 通信的原始 JSON 打出来看。在 Client 侧加日志把每次 send 和 receive 的内容打印出来对照 JSON-RPC 规范看哪里不对。大部分问题看原始消息就能定位。6. 把 MCP 接进你的 Agent 工作流跑通验证之后下一步是把它接进实际工作流。我自己的做法是分三层底层是 MCP Server 池中间是 Agent 的工具适配层上层是具体的业务 Agent。底层 Server 池里常用的工具各起一个 Server。文件操作一个、数据库查询一个、内部 API 一个。每个 Server 独立进程互不影响。Server 挂了只影响对应的工具不会拖垮整个 Agent。中间适配层负责把 MCP 工具转成 Agent 能直接用的格式。大部分 Agent 框架都有现成的适配器比如把 MCPToolDefinition 转成 BaseTool。适配逻辑很薄就是包一层run 方法内部调 client.call_tool。这层的好处是 Agent 代码不用关心工具是本地还是远程统一按 BaseTool 调。上层业务 Agent 按场景组合工具。客服 Agent 只需要订单查询和知识库工具代码 Agent 只需要文件操作和搜索工具。按需加载不用把所有工具都塞给一个 Agent。如果你要长期跑 Agent 任务建议用 Coding Plan 这类方案管理模型调用把 MCP 工具调用和模型推理分开计费和监控。MCP 工具调用是本地或内网通信模型推理走 API两者的稳定性要求不一样分开管理更容易排查问题。实际用下来MCP 最大的价值不是技术多先进而是把工具接入这件事标准化了。以前每接一个新工具都要写适配代码现在只要 Server 符合 MCP 规范配置里加一行就能用。社区里现成的 MCP Server 越来越多文件系统、GitHub、数据库、搜索都有直接拿来用就行。最后给一个实用建议自己写 MCP Server 的时候工具描述要写清楚。Agent 是靠描述来决定调哪个工具的描述模糊会导致 Agent 调错工具。参数 schema 也要写全required 字段标清楚不然 Agent 可能漏传参数。这两点做好Agent 调工具的准确率会高很多。
返回列表