做 AI Agent 落地的人,大概率都经历过这个阶段:先用 Function Calling 让模型能查天气、查订单,跑得挺顺;等工具多起来,开始怀疑人生。每家模型厂商的函数调用格式不一样,每个工具都要单独维护 JSON Schema 和结果解析,二十个工具就是二十套胶水代码。MCP 这个名字就在这时候反复出现——Anthropic 开源的 Model Context Protocol,目标是把"工具接入"这件事做成一套通用标准。这篇文章不打算做概念科普,而是直接拆解:Function Calling 的边界究竟在哪,MCP 靠什么设计补上了这些边界,以及这套设计哲学在实际落地中的真实样子。尤其最近社区里全是 playwright mcp、figma mcp、burpsuite mcp 这类集成,工业化工具集体接入一个协议,光是这个现象本身就值得聊一聊。
1. 从一次"工具地狱"说起:Function Calling 的边界到底画在哪
1.1 它到底解决了什么:给模型一只可调用的手
Function Calling 的机制,说白了就是把一批"工具定义"随每次推理请求一起交给模型。定义里包含函数名、功能描述、参数 JSON Schema,模型根据用户的问题决定要不要触发某个函数,要触发就按 Schema 返回 JSON 参数。应用层拿到调用指令,去执行真实代码,再把执行结果回填给模型,模型基于结果组织最终的话术。
这套机制解决的是大模型最原始的一块短板:只会生成文本,碰不到现实系统。查数据库、发邮件、订票、控制设备,这些原本要人工搬运的活,现在模型能自己"指使"代码去干。在单工具、单模型的小范围场景里,这套机制非常顺手。我见过不少内部提效工具,一开始就是靠一个简单的 Function Calling 跑起来的。
但它的边界也特别清楚:工具清单是静态焊死在请求里的,工具执行默认同步阻塞,模型只负责"选函数、填参数"。至于工具怎么部署、怎么鉴权、怎么被发现、怎么跟别的工具配合,全都不在它的射程里。你可以这么理解:Function Calling 像你给临时助手写的一张任务清单,清单上的事他能办,但清单怎么更新、任务之间怎么协作、谁有权限派任务,全部要你自己操心。它可靠,但脆弱,复杂度都堆在应用层。
1.2 三个真实场景暴露的缺口
第一个缺口是跨模型兼容。团队把同一个工具接进 OpenAI、Claude、Gemini 三个模型,每个厂商对工具描述的细节要求都有差别。有的严格限制 additionalProperties,有的对嵌套 $ref 支持不友好,参数命名有自己的偏好。写一套共用的 Schema 几乎不可能,最后我被迫维护三份几乎重复的定义。排查一个"参数类型不匹配"的报错,得在三家文档之间反复横跳。
第二个缺口是动态能力。后来我要给 Agent 加一个新数据接口,脑海里美好的预期是"部署完就能被模型发现"。Function Calling 模式下这个预期落空了——工具定义跟着请求走,你想让模型知道新函数,必须自己实现配置下发和热更新,把新增的函数实时拼进请求体。这就等于每个项目都要重新造一遍工具注册、刷新、缓存的轮子。
第三个缺口更隐蔽:权限、状态和流式输出全得自己造。真实 Agent 的工具是带状态的,会话锁定用户身份,流程有中间进度,工具调用还要考虑并发和取消。Function Calling 不管这些,于是团队只能自己设计鉴权中间件、会话状态池、进度上报接口。这些工作不是做不了,而是每个项目都重复做一遍,没有任何标准化积累。经历过这些之后,我习惯用一句话判断自己是否需要替代方案:如果"工具的定义、执行、权限、发现"这四件事里,有任何一件需要我手写通用机制,那这个场景就已经摸到 Function Calling 的天花板了。
2. MCP 的设计哲学:把"函数清单"改造成"工具生态"
2.1 Host、Client、Server 三层架构各自守什么
MCP 把架构拆成三层:Host 是你的 AI 应用——Claude Desktop、Cursor、Codex、或者自研 Agent,负责对话、模型调用和决策;Client 是和某个 MCP Server 一一绑定的连接器,负责协议握手、请求转发、响应分发;Server 是工具的实际提供方,可以用 Python、TypeScript 实现,也可以直接写 HTTP 端点。
为什么要拆三层?核心目的是把"造工具的人"和"用工具的人"解耦。模型公司不需要知道全世界有多少种工具,工具方也不需要关心你的模型是哪家、函数调用格式长什么样。大家各守一层协议,剩下的事情交给标准。打个比方,这非常像 USB 的设计思路——设备厂商做驱动,操作系统做设备枚举,用户只管插拔。MCP 里的 tools/list 就承担了类似"设备枚举"的角色,让 Host 在运行时发现 Server 能用什么。
在我实际搭建的经验里,Client 层是最容易被低估的。很多人只写了个 Server,想着"能连上就行",结果在 Client 侧的连接管理、超时控制、请求路由上吃了大亏。三层架构的真正价值,是让每一层都有清晰的生命周期和职责边界,你才不会在调试工具调用时把问题扯到模型提示词上。
2.2 不止 Tools:Resources、Prompts 与 Sampling 的设计意图
很多人以为 MCP 就是"标准化的 Function Calling",其实它定义了四类原语,Tools 只是其中一类。
- Tools:语义上接近函数,但拥有完整的"发现—注册—调用"生命周期。
- Resources:以 URI 标识的数据片段,比如文档、数据库记录、文件,模型可以按需读取。
- Prompts:Server 预置的可复用提示词模板,Host 可以直接拉取复用。
- Sampling:反向通道,Server 可以请求 Host 调用模型生成内容。
四类原语里,我尤其看重 Resources 和 Sampling。Resources 把"工具定义"和"工具数据"分开,模型不必每次都被塞进大量静态描述,这直接削减了 token 成本。Sampling 则把单向调用变成了双向协议,让 Server 有机会在关键节点上向模型要结论。比如一个数据分析 Server 在执行 SQL 之前,可以先请模型预计一下结果形态,再决定要不要跑。这种反向协作,是单个 Function Calling 完全覆盖不了的。
2.3 能力协商、动态发现与一次完整的 MCP 会话
一次 MCP 连接大致是这样:Client 发 initialize,带上协议版本和客户端能力说明;Server 回传 capabilities,声明支持哪些原语;随后 Client 发 initialized 通知,握手完成。接下来 Client 调用 tools/list 获取工具列表,这个列表是动态的,Server 随时可以通过通知让 Client 刷新。工具方新增工具,Host 端什么都不用改,下一次 list 自然就带了新工具。这正好补上了 Function Calling 时代的"动态能力缺口"。
传输层方面,MCP 同时支持 stdio 和 HTTP+SSE。本地开发用 stdio,AI 应用通过子进程启动 Server,标准输入输出就是协议通道;远程场景用 HTTP+SSE,Client 订阅事件流,再通过 POST 回传调用指令。最近的协议版本里,HTTP+SSE 的固定组合也在被 Streamable HTTP 替代,远程接入体验又简化了一截。这种"本地优先、远程可选"的格局,让很多团队在实验阶段能快速上手。
MCP 选用 JSON-RPC 2.0 而不是 gRPC 或 GraphQL,我猜主要图它轻、跨语言、无状态。工具方用任何语言写 Server,都只需要处理几个 method 和 payload 结构,接入门槛低,生态才能铺得快。贴一个最常用的 RPC 消息体,感受一下协议的干净程度:
{ "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "get_order_status", "arguments": { "order_id": "A2024001" } }, "id": 1 }3. 一张表说清差异:Function Calling 与 MCP 的九层对比
3.1 本质区别:一个偏模型 API,一个偏应用层协议
先澄清一个高频误解:MCP 不是 Function Calling 的升级包。两者根本不在同一层。Function Calling 是模型厂商在 API 里提供的入口参数,本质上是模型推理时的函数选择机制;MCP 是应用层协议,规定的是"AI 应用如何发现、连接、调用外部工具"的整体流程。
甚至可以共存:MCP Server 返回的工具调用,最终到模型 API 层时依旧会走 Function Calling。只是工具清单不再由开发者手写进每次请求,而是由 MCP Client 动态注入。想清楚这一层,在技术方案评审里就不会被"用了 MCP 就不用 Function Calling"这种话带偏。
3.2 九个维度的对照表
把两个方案放在九个维度上对比,边界就非常清楚了:
| 对比维度 | Function Calling | MCP |
|---|---|---|
| 工具来源 | 开发者逐请求硬编码 | Server 通过 tools/list 动态提供 |
| 协议标准 | 每家模型厂商各搞一套 | JSON-RPC 2.0 统一消息格式 |
| 上下文成本 | 全部工具描述每轮都携带 | 按需拉取,Resources 进一步省 token |
| 动态发现 | 需自研注册/刷新机制 | 内建能力协商与刷新通知 |
| 权限控制 | 应用层自己实现 | 用户授权模型/管理员授权 Server 分级 |
| 长任务 | 同步阻塞,需外挂方案 | 内置进度通知、取消、错误码 |
| 跨模型复用 | 同一个工具描述要写多份 | Server 一次编写,任意 MCP Host 复用 |
| 生态连接 | 单点集成 | 工具方只需开发一个 Server |
| 适用层级 | 模型推理参数 | Agent 应用层基础设施 |
表里最值得玩味的是"适用层级"这一行。Function Calling 本质是模型参数的延伸,MCP 本质是应用基础设施。如果你对"工具生态化共享"没有需求,那么表里 MCP 赢的维度对你毫无意义;反过来,一旦工具开始跨应用复用,Function Calling 那些"够用"就变成实实在在的债。还有一点容易被忽略:上下文成本那一行,工具一多,光是函数描述就能吃掉上千 tokens,MCP 用按需拉取把这块成本削掉了大半。
3.3 集成成本从 50 份适配到 15 份:量化视角
用一组常见的团队配置算笔账:5 个 AI 应用(Claude Desktop、Cursor、Codex、自研 Agent、内部 Bot),10 个工具(天气、数据库查询、Git 操作、浏览器控制、Figma 读取、安全扫描、日志分析等)。Function Calling 模式下,每个应用都要为每个工具维护适配层,最差情况 5×10=50 份胶水代码;换一家模型 API,这 50 份代码里的描述格式可能都要跟着改。MCP 模式下,10 个工具做成 10 个 Server,5 个应用各实现一次 MCP Client 能力,工作量大约 10+5=15 份。
数字不是重点,重点在演进曲线。新工具上线时,Function Calling 要同步改 5 个应用并排队发布;MCP 模式下写完 Server 启动即可,所有 Host 自动发现。这个平滑度差异,远不是 50 和 15 这两个数字能体现的。落到选型上,我的经验是:只有 1 个模型、1~2 个工具、不准备动态发现,直接 Function Calling;模型不只一家、工具数量超过 5 个,或者工具要被多个应用复用,MCP 带来的收益就开始超过协议复杂度了。
4. 生态热词背后:为什么工具厂商突然集体拥抱 MCP
4.1 浏览器、设计稿、3D 建模:三种被"插件化"的典型场景
最近社区里的集成热词,几乎都指向同一件事:给 AI 加眼睛和手。Playwright MCP 和 Chrome DevTools MCP 就是典型。区别在哪?Playwright MCP 更偏端到端操作——打开页面、点击、断言、跑用例;Chrome DevTools MCP 更底一层——网络面板、性能分析、DOM 调试。同一个浏览器,封装粒度不同,解决的是不同环节的自动化问题。这说明协议本身不限制封装层次,怎么封装纯粹是场景选择。
Figma MCP 的火爆我完全能理解。设计稿转代码一直是个苦差事,Figma 的 API 早就开放,但真要把图层树、样式 token、组件实例抠出来,得写一堆胶水代码。MCP Server 把这些数据变成按需读取的资源后,Agent 才能顺畅地"看图写码"。很多人在 VSCode、Codex 里配 Figma MCP,本质上就是给设计到代码这条链路装了个标准话务台。
Blender MCP、Unity MCP 又把 AI 拉进了 3D 生产。生成基础模型、调整材质、驱动脚本、搭场景,对游戏工作室和影视资产管线来说,这是把本来封闭的流程撕开一个口子。这类工业级工具愿意接 MCP,说明它真能盘活复杂的工具链,而不是停留在玩具 Demo 的层次。
4.2 安全、逆向与垂直领域:MCP 在长链路工具上的价值
安全圈的动作比我想象中快。Burp Suite MCP、Yakit MCP、IDA Pro MCP 都有人在做。细想也不意外:Burp 的拦截、扫描、重放都是典型的长流程 GUI 操作,Agent 要的是"记住上下文、跨步骤执行、失败重试"。MCP 的进度通知、取消机制和错误处理,刚好承接这类长任务。我自己调 Burp MCP 的时候就发现,把扫描目标、请求记录、漏洞等级这些状态用 Resource 暴露,比全堆在 Tools 里更省心。
IDA Pro MCP 的价值更明显。逆向工程里大量重复劳动——查交叉引用、追踪字符串、定位函数边界,把这些暴露成 tools 之后,AI 可以协助做恶意样本分析、调用关系梳理。对研究员的真正价值不是一键反编译,而是把几十步的固定动作变成可统筹执行的链路。垂直领域也在跟进,热词里能看到同花顺 MCP、TIA Portal Openness MCP。金融数据和工业自动化工具的特点是:API 复杂、变更频繁、使用者专业。MCP 在这些领域扮演统一适配层,把复杂 API 折叠成语义明确的工具,让 Agent 像人一样按意图调用,而不是翻接口文档。
4.3 Swagger 转 MCP 与存量 API 的接入摩擦
另一个信号是 Swagger 转 MCP 这种适配器的出现。存量 API 资产不可能全用手写 Server 重来,最实际的方式是用工具把 OpenAPI 描述文件直接转成 MCP Server。这和协议层适配器的思路一脉相承:MCP 要赢,就得把接入摩擦降到最低。谁能先让"已有 API 三分钟变 MCP Server",谁就能圈住最大批工具方。类似思路也可以移植到 gRPC、GraphQL 的桥接上,这条路会随着协议成熟越走越宽。
5. MCP 落地避坑:授权边界、超时问题与日志治理
5.1 远程 MCP 的认证不是"加个 token 就完事"
我在热词里看到过 wss://api.xiaozhi.me/mcp/?token=... 这样的调用样例,忍不住想多说一句:把 token 放在 URL 上是个值得警惕的姿势。URL 会进代理服务器日志、网关日志、浏览器历史,相当于把钥匙贴在了门框上。哪怕只是测试环境,我也建议至少改成 Authorization 请求头。更大的风险是授权粒度——如果 Server 绑定了数据库的读写全部权限,模型在这个 Server 上能调用的工具就等于全部工具。
我自己的习惯是:每个远程 MCP Server 单独签发短期 token,再加 IP 白名单。原因很现实,Agent 一旦被提示注入诱导去调用危险工具,Server 侧的最小权限还能兜底。规范里也有分级授权思路——用户批准模型访问哪些工具,管理员批准 Server 访问哪些底层数据,这两层应该分开管控。很多团队接入远程 MCP 图省事,把这两层合并成一把万能钥匙,哪天出了问题一定是先被攻破的那个。
5.2 30 秒超时、冷启动与日志别打进协议通道
社区里高频出现的报错是 "mcp client for codex apps timed out after 30 seconds"。这类问题绝大多数跟协议无关,就是 Server 冷启动慢。Python Server 初始化时要加载模块、连数据库、读配置,如果全塞在 initialize 握手完成前,非常容易撞上客户端 30 秒上限。
处理手法我总结三个:一是懒加载,数据库连接、重型模型延迟到第一次 tools/call 真正执行时再建;二是给超时分级,initialize 和 tools/list 这类元操作给足时间,单次工具执行反而设短超时,避免慢工具拖垮整个会话;三是善用 progress notification,工具执行时间长了先推一条进度消息,让客户端知道还活着。很多超时不是 Server 挂了,而是客户端等得不耐烦。
另一个容易被忽略的坑是日志。MCP 的 stdio 通道是协议消息专用通道,你在 Server 里 print 一行 debug,Client 端解析就会错乱。正确做法是应用日志写文件或独立的日志端点,需要协议级日志时走规范里的 logging notification,按 debug/info/warning 分级发送。动手调试的时候,区分"协议日志"和"业务日志"能省下大量排查时间。热词里那条"mcp 回写打通",本质上是让 Server 在任务完成后反向给 Host 发通知,MCP 的 notification 机制加上自研回调可以做到,但要注意别设计成循环调用,否则一条写库通知会引发一批新任务。
5.3 什么时候你根本不需要 MCP
最后泼一点冷水。下面几类场景完全不需要 MCP:只有一个模型、一两个工具、内部短连接,Function Calling 直接写最快,协议握手反而是杀鸡用牛刀;工具完全专用,不和别的应用共享,也没有被其他 Agent 复用的打算,引入协议层是纯负担;团队已有成熟的自研工具调用框架,迁移成本大于收益,更务实的是写适配器把老框架暴露成 MCP Server,而不是推翻重来。
判断标准其实很朴素:当工具方和模型方不是你一家说了算的时候,MCP 的标准化收益远大于协议开销;当一切都在掌控中、工具不过三五个,Function Calling 才是更省事的答案。千万别为了追赶热词硬上 MCP,协议层加进来之后,要维护的东西就多了——连接、版本、权限、日志,每一样都是成本。真正需要它的团队,通常不是被概念说服的,而是被工具数量压垮后自然摸到这条路的。
踩过这些坑之后,我现在看一个新 Agent 项目,第一反应不是"要不要上 MCP",而是"这里到底有没有第二家模型、第三个工具、跨应用复用的可能"。没有,就老老实实用 Function Calling;有,就认真做 MCP Server 的边界设计和可观测性。我最近在内部项目里把 8 个工具统一成了 4 个 MCP Server,最直观的变化不是代码量少了,而是工具终于可以独立于模型演进,换模型厂商的时候,那颗心终于不用悬着了。