🔌 第1章 快速了解MCP
📄 一句话概括

它的全部优势可以写成一行:N 乘 M 变成 N 加 M。 3 乘 4 只省 42%,10 乘 30 省掉 87%——价值随规模长,不随功能长。 所以规模小的时候用 MCP 是绕远路,这话得说在前面。

📝 本章和前面几章的关系

前两节讲了案例和历史,这一节换算账。 顺便把 MCP 和 Function Calling、OpenAPI 摆在一起,说清它们不在同一层。 最后给三张场景清单:哪类活适合接 MCP,哪类不该接。

一、先把账算一遍

假设有 N 个宿主、M 个服务。各写各的,要写 N 乘 M 份适配; 走 MCP,写 M 个 Server,宿主那边照协议连,一份不用写。 合计下来是 M 加 N。省下的比例就是 1 减去 (M+N) 除以 (N*M)。 3 个宿主配 4 个服务:12 份变 7 份,省 42%。这个数不好看,所以小规模的时候不该硬上 MCP。

10 个宿主配 30 个服务:300 份变 40 份,省 87%。同一套约定,规模差三倍,收益差一倍。 这也是为什么这东西先在平台侧火起来,而不是先在个人开发者手里火:平台天生就是多宿主、多服务。

二、它和 Function Calling 不在同一层

这两件事经常被混着说,其实分工很清楚。 Function Calling 解决的是「模型怎么把『我要调某个函数』这句话说出来」,格式由模型厂商定, 你有几个函数就注册几份定义。 MCP 解决的是「说出来之后,去哪儿调、怎么调、调完怎么回」。

所以它们不冲突,还能叠着用:宿主把 MCP 的工具表翻译成自己家的 Function Calling 格式,再交给模型。 演示第三段就是这件事的缩影。 一句话记:Function Calling 管「说」,MCP 管「接」。

三、四种接法,各自卡在哪一层

把常见的四种摆出来,差别一眼就清楚。

接法 谁定格式 换个宿主 换个服务
直接写死 你自己 重写 重写
Function Calling 模型厂商 照它的格式重写 加一份定义
OpenAPI 接口作者 宿主得支持 OpenAPI 写一份 yaml
MCP 协议本身 照协议连 写一个 Server

OpenAPI 那一行值得多说一句:它跟 MCP 长得很像,都是一份自描述的接口清单。 区别在面向对象不同。OpenAPI 面向的是会写代码的人,MCP 面向的是会调工具的模型。 前者要你读文档拼请求,后者要模型自己看着描述选工具。

所以 MCP 的 schema 里,description 字段的位置比 OpenAPI 重要得多——那句话是写给模型看的, 第 3 章会讲它被投毒之后有多危险。

四、三个核心优势,按可靠性排序

第一,接口统一。同一份工具表,任何宿主都读得懂。这一条最硬,因为它不依赖任何一方的配合。 第二,能力可插拔。加一个工具不用动宿主,Server 推一条通知就行。 这一条依赖宿主愿意实现那个通知,所以打了点折扣。 第三,生态可复用。别人写好的 Server 你能直接用。 这一条最软——目录站上很多 Server 半年没更新,装上跑不起来是常事。

这三条按「不需要别人配合」排序。第一条自己就能实现,第三条要看别人脸色。 【判断一个优势靠不靠得住,就问一句:这件事需不需要第三方配合。】

五、什么时候不该用

只接一个服务、只用一个宿主,用 MCP 是多绕一层。中间多了一个进程、一套报文、一处可能出错的地方。 能力是固定的、一辈子不变的那种,写死更省事。省下的不是代码量,是心智负担。 需要极致低延迟的场景,也先别上。 演示里握手要 500 毫秒(解释器启动),每次往返 0.15 毫秒——单次调用比直接函数调用贵两个数量级。

还有一类最容易被忽略:要精确控制每一次调用的场景。走 MCP 之后,调不调、调几次由模型决定, 你只能看日志。 典型应用场景可以归成三堆:一是跨平台复用同一批能力,二是让非开发者也能给自己的助手加能力, 三是把内部系统包一层给 AI 用。第 8 章往后的九个案例,基本都落在第三堆里。

六、动手 Demo:N 乘 M 变成 N 加 M

四组规模各算一遍,看省下的比例怎么变。 然后把同一条工具定义装进两个不同宿主的形状里,看变的是什么、不变的是什么。

for n, m in [(3, 4), (4, 6), (6, 10), (10, 30)]:
    hard = n * m              # 各写各的
    mcp = n + m               # 写 Server + 照协议连
    print(n, 'x', m, hard, mcp, '%d%%' % round((1 - mcp / hard) * 100))

# 同一条工具定义,装进两个宿主各自的形状
tool = {'name': 'weather.forecast',
        'description': '查某城市某天的天气',
        'inputSchema': {'type': 'object',
                        'properties': {'city': {'type': 'string'},
                                       'day': {'type': 'string'}},
                        'required': ['city', 'day']}}

host_a = {'name': tool['name'], 'input_schema': tool['inputSchema']}
host_b = {'type': 'function',
          'function': {'name': tool['name'],
                       'parameters': tool['inputSchema']}}

实跑输出:

--------------------------------------------------------------------
N 乘 M 变成 N 加 M
--------------------------------------------------------------------


  ① 先数一遍
  --------------------------------------------------------------------

| 宿主 × 服务 | 各写各的 | 走 MCP | 少写 |
|---|---:|---:|---:|
| 3 × 4 | 12 | 7 | 省 42% |
| 4 × 6 | 24 | 10 | 省 58% |
| 6 × 10 | 60 | 16 | 省 73% |
| 10 × 30 | 300 | 40 | 省 87% |

  3 乘 4 省得不多,10 乘 30 省掉 87%。【MCP 的价值随规模长,不随功能长。】
  只接一个服务、只用一个宿主的人,用 MCP 反而是多绕一层。这话得说在前面。

  ② 四种接法,差在哪一层
  --------------------------------------------------------------------

| 接法 | 谁定格式 | 换个宿主 | 换个服务 |
|---|---|---|---|
| 直接写死 | 你自己 | 重写 | 重写 |
| Function Calling | 模型厂商 | 照它的格式重写 | 加一份定义 |
| OpenAPI | 接口作者 | 宿主得支持 OpenAPI | 写一份 yaml |
| MCP | 协议本身 | 照协议连 | 写一个 Server |

  Function Calling 和 MCP 不是一回事,也不冲突:前者是「模型怎么说出要调什么」,
  后者是「说出来之后,去哪儿调、怎么调」。【它们在不同层。】

  ③ 同一份表,两个宿主各自装
  --------------------------------------------------------------------

服务端吐出来的一条工具定义,长这样:
    {
      "name": "weather.forecast",
      "description": "查某城市某天的天气",
      "inputSchema": {
        "type": "object",
        "properties": {
          "city": {
            "type": "string",
            "description": "城市名"
          },
          "day": {
            "type": "string",
            "description": "今天/明天/后天"
          }
        },
        "required": [
          "city",
          "day"
        ],
        "additionalProperties": false
      }
    }

宿主 A 把它装进自己的形状(字段叫 input_schema):
  {"name": "weather.forecast", "input_schema": {...}}

宿主 B 把它装成另一种(套一层 function,字段叫 parameters):
  {"type": "function", "function": {"name": ..., "parameters": {...}}}

  外层字段名两处不同,可里面装的 schema 一个字都没改。
  【这就是「标准接口」的全部含义:变的是包装,不变的是内容。】
  它保证的不是「功能更强」,是「不用为第二个宿主再干一遍」。

  ④ 一句话说清 MCP 是什么
  --------------------------------------------------------------------
一个约定:把「能做什么」写成一张表(name + description + inputSchema),
用 JSON-RPC 2.0 收发,走 stdio 或 HTTP。就这些。
剩下的全是这句话的推论:既然只有一张表,谁都能读;既然只有一种报文,谁都能发。
  ! 下面十章都是在这句话上加东西:加安全、加调试、加部署、加九个案例。

①第一张表四行,看最后那一列的走势就够了:42、58、73、87。 规模每翻一倍,省下的比例往上涨一截,但涨得越来越慢。 ②3 乘 4 那一行只有 42%,这不是笔好买卖。 中间多了一个进程、一套报文、一处会出错的地方,换 42% 的代码量,很多项目不划算。 ③第二张表四行,讲的是四种接法卡在哪一层。 写死卡在宿主,Function Calling 卡在模型厂商,OpenAPI 卡在宿主支不支持,MCP 卡在协议本身。 ④第三段是本节最值得抄的一段:同一条工具定义,装进两个宿主的形状,外层字段名一个叫 input_schema、 一个叫 parameters,里面装的 schema 一个字没改。 ⑤【变的是包装,不变的是内容】——这就是「标准接口」的全部含义。 它保证的不是功能更强,是不用为第二个宿主再干一遍。

30 秒实验

  1. 把演示第一段加上 (2, 2) 和 (20, 50) 两组。哪一组的省下比例最低,为什么。
  2. 用一句话说清 Function Calling 和 MCP 各自管哪一段。说不清就再读一遍那张四行的表。
  3. 想一个你手上「只接一个服务、只用一个宿主」的场景。判断一下值不值得上 MCP。

常见误区

  1. 「MCP 比 Function Calling 先进。」两者不在同一层,一个管怎么说,一个管怎么接,能叠着用。
  2. 「用了 MCP 性能更好。」中间多一个进程、多一轮报文,单次调用比直接函数调用贵得多。 它省的是维护成本,不是延迟。
  3. 「规模小也该上 MCP,反正早晚要用。 」3 乘 4 只省 42%,先写死,等宿主或服务涨到两位数再抽出来,成本更低。
自测三问(点开对答案) 1. 10 个宿主、30 个服务,走 MCP 能省多少? —— 各写各的是 300 份,MCP 是 30 加 10 等于 40 份,省 87%。 2. Function Calling 和 MCP 各管哪一段? —— 前者管模型怎么把「我要调什么」说出来,格式由厂商定;后者管说出来之后去哪儿调、怎么调。 3. 三个核心优势里,哪一个最不依赖别人配合? —— 接口统一。它只要协议本身在就成立; 能力可插拔要宿主实现通知,生态可复用要看别人的 Server 还维护不维护。

优势算完了,从下一章开始拆内部。 第 2 章会把 Host、Client、Server 三个角色摆开,再讲一条报文怎么从这头走到那头。

相关笔记