内参日期:2026-08-11
作者/来源:workos.com
原文:https://workos.com/blog/mcp-vs-rest

WorkOS:给 agent 接 API,该走 MCP 还是 REST

作者的判断是两者并不对立,MCP server 内部往往还是在调 REST;真正好的 MCP 实现按 agent 的目标来设计,而不是把每个 endpoint 机械翻译一遍。12 分钟,接口设计层面的实操参考。

导读

REST api vs MCP

核心观点

REST 擅长什么:为开发者写的通用接口

当消费者变成 agent,REST 的四个结构性缺口

MCP:为 AI agent 而生的协议层

三原语与运行时发现

REST 与 MCP 的逐维对照

不是对手,是层次

按结果设计,而不是按端点

认证:MCP 的强意见

何时用什么,以及怎么开始

底线

概念网络

关键概念

新一类 API 消费者

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 正是对这个缺口的直接回答。

费曼一下:静态文档像贴在墙上的菜单,改了菜得有人去换纸;运行时自描述则是每次客人进门都当场报一遍今天有什么菜。前者要靠人同步,后者永远是最新的。

无状态 vs 有状态会话

context:REST「stateless by design」是它易于缓存、负载均衡、横向扩展的原因,也是 agent 的痛点:查用户、看订单历史、改地址三步之间,上下文得手动搬。MCP 维持持久会话,初始化时双方声明各自支持什么,形成本次会话的契约,会话内上下文持续存在——「this is the opposite of REST's stateless model」。

费曼一下:无状态是每次打电话都要从「您好,我是谁、我要干嘛」重讲一遍;有状态会话是一通电话没挂,说到第三件事时对方还记得前两件。多步任务里,这个差别决定了能不能干成事。

每个 API 都是雪花与 N×M 适配器问题

context:「Every API is a snowflake」——每个 REST API 有自己的端点、参数格式、认证方案和错误约定。agent 要用五个 API 就要写五套适配器。作者在对照表里把它量化成集成成本:N 个 API × M 个 agent = N×M 个适配器;MCP 用统一协议消除这个乘法。

费曼一下:每家店插座形状都不一样,你带五个转换头出门。统一成同一种插头之后,你带一根线就够了——省下的不是一点力气,是一个乘法。

token 税与 agentic 迭代成本

context:这是全文最锋利的一处判断。REST 的原子化小端点原则「actively hurt AI agents, who pay a token cost for every tool definition they have to reason over」。作者进一步区分两种迭代的经济学:程序化迭代很便宜(人读一次文档,代码跑多少遍都不加价),agentic 迭代昂贵——每多一个工具,每一次交互都要多付 token 与延迟。

费曼一下:给人看的菜单可以有两百道菜,他扫一眼点完就走。给模型的菜单每一行都要它读完、想一遍,而且每来一次都重读一遍,读的钱按字算。所以菜单越长越贵,贵的还不是一次性的。

MCP 三原语

context:MCP server 通过 Resources(读取型、通常无副作用的信息检索,如 searchArticles)、Tools(有副作用的主动操作,如 sendSlackMessage)、Prompts(可复用的提示模板或工作流,如 sqlQueryTemplate)三类原语广播能力。并非每个 server 都用满三种,实践中多以 tools 为主。

费曼一下:一个是「查东西」(只读,不改变世界),一个是「做事」(会改变世界,所以要校验和安全检查),一个是「教你怎么问」(把常用的问法做成模板)。三种能力性质不同,所以协议把它们分开命名。

USB-C 类比

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」。

费曼一下:标准的价值不在于它多先进,而在于它让「谁造的」这件事不再重要。一旦接口相同,生态就可以各造各的,用户端却只学一次。

静态规范 vs 活的协议交互

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 无需重新部署代码就能用上新功能。

费曼一下:一份是印好的说明书,一次是当面的问答。说明书可能过期,问答不会——因为它是在你要用的那一刻才发生的。

协议分层:MCP 包裹 REST

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_userget_ordersget_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 设计目标根本不同:前者慷慨(选择越多越好),后者必须克制。

费曼一下:能一键导出,不代表该一键导出。把两百个内部按钮全搬到客人面前,看起来是功能齐全,实际是把选择的负担和成本一起转嫁给了对方。

OAuth 2.1 与作用域令牌

context:MCP 在认证上「takes an opinionated stance」:OAuth 2.1 + PKCE 是规范强制的标准,授权规范经 2025 年 3 月、6 月、11 月多次修订,定义了发现授权要求、动态注册、获取作用域令牌的方式。企业场景的关键诉求是知道哪个用户授权了哪些动作,作用域令牌让 server 能在工具调用时校验该令牌是否具备该操作所需的 scope。

费曼一下:agent 代你办事,就必须能查清楚「是谁批准它办这件事、批准的范围有多大」。作用域令牌相当于一张写明了「只能进这几个房间、只能做这几件事」的门禁卡,而不是一把万能钥匙。

把 MCP server 当界面来做

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)的产品视角。这个概念是网络中最抽象也最可迁移的一个——它把一个协议选型问题,重新表述成一个用户理解问题,而唯一的变化是这次的用户是模型。

费曼 x3

把接口设计成什么样,取决于谁来读它。这句话听起来像常识,但过去十五年它一直不需要被认真对待——读接口的始终是人,人读一次文档,写一次代码,剩下的交给机器重复执行。直到消费者变成一个要在运行时自己判断该调用什么的模型。

有意思的是,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_userget_ordersget_shipments 三个端点原样搬过去,而是给出一个 track_order,让它内部跑完三步,直接回一句「订单已由 FedEx 发出,周四送达」。同样的结果,一次调用,为真实的消费者而设计。

这其实是在说,MCP server 是一个界面。你为界面做的所有产品思考——用户想完成什么、他能承受多少选择、什么才算一次完整的答复——一条都不能省,只不过这次的用户是个模型。能一键把整个 API 导出成工具,不代表就该这么做;谁先把它当产品设计而不是当接口导出,谁的 agent 就更早真正好用。