说是开发,其实绝大多数时间都在拷打Windsurf里的Claude、GPT和Gemini老师。
我们意念合一.jpg
一、MCP到底是什么
MCP 是 Model Context Protocol 的缩写,最早由 Anthropic 提出。其作用可以理解为一种 「接口协议」,让 LLM 能够和外部的各种工具、数据库、服务进行交互。
但光看这一段定义,实际上我自己是很迷糊的,我想知道的是,一个只能预测文本tokens的模型,到底是如何调用一个外部API呢?
调用MCP的基本流程大概有五步:
第1步:准备阶段-向 LLM 提供工具调用说明
在用户与LLM开始对话之前,开发者会先将所有可用的工具(API、函数)按照特定格式(例如JSON Schema)定义好,并作为一条特殊的“系统指令”(System Prompt)或上下文的一部分,发送给LLM。
这些说明看起来像这样:
{
"tools": [
{
"type": "function",
"function": {
"name": "query_weather",
"description": "获取指定城市实时的天气信息",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市名称,例如:北京"
}
},
"required": ["city"]
}
}
},
{
"type": "function",
"function": {
"name": "book_flight_ticket",
"description": "为用户预订从出发地到目的地的机票",
"parameters": {
"type": "object",
"properties": {
"departure_city": {"type": "string", "description": "出发城市"},
"destination_city": {"type": "string", "description": "目的地城市"},
"date": {"type": "string", "description": "出行日期,格式 YYYY-MM-DD"}
},
"required": ["departure_city", "destination_city", "date"]
}
}
}
]
}
现在,LLM 已经知道了它有两个工具:query_weather
和 book_flight_ticket
,以及如何使用它们。
be like 给大家一本教辅资料,先看定义和例题,然后照葫芦画瓢去做练习题
第2步:分析并产生调用意图
用户输入一个指令,例如:“帮我查一下北京今天的天气怎么样?”
LLM在它庞大的语言知识和刚刚收到的工具说明书的共同作用下,进行推理:
- 用户的意图是“查询天气”。
- 我拥有的工具里,有一个叫
query_weather
的工具,它的描述是“获取指定城市实时的天气信息”,正好匹配。 - 这个工具需要一个
city
参数。 - 用户的输入里提到了“北京”,可以作为
city
参数的值。
因此,LLM 会生成一段文本,而这段文本恰好是一个结构化的 JSON,代表了它想要调用的工具和参数。LLM的输出会是这样的(这不是最终给用户的回答,而是一个中间步骤):
{
"tool_calls": [
{
"id": "call_abc123", // 一个唯一的调用ID,用于后续匹配结果
"type": "function",
"function": {
"name": "query_weather",
"arguments": "{\"city\": \"北京\"}" // 参数被序列化为JSON字符串
}
}
]
}
第3步:MCP框架解析并执行
我们的应用程序后端(MCP框架或者别的什么代码)会接收到 LLM 在上一步生成的这个 JSON。
- 解析:程序看到
tool_calls
字段,知道这是一个工具调用请求。 - 定位:它根据
name: "query_weather"
找到本地真正定义的Python函数def query_weather(city): ...
。 - 传参并执行:它解析
arguments
里的"{\"city\": \"北京\"}"
,得到参数city="北京"
,然后执行query_weather("北京")
。 - 获取结果:这个函数可能会去调用一个真实的天气API,然后返回一个结果,比如:
{"temperature": "25°C", "condition": "晴"}
。
第4步:结果返回 - 将执行结果喂回给LLM
MCP框架将第4步的执行结果,连同之前的调用ID,再次打包成一个结构化的信息,发送给LLM,以继续刚才的对话。
第5步:整合回答 - LLM生成最终的自然语言回复
LLM现在收到了新的信息。它会基于全部的上下文(原始问题 + 它的调用决策 + 工具的返回结果)来组织最终的回答。它看到:
- 用户问了北京天气。
- 我决定调用
query_weather
工具。 - 工具告诉我结果是
{"temperature": "25°C", "condition": "晴"}
。
于是,它生成了对用户友好的、最终的自然语言回答:“北京今天的天气是25摄氏度,晴天。”
二、MCP 的能与不能
现在,我们知道了MCP的原理,那它到底能做什么,和做不了什么呢?
1.能
目前 MCP 还只是一个被动调用的工具,但也确实极大地拓展了 LLM 的能力范围。回想一下,Funciton calling 其实早已存在,它是 MCP 的前身或者说技术雏形。MCP 的贡献在于将这一机制进一步规范化、标准化,使得不同模型、不同工具之间的交互能够更顺畅、更一致。
因此,MCP 给原本囿于纯文本生成的 LLM 打开了一扇通往外部真实世界的大门。通过各种search、hook、数据分析工具、GUI操作工具,MCP 让 LLM 具备了影响和改变外部环境的能力,实现了原本 LLM 想都不敢想的复杂流程。
除了基础工具端,目前的旗舰模型(GPT5、Gemini 2.5 Pro、Claude、DeepSeek V3.1等)的能力也确实已经足够好,能够比较可靠地理解工具定义、判断调用时机、生成正确参数,从而有效地执行各种工具调用,充当起了 agentic 模型的核心角色,这就是为什么有部分批评言论说manus实际上没有门槛,他们的实际业务核心是 anthropic 的 Claude 系列模型,而这些批评并不能说全无道理。
2.不能
然而,MCP 作为一个被动的工具调用协议,其局限性同样也非常明显。很多我所希望的 LLM Agent 能够实现的智能效果,都或多或少被 MCP 的现有范式所束缚住了。
举个例子,MCP 的典型流程是用户提问 -> LLM 决定是否调用工具 -> 执行 -> 回答,这是一种强用户驱动的模式。LLM 很难主动发起一系列与当前用户 query 不完全直接相关但对达成更优结果有帮助的工具调用。一个科研助手,能否在用户提出一个初步想法后,主动去搜索相关文献、对比不同研究方法、甚至设计简单的实验验证假设?这在现有 MCP 框架下很难实现,因为它缺乏一个内在的、持续的“驱动力”去主动规划长期目标和探索路径。
但实际目前像 GPT5 Pro 和 Gemini 2.5 Pro UltraThink 的顶级旗舰推理模型也是可以通过更高的推理预算、更长的推理时间来部分达成上述效果的,只是目前这么做成本比较高,相对平民化的框架设计(Deepresearch和OpenAI的智能代理)的局限性和目标性又太明显太强。
MCP 本身也缺乏状态管理,难以维系长期上下文与任务记忆。虽然对话历史可以在一定程度上保留上下文,但对于一个需要执行多步骤、跨会话的复杂任务,Agent 如何追踪任务进度、保存中间结果、恢复中断的任务?MCP 本身是没有任何权限去控制约束system prompt和上下文的,没有规范如何让 Agent“记住”上次做到了哪一步,收集到了哪些信息,下一步计划是什么。这些都需要开发者在 MCP 之外额外构建复杂的状态管理系统。
关于上下文,我在《浅谈ChatGPT的记忆实现机制 兼论工程端记忆设计》和《关于酒馆SillyTavern所代表的伴侣模型系统的一些小思考》中亦有相关讨论,欢迎感兴趣的读者前去阅读。
最后,MCP 要求工具必须有清晰的预定义(如 JSON Schema)。对于一些接口不固定、参数含义动态变化,或者需要“创造性”使用的工具,这种强定义就显得非常僵化。Agent 无法像人类一样,面对一个新工具时,通过阅读模糊的文档、尝试错误来逐渐掌握其用法。它必须严格按照给定的“剧本”(工具定义)来“表演”。
三、简单的开发感悟
在讨论了 MCP 宏大的技术原理和能力边界后,我想结合自己开发一个简单 Todo-MCP 的心路历程,分享一些更接地气的感悟。这或许能解释,为什么一个看似简单的协议,在实际落地时会面临诸多取舍。
1.最初的灵感:从project.md
工作流开始
我开发 Todo-MCP 的想法,源于我之前使用 Windsurf 或 Cursor 编写代码时养成的一个习惯。为了更好地约束 AI 编程助手并提升代码质量,我会在模型的System Prompt中告诉它:我们来共同维护一个名为 project.md
的文件。
在这个 Markdown 文件里,我会记录下当前的任务序列、未来的功能规划,以及已经完成的进度总结。我要求 AI 每次执行任务前,都必须先读取这个文件,以便充分了解项目的上下文和下一步计划。
当时我认为这种方法非常有效。它就像给了一个健忘但聪明的助手一本工作日志,让他随时都能“记起”我们要做什么、做到了哪里,从而使他的输出更有条理,避免在复杂的对话中迷失方向。我的初衷,就是希望将这种临时想到的工作流,用一个更稳定、更标准化的方式固定下来,在部分特化场景(如和AI一起学习)发挥作用——这便是 Todo-MCP 的思路来源。
2.为什么在开发中不断的砍功能?
基于上述想法,我最初设计的 MCP 功能其实相当完整,大致包含了三个模块:
- 基础待办事项(Todo):这是最核心的功能,类似于现在很多编程助手在执行任务前会先列出具体步骤,完成后再逐一勾选。
- 笔记与日志(Notes):熟悉 Windsurf 的朋友可能会有共鸣——Windsurf 会维护一个包含待办、任务进展和笔记的 Markdown 文件。我希望能实现类似的效果,让 AI 能记录临时的想法和关键信息。
- 任务状态管理(State Management):例如,当一个任务临近到期时,由 AI 主动提醒我,并推动任务的完成。
然而,最终交付的产品,却是一个极其简洁的、只包含核心功能的 Todo-MCP,主要的考量如下:
当然最核心的原因肯定还是我太菜了+没有充足的开发时间
首先,技术实现过于臃肿,且缺乏基础设施支撑。 让模型通过 MCP 自主维护一个完整的 Markdown 文件,涉及到对文件内容部分或全部的增、删、改、查,这套流程对于一个轻量级插件来说太过重了。Windsurf 之所以能做得不错,是因为它背后有一整个 AI IDE 作为强大的基础设施(Infra)来支撑。而我的主要应用场景是跨平台的:PC 端的 Cherry Studio,加上手机端的 Rikka Hub,我需要和 AI 一起制定计划、学习知识。这个场景缺乏一个统一且完善的底层设施来承载复杂的交互。
其次,回归本质:一个持久化的“记忆组件”就已足够。 在反复权衡后,我发现一个简单的 Todo 组件就已经能满足我绝大多数的需求。我真正需要的,无非是一个能够跨越当前对话上下文的、持久化的状态管理和记忆组件。它需要比通用的记忆功能(如 Rikka 的记忆模块)更有条理、更方便管理,但其复杂度又不能远超于此。Todo 列表恰好完美地命中了这个甜点区。
最后,保持简洁,为未来探索保留可能性。 从复杂的构想回归到最核心的需求,本身也是一种明智的开发策略。先实现最核心、最高频的功能,确保其稳定可靠,远比构建一个庞大但处处是短板的系统要好。当然,这不代表放弃了最初的设想,或许在未来,我会基于这个小插件,逐步探索加上类似笔记和状态提醒的功能吧。