将大语言模型(LLM)与外部工具连接,是扩展其能力的主流方法。通过给模型提供工具,它就能查询数据、调用 API,完成更复杂的任务。
提供工具时,可以使用 MCP 模式,简单来说:由一个外部的、集中的工具服务来提供所有工具,开发者写的应用直接调用这个服务,让它去和模型沟通。
这样做表面上简化了开发,开发者不用自己管理工具了。但问题也恰恰出在这里:应用的开发者,失去了对模型上下文的精确控制权。
这是一个根本性的问题,会引发一连串的麻烦。
一个具体的例子:MCP 架构
我们来看看 MCP(Model Context Protocol)这个框架的实现方式。它清楚地展示了这种分离模式。
在 MCP 的世界里,开发者分为两类:
- Server 开发者:他们负责创建工具。
- Client 开发者:他们负责写真正面向用户的应用,并调用 Server 提供的工具。
在 /weather.py 这个 Server 示例中,Server 开发者用一个 @mcp.tool() 的修饰符来定义一个工具:
Python
@mcp.tool()
async def get_alerts(state: str) -> str:
"""Get weather alerts for a US state.
Args:
state: Two-letter US state code (e.g. CA, NY)
"""
# ... function logic here
工具的名称 get_alerts、它的描述和参数,都由 Server 的开发者在这里定义好了。
然后,一个完全不同的 Client 开发者,在他的应用 client.py 中需要使用工具。他并不会自己定义工具,而是通过代码去"询问"Server 有哪些可用的工具。
Python
#
# Client开发者写的代码
#
# 向Server询问有哪些可用的工具
response = await self.session.list_tools()
# 将Server返回的工具列表转换成模型需要的格式
available_tools = [{
"name": tool.name,
"description": tool.description,
"input_schema": tool.inputSchema
} for tool in response.tools]
# 把这个从Server拿到的工具列表,直接传给大模型
response = self.anthropic.messages.create(
model="claude-3-5-sonnet",
messages=messages,
tools=available_tools # <--- 工具在这里被传入
)
这段代码清楚地显示了问题所在。Client 开发者写的代码,只是一个搬运工。它从 Server 获取工具列表,然后原封不动地交给大模型。
这就是我们要讨论的核心风险。
1. 控制权交给了别人
AI 应用的核心,是开发者与大模型之间的交互。开发者应该精确地告诉模型在当前场景下,它能做什么。
在上面的例子中,Client 开发者完全放弃了这种控制。如果 weather-server 的开发者某天在服务器上增加了一个新工具,或者修改了 get_alerts 工具的描述,所有使用这个 Server 的 Client 应用,都会在不知情的情况下立刻受到影响。
2. 浪费了宝贵的上下文
大模型的"上下文窗口"是有限且有成本的。
MCP 官方的示例中,就罗列了大量的"即插即用"型 Server,比如 Filesystem、Git、PostgreSQL。一个 Client 应用可能会连接多个 Server。
想象一下,你的应用只是想问个天气,但因为连接了多个 Server,list_tools() 可能会返回十几个不相关的工具(比如文件读写、数据库查询等)。这些多余的工具定义会被全部发送给大模型,不仅增加了推理成本和时间,还会像噪音一样干扰模型,让它难以选择正确的工具。
3. 工具的质量无法保证
工具的名称和描述对模型能否正确使用至关重要。最了解应用需要什么样工具的人,是 Client 开发者自己。
但是,在 MCP 模式下,Client 开发者无法修改 Server 提供的工具描述。如果 Server 的工具描述写得含糊不清(比如一个叫 process_data 的函数),Client 开发者要么只能忍受,要么就得放弃使用整个 Server。他失去了根据自己业务场景,"精雕细琢"工具描述的能力。
4. 安全性和稳定性的风险
这是最严重的问题。由于 Client 开发者无法控制工具集,应用的安全和稳定就依赖于外部的 Server。
以 mcp-server-filesystem 这个官方工具为例,它能执行文件操作。如果一个 Client 应用连接了它,就等于把文件操作的潜力暴露给了大模型。Client 开发者必须完全信任这个 Server 的实现是绝对安全、没有漏洞的。
如果 Server 的开发者在一次更新中,不小心引入了一个允许删除任意文件的 Bug,那么所有连接到这个 Server 的 Client 应用,都可能在用户不知情的情况下,执行危险的操作。这种风险是灾难性的。
正确的做法是什么?
结论很直接:开发者必须在每一次调用大模型时,都拥有对工具集的完全控制权。
这意味着,工具的 JSON 定义应该由 Client 开发者在自己的应用代码中明确构建,而不是通过 session.list_tools() 从外部获取。
当模型决定调用 get_weather_forecast 时,Client 代码再去调用后端服务或 API 来执行具体逻辑。这样一来,决定权始终在 Client 开发者手中。
优势面:无与伦比的易用性和扩展性
话分两头说,尽管 MCP 架构存在上述风险,但我们也必须看到它解决了哪个核心问题:如何让一个已有的、封闭的应用程序(如一个桌面聊天软件),能够轻松地、动态地使用外部工具?
MCP 的易用性在"为 AI 聊天工具集成本地工具"这个场景下,体现得淋漓尽致。
我们可以从官方文档中"Testing your server with Claude for Desktop"这个例子得到启发:
- 开发者创建了一个工具:比如 weather.py,它能在本地查询天气。
- 用户有一个现成的聊天工具:Claude for Desktop。
- 用户想让聊天工具用上这个天气功能,他需要做什么?他不需要修改 Claude for Desktop 的任何一行代码。他只需要打开一个名为 claude_desktop_config.json 的配置文件,在里面加上几行:
JSON
{
"mcpServers": {
"weather": {
"command": "uv",
"args": ["run", "C:\\path\\to\\weather.py"]
}
}
}
- 保存文件,重启 Claude for Desktop。然后,这个聊天工具就奇迹般地拥有了调用 weather.py 里 get_alerts 和 get_forecast 工具的能力。
这就是 MCP 模式的魅力所在。 它在这里扮演了一个"热插拔"的插件系统角色。
- 对于平台方(如 Claude Desktop 的开发者):他们不需要为每一种可能的工具编写专门的集成代码。他们只需要让自己的应用支持 MCP 协议,就能开放一个巨大的生态,让全世界的开发者为它开发"插件"(MCP Server)。
- 对于终端用户或开发者:他们可以非常方便地将自己编写的、或者别人提供的本地工具,集成到他们日常使用的 AI 平台中,极大地增强了平台的能力。
- 对于工具开发者:他们可以"一次编写,到处运行"。一个标准的 MCP Server,理论上可以被任何支持 MCP 的 Client(如不同的 AI 桌面应用)使用。
在这种场景下,"控制权外移"反而成了一种优点,因为它实现了应用核心功能与扩展功能之间的解耦,带来了巨大的灵活性和生态潜力。
结论
所以,MCP 模式及其代表的架构思想,本身并不是一个绝对的好与坏,而是需要开发者清醒认识它并做出抉择。
- 如果你在开发一个高度可控、业务逻辑闭环、安全要求极高的专业应用,那么你应该将工具的定义权牢牢掌握在自己手中。每一次调用大模型,都应该由你自己的代码来精确构建tools列表,避免依赖外部、不可控的工具源。
- 如果你在构建一个开放平台、一个操作系统或一个希望用户能自由扩展的桌面应用 ,那么 MCP 这种模式就非常有价值。它用开发者的部分控制权 ,换来了用户的灵活性 和平台的生态潜力。
最终,选择哪条路,取决于你的产品目标和应用场景。理解这背后的利弊,才能做出最适合自己项目的技术决策。