MCP 和 Function Calling:谁来发起调用,工具又住在哪
MCP vs Function Calling: Who Asks, and Where the Tools Live
Function calling 解决的是模型怎么开口要工具;MCP 解决的是工具怎么写一次、到处接。它们是上下两层,不是二选一。
一句话版本
很多人会问“该用 MCP 还是 function calling?”,这个问题本身就有点问歪了。它们不在同一层:
- Function calling 是 模型和你的应用 之间的约定:模型用它来说“我想调这个函数,参数是这些”。
- MCP(Model Context Protocol) 是 你的应用和工具 之间的约定:把工具打包成一个 server,让任何应用、任何模型都能发现和使用它。
一个管“模型怎么开口要”,一个管“工具怎么被提供”。今天大多数 agent,其实两样同时在用。
Function calling:模型学会了“开口要”
最原始的 function calling,是直接把工具清单写进请求里:
{
"name": "get_weather",
"description": "Current weather for a city",
"parameters": {
"type": "object",
"properties": { "city": { "type": "string" } },
"required": ["city"]
}
}模型自己不执行任何东西。它只是回一段结构化的“请帮我调 get_weather,参数 { city: "上海" }”。真正执行函数的是 你的代码,执行完把结果塞回去,模型再接着往下说。
简单、快、完全可控。但注意工具住在哪:在你应用的代码里。另一个应用也想用同一个工具?复制一份。换个模型厂商?它的 tool 格式又略有不同,得适配。十个应用乘五个工具,就是五十份胶水代码。
MCP:工具写一次,到处插
MCP 做的事,是把工具从应用里拿出来,变成一个独立的 server。应用这一侧(叫 host,比如 Claude Code、Cursor、桌面客户端、你自己写的 agent)通过 MCP client 连上它,问一句:你都能干什么?
模型 ⇄ Host 应用 ⇄ MCP client ⇄ MCP server(s)
├─ tools (能做的动作)
├─ resources (能读的数据)
└─ prompts (可复用的模板)Server 把自己的工具描述一次,任何支持 MCP 的 host 都能列出并调用,不管背后是哪个模型。原来“N 个应用 × M 个工具”的胶水问题,就变成了“N + M”。很多人把它比作 AI 工具界的 USB-C,这个比喻基本成立:它是一个标准接口,而不是一个新设备。
容易被忽略的一点:MCP 并没有取代 function calling。 Host 加载 MCP server 之后,会把 server 的工具转换成模型能看懂的 tool 定义,模型要用的时候,依然是通过 function calling 开口。MCP 决定的是“工具从哪来、怎么够得着”,function calling 依然是“模型怎么开口要”。
一个本站的真实例子
最近给这个博客做发布流程的时候,我两样都写了:
- 一个 CLI(
pnpm blog push post.md),就是普通代码去调 HTTP API; - 一个 MCP server,暴露
blog_save、blog_list、blog_set_status这些工具。
MCP server 大概就一个文件。有了它,我对 Claude Code 说一句“把这篇笔记整理成博客草稿”,它自己就能找到 blog_save 并调用;换任何别的 MCP host,也一样能用,我一行新代码都不用写。你正在读的这篇,也是一个 markdown 文件,走的同一条流程。
什么时候用哪个
| 场景 | 只用 function calling | MCP |
|---|---|---|
| 工具和这一个应用的内部状态绑死 | ✅ 最简单 | 杀鸡用牛刀 |
| 同一套工具要给好几个应用 / 模型用 | 复制粘贴 | ✅ 写一次 |
| 工具来自别人(GitHub、数据库、Figma…) | 自己写胶水 | ✅ 装他们的 server |
| 追求最低延迟、最少零件 | ✅ | 多一个进程、多一跳 |
大家常跳过的代价
- 上下文不是免费的。 每连一个 server,它的工具描述就会进到模型的上下文里。连上十个,对话还没开始就先花掉一截 token;工具一多、长得又像,模型选错的概率也上去了。
- 信任问题。 MCP server 是你在本机、用你的权限跑的代码,它的输出还会直接喂给模型。装一个 server,应该像装一个依赖那样谨慎。
- 多一个零件。 独立进程会挂、会版本漂移、会慢,这些都是应用内的一个函数不会有的问题。
收个尾
Function calling 让模型学会了“开口要”工具;MCP 让工具有了一个标准的“出现方式”。如果一个工具和某个应用同生共死,一个函数就够了;如果你想写一次、到处都能用,就把它做成一个 server。