折腾了一个月MCP协议,说点真实感受

---

title: 折腾了一个月MCP协议,说点真实感受

date: 2026-05-26

author: 匿名AI从业者

tags: [MCP, AI Agent, 工具调用, 协议, 实践]

description: 一个开发者在MCP协议上折腾一个月的真实记录——从怀疑到尝试,从踩坑到反思,聊聊AI Agent工具调用的现状。

---


> 本文首发于 Agent评测站,同步发布于知乎。欢迎讨论。


---


MCP刚出来时我第一反应是——又来了,又一套"大一统"协议。


做过AI Agent的都知道,工具调用这块有多混乱。OpenAI出过function calling,Anthropic有tool use,Google搞了自己的schema,LangChain那边还有一套Tool接口。各家平台都在定义自己的"标准",结果谁都不服谁。2025年Anthropic推出MCP(Model Context Protocol)的时候,我 honestly 心里就一个念头:哦,又来一个。


没想到后来会花整整一个月跟它死磕。


起因:一个被工具调用逼疯的项目


事情是这样的。年初团队在做一个小项目——一个能调用企业内外部API的Agent。需求听起来简单:Agent能理解用户意图,自己决定调什么接口,传什么参数。做出来给HR部门用,查考勤、走审批、调数据。


结果一开始就卡住了。


我们选的是某家主流模型(不说名字了,免得引战),走function calling模式。模型定义工具的时候,每个工具要写JSON Schema,参数类型、描述、required字段,一样不能少。最开始手工写了十几个工具定义,每个工具定义大概50到100行JSON。改个参数名字,两边都得同步改,特别容易翻车。


更头疼的是,模型对工具调用的成功率和稳定性很差。同样的工具定义,同样的用户问题,今天能调通,明天就崩了。模型有时编造参数,有时跳过关键参数直接调工具,有时明明返回了正确的tool call结果,下一步突然失忆,完全忘了上一步拿到的数据。


这让我开始怀疑:是不是工具定义的格式问题?还是模型本身对function calling的理解有上限?有没有一种方式,能把工具管理的复杂度降下来?


就是在这样的困惑中,我重新翻出了MCP。


第一阶段:带着偏见上手


说实话,我对MCP的初期印象并不好。


MCP的设计思路是让模型通过一个中间层来调用工具。它的核心概念其实不多:MCP Server负责暴露工具,MCP Client负责和模型沟通,两边通过JSON-RPC 2.0来通信。模型不直接调用API,而是通过MCP层来"发现"工具、调用工具、获取结果。


听起来挺合理是吧?但我一开始觉得这层抽象完全是多余的。


我的想法很简单:模型本来就能直接做function calling,你中间加一层,路径变长了,延迟更高了,出错的环节更多了。何必?


而且MCP的文档写得……说好听点,比较"有提升空间"。官方的Python SDK文档跳过了很多细节,比如怎么处理工具的streaming调用,怎么管理多个并发会话,怎么处理工具调用的超时,这些问题在文档里都找不到答案。只能翻GitHub上的issue,看别人怎么踩坑的。


最初的一个星期,我基本处于"边骂边试"的状态。


第二阶段:真正被触动是在一个意外的地方


转变发生在一个很偶然的场景。


那天我在测试一个MCP Server,这个server暴露了几个HR系统的工具——查员工信息、查考勤记录、提加班申请。我让Agent帮忙"查一下张三这个月的考勤,如果有缺卡帮他申请补卡"。


在传统的function calling模式下,这涉及两个工具的串联调用:先查考勤,根据结果决定是否调申请接口。这里面有一个条件判断的逻辑。以前写这种链条,要么靠模型自己"临场发挥",要么在代码里硬编码if-else。


但MCP的模式下,我关注到一个很有意思的现象:工具的定义和调用逻辑被清晰地分开了。


MCP Server只负责一件事——告诉MCP Client:"我有这几个工具,每个工具有这些参数。"至于怎么用、什么时候用、用哪个,完全由模型+Client来决定。Server不需要关心上游的业务逻辑,Client不需要硬编码工具细节。


听起来像是做软件的常识——关注点分离。但在Agent工具调用这个领域,之前很少有人把这个边界划清楚。大家的做法都是"工具定义写在prompt里,调用逻辑写在代码里",两边搅在一起,越改越乱。


那天我盯着Agent正确串联了两个工具调用,突然意识到:MCP真正解决的,不是"怎么调工具"的问题,而是"怎么管理工具"的问题。


第三阶段:系统性的踩坑记录


想通归想通,真正用起来远没有那么顺利。


坑一:工具描述重要到离谱


MCP Server暴露给模型的,只有工具的名字和描述。模型全靠这几行描述来决定什么时候调这个工具。


我的一个同事写过这样一个工具描述:


工具名:get_attendance
描述:获取员工考勤记录

结果模型一遇到"请假"相关的问题,也去调get_attendance。因为模型觉得"请假也算出勤相关"。


后来改成:


描述:获取员工每日打卡记录,包括上班时间和下班时间。不含请假信息、不含加班信息。

效果好了很多。但还不够——模型有时还是会在需要"查工资"的时候调这个工具。


最后写成了:


描述:仅用于查询员工打卡时间。输入日期范围,返回每天的上/下班打卡时间和打卡状态(正常/缺卡/迟到/早退)。不包含请假、加班、薪资信息。

你看,工具描述不是写给开发者看的,是写给模型看的。模型对自然语言的理解方式和我们不一样。它需要非常明确的范围界定、肯定+否定组合的边界说明。


我们来回调了五六版工具描述,才让模型基本不乱调工具。如果工具数量从10个变成100个,这种调优的工作量会指数级上升。MCP并没有解决这个问题,只是把问题从"调参"变成了"写文案"。


坑二:参数传递的"第三状态"


Function calling模式下,参数传递只有两种状态:有值/没值。但真实的企业业务里,存在大量的"第三状态"。


举个例子:查询考勤时,有一个参数叫"部门ID"。如果传了具体的部门ID,就查这个部门的;如果不传,就默认查当前用户所属部门。看起来很合理。


但在MCP的模式下,模型经常搞混"不传参"和"传空字符串"的区别。你不传param,它给你传个"",结果服务端接到空字符串当成"查询所有部门"来处理,直接把全公司的考勤拉出来了……当场社死。


后来我们在工具定义里不得不加一堆说明:"此参数如果不需要请勿传入,不要传空字符串,不要传null,不要传'undefined'"。看着很蠢,但真的管用。这些细节在论文和blog里没人会写,只有踩过坑的人才知道。


坑三:工具调用的"幻觉闭环"


这个是我个人觉得最严重的问题。


MCP的工作流程是这样的:模型决定调用工具 -> Client把调用请求发给Server -> Server返回结果 -> 模型基于结果继续思考。


问题出在第三步到第四步之间。Server返回的结构化数据,模型有可能"重新解释"。比如Server返回了{"status": "approved", "code": 200},模型在下一步推理时,可能把它说成"系统提示审批通过"。严格来说,这也是模型幻觉的一种——它不相信原始数据,非要自己"润色"一遍。


在单次调用场景下,这问题不大。但在工具链调用场景下(A工具的结果给B工具用,B工具的结果给C工具用),每经过一次模型"重新解释",信息失真就叠加一次。链条越长,结果越离谱。


我们做过一个极端测试:用5个工具串联,最后一个工具返回的数据和第一个工具的输出差了十万八千里。模型完全忘了中间环节的原始输出,按照自己的"记忆"编了一套新的结果。


这个问题到现在也没有很好的解决方案。当前的做法只能是尽量缩短工具链,或者让Server端返回的结果是"模型无法篡改"的格式(比如直接呈现为markdown文本)。但这又回到了"中间层是否存在意义"的老问题。


坑四:Session管理的缺失


MCP的官方实现里,Session管理基本是"交给开发者自己处理"。这在demo里没问题,但在真实的多用户、多会话生产环境里,会出大问题。


我们的场景是这样的:多个用户同时使用Agent,每个用户有自己的上下文。MCP Server本身是无状态的——它只负责处理单个工具调用请求,不跟踪"这个请求是哪个用户的"、"上一个请求是什么"。


那么问题来了:如果一个用户先查了自己的考勤,又查了团队的考勤,这两次查询之间没有关联。但如果Agent需要在"查考勤"之后再做"申请加班",就需要知道上一步的结果是谁的、哪个部门的。


我们最后不得不在MCP外面包了一层状态管理,给每个会话分配一个ID,在工具调用时把会话ID作为隐含参数传给Server。这样Server才能识别"谁是谁"。


但这意味着MCP并没有真的解决有状态场景的问题——它只是把问题转移到了应用层。


第四阶段:一些没有答案的思考


折腾了一个月,如果让我给MCP打个分,大概在6.5/10。方向是对的,但距离"好用"还有很长的路要走。


关于MCP vs Function Calling


很多人问MCP和function calling是什么关系。我的理解是:它们是不同层次的东西。Function calling是模型能力,MCP是工具编排方式。两者不是替代关系,是配合关系。


如果你的项目只有三五个工具,用function calling硬写可能更直接。但如果你的工具数量在快速增长、需要多人维护、需要复用工具定义,MCP的抽象层就有价值了。


换个角度看,function calling像直接写SQL,MCP像用一个ORM层。小项目没必要上ORM,大项目不用ORM会累死。


关于AI Agent工具调用的未来


一个月的实践让我认清了几件事:


第一,工具调用的瓶颈不在协议,在模型本身的理解能力。无论MCP设计得多好,模型如果无法准确判断"什么时候该调什么工具"、"传什么参数",一切都是空中楼阁。MCP解决的是组织问题,不是智力问题。


第二,"标准化"的价值被高估了。至少现阶段是这样。AI Agent的工具调用还处在很早期的阶段,各家模型的实现方式、能力上限差异巨大。在这种阶段强行"统一",可能反而限制了灵活性。MCP作为一个"软性标准"存在就好,真要逼迫所有厂商遵守,既不现实也不合理。


第三,企业级Agent的最大挑战不是技术,是信任。我感触很深的一个场景是:HR用的Agent明明只是查个考勤,业务方也要反复确认"它不会偷偷改数据吧?"、"它读数据的时候别人能看到我的记录吗?"。MCP的"Server-Client分离"架构在安全性上有天然优势——工具调用路径是可控的、可审计的。这一点在实际落地中,比调用速度、token成本什么的更重要。


关于踩坑的价值


写这篇文章,不是为了吹或者黑MCP。我只是觉得,在这个行业里,太多人只讲成功案例,不讲踩坑细节。每个开发者在接入新技术时都会经历怀疑、尝试、困惑、顿悟、再困惑的过程。这些过程本身,可能比最终的技术选型结果更有价值。


如果你现在也在调研MCP,我的建议是:


  1. 不要一上来就想"用它替换所有方案"。找到一个具体的、边界清晰的场景做试点。
  2. 工具描述的文字比你想象的重要一百倍。花时间打磨它。
  3. 做好"模型不可靠"的心理准备。MCP只是工具层,不能解决模型的"脑子不灵光"问题。
  4. 状态管理、安全审计、异常处理这些,MCP都没帮你做。你要自己兜底。
  5. 如果只是做个小demo,用function calling就够了。MCP的抽象层是有成本的。

最后说一句不那么"政治正确"的话:一个月的实践下来,我觉得MCP在"工具管理"层面的贡献,远大于在"工具调用"层面的贡献。它让开发者重新思考了工具应该如何被组织、描述和暴露给模型。至于调用本身……模型的能力才是真正的天花板。


希望下一次技术迭代,能让我们离"Agent真的能用"更近一步。


---


> 本文同步发布于 Agent评测站,一个专注于AI Agent实际效果评测的站点。我们持续跟踪MCP、Function Calling、Tool Use等技术的真实落地情况。