作者的判断是两者并不对立,MCP server 内部往往还是在调 REST;真正好的 MCP 实现按 agent 的目标来设计,而不是把每个 endpoint 机械翻译一遍。12 分钟,接口设计层面的实操参考。
REST api vs MCP
GET /users/123、解析响应、处理错误。searchArticles 资源接受一个查询、返回语料中的相关文章。资源常要处理大数据(文件、大查询结果),处理器可能支持分块流式或分页。
sendSlackMessage 工具接受频道和消息并发帖。因为有副作用,工具处理器通常包含校验与安全检查,并清楚定义输入参数和输出 schema。sqlQueryTemplate 帮助 AI 一致地组织数据库查询请求;这类处理器可能完全不碰外部系统,只返回精心设计的模板,或发起多步工作流。tools/list,server 返回一份机器可读的目录,每个工具带名称、自然语言描述和定义输入的完整 JSON Schema;resources/list 和 prompts/list 同理。tools/list 是一次活的协议交互——agent 问,server 答,agent 当场适应。tools/call,发现流程永远是 tools/list,初始化握手永远相同。agent 要接 GitHub、Slack、数据库、Google Calendar、文件系统五个服务,用的是同一套协议,没有自定义适配器,没有针对具体服务的集成代码——build once, integrate many。tools/list 拿到活目录。GET /books/123);MCP 是 JSON-RPC 2.0 方法调用(tools/call → get_book)。repository/list 这类高层工具,任何 agent 都能通过标准协议发现并调用;内部则把每次工具调用翻译成对应的 GitHub REST 请求。MCP 层负责发现、会话管理和统一接口,REST 层负责真正的 GitHub 操作。get_user()、get_orders(user_id)、get_order_details(order_id) 用快速网络跳转串起来。对开发者而言,选择越多越好。get_user、get_orders、get_shipments 三个独立工具,逼 agent 跑三个来回,还要把中间结果留在对话历史里。
track_order(email) 工具,内部调完三个端点,直接返回完整而有上下文的答案——「订单 #12345 已由 FedEx 发出,周四送达」。context:全文的问题起点。作者指出「a new class of API consumer has arrived: LLM-powered agents」——它们需要在运行时 discover 你的 API 能做什么、reason 该用哪个操作、maintain context 跨多步流程,而这一切都没有人预先写好集成代码。REST 的所有讨论都建立在「消费者是照 spec 写代码的开发者」这个前提上,前提一换,结论全变。
费曼一下:以前用你接口的是「先读说明书再动手的人」,他看完文档就把步骤写死进代码里。现在来了一位「边看边决定的顾客」,他不提前学,进门才问你这儿有什么、我该点哪个,而且他还记不住上一句说过啥——除非你给他一张现场菜单和一张能记事的桌子。
context:作者列的第一个结构性缺口是「No self-description at runtime」——REST API 不会告诉你它能做什么。OpenAPI 规范存在,但那是给开发者工具用的静态文档,API 本身没法说「hey, I have a new capability you might want to use」。MCP 的 tools/list 正是对这个缺口的直接回答。
费曼一下:静态文档像贴在墙上的菜单,改了菜得有人去换纸;运行时自描述则是每次客人进门都当场报一遍今天有什么菜。前者要靠人同步,后者永远是最新的。
context:REST「stateless by design」是它易于缓存、负载均衡、横向扩展的原因,也是 agent 的痛点:查用户、看订单历史、改地址三步之间,上下文得手动搬。MCP 维持持久会话,初始化时双方声明各自支持什么,形成本次会话的契约,会话内上下文持续存在——「this is the opposite of REST's stateless model」。
费曼一下:无状态是每次打电话都要从「您好,我是谁、我要干嘛」重讲一遍;有状态会话是一通电话没挂,说到第三件事时对方还记得前两件。多步任务里,这个差别决定了能不能干成事。
context:「Every API is a snowflake」——每个 REST API 有自己的端点、参数格式、认证方案和错误约定。agent 要用五个 API 就要写五套适配器。作者在对照表里把它量化成集成成本:N 个 API × M 个 agent = N×M 个适配器;MCP 用统一协议消除这个乘法。
费曼一下:每家店插座形状都不一样,你带五个转换头出门。统一成同一种插头之后,你带一根线就够了——省下的不是一点力气,是一个乘法。
context:这是全文最锋利的一处判断。REST 的原子化小端点原则「actively hurt AI agents, who pay a token cost for every tool definition they have to reason over」。作者进一步区分两种迭代的经济学:程序化迭代很便宜(人读一次文档,代码跑多少遍都不加价),agentic 迭代昂贵——每多一个工具,每一次交互都要多付 token 与延迟。
费曼一下:给人看的菜单可以有两百道菜,他扫一眼点完就走。给模型的菜单每一行都要它读完、想一遍,而且每来一次都重读一遍,读的钱按字算。所以菜单越长越贵,贵的还不是一次性的。
context:MCP server 通过 Resources(读取型、通常无副作用的信息检索,如 searchArticles)、Tools(有副作用的主动操作,如 sendSlackMessage)、Prompts(可复用的提示模板或工作流,如 sqlQueryTemplate)三类原语广播能力。并非每个 server 都用满三种,实践中多以 tools 为主。
费曼一下:一个是「查东西」(只读,不改变世界),一个是「做事」(会改变世界,所以要校验和安全检查),一个是「教你怎么问」(把常用的问法做成模板)。三种能力性质不同,所以协议把它们分开命名。
context:作者用来解释标准化价值的核心类比——「MCP is to AI applications what USB-C is to laptops」。有 USB-C 的笔记本能接任何厂商的外设,用 MCP 的 agent 能接任何 MCP server,「it doesn't matter who built the server; the interface is identical」。
费曼一下:标准的价值不在于它多先进,而在于它让「谁造的」这件事不再重要。一旦接口相同,生态就可以各造各的,用户端却只学一次。
context:作者强调 tools/list 与 OpenAPI 的差别是性质上的而非格式上的:「An OpenAPI spec is a static document designed for developer tooling. MCP's tools/list is a live protocol interaction: the agent asks, the server answers, and the agent adapts.」由此 agent 无需重新部署代码就能用上新功能。
费曼一下:一份是印好的说明书,一次是当面的问答。说明书可能过期,问答不会——因为它是在你要用的那一刻才发生的。
context:全文的核心主张。MCP「doesn't replace your REST API」,它是坐在既有 API 之上的协议层;现实中「MCP servers use REST APIs internally」——GitHub 的 MCP server 暴露 repository/list 这类高层工具,内部翻译成 GitHub REST 请求,Stripe、Slack、Notion、Salesforce 同理。两者是 AI 栈里互补的层,不是竞争的标准。
费曼一下:不是换发动机,是加一个新的方向盘。车还是那辆车,业务逻辑还在原处跑,只是多开了一个专门给机器司机用的操作面板。
context:作者给出的正面方法论:「design MCP tools around what the user is trying to accomplish, not around your internal API structure」。反例是把 get_user、get_orders、get_shipments 原样暴露,逼 agent 跑三个来回并自行保管中间结果;正例是一个 track_order(email),内部调完三个端点,返回「Order #12345 shipped via FedEx, arriving Thursday」这样完整而有上下文的答案。
费曼一下:别把厨房的操作步骤当菜单卖给客人。客人要的是「一份做好的菜」,不是「切、炒、装盘」三个可以分别下单的动作。
context:作者点名最常见的采纳错误——「auto-converting every REST endpoint into an MCP tool」,FastMCP 的 OpenAPI 转换器让这只需一行代码,因而格外诱人。陷阱在于好的 REST API 与好的 MCP server 设计目标根本不同:前者慷慨(选择越多越好),后者必须克制。
费曼一下:能一键导出,不代表该一键导出。把两百个内部按钮全搬到客人面前,看起来是功能齐全,实际是把选择的负担和成本一起转嫁给了对方。
context:MCP 在认证上「takes an opinionated stance」:OAuth 2.1 + PKCE 是规范强制的标准,授权规范经 2025 年 3 月、6 月、11 月多次修订,定义了发现授权要求、动态注册、获取作用域令牌的方式。企业场景的关键诉求是知道哪个用户授权了哪些动作,作用域令牌让 server 能在工具调用时校验该令牌是否具备该操作所需的 scope。
费曼一下:agent 代你办事,就必须能查清楚「是谁批准它办这件事、批准的范围有多大」。作用域令牌相当于一张写明了「只能进这几个房间、只能做这几件事」的门禁卡,而不是一把万能钥匙。
context:作者的收束思想,也是全文最可迁移的判断:「Think of your MCP server as a user interface: same product thinking you'd apply to a UI, just for a non-human user.」平庸与优秀的 MCP server 之间的差距,等同于坏 UI 与好 UI 之间的差距——是否理解你的用户,「the user just happens to be an LLM」。
费曼一下:接口不只是技术活,是产品活。你要问的还是那几个老问题:用户想干成什么、他能承受多少选项、什么才算一次完整的答复。唯一变的是这次用户不是人。
graph TD
C1["新一类 API 消费者:LLM agent"]
C2["REST:面向开发者的通用接口"]
C3["运行时无自描述"]
C4["无状态设计"]
C5["每个 API 都是雪花:N×M 适配器"]
C6["token 税与 agentic 迭代成本"]
C7["MCP:为 agent 而生的协议层"]
C8["三原语:资源、工具、提示"]
C9["运行时发现 tools/list"]
C10["有状态会话"]
C11["统一协议:一次编写处处集成"]
C12["OAuth 2.1 与作用域令牌"]
C13["MCP 包裹 REST 的分层架构"]
C14["按结果设计工具"]
C15["自动转换每个端点的陷阱"]
C16["把 MCP server 当界面来做"]
C17["核心主张:不是二选一而是分层"]
C1 ---|错配| C2
C1 -->|催生| C7
C2 -->|缺口| C3
C2 -->|缺口| C4
C2 -->|缺口| C5
C2 -->|缺口| C6
C3 -->|解法| C9
C4 -->|解法| C10
C5 -->|解法| C11
C6 -->|解法| C14
C7 -->|层级| C8
C7 -->|机制| C9
C7 -->|机制| C10
C7 -->|机制| C11
C7 -->|规定| C12
C7 -->|层级| C13
C2 -->|承载业务| C13
C15 ---|对立| C14
C14 -->|来自| C16
C13 -->|支撑| C17
C14 -->|支撑| C17
整张网络的起点是一次消费者变更:过去读接口的是开发者,现在是 LLM agent(C1)。文章所有论证都由这一个变更展开——不是 REST(C2)退化了,而是它与新消费者之间出现了错配,作者反复强调 REST「wasn't created with AI or LLMs in mind, and that's fine」。
第一层是缺口的展开。错配不是笼统的「不好用」,而是四个可分别指认的结构性缺口:运行时无自描述(C3)、无状态(C4)、每个 API 都是雪花(C5)、为开发者而非模型设计所带来的 token 税(C6)。它们之间不是并列的抱怨,而是同一个设计前提在四个维度上的自然后果——REST 把「谁在调用、调用什么」这件事交给了预先写好的代码,于是运行时的自我说明、跨调用的记忆、跨服务的一致性、面向模型的成本控制全都不在它的职责里。
第二层是缺口与解法的一一对位,这是本文结构上最清晰的地方。C3 对应运行时发现(C9),C4 对应有状态会话(C10),C5 对应统一协议(C11),C6 对应按结果设计工具(C14)。前三条由协议本身提供,是 MCP(C7)作为标准的产物;第四条不同——它是设计纪律,协议给不了,只能由建 server 的人自己守。这条不对称是全文的关键张力:装上 MCP 不等于解决了 token 税,把每个端点自动转成工具(C15)的做法甚至会把这个缺口放大,因为它把 REST「慷慨」的设计哲学原封不动地搬进了一个昂贵得多的消费场景。C15 与 C14 因此构成直接对立的两条路线。
第三层是层级关系而非替代关系(C13)。C7 提供三原语(C8)、发现、会话、统一接口与强制认证(C12),C2 继续承载业务逻辑,二者在同一个栈里各就各位——GitHub 的 MCP server 对外说协议、对内调 REST,是这个结构最直白的证据。C13 与 C14 共同支撑文章的核心主张(C17):问题从来不是「MCP 还是 REST」,而是「两者如何分层」。
最后一条边指向文章的思想落点:按结果设计(C14)之所以成立,是因为它背后是把 MCP server 当界面来做(C16)的产品视角。这个概念是网络中最抽象也最可迁移的一个——它把一个协议选型问题,重新表述成一个用户理解问题,而唯一的变化是这次的用户是模型。
把接口设计成什么样,取决于谁来读它。这句话听起来像常识,但过去十五年它一直不需要被认真对待——读接口的始终是人,人读一次文档,写一次代码,剩下的交给机器重复执行。直到消费者变成一个要在运行时自己判断该调用什么的模型。
有意思的是,REST 的每一项优点,在这个新消费者面前几乎都恰好翻转成缺点。无状态让它易于缓存和横向扩展,却让 agent 必须把上下文在每次调用之间手动搬运一遍。端点小而可组合让开发者拥有自由,却让模型为每一条工具描述缴一份 token 税——你不是在赋能它,你是在淹没它。每个 API 都是一片雪花,这对读文档的人只是一次性的学习成本,对要同时接五个服务的 agent 却是 N×M 个适配器。REST 并没有设计得不好,它只是没有为这个消费者设计,而它本来也不必。
于是有了 MCP,但把它读成 REST 的替代者是最常见的误解。真正的形状是分层:MCP server 对外说统一协议,对内老老实实调你已有的端点。GitHub 的 MCP server 暴露的是 agent 能发现的高层工具,落到实处仍是那套 REST 请求。差别不在谁取代谁,而在 OpenAPI 是一份写给人看的静态文档,tools/list 是一次活的对话——agent 问,server 答,agent 当场适应。
最有价值的判断藏在文章后半段:好的 REST API 和好的 MCP server,设计目标根本不同。REST 慷慨,因为程序化迭代很便宜;MCP 必须克制,因为 agentic 迭代每一次都在花钱。所以别把 get_user、get_orders、get_shipments 三个端点原样搬过去,而是给出一个 track_order,让它内部跑完三步,直接回一句「订单已由 FedEx 发出,周四送达」。同样的结果,一次调用,为真实的消费者而设计。
这其实是在说,MCP server 是一个界面。你为界面做的所有产品思考——用户想完成什么、他能承受多少选择、什么才算一次完整的答复——一条都不能省,只不过这次的用户是个模型。能一键把整个 API 导出成工具,不代表就该这么做;谁先把它当产品设计而不是当接口导出,谁的 agent 就更早真正好用。