[music]

大家好,我是佩德罗。我在 Airbyte 担任软件工程师。今天我们要讨论的是我们如何构建代理 MCP(模型上下文协议)以及我们的命令行界面(CLI)。在进入这些细节之前,我想先提供一些背景信息,介绍我们在 Airbyte 所做的工作以及这一切如何融入我们的产品体系。Airbyte 已经存在一段时间了,我们最初是作为数据移动领域的开源标准起步的。

我们将各种 SaaS 工具(如 Zendesk、Stripe、GitHub 等)连接到您的数据分析管道。现在,我们利用同样的专业知识并进行了演变,成为 AI 代理的数据和操作层。我们通过提供所谓的“上下文存储”来实现这一目标,该功能连接到之前提到的所有工具,并位于您的代理之间。它为您提供数据的高效搜索优化索引,使代理能够更有效地查询这些数据。

因此,您可以轻松收集关于这些工具中所有记录、联系人等信息并将它们整合在一起。我们的目标是让数据随处可用且可操作。为此,我们提供了多种访问上下文存储的接口:一是我们的 SDK,更适合构建自有代理和工具的开发人员使用;二是我们的用户界面(UI),通过 Web 应用快速访问。

而本次演讲的重点是 MCP 和 CLI,这是一种让代理(如 ChatGPT 和 Claude)轻松访问这些数据的方式。我们通过这些接口主要暴露三方面的功能:第一,管理产品的核心资源,包括组织工作区和连接器;第二,安全地认证第三方服务,确保凭证不暴露给 AI 代理。

第三是“查询与行动”,即允许在上下文存储中搜索,并读取、写入或对所有系统采取行动。我们希望所有接口都能涵盖这三项能力。MCP 和 CLI 被设计为我们平台的薄统一接口,而非平台功能的重新实现。因此,这些接口的表层主要是展示性的:接收命令、调用平台并以特定格式返回结果。

这一切都建立在我们共享的平台和 API 之上。由于我们的平台每天变更超过十次,我们需要保持这些接口同步更新。因此,我们将开放 API 规范作为连接我们 API 的唯一事实来源,许多接口都是基于这些规范构建的。接下来让我们深入探讨这些接口,首先从 MCP 开始。

MCP 对于非技术用户非常友好。在我们的案例中,它非常适合那些希望轻松将数据直接接入 ChatGPT 或 Claude 的用户。它的优势在于打包精美且易于安装。例如,我们在 ChatGPT 官方应用商店中有 listings。对于 Anthropic(Claude 的开发商),我们也有相应的应用,目前仍在等待审批。

即使不在这些市场上架,终端用户安装起来也非常简单:只需提供托管的 MCP URL 即可快速连接。让我们看一个实际例子。这是我们的 MCP 正在运行:我询问有哪些可用的连接器,它回复说该工作区已设置四个连接器。接着我可以问关于数据的问题,例如“显示五个有 Zendesk 支持工单的付费客户”。

此时,MCP 正在查看所有连接器,并在上下文存储中搜索我要求的信息。然后它将 Zendesk 和 Stripe 中的数据结合起来为我生成答案。结果它交叉引用了这两个平台,返回了五个有未决支持工单的客户。这非常酷。接下来谈谈我们在构建 MCP 过程中的一些经验教训,如果您在做类似的事情,这些值得注意。

首先,经过反复试验,我们决定保持工具数量精简。在提取数据的执行部分,我们只暴露两个主要工具:一是描述连接器以获取查询信息的模式;二是“执行”操作,能够读取、写入和删除记录。相反的做法是为每个连接器和每种动作暴露一个 MCP 工具,但这会导致上下文过载。

我们发现保持接口狭窄、通过渐进式发现连接器所有可用能力,并严格限制注入工具描述中的上下文量,能显著提升 MCP 服务器的性能。第二点:OAuth 是王道。这指的是如何对 MCP 服务器本身进行身份验证。以 Anthropic 为例,点击连接按钮会启动我们的 OAuth 流程:您登录 Airbyte、接受权限设置即可。

虽然 MCP 规范支持其他认证机制,但如果您尝试使用 Bearer Token 等其他方式会非常困难。因为规范重度依赖 OAuth,且主要客户端都倾向于将 OAuth 作为安装连接器的更佳用户体验。例如在 Anthropic 的自定义连接器注册中,没有放置 Bearer Token 的方式,默认全部指向 OAuth。

重要的一点是,用户确实期望通过这些 MCP 服务器拥有长期存在的会话。与 Web 应用不同——在 Web 应用中,你可能可以接受偶尔重新登录几次——用户期望像使用移动应用一样使用 MCPs,他们不需要真正去思考身份验证的问题。它应该随时可用,只要用户想用它时就能用。因此,在实现 OAuth 时有其自身需要注意的一些细节:保持离线长期会话,这些会话存储在 Web 应用之外,并拥有自己的一套生命周期。

然而,“elicitation”(信息 elicitation)并不是我们要采用的方式,稍后我会详细解释什么是 elicitation。但我之前提到的 MCP 服务器的一大关键部分是:你需要能够连接你的工具,并且需要以安全的方式提供凭据。为此,基本上当你询问“我如何连接到 HubSpot?”时,我们会生成一个链接供你访问。该链接会弹出一个小部件,允许你选择想要连接的实体,并重定向到 HubSpot 以完成 OAuth 流程。

从技术上讲,MCP 规范中确实有一种允许你展示这类 URL 并处理这些输入的功能,称为 elicitation。这是一种向用户请求信息的方式。很长一段时间以来,只有表单模式的 elicitation 可用,但这不适用于 API 凭据等敏感数据。但最近出现了 URL 模式的 elicitation,这与我们正在做的非常相似。

问题在于 MCP 客户端之间的支持情况不一致。这是使用 MCP 时的一个主要主题:尽管有规范,但各个客户端支持的差异很大。例如在 Cloud Desktop 中,它告诉我:“嘿,我不支持 URL 模式的 elicitation,我做不到。”因此我们不得不回到自己实现的方式,而不使用规范中的这一部分。

好,让我们谈谈 CLI(命令行界面)。如果 MCPs 对这些非技术用户来说很容易使用,那么 CLI 则是更偏向技术的对应物。在某些方面它可能被称为功能更强,稍后我们会深入探讨这一点。这是我们之前看到的同一个例子,但这次使用的是 CLI 而不是 MCP。我们询问有哪些可用的连接器。

这一次加载了一个技能(skill),告知它有关 CLI 的信息,然后使用 CLI 本身来查询事物。我们得到了连接器列表。接着我们询问:“显示五个有 Zenes 支持工单的付费客户。”与上次一样,这次它能够使用 CLI 查询上下文存储、聚合数据并最终返回结果。

好的,所以这里有相同的五个用户。在 CLI 的设计方面,我认为需要记住的一个重要一点是:以代理(agent)为设计目标来构建 CLI。有些对用户有帮助的特性,可能会成为代理的障碍,稍后我们会更详细地讨论这一点。

我们要做的第一个设计决策是:输入 JSON,输出 JSON。对于人类用户来说,指定一堆标志(flags)来使用 CLI 是很好的;但当涉及到构建这些查询所需的复杂输入时,代理非常擅长处理 JSON。它们能够以比使用标志更直接、更少猜测的方式构建这些输入。

在输出方面,JSON 也使得处理较长响应变得容易,并能够利用 CLI 的关键功能之一:即 Unix 系统中的管道机制。你可以将输出重定向到不同的程序,例如使用 jq 从输出中选择特定内容并进行修剪。因此 JSON 真正解锁了大量此类能力。

一致性在这里非常关键。代理必须能够清楚地知道如何在你暴露的所有资源上执行操作。如果你以名词后跟动词的方式结构化 CLI(例如本例中的 connectors list、connectors create),那么在实现工作区 CLI 命令时,也必须遵循相同的格式。例如 workspace list 也是我们要做的之一。

因此,确保代理知道正确的操作方式至关重要,而且要让它们无需阅读大量文档就能轻松掌握正确的方式。另一个有趣的事实是:如果你看到代理反复犯同样的错误,多次调用错误的命令,那么考虑将这种“错误”转化为你的规范。让代理轻易地偶然发现正确的使用方式,因为这通常意味着在你的 API 或 CLI 规范中,某些内容并不像应有的那样直观。

第三点:技能(skills)随 CLI 工具一起分发。在进行 CLI 开发时,技能是必须的。否则,如果安装了 CLI,代理如何知道该 CLI 的存在呢?这极大地帮助了发现可用工具的过程。同时它们也有助于有效地使用该工具。

我们有一个被暴露的技能,它包含一个描述信息。当你询问任何关于连接器的问题时,它会触发该技能,知道要调用那个 CLI 并执行它。此外,你如何结构化这个技能也是需要考虑的事情。你不希望将过多的内容放入技能中,以免拖慢上下文加载速度。

技能具有分离为引用的能力,你可以拥有一个主技能指向技能目录中的其他文件,从而扩展不同命令的信息。这确实有助于减少加载的上下文量,并利用这些代理渐进式发现的能力。

另一个有趣的事实是:对于 OAuth 而言,在 MCP 方面很有趣;而在 CLI 方面则因其他原因显得有趣。我们无法假设人类会始终在场以与这些提示进行交互。当代理调用 CLI 时,它不能坐在那里等待你输入提示到 CLI 中。

你必须通过标志或凭据、环境变量和配置文件等方式暴露相关信息,以便在外部设置好这些内容。这样你在调用 CLI 时就不必等待那些交互过程了。

这是一个普遍现象,许多为人类设计的命令行界面(CLI)在用户输入命令后会等待提示符。这种方式并不适合代理程序使用,它们往往会因此卡住。那么 CLI 和 MCP(模型上下文协议)哪个更好呢?两者各有优劣。MCP 的规范并未得到统一实施,许多提供商在规范之外还自行设定了诸多限制。例如,Claude 对服务器和工具调用的描述长度限制了 2KB,同时也规定了工具的运行时长上限。因此,通过 MCP 执行更长时间的进程会比较困难,此外还有其他一些他们引入的限制条件。

另一方面,MCP 的亮点在于你可以拥有一个托管式的 MCP 服务器,它始终保持更新。而 CLI 则不同,你需要更多地考虑版本控制问题,随着新功能的不断推出,保持软件包更新变得更加困难。虽然 MCP 方面的限制可能会带来局限,但 CLI 在工具调用中没有这些约束,这有时会导致问题发生并陷入无限循环,使代理程序空转。

我认为两者都有其适用场景。MCP 非常适合非技术用户,能够快速进行原型设计、生成一次性报告,甚至利用 MCP 生态系统让不同工具之间相互协作。而 CLI 则更适合技术用户处理更长时间的运行任务,或者当输出内容超长时,可以利用 Unix 管道特性将重定向到文件、使用 tail 或 head 命令查看输出。正是在这些方面,CLI 开始展现出其优势。

好了,以上就是我分享的全部内容。我们在 Airbyte 的展位在那边(指向某处),欢迎大家去看看并加入我们团队工作。谢谢。